diff --git a/CHANGELOG.txt b/CHANGELOG.txt index cc4eb659b383cde47c28eace482ecb0711c48906..68c580e7738bf1217f4f4f594eefa713df9e340e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,6 @@ -// $Id: CHANGELOG.txt,v 1.369 2010/07/09 00:14:02 webchick Exp $ +// $Id: CHANGELOG.txt,v 1.371 2010/09/16 01:09:34 webchick Exp $ -Drupal 7.0 alpha 6, 2010-07-08 +Drupal 7.0 alpha 7, 2010-09-15 ---------------------- - Database: * Fully rewritten database layer utilizing PHP 5's PDO abstraction layer. diff --git a/INSTALL.sqlite.txt b/INSTALL.sqlite.txt index 5fdd09de4552c98e69586b2546738ab3aa00c5f4..92ccb1129633a8b7e562e9a3498d92a80594c316 100644 --- a/INSTALL.sqlite.txt +++ b/INSTALL.sqlite.txt @@ -1,4 +1,4 @@ -// $Id: INSTALL.sqlite.txt,v 1.2 2009/11/10 17:27:53 webchick Exp $ +// $Id: INSTALL.sqlite.txt,v 1.3 2010/09/01 02:39:57 dries Exp $ SQLITE REQUIREMENTS ------------------- @@ -12,7 +12,9 @@ SQLITE DATABASE CREATION The Drupal installer will create the SQLite database for you. The only requirement is the installer must have write permissions the directory where -the database file resides. +the database file resides. This directory (not just the database file) also has +to remain writeable by the web server going forward for SQLite to continue to be +able to operate. On the "Database configuration" form in the "Database name" field, you must supply the exact path to where you wish your database file to reside. It is @@ -31,4 +33,5 @@ file from downloading. USERNAME, PASSWORD, and ADVANCED OPTIONS ---------------------------------------- -No username, password, or advanced options are necessary and should not be used. +No username, password, or advanced options are necessary, and they should not be +used. diff --git a/INSTALL.txt b/INSTALL.txt index c2b97d6d27a4741ead0846dc1dfbfcc2c24975ce..3630823ae21a7ffe9e23d52d51d60b05dd3eca6f 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,4 +1,4 @@ -// $Id: INSTALL.txt,v 1.80 2010/04/30 08:09:21 dries Exp $ +// $Id: INSTALL.txt,v 1.82 2010/09/14 18:46:40 webchick Exp $ CONTENTS OF THIS FILE --------------------- @@ -16,11 +16,14 @@ REQUIREMENTS Drupal requires: - - a web server, Apache (version 2.0 or greater) is recommended, - - PHP 5 (5.2.0 or greater) (http://www.php.net/), - - and either MySQL (5.0.15 or greater) (http://www.mysql.com/), PostgreSQL (8.3 - or greater) (http://www.postgresql.org/), or SQLite (3.4.2 or greater) - (http://www.sqlite.org/). +- A web server. Apache (version 2.0 or greater) is recommended. +- PHP 5.2.0 (or greater) (http://www.php.net/). +- One of the following databases: + - MySQL 5.0.15 (or greater) (http://www.mysql.com/). + - MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully + compatible drop-in replacement for MySQL. + - PostgreSQL 8.3 (or greater) (http://www.postgresql.org/). + - SQLite 3.4.2 (or greater) (http://www.sqlite.org/). For more detailed information about Drupal requirements, including a list of PHP extensions and configurations that are required, see "System requirements" @@ -76,13 +79,15 @@ INSTALLATION http://drupal.org/project/translations and download the package. Extract the contents to the same directory where you extracted Drupal into. -2. CREATE THE CONFIGURATION FILE AND GRANT WRITE PERMISSIONS +2. IF NECESSARY, CREATE THE CONFIGURATION FILE AND GRANT WRITE PERMISSIONS Drupal comes with a default.settings.php file in the sites/default directory. The installer uses this file as a template to create your settings file using the details you provide through the install process. To avoid problems when upgrading, Drupal is not packaged with an actual - settings file. You must create a file named settings.php. You may do so + settings file. During installation, Drupal will try to create this settings + file automatically. If this fails (which it can due to different server + setups), you must create a file named settings.php yourself. You may do so by making a copy of default.settings.php (or create an empty file with this name in the same directory). For example, (from the installation directory) make a copy of the default.settings.php file with the command: diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index d15851129939d14dcd4eda9fe3c8041dcbf47275..d5088ae2bd614ffd634fcac70376da431d83ead0 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -1,4 +1,4 @@ -// $Id: MAINTAINERS.txt,v 1.42 2010/06/03 13:50:54 dries Exp $ +// $Id: MAINTAINERS.txt,v 1.46 2010/09/11 14:49:42 dries Exp $ Drupal core is maintained by the community. To participate, go to @@ -115,7 +115,7 @@ Update system - ? XML-RPC system -- ? +- Frederic G. Marand 'fgm' <http://drupal.org/user/27985> Topic coordinators @@ -123,6 +123,7 @@ Topic coordinators Accessibility - Everett Zufelt 'Everett Zufelt' <http://drupal.org/user/406552> +- Brandon Bowersox 'brandonojc' <http://drupal.org/user/186415> Documentation - Addison Berry 'add1sun' <http://drupal.org/user/65088> @@ -274,8 +275,11 @@ User module Theme maintainers ----------------- +Bartik theme +- Jen Simmons 'jensimmons' <http://drupal.org/user/140882> + Garland theme -- Stefan Nagtegaal 'steef' <http://drupal.org/user/612> +- ? Seven theme - ? diff --git a/UPGRADE.txt b/UPGRADE.txt index f9da7d532cf15373b912ee8545be5f0d6f150f24..97d76722271f0dedfe9b55cbdc4fafc032590397 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -1,4 +1,4 @@ -// $Id: UPGRADE.txt,v 1.22 2010/05/26 19:51:01 dries Exp $ +// $Id: UPGRADE.txt,v 1.24 2010/08/11 01:06:44 dries Exp $ UPGRADING --------- @@ -45,7 +45,8 @@ Let's begin! option is at http://www.example.com/?q=admin/config/development/maintenance (replace www.example.com with your installation's domain name and path). -4. If using a custom or contributed theme, switch to Garland. +4. If using a custom or contributed theme, switch to a core theme such as + Bartik or Garland. 5. Disable all custom and contributed modules. This includes any modules that are not listed under 'Core - required' or 'Core - optional' on diff --git a/includes/actions.inc b/includes/actions.inc index d7c625c8aecbbe0ca436d192d0f2dac467f18ce2..82c51662cf13a85efaadd012b0e02a1686ee5e0e 100644 --- a/includes/actions.inc +++ b/includes/actions.inc @@ -1,5 +1,5 @@ <?php -// $Id: actions.inc,v 1.38 2010/05/06 05:59:30 webchick Exp $ +// $Id: actions.inc,v 1.39 2010/08/22 11:04:09 dries Exp $ /** * @file @@ -292,7 +292,7 @@ function actions_synchronize($delete_orphans = FALSE) { 'label' => $array['label'], )) ->execute(); - watchdog('actions', "Action '%action' added.", array('%action' => filter_xss_admin($array['label']))); + watchdog('actions', "Action '%action' added.", array('%action' => $array['label'])); } } } @@ -305,7 +305,7 @@ function actions_synchronize($delete_orphans = FALSE) { $actions = db_query('SELECT aid, label FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll(); foreach ($actions as $action) { actions_delete($action->aid); - watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => filter_xss_admin($action->label))); + watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => $action->label)); } } else { diff --git a/includes/ajax.inc b/includes/ajax.inc index 74b4e73ddabbc7b30fff220f0fcd252d2a953783..37b3d9c597b4624871970ca5ad7fed3ee27c8f20 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -1,5 +1,5 @@ <?php -// $Id: ajax.inc,v 1.31 2010/04/30 08:07:54 dries Exp $ +// $Id: ajax.inc,v 1.32 2010/09/13 01:09:25 dries Exp $ /** * @file @@ -239,6 +239,12 @@ function ajax_get_form() { // Since some of the submit handlers are run, redirects need to be disabled. $form_state['no_redirect'] = TRUE; + // When a form is rebuilt after AJAX processing, its #build_id and #action + // should not change. + // @see drupal_rebuild_form() + $form_state['rebuild_info']['copy']['#build_id'] = TRUE; + $form_state['rebuild_info']['copy']['#action'] = TRUE; + // The form needs to be processed; prepare for that by setting a few internal // variables. $form_state['input'] = $_POST; @@ -263,18 +269,15 @@ function ajax_get_form() { * enhanced function. */ function ajax_form_callback() { - list($form, $form_state, $form_id, $form_build_id) = ajax_get_form(); - - // Build, validate and if possible, submit the form. - drupal_process_form($form_id, $form, $form_state); - - // This call recreates the form relying solely on the $form_state that - // drupal_process_form() set up. - $form = drupal_rebuild_form($form_id, $form_state, $form); - - // As part of drupal_process_form(), the element that triggered the form - // submission is determined, and in the case of AJAX, it might not be a - // button. This lets us route to the appropriate callback. + list($form, $form_state) = ajax_get_form(); + drupal_process_form($form['#form_id'], $form, $form_state); + + // We need to return the part of the form (or some other content) that needs + // to be re-rendered so the browser can update the page with changed content. + // Since this is the generic menu callback used by many AJAX elements, it is + // up to the #ajax['callback'] function of the element (may or may not be a + // button) that triggered the AJAX request to determine what needs to be + // rendered. if (!empty($form_state['triggering_element'])) { $callback = $form_state['triggering_element']['#ajax']['callback']; } diff --git a/includes/batch.inc b/includes/batch.inc index 36cfd1423953ea050f9d8b484619f1b4b7e9f8da..4f062758cc997daa0364565973d84052400ef5ac 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -1,5 +1,5 @@ <?php -// $Id: batch.inc,v 1.50 2010/02/17 22:44:51 webchick Exp $ +// $Id: batch.inc,v 1.51 2010/09/13 01:09:25 dries Exp $ /** @@ -495,9 +495,12 @@ function _batch_finished() { // Use drupal_redirect_form() to handle the redirection logic. drupal_redirect_form($_batch['form_state']); - // If no redirection happened, save the final $form_state value to be - // retrieved by drupal_get_form() and redirect to the originating page. - $_SESSION['batch_form_state'] = $_batch['form_state']; + // If no redirection happened, redirect to the originating page. In case the + // form needs to be rebuilt, save the final $form_state for + // drupal_build_form(). + if (!empty($_batch['form_state']['rebuild'])) { + $_SESSION['batch_form_state'] = $_batch['form_state']; + } $function = $_batch['redirect_callback']; if (function_exists($function)) { $function($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id']))); diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 61e61f3966bd0f7bbcecf01e71567f5ceca65d62..e23ac2fbe4ca749fc16d9e8e93a2b040ed5bee03 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1,5 +1,5 @@ <?php -// $Id: bootstrap.inc,v 1.407 2010/07/09 00:14:02 webchick Exp $ +// $Id: bootstrap.inc,v 1.418 2010/09/16 01:09:34 webchick Exp $ /** * @file @@ -9,7 +9,7 @@ /** * The current system version. */ -define('VERSION', '7.0-alpha6'); +define('VERSION', '7.0-alpha7'); /** * Core API compatibility. @@ -560,7 +560,7 @@ function drupal_settings_initialize() { global $base_url, $base_path, $base_root; // Export the following settings.php variables to the global namespace - global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url; + global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url; $conf = array(); if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) { @@ -1453,7 +1453,7 @@ function t($string, array $args = array(), array $options = array()) { case '%': default: // Escaped and placeholder. - $args[$key] = drupal_placeholder(array('text' => $value)); + $args[$key] = drupal_placeholder($value); break; case '!': @@ -1645,7 +1645,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO 'link' => $link, 'user' => $user, 'request_uri' => $base_root . request_uri(), - 'referer' => $_SERVER['HTTP_REFERER'], + 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', 'ip' => ip_address(), 'timestamp' => REQUEST_TIME, ); @@ -1689,7 +1689,7 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) { $_SESSION['messages'][$type][] = $message; } - // Mark this page has being not cacheable. + // Mark this page as being uncacheable. drupal_page_is_cacheable(FALSE); } @@ -2115,7 +2115,7 @@ function _drupal_bootstrap_page_cache() { // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. if (!isset($_COOKIE[session_name()]) && $cache_enabled) { - // Make sure there is a user object because it's timestamp will be + // Make sure there is a user object because its timestamp will be // checked, hook_boot might check for anonymous user etc. $user = drupal_anonymous_user(); // Get the page from the cache. @@ -2181,7 +2181,7 @@ function _drupal_bootstrap_database() { if (!isset($value['prefix'])) { $current_prefix = ''; } - else if (is_array($value['prefix'])) { + elseif (is_array($value['prefix'])) { $current_prefix = $value['prefix']['default']; } else { @@ -2327,15 +2327,15 @@ function get_t() { function drupal_language_initialize() { $types = language_types(); - // Ensure the language is correctly returned, even without multilanguage support. + // Ensure the language is correctly returned, even without multilanguage + // support. Also make sure we have a $language fallback, in case a language + // negotiation callback needs to do a full bootstrap. // Useful for eg. XML/HTML 'lang' attributes. - if (!drupal_multilingual()) { - $default = language_default(); - foreach ($types as $type) { - $GLOBALS[$type] = $default; - } + $default = language_default(); + foreach ($types as $type) { + $GLOBALS[$type] = $default; } - else { + if (drupal_multilingual()) { include_once DRUPAL_ROOT . '/includes/language.inc'; foreach ($types as $type) { $GLOBALS[$type] = language_initialize($type); @@ -2906,34 +2906,35 @@ function registry_update() { */ function &drupal_static($name, $default_value = NULL, $reset = FALSE) { static $data = array(), $default = array(); - if (!isset($name)) { - // All variables are reset. This needs to be done one at a time so that - // references returned by earlier invocations of drupal_static() also get - // reset. - foreach ($default as $name => $value) { - $data[$name] = $value; - } - // As the function returns a reference, the return should always be a - // variable. - return $data; - } - if ($reset) { - // The reset means the default is loaded. - if (array_key_exists($name, $default)) { + // First check if dealing with a previously defined static variable. + if (isset($data[$name]) || array_key_exists($name, $data)) { + // Non-NULL $name and both $data[$name] and $default[$name] statics exist. + if ($reset) { + // Reset pre-existing static variable to its default value. $data[$name] = $default[$name]; } - else { + return $data[$name]; + } + // Neither $data[$name] nor $default[$name] static variables exist. + if (isset($name)) { + if ($reset) { // Reset was called before a default is set and yet a variable must be // returned. return $data; } - } - elseif (!array_key_exists($name, $data)) { - // Store the default value internally and also copy it to the reference to - // be returned. + // First call with new non-NULL $name. Initialize a new static variable. $default[$name] = $data[$name] = $default_value; + return $data[$name]; } - return $data[$name]; + // Reset all: ($name == NULL). This needs to be done one at a time so that + // references returned by earlier invocations of drupal_static() also get + // reset. + foreach ($default as $name => $value) { + $data[$name] = $value; + } + // As the function returns a reference, the return should always be a + // variable. + return $data; } /** @@ -2957,38 +2958,36 @@ function drupal_is_cli() { * Formats text for emphasized display in a placeholder inside a sentence. * Used automatically by t(). * - * @param $variables - * An associative array containing: - * - text: The text to format (plain-text). + * @param $text + * The text to format (plain-text). * * @return * The formatted text (html). */ -function drupal_placeholder($variables) { - return '<em class="placeholder">' . check_plain($variables['text']) . '</em>'; +function drupal_placeholder($text) { + return '<em class="placeholder">' . check_plain($text) . '</em>'; } /** - * * Register a function for execution on shutdown. * - * Wrapper for register_shutdown_function() which catches thrown exceptions - * to avoid "Exception thrown without a stack frame in Unknown". + * Wrapper for register_shutdown_function() that catches thrown exceptions to + * avoid "Exception thrown without a stack frame in Unknown". * * @param $callback * The shutdown function to register. - * @param $parameters - * It is possible to pass parameters to the shutdown function by passing - * additional parameters. + * @param ... + * Additional arguments to pass to the shutdown function. + * * @return * Array of shutdown functions to be executed. * * @see register_shutdown_function() * @ingroup php_wrappers */ -function &drupal_register_shutdown_function($callback = NULL, $parameters = NULL) { - // We cannot use drupal_static() here because the static cache is reset - // during batch processing, which breaks batch handling. +function &drupal_register_shutdown_function($callback = NULL) { + // We cannot use drupal_static() here because the static cache is reset during + // batch processing, which breaks batch handling. static $callbacks = array(); if (isset($callback)) { diff --git a/includes/common.inc b/includes/common.inc index 0bae0379fd604157b9cd02c029ad9ec1b11d0f51..3069aff273e8f87d6bfc3634aa3ef8b0b6e6a16c 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1,5 +1,5 @@ <?php -// $Id: common.inc,v 1.1191 2010/07/07 17:00:42 webchick Exp $ +// $Id: common.inc,v 1.1220 2010/09/15 04:34:26 webchick Exp $ /** * @file @@ -741,6 +741,8 @@ function drupal_access_denied() { * A float representing the maximum number of seconds the function call * may take. The default is 30 seconds. If a timeout occurs, the error * code is set to the HTTP_REQUEST_TIMEOUT constant. + * - context + * A context resource created with stream_context_create(). * @return * An object which can have one or more of the following parameters: * - request @@ -791,21 +793,27 @@ function drupal_http_request($url, array $options = array()) { 'method' => 'GET', 'data' => NULL, 'max_redirects' => 3, - 'timeout' => 30, + 'timeout' => 30.0, + 'context' => NULL, ); + // stream_socket_client() requires timeout to be a float. + $options['timeout'] = (float) $options['timeout']; switch ($uri['scheme']) { case 'http': case 'feed': $port = isset($uri['port']) ? $uri['port'] : 80; - $host = $uri['host'] . ($port != 80 ? ':' . $port : ''); - $fp = @fsockopen($uri['host'], $port, $errno, $errstr, $options['timeout']); + $socket = 'tcp://' . $uri['host'] . ':' . $port; + // RFC 2616: "non-standard ports MUST, default ports MAY be included". + // We don't add the standard port to prevent from breaking rewrite rules + // checking the host that do not take into account the port number. + $options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : ''); break; case 'https': // Note: Only works when PHP is compiled with OpenSSL support. $port = isset($uri['port']) ? $uri['port'] : 443; - $host = $uri['host'] . ($port != 443 ? ':' . $port : ''); - $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, $options['timeout']); + $socket = 'ssl://' . $uri['host'] . ':' . $port; + $options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : ''); break; default: $result->error = 'invalid schema ' . $uri['scheme']; @@ -813,12 +821,20 @@ function drupal_http_request($url, array $options = array()) { return $result; } + if (empty($options['context'])) { + $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout']); + } + else { + // Create a stream with context. Allows verification of a SSL certificate. + $fp = @stream_socket_client($socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $options['context']); + } + // Make sure the socket opened properly. if (!$fp) { // When a network error occurs, we use a negative number so it does not // clash with the HTTP status codes. $result->code = -$errno; - $result->error = trim($errstr); + $result->error = trim($errstr) ? trim($errstr) : t('Error opening socket @socket', array('@socket' => $socket)); // Mark that this request failed. This will trigger a check of the web // server's ability to make outgoing HTTP requests the next time that @@ -840,11 +856,6 @@ function drupal_http_request($url, array $options = array()) { 'User-Agent' => 'Drupal (+http://drupal.org/)', ); - // RFC 2616: "non-standard ports MUST, default ports MAY be included". - // We don't add the standard port to prevent from breaking rewrite rules - // checking the host that do not take into account the port number. - $options['headers']['Host'] = $host; - // Only add Content-Length if we actually have any content or if it is a POST // or PUT request. Some non-standard servers get confused by Content-Length in // at least HEAD/GET requests, and Squid always requires Content-Length in @@ -876,8 +887,12 @@ function drupal_http_request($url, array $options = array()) { } $request .= "\r\n" . $options['data']; $result->request = $request; - - fwrite($fp, $request); + // Calculate how much time is left of the original timeout value. + $timeout = $options['timeout'] - timer_read(__FUNCTION__) / 1000; + if ($timeout > 0) { + stream_set_timeout($fp, floor($timeout), floor(1000000 * fmod($timeout, 1))); + fwrite($fp, $request); + } // Fetch response. Due to PHP bugs like http://bugs.php.net/bug.php?id=43782 // and http://bugs.php.net/bug.php?id=46049 we can't rely on feof(), but @@ -1340,6 +1355,8 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', ( <(?=[^a-zA-Z!/]) # a lone < | # or + <!--.*?--> # a comment + | # or <[^>]*(>|$) # a string that starts with a <, up until the > or the end of the string | # or > # just a > @@ -1378,7 +1395,7 @@ function _filter_xss_split($m, $store = FALSE) { return '<'; } - if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches)) { + if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) { // Seriously malformed return ''; } @@ -1386,12 +1403,21 @@ function _filter_xss_split($m, $store = FALSE) { $slash = trim($matches[1]); $elem = &$matches[2]; $attrlist = &$matches[3]; + $comment = &$matches[4]; + + if ($comment) { + $elem = '!--'; + } if (!isset($allowed_html[strtolower($elem)])) { // Disallowed HTML element return ''; } + if ($comment) { + return $comment; + } + if ($slash != '') { return "</$elem>"; } @@ -1551,8 +1577,8 @@ function filter_xss_bad_protocol($string, $decode = TRUE) { * Arbitrary elements may be added using the $args associative array. */ function format_rss_channel($title, $link, $description, $items, $langcode = NULL, $args = array()) { - global $language; - $langcode = $langcode ? $langcode : $language->language; + global $language_content; + $langcode = $langcode ? $langcode : $language_content->language; $output = "<channel>\n"; $output .= ' <title>' . check_plain($title) . "</title>\n"; @@ -1997,7 +2023,7 @@ function format_username($account) { * - 'external': Whether the given path is an external URL. * - 'language': An optional language object. If the path being linked to is * internal to the site, $options['language'] is used to look up the alias - * for the URL. If $options['language'] is omitted, the global $language + * for the URL. If $options['language'] is omitted, the global $language_url * will be used. * - 'https': Whether this URL should point to a secure location. If not * defined, the current scheme is used, so the user stays on http or https @@ -2049,7 +2075,7 @@ function url($path = NULL, array $options = array()) { // Allow other modules to alter the outbound URL and options. drupal_alter('url_outbound', $path, $options, $original_path); - if ($options['fragment']) { + if (isset($options['fragment']) && $options['fragment'] !== '') { $options['fragment'] = '#' . $options['fragment']; } @@ -2149,7 +2175,7 @@ function url($path = NULL, array $options = array()) { * @param $path * The internal path or external URL being linked to, such as "node/34" or * "http://example.com/foo". - * @return + * @return * Boolean TRUE or FALSE, where TRUE indicates an external path. */ function url_is_external($path) { @@ -2292,29 +2318,29 @@ function l($text, $path, array $options = array()) { } /** - * Deliver a page callback result to the browser in the format appropriate. + * Delivers a page callback result to the browser in the appropriate format. * * This function is most commonly called by menu_execute_active_handler(), but * can also be called by error conditions such as drupal_not_found(), * drupal_access_denied(), and drupal_site_offline(). * - * When a user requests a page, index.php calls menu_execute_active_handler() + * When a user requests a page, index.php calls menu_execute_active_handler(), * which calls the 'page callback' function registered in hook_menu(). The page * callback function can return one of: - * - NULL: to indicate no content - * - an integer menu status constant: to indicate an error condition - * - a string of HTML content - * - a renderable array of content + * - NULL: to indicate no content. + * - An integer menu status constant: to indicate an error condition. + * - A string of HTML content. + * - A renderable array of content. * Returning a renderable array rather than a string of HTML is preferred, * because that provides modules with more flexibility in customizing the final * result. * * When the page callback returns its constructed content to - * menu_execute_active_handler(), this functions gets called. The purpose of + * menu_execute_active_handler(), this function gets called. The purpose of * this function is to determine the most appropriate 'delivery callback' * function to route the content to. The delivery callback function then * sends the content to the browser in the needed format. The default delivery - * callback is drupal_deliver_html_page() which delivers the content as an HTML + * callback is drupal_deliver_html_page(), which delivers the content as an HTML * page, complete with blocks in addition to the content. This default can be * overridden on a per menu router item basis by setting 'delivery callback' in * hook_menu() or hook_menu_alter(), and can also be overridden on a per request @@ -2330,6 +2356,16 @@ function l($text, $path, array $options = array()) { * they should not issue any "print" or "echo" statements, but instead just * return content. * + * Also note that this function does not perform access checks. The delivery + * callback function specified in hook_menu(), hook_menu_alter(), or + * hook_page_delivery_callback_alter() will be called even if the router item + * access checks fail. This is intentional (it is needed for JSON and other + * purposes), but it has security implications. Do not call this function + * directly unless you understand the security implications, and be careful in + * writing delivery callbacks, so that they do not violate security. See + * drupal_deliver_html_page() for an example of a delivery callback that + * respects security. + * * @param $page_callback_result * The result of a page callback. Can be one of: * - NULL: to indicate no content. @@ -2362,7 +2398,7 @@ function drupal_deliver_page($page_callback_result, $default_delivery_callback = // If a delivery callback is specified, but doesn't exist as a function, // something is wrong, but don't print anything, since it's not known // what format the response needs to be in. - watchdog('delivery callback not found', check_plain($delivery_callback) . ': ' . check_plain($_GET['q']), NULL, WATCHDOG_ERROR); + watchdog('delivery callback not found', 'callback %callback not found: %q.', array('%callback' => $delivery_callback, '%q' => $_GET['q']), WATCHDOG_ERROR); } } @@ -2395,7 +2431,7 @@ function drupal_deliver_html_page($page_callback_result) { // Print a 404 page. drupal_add_http_header('Status', '404 Not Found'); - watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); + watchdog('page not found', 'page not found: %q.', array('%q' => $_GET['q']), WATCHDOG_WARNING); // Keep old path for reference, and to allow forms to redirect to it. if (!isset($_GET['destination'])) { @@ -2424,7 +2460,7 @@ function drupal_deliver_html_page($page_callback_result) { case MENU_ACCESS_DENIED: // Print a 403 page. drupal_add_http_header('Status', '403 Forbidden'); - watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); + watchdog('access denied', 'access denied: %q', array('%q' => $_GET['q']), WATCHDOG_WARNING); // Keep old path for reference, and to allow forms to redirect to it. if (!isset($_GET['destination'])) { @@ -2642,27 +2678,26 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * Calling drupal_static_reset('drupal_add_css') will clear all cascading * stylesheets added so far. * - * If preprocessing is turned on, the cascading style sheets added using this - * function will be preprocessed before they are added to the HTML header of the - * page. Preprocessing merges all the CSS files into one file, which is then - * compressed by removing all extraneous white space. Note that preprocessed - * inline stylesheets will not be aggregated into this single file; instead, - * they will just be compressed when being output on the page. External - * stylesheets will also not be aggregated. + * If CSS aggregation/compression is enabled, all cascading style sheets added + * with $options['preprocess'] set to TRUE will be merged into one aggregate + * file and compressed by removing all extraneous white space. + * Preprocessed inline stylesheets will not be aggregated into this single file; + * instead, they are just compressed upon output on the page. Externally hosted + * stylesheets are never aggregated or compressed. * - * The reason for merging the CSS files is outlined quite thoroughly here: + * The reason for aggregating the files is outlined quite thoroughly here: * http://www.die.net/musings/page_load_time/ "Load fewer external objects. Due * to request overhead, one bigger file just loads faster than two smaller ones * half its size." * - * However, you should *not* preprocess every file as this can lead to redundant - * caches. You should set $options['preprocess'] to FALSE when your styles are - * only used on a few pages of the site. This could be a special admin page, the - * homepage, or a handful of pages that does not represent the majority of the - * pages on your site. + * $options['preprocess'] should be only set to TRUE when a file is required for + * all typical visitors and most pages of a site. It is critical that all + * preprocessed files are added unconditionally on every page, even if the + * files do not happen to be needed on a page. This is normally done by calling + * drupal_add_css() in a hook_init() implementation. * - * Typical candidates for preprocessing are for example styles for nodes across - * the site, or styles used in the theme. + * Non-preprocessed files should only be added to the page when they are + * actually needed. * * @param $data * (optional) The stylesheet data to be added, depending on what is passed @@ -2710,9 +2745,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * always come last. * - 'media': The media type for the stylesheet, e.g., all, print, screen. * Defaults to 'all'. - * - 'preprocess': If TRUE, Allows the CSS to be aggregated and compressed if - * the Optimize CSS feature has been turned on under the performance - * section. Defaults to TRUE. + * - 'preprocess': If TRUE and CSS aggregation/compression is enabled, the + * styles will be aggregated and compressed. Defaults to FALSE. * - 'browsers': An array containing information specifying which browsers * should load the CSS item. See drupal_pre_render_conditional_comments() * for details. @@ -2740,7 +2774,7 @@ function drupal_add_css($data = NULL, $options = NULL) { 'type' => 'file', 'weight' => CSS_DEFAULT, 'media' => 'all', - 'preprocess' => TRUE, + 'preprocess' => FALSE, 'data' => $data, 'browsers' => array(), ); @@ -2777,7 +2811,7 @@ function drupal_add_css($data = NULL, $options = NULL) { * module styles through CSS selectors. * * Themes may replace module-defined CSS files by adding a stylesheet with the - * same filename. For example, themes/garland/system-menus.css would replace + * same filename. For example, themes/bartik/system-menus.css would replace * modules/system/system-menus.css. This allows themes to override complete * CSS files, rather than specific selectors, when necessary. * @@ -3197,7 +3231,7 @@ function drupal_build_css_cache($css) { $base = base_path() . dirname($stylesheet['data']) . '/'; _drupal_build_css_path(NULL, $base); // Prefix all paths within this CSS file, ignoring external and absolute paths. - $data .= preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $contents); + $data .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents); } } @@ -3317,7 +3351,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { // Regexp to match single quoted strings. $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'"; $contents = preg_replace( - "<($double_quot|$single_quot)|$comment>Sus", // Strip all comment blocks + "<($double_quot|$single_quot)|$comment>Ss", // Strip all comment blocks "$1", // but keep double/single $contents); // quoted strings. $contents = preg_replace( @@ -3329,7 +3363,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { // Replaces @import commands with the actual stylesheet content. // This happens recursively but omits external files. - $contents = preg_replace_callback('/@import\s*(?:url\()?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\)?;/', '_drupal_load_stylesheet', $contents); + $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents); return $contents; } @@ -3353,7 +3387,7 @@ function _drupal_load_stylesheet($matches) { // Alter all internal url() paths. Leave external paths alone. We don't need // to normalize absolute paths here (i.e. remove folder/... segments) because // that will be done later. - return preg_replace('/url\s*\(([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file); + return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file); } /** @@ -3576,6 +3610,25 @@ function drupal_region_class($region) { * Calling drupal_static_reset('drupal_add_js') will clear all JavaScript added * so far. * + * If JavaScript aggregation is enabled, all JavaScript files added with + * $options['preprocess'] set to TRUE will be merged into one aggregate file. + * Preprocessed inline JavaScript will not be aggregated into this single file. + * Externally hosted JavaScripts are never aggregated. + * + * The reason for aggregating the files is outlined quite thoroughly here: + * http://www.die.net/musings/page_load_time/ "Load fewer external objects. Due + * to request overhead, one bigger file just loads faster than two smaller ones + * half its size." + * + * $options['preprocess'] should be only set to TRUE when a file is required for + * all typical visitors and most pages of a site. It is critical that all + * preprocessed files are added unconditionally on every page, even if the + * files are not needed on a page. This is normally done by calling + * drupal_add_css() in a hook_init() implementation. + * + * Non-preprocessed files should only be added to the page when they are + * actually needed. + * * @param $data * (optional) If given, the value depends on the $options parameter: * - 'file': Path to the file relative to base_path(). @@ -3617,9 +3670,8 @@ function drupal_region_class($region) { * - cache: If set to FALSE, the JavaScript file is loaded anew on every page * call; in other words, it is not cached. Used only when 'type' references * a JavaScript file. Defaults to TRUE. - * - preprocess: Aggregate the JavaScript if the JavaScript optimization - * setting has been toggled in admin/config/development/performance. Note - * that JavaScript of type 'external' is not aggregated. Defaults to TRUE. + * - preprocess: If TRUE and JavaScript aggregation is enabled, the script + * file will be aggregated. Defaults to FALSE. * * @return * The current array of JavaScript files, settings, and in-line code, @@ -3712,7 +3764,7 @@ function drupal_js_defaults($data = NULL) { 'scope' => 'header', 'cache' => TRUE, 'defer' => FALSE, - 'preprocess' => TRUE, + 'preprocess' => FALSE, 'version' => NULL, 'data' => $data, ); @@ -4546,18 +4598,24 @@ function _drupal_bootstrap_full() { // Running inside the simpletest child site, log fatal errors to test // specific file directory. ini_set('log_errors', 1); - ini_set('error_log', file_directory_path() . '/error.log'); + ini_set('error_log', 'public://error.log'); } // Initialize $_GET['q'] prior to invoking hook_init(). drupal_path_initialize(); - // Set a custom theme for the current page, if there is one. We need to run - // this before invoking hook_init(), since any modules which initialize the - // theme system will prevent a custom theme from being correctly set later. - menu_set_custom_theme(); - // Let all modules take action before menu system handles the request + + // Let all modules take action before the menu system handles the request. // We do not want this while running update.php. if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { + // Prior to invoking hook_init(), initialize the theme (potentially a custom + // one for this page), so that: + // - Modules with hook_init() implementations that call theme() or + // theme_get_registry() don't initialize the incorrect theme. + // - The theme can have hook_*_alter() implementations affect page building + // (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()), + // ahead of when rendering starts. + menu_set_custom_theme(); + drupal_theme_initialize(); module_invoke_all('init'); } } @@ -5342,9 +5400,12 @@ function drupal_render_cid_parts($granularity = NULL) { global $theme, $base_root, $user; $cid_parts[] = $theme; - if (module_exists('locale')) { - global $language; - $cid_parts[] = $language->language; + // If Locale is enabled but we have only one language we do not need it as cid + // part. + if (drupal_multilingual()) { + foreach (language_types_configurable() as $language_type) { + $cid_parts[] = $GLOBALS[$language_type]->language; + } } if (!empty($granularity)) { @@ -5404,6 +5465,15 @@ function element_sort($a, $b) { return ($a_weight < $b_weight) ? -1 : 1; } +/** + * Array sorting callback; sorts elements by title. + */ +function element_sort_by_title($a, $b) { + $a_title = (is_array($a) && isset($a['#title'])) ? $a['#title'] : ''; + $b_title = (is_array($b) && isset($b['#title'])) ? $b['#title'] : ''; + return strnatcasecmp($a_title, $b_title); +} + /** * Retrieve the default properties for the defined element type. */ @@ -5478,7 +5548,7 @@ function element_children(&$elements, $sort = FALSE) { $children = array(); $sortable = FALSE; foreach ($elements as $key => $value) { - if ($key[0] !== '#') { + if ($key === '' || $key[0] !== '#') { $children[$key] = $value; if (is_array($value) && isset($value['#weight'])) { $sortable = TRUE; @@ -5531,6 +5601,180 @@ function element_get_visible_children(array $elements) { return array_keys($visible_children); } +/** + * Sets a value in a nested array with variable depth. + * + * This helper function should be used when the depth of the array element you + * are changing may vary (that is, the number of parent keys is variable). It + * is primarily used for form structures and renderable arrays. + * + * Example: + * @code + * // Assume you have a 'signature' element somewhere in a form. It might be: + * $form['signature_settings']['signature'] = array( + * '#type' => 'text_format', + * '#title' => t('Signature'), + * ); + * // Or, it might be further nested: + * $form['signature_settings']['user']['signature'] = array( + * '#type' => 'text_format', + * '#title' => t('Signature'), + * ); + * @endcode + * + * To deal with the situation, the code needs to figure out the route to the + * element, given an array of parents that is either + * @code array('signature_settings', 'signature') @endcode in the first case or + * @code array('signature_settings', 'user', 'signature') @endcode in the second + * case. + * + * Without this helper function the only way to set the signature element in one + * line would be using eval(), which should be avoided: + * @code + * // Do not do this! Avoid eval(). + * eval('$form[\'' . implode("']['", $parents) . '\'] = $element;'); + * @endcode + * + * Instead, use this helper function: + * @code + * drupal_array_set_nested_value($form, $parents, $element); + * @endcode + * + * However if the number of array parent keys is static, the value should always + * be set directly rather than calling this function. For instance, for the + * first example we could just do: + * @code + * $form['signature_settings']['signature'] = $element; + * @endcode + * + * @param $array + * A reference to the array to modify. + * @param $parents + * An array of parent keys, starting with the outermost key. + * @param $value + * The value to set. + * + * @see drupal_array_get_nested_value() + */ +function drupal_array_set_nested_value(array &$array, array $parents, $value) { + $ref = &$array; + foreach ($parents as $parent) { + // Note that PHP is fine with referencing a not existing array key - in this + // case it just creates an entry with NULL as value. + $ref = &$ref[$parent]; + } + $ref = $value; +} + +/** + * Retrieves a value from a nested array with variable depth. + * + * This helper function should be used when the depth of the array element being + * retrieved may vary (that is, the number of parent keys is variable). It is + * primarily used for form structures and renderable arrays. + * + * Without this helper function the only way to get a nested array value with + * variable depth in one line would be using eval(), which should be avoided: + * @code + * // Do not do this! Avoid eval(). + * // May also throw a PHP notice, if the variable array keys do not exist. + * eval('$value = $array[\'' . implode("']['", $parents) . "'];"); + * @endcode + * + * Instead, use this helper function: + * @code + * $value = drupal_array_get_nested_value($form, $parents); + * @endcode + * + * The return value will be NULL, regardless of whether the actual value is NULL + * or whether the requested key does not exist. If it is required to know + * whether the nested array key actually exists, pass a third argument that is + * altered by reference: + * @code + * $key_exists = NULL; + * $value = drupal_array_get_nested_value($form, $parents, $key_exists); + * if ($key_exists) { + * // ... do something with $value ... + * } + * @endcode + * + * However if the number of array parent keys is static, the value should always + * be retrieved directly rather than calling this function. For instance: + * @code + * $value = $form['signature_settings']['signature']; + * @endcode + * + * @param $array + * The array from which to get the value. + * @param $parents + * An array of parent keys of the value, starting with the outermost key. + * @param &$key_exists + * (optional) If given, an already defined variable that is altered by + * reference. + * + * @return + * The requested nested value. Possibly NULL if the value is NULL or not all + * nested parent keys exist. $key_exists is altered by reference and is a + * Boolean that indicates whether all nested parent keys exist (TRUE) or not + * (FALSE). This allows to distinguish between the two possibilities when NULL + * is returned. + * + * @see drupal_array_set_nested_value() + */ +function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) { + $ref = &$array; + foreach ($parents as $parent) { + // array_key_exists() is slower than isset() and triggers notices if the + // second argument is not an array, so only call it when absolutely + // necessary. + if (isset($ref[$parent]) || (is_array($ref) && array_key_exists($parent, $ref))) { + $ref = &$ref[$parent]; + } + else { + $key_exists = FALSE; + return NULL; + } + } + $key_exists = TRUE; + return $ref; +} + +/** + * Determines whether a nested array with variable depth contains all of the requested keys. + * + * This helper function should be used when the depth of the array element to be + * checked may vary (that is, the number of parent keys is variable). See + * drupal_array_set_nested_value() for details. It is primarily used for form + * structures and renderable arrays. + * + * If it is required to also get the value of the checked nested key, use + * drupal_array_get_nested_value() instead. + * + * If the number of array parent keys is static, this helper function is + * unnecessary and the following code can be used instead: + * @code + * $value_exists = isset($form['signature_settings']['signature']); + * $key_exists = array_key_exists('signature', $form['signature_settings']); + * @endcode + * + * @param $array + * The array with the value to check for. + * @param $parents + * An array of parent keys of the value, starting with the outermost key. + * + * @return + * TRUE if all the parent keys exist, FALSE otherwise. + * + * @see drupal_array_get_nested_value() + */ +function drupal_array_nested_key_exists(array $array, array $parents) { + // Although this function is similar to PHP's array_key_exists(), its + // arguments should be consistent with drupal_array_get_nested_value(). + $key_exists = NULL; + drupal_array_get_nested_value($array, $parents, $key_exists); + return $key_exists; +} + /** * Provide theme registration for themes across .inc files. */ @@ -5822,7 +6066,7 @@ function drupal_get_schema_unprocessed($module, $table = NULL) { if (!is_null($table) && isset($schema[$table])) { return $schema[$table]; } - else if (!empty($schema)) { + elseif (!empty($schema)) { return $schema; } return array(); @@ -5883,7 +6127,7 @@ function drupal_schema_fields_sql($table, $prefix = NULL) { * @param $table * The name of the table; this must be defined by a hook_schema() * implementation. - * @param $object + * @param $record * An object or array representing the record to write, passed in by * reference. The function will fill in defaults from the schema and add an * ID value to serial fields. @@ -5897,10 +6141,10 @@ function drupal_schema_fields_sql($table, $prefix = NULL) { * Failure to write a record will return FALSE. Otherwise SAVED_NEW or * SAVED_UPDATED is returned depending on the operation performed. The $object * parameter will contain values for any serial fields defined by the $table. - * For example, $object->nid or $object['nid'] will be populated after + * For example, $record->nid or $record['nid'] will be populated after * inserting a new a new node. */ -function drupal_write_record($table, &$object, $primary_keys = array()) { +function drupal_write_record($table, &$record, $primary_keys = array()) { // Standardize $primary_keys to an array. if (is_string($primary_keys)) { $primary_keys = array($primary_keys); @@ -5911,19 +6155,10 @@ function drupal_write_record($table, &$object, $primary_keys = array()) { return FALSE; } - // Convert to an object if needed. - if (is_array($object)) { - $object = (object) $object; - $array = TRUE; - } - else { - $array = FALSE; - } - + $object = (object) $record; $fields = array(); - // Go through our schema, build SQL, and when inserting, fill in defaults for - // fields that are not set. + // Go through the schema to determine fields to write. foreach ($schema['fields'] as $field => $info) { if ($info['type'] == 'serial') { // Skip serial types if we are updating. @@ -5935,24 +6170,24 @@ function drupal_write_record($table, &$object, $primary_keys = array()) { $serial = $field; } + // Skip field if it is in $primary_keys as it is unnecessary to update a + // field to the value it is already set to. + if (in_array($field, $primary_keys)) { + continue; + } + if (!property_exists($object, $field)) { - // Skip fields that are not provided, unless we are inserting and a - // default value is provided by the schema. - if (!empty($primary_keys) || !isset($info['default'])) { - continue; - } - $object->$field = $info['default']; + // Skip fields that are not provided, default values are already known + // by the database. + continue; } // Build array of fields to update or insert. if (empty($info['serialize'])) { $fields[$field] = $object->$field; } - elseif (isset($object->$field)) { - $fields[$field] = serialize($object->$field); - } else { - $fields[$field] = ''; + $fields[$field] = serialize($object->$field); } // Type cast to proper datatype, except when the value is NULL and the @@ -5975,11 +6210,6 @@ function drupal_write_record($table, &$object, $primary_keys = array()) { } if (empty($fields)) { - // No changes requested. - // If we began with an array, convert back so we don't surprise the caller. - if ($array) { - $object = (array) $object; - } return; } @@ -6031,9 +6261,18 @@ function drupal_write_record($table, &$object, $primary_keys = array()) { $return = FALSE; } - // If we began with an array, convert back so we don't surprise the caller. - if ($array) { - $object = (array) $object; + // If we are inserting, populate empty fields with default values. + if (empty($primary_keys)) { + foreach ($schema['fields'] as $field => $info) { + if (isset($info['default']) && !property_exists($object, $field)) { + $object->$field = $info['default']; + } + } + } + + // If we began with an array, convert back. + if (is_array($record)) { + $record = (array) $object; } return $return; @@ -6068,7 +6307,7 @@ function drupal_write_record($table, &$object, $primary_keys = array()) { * - stylesheets: Theme stylesheets eg: stylesheets[all][] = my-style.css * - scripts: Theme scripts eg: scripts[] = my-script.css * - * @see garland.info + * @see bartik.info * * @param $filename * The file we are parsing. Accepts file with relative or absolute path. @@ -6274,6 +6513,11 @@ function drupal_flush_all_caches() { foreach ($cache_tables as $table) { cache_clear_all('*', $table, TRUE); } + + // Rebuild the bootstrap module list. We do this here so that developers + // can get new hook_boot() implementations registered without having to + // write a hook_update_N() function. + _system_update_bootstrap_status(); } /** @@ -6445,6 +6689,14 @@ function entity_get_info($entity_type = NULL) { if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label'])); } + // Prepare entity schema fields SQL info for + // DrupalEntityControllerInterface::buildQuery(). + if (isset($entity_info[$name]['base table'])) { + $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']); + if (isset($entity_info[$name]['revision table'])) { + $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']); + } + } } // Let other modules alter the entity info. drupal_alter('entity_info', $entity_info); @@ -6667,6 +6919,30 @@ function entity_uri($entity_type, $entity) { return $entity->uri ? $entity->uri : NULL; } +/** + * Returns the label of an entity. + * + * @param $entity_type + * The entity type; e.g. 'node' or 'user'. + * @param $entity + * The entity for which to generate a path. + * + * @return + * A string with the entity label (e.g. node title), or FALSE if not found. + */ +function entity_label($entity_type, $entity) { + $label = FALSE; + $info = entity_get_info($entity_type); + if (isset($info['label callback']) && function_exists($info['label callback'])) { + $label = $info['label callback']($entity); + } + elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) { + $label = $entity->{$info['entity keys']['label']}; + } + + return $label; +} + /** * Helper function for attaching field API validation to entity forms. */ @@ -6695,7 +6971,7 @@ function entity_form_field_validate($entity_type, $form, &$form_state) { * For some entity forms (e.g., forms with complex non-field data and forms that * simultaneously edit multiple entities), this behavior may be inappropriate, * so the #builder_function for such forms needs to implement the required - * functionality instead of calling this function. + * functionality instead of calling this function. */ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) { $info = entity_get_info($entity_type); @@ -6718,16 +6994,22 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st /** * Performs one or more XML-RPC request(s). * + * Usage example: + * @code + * $result = xmlrpc('http://example.com/xmlrpc.php', array( + * 'service.methodName' => array($parameter, $second, $third), + * )); + * @endcode + * * @param $url * An absolute URL of the XML-RPC endpoint. - * Example: - * http://www.example.com/xmlrpc.php - * @param ... - * For one request: - * The method name followed by a variable number of arguments to the method. - * For multiple requests (system.multicall): - * An array of call arrays. Each call array follows the pattern of the single - * request: method name followed by the arguments to the method. + * @param $args + * An associative array whose keys are the methods to call and whose values + * are the arguments to pass to the respective method. If multiple methods + * are specified, a system.multicall is performed. + * @param $options + * (optional) An array of options to pass along to drupal_http_request(). + * * @return * For one request: * Either the return value of the method on success, or FALSE. @@ -6737,10 +7019,9 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st * returned by the method called, or an xmlrpc_error object if the call * failed. See xmlrpc_error(). */ -function xmlrpc($url) { +function xmlrpc($url, $args, $options = array()) { require_once DRUPAL_ROOT . '/includes/xmlrpc.inc'; - $args = func_get_args(); - return call_user_func_array('_xmlrpc', $args); + return _xmlrpc($url, $args, $options); } /** @@ -6769,6 +7050,27 @@ function archiver_get_info() { return $archiver_info; } +/** + * Returns a string of supported archive extensions. + * + * @return + * A space-separated string of extensions suitable for use by the file + * validation system. + */ +function archiver_get_extensions() { + $valid_extensions = array(); + foreach (archiver_get_info() as $archive) { + foreach ($archive['extensions'] as $extension) { + foreach (explode('.', $extension) as $part) { + if (!in_array($part, $valid_extensions)) { + $valid_extensions[] = $part; + } + } + } + } + return implode(' ', $valid_extensions); +} + /** * Create the appropriate archiver for the specified file. * diff --git a/includes/database/database.inc b/includes/database/database.inc index 5dc7a9367b9167ab9432d93ee636006b0f2d7346..0b8b95d5bb4541f947cea3c6fe6ee138cee70da5 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -1,5 +1,5 @@ <?php -// $Id: database.inc,v 1.126 2010/06/28 19:57:34 dries Exp $ +// $Id: database.inc,v 1.135 2010/09/11 21:14:31 webchick Exp $ /** * @file @@ -537,6 +537,8 @@ abstract class DatabaseConnection extends PDO { * query builder and should not be set by a user. If there is an error, * this method will return NULL and may throw an exception if * $options['throw_exception'] is TRUE. + * + * @throws PDOException */ public function query($query, array $args = array(), $options = array()) { @@ -646,7 +648,7 @@ abstract class DatabaseConnection extends PDO { * @return string * The name of the class that should be used for this driver. */ - protected function getDriverClass($class) { + public function getDriverClass($class) { if (empty($this->driverClasses[$class])) { $this->driverClasses[$class] = $class . '_' . $this->driver(); if (!class_exists($this->driverClasses[$class])) { @@ -807,6 +809,20 @@ abstract class DatabaseConnection extends PDO { return preg_replace('/[^A-Za-z0-9_.]+/', '', $field); } + /** + * Escapes an alias name string. + * + * Force all alias names to be strictly alphanumeric-plus-underscore. In + * contrast to DatabaseConnection::escapeField() / + * DatabaseConnection::escapeTable(), this doesn't allow the period (".") + * + * @return + * The sanitized field name string. + */ + public function escapeAlias($field) { + return preg_replace('/[^A-Za-z0-9_]+/', '', $field); + } + /** * Escapes characters that work as wildcard characters in a LIKE pattern. * @@ -875,9 +891,14 @@ abstract class DatabaseConnection extends PDO { * The name of the savepoint. The default, 'drupal_transaction', will roll * the entire transaction back. * + * @throws DatabaseTransactionNoActiveException + * * @see DatabaseTransaction::rollback() */ public function rollback($savepoint_name = 'drupal_transaction') { + if (!$this->supportsTransactions()) { + return; + } if (!$this->inTransaction()) { throw new DatabaseTransactionNoActiveException(); } @@ -901,9 +922,7 @@ abstract class DatabaseConnection extends PDO { return; } } - if ($this->supportsTransactions()) { - parent::rollBack(); - } + parent::rollBack(); } /** @@ -911,6 +930,8 @@ abstract class DatabaseConnection extends PDO { * * If no transaction is already active, we begin a new transaction. * + * @throws DatabaseTransactionNameNonUniqueException + * * @see DatabaseTransaction */ public function pushTransaction($name) { @@ -941,6 +962,9 @@ abstract class DatabaseConnection extends PDO { * @param $name * The name of the savepoint * + * @throws DatabaseTransactionNoActiveException + * @throws DatabaseTransactionCommitFailedException + * * @see DatabaseTransaction */ public function popTransaction($name) { @@ -1038,6 +1062,11 @@ abstract class DatabaseConnection extends PDO { */ abstract public function driver(); + /** + * Returns the version of the database server. + */ + abstract public function version(); + /** * Determines if this driver supports transactions. * @@ -1093,6 +1122,8 @@ abstract class DatabaseConnection extends PDO { * A direct commit bypasses all of the safety checks we've built on top of * PDO's transaction routines. * + * @throws DatabaseTransactionExplicitCommitNotAllowedException + * * @see DatabaseTransaction */ public function commit() { @@ -1340,7 +1371,7 @@ abstract class Database { 'default' => '', ); } - else if (!is_array($database_info[$index][$target]['prefix'])) { + elseif (!is_array($database_info[$index][$target]['prefix'])) { // Transform the flat form into an array form. $database_info[$index][$target]['prefix'] = array( 'default' => $database_info[$index][$target]['prefix'], @@ -1467,6 +1498,9 @@ abstract class Database { * "default". * @param $target * The database target to open. + * + * @throws DatabaseConnectionNotDefinedException + * @throws DatabaseDriverNotSpecifiedException */ final protected static function openConnection($key, $target) { if (empty(self::$databaseInfo)) { @@ -2118,8 +2152,16 @@ function db_autoload($class) { foreach ($driver_files as $file => $classes) { if (in_array($base, $classes)) { - require_once "{$base_path}/{$driver}/{$file}"; - return; + $filename = "{$base_path}/{$driver}/{$file}"; + // We might end up looking in a file that doesn't exist, so check that. + if (file_exists($filename)) { + require_once $filename; + // If the class now exists, we're done. Otherwise keep searching in + // additional files. + if (class_exists($class, FALSE) || interface_exists($class, FALSE)) { + return; + } + } } } } diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index b74035ab601748eda2c02efde5c1393432d38d6c..f937378fe6a0d762a76203c2bbad8ca10004174f 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -1,5 +1,5 @@ <?php -// $Id: database.inc,v 1.31 2010/06/30 16:55:49 webchick Exp $ +// $Id: database.inc,v 1.33 2010/08/11 02:17:39 dries Exp $ /** * @file @@ -47,8 +47,15 @@ class DatabaseConnection_mysql extends DatabaseConnection { PDO::ATTR_CASE => PDO::CASE_LOWER, )); - // Force MySQL to use the UTF-8 character set by default. - $this->exec('SET NAMES "utf8"'); + // Force MySQL to use the UTF-8 character set. Also set the collation, if a + // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci' + // for UTF-8. + if (!empty($connection_options['collation'])) { + $this->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']); + } + else { + $this->exec('SET NAMES utf8'); + } // Force MySQL's behavior to conform more closely to SQL standards. // This allows Drupal to run almost seamlessly on many different @@ -72,6 +79,11 @@ class DatabaseConnection_mysql extends DatabaseConnection { return 'mysql'; } + public function version() { + $data = $this->query('SHOW variables LIKE :name', array(':name' => 'version'))->fetchAssoc(); + return $data['value']; + } + public function databaseType() { return 'mysql'; } diff --git a/includes/database/mysql/install.inc b/includes/database/mysql/install.inc index a861d269b4b3c4c5fe9d5a5fb387ca035de650f9..64bf8a1a024d5a87b4030b1f40e7157fbd971de5 100644 --- a/includes/database/mysql/install.inc +++ b/includes/database/mysql/install.inc @@ -1,18 +1,28 @@ <?php -// $Id: install.inc,v 1.3 2009/07/27 19:42:56 dries Exp $ +// $Id: install.inc,v 1.4 2010/07/30 01:59:14 dries Exp $ /** * @file * Installation code for MySQL embedded database engine. */ - -// MySQL specific install functions - +/** + * Specifies installation tasks for MySQL and equivalent databases. + */ class DatabaseTasks_mysql extends DatabaseTasks { + + /** + * The PDO driver name for MySQL and equivalent databases. + * + * @var string + */ protected $pdoDriver = 'mysql'; + + /** + * Returns a human-readable name string for MySQL and equivalent databases. + */ public function name() { - return 'MySQL'; + return 'MySQL, MariaDB, or equivalent'; } } diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index 6c5595acdd8fdb41887133041cd445f132a8ee70..3a6815528d794606ca9d0eebde69601a3565a720 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.38 2010/06/28 19:57:34 dries Exp $ +// $Id: schema.inc,v 1.41 2010/08/02 18:55:17 webchick Exp $ /** * @file @@ -30,16 +30,19 @@ class DatabaseSchema_mysql extends DatabaseSchema { * @return * A keyed array with information about the database, table name and prefix. */ - protected function getPrefixInfo($table = 'default') { + protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) { $info = array('prefix' => $this->connection->tablePrefix($table)); - if (($pos = strpos($info['prefix'], '.')) !== FALSE) { - $info['database'] = substr($info['prefix'], 0, $pos); - $info['table'] = substr($info['prefix'], ++$pos) . $table; + if ($add_prefix) { + $table = $info['prefix'] . $table; + } + if (($pos = strpos($table, '.')) !== FALSE) { + $info['database'] = substr($table, 0, $pos); + $info['table'] = substr($table, ++$pos); } else { $db_info = Database::getConnectionInfo(); $info['database'] = $db_info['default']['database']; - $info['table'] = $info['prefix'] . $table; + $info['table'] = $table; } return $info; } @@ -52,10 +55,10 @@ class DatabaseSchema_mysql extends DatabaseSchema { * database as the schema unless specified otherwise, and exclude table_catalog * from the condition criteria. */ - protected function buildTableNameCondition($table_name, $operator = '=') { + protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) { $info = $this->connection->getConnectionOptions(); - $table_info = $this->getPrefixInfo($table_name); + $table_info = $this->getPrefixInfo($table_name, $add_prefix); $condition = new DatabaseCondition('AND'); $condition->condition('table_schema', $table_info['database']); @@ -74,10 +77,12 @@ class DatabaseSchema_mysql extends DatabaseSchema { * An array of SQL statements to create the table. */ protected function createTableSql($name, $table) { - // Provide some defaults if needed + $info = $this->connection->getConnectionOptions(); + + // Provide defaults if needed. $table += array( 'mysql_engine' => 'InnoDB', - 'mysql_character_set' => 'UTF8', + 'mysql_character_set' => 'utf8', ); $sql = "CREATE TABLE {" . $name . "} (\n"; @@ -97,6 +102,13 @@ class DatabaseSchema_mysql extends DatabaseSchema { $sql = substr($sql, 0, -3) . "\n) "; $sql .= 'ENGINE = ' . $table['mysql_engine'] . ' DEFAULT CHARACTER SET ' . $table['mysql_character_set']; + // By default, MySQL uses the default collation for new tables, which is + // 'utf8_general_ci' for utf8. If an alternate collation has been set, it + // needs to be explicitly specified. + // @see DatabaseConnection_mysql + if (!empty($info['collation'])) { + $sql .= ' COLLATE ' . $info['collation']; + } // Add table comment. if (!empty($table['description'])) { @@ -224,12 +236,6 @@ class DatabaseSchema_mysql extends DatabaseSchema { 'blob:big' => 'LONGBLOB', 'blob:normal' => 'BLOB', - - 'date:normal' => 'DATE', - - 'datetime:normal' => 'DATETIME', - - 'time:normal' => 'TIME', ); return $map; } diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc index eab334e33e3cd555f5b71c0e5bee20342d2c46fd..dbb32867148d2242c75303795e7663066dbbc833 100644 --- a/includes/database/pgsql/database.inc +++ b/includes/database/pgsql/database.inc @@ -1,5 +1,5 @@ <?php -// $Id: database.inc,v 1.39 2010/06/16 04:56:07 dries Exp $ +// $Id: database.inc,v 1.41 2010/08/11 02:17:39 dries Exp $ /** * @file @@ -33,7 +33,7 @@ class DatabaseConnection_pgsql extends DatabaseConnection { // PostgreSQL in trust mode doesn't require a password to be supplied. if (empty($connection_options['password'])) { - $connection_options['password'] = null; + $connection_options['password'] = NULL; } $this->connectionOptions = $connection_options; @@ -126,6 +126,10 @@ class DatabaseConnection_pgsql extends DatabaseConnection { return 'pgsql'; } + public function version() { + return $this->query('SHOW SERVER_VERSION')->fetchField(); + } + public function databaseType() { return 'pgsql'; } diff --git a/includes/database/pgsql/install.inc b/includes/database/pgsql/install.inc index 5d07d9dae47b48f09315e631386ed4df4c118017..72322ef794f08a97b1adaa7495298dba1f7cc862 100644 --- a/includes/database/pgsql/install.inc +++ b/includes/database/pgsql/install.inc @@ -1,5 +1,5 @@ <?php -// $Id: install.inc,v 1.9 2010/06/21 01:32:21 webchick Exp $ +// $Id: install.inc,v 1.10 2010/08/16 21:01:23 dries Exp $ /** * @file @@ -57,29 +57,18 @@ class DatabaseTasks_pgsql extends DatabaseTasks { /** * Check PHP version. * - * There is a bug in PHP versions < 5.2.11 and < 5.3.1 that prevents - * PostgreSQL from inserting integer values into numeric columns that exceed - * the PHP_INT_MAX value (value varies dependant on 32 or 64 bit CPU). + * There are two bugs in PDO_pgsql affecting Drupal: + * + * - in versions < 5.2.7, PDO_pgsql refuses to insert an empty string into + * a NOT NULL BLOB column. See: http://bugs.php.net/bug.php?id=46249 + * - in versions < 5.2.11 and < 5.3.1 that prevents inserting integer values + * into numeric columns that exceed the PHP_INT_MAX value. + * See: http://bugs.php.net/bug.php?id=48924 */ function checkPHPVersion() { - try { - $txn = db_transaction(); - db_query("CREATE TABLE test_php_version ( test_int INT NOT NULL )"); - // See http://bugs.php.net/bug.php?id=48924 as to why this query may - // fail. The error will throw an Exception so there is no need to test to - // see if the row inserted or not. - db_query("INSERT INTO test_php_version ( test_int ) VALUES (:big_int)", array(':big_int' => PHP_INT_MAX + 1)); - db_query("DROP TABLE test_php_version"); - $this->pass(st('PHP is at the correct version to run on PostgreSQL.')); - } - catch (Exception $e) { - // Failing is not fatal but the user should still be warned of the - // limitations of running PostgreSQL on the current version of PHP. - $text = 'The version of PHP you are using has known issues with PostgreSQL. You can see more at '; - $text .= l('http://drupal.org/node/515310', 'http://drupal.org/node/515310') . '. '; - $text .= 'We suggest you upgrade PHP to 5.2.11, 5.3.1 or greater. Failing to do so may result in serious data corruption later.'; - drupal_set_message(st($text), 'warning'); - } + if (!version_compare(PHP_VERSION, '5.2.11', '>=') || (version_compare(PHP_VERSION, '5.3.0', '>=') && !version_compare(PHP_VERSION, '5.3.1', '>='))) { + $this->fail(st('The version of PHP you are using has known issues with PostgreSQL. You need to upgrade PHP to 5.2.11, 5.3.1 or greater.')); + }; } /** diff --git a/includes/database/pgsql/query.inc b/includes/database/pgsql/query.inc index dd2d6345e5f6c44113f20aa80b426bafd4d60c21..a1c6e0932e83efab79fab99ec14513a2e21c60a0 100644 --- a/includes/database/pgsql/query.inc +++ b/includes/database/pgsql/query.inc @@ -1,5 +1,5 @@ <?php -// $Id: query.inc,v 1.18 2010/05/15 07:04:21 dries Exp $ +// $Id: query.inc,v 1.22 2010/09/01 01:43:50 webchick Exp $ /** * @ingroup database @@ -27,7 +27,7 @@ class InsertQuery_pgsql extends InsertQuery { $max_placeholder = 0; $blobs = array(); $blob_count = 0; - foreach ($this->insertValues as &$insert_values) { + foreach ($this->insertValues as $insert_values) { foreach ($this->insertFields as $idx => $field) { if (isset($table_information->blob_fields[$field])) { $blobs[$blob_count] = fopen('php://memory', 'a'); @@ -43,10 +43,37 @@ class InsertQuery_pgsql extends InsertQuery { $stmt->bindParam(':db_insert_placeholder_' . $max_placeholder++, $insert_values[$idx]); } } + // Check if values for a serial field has been passed. + if (!empty($table_information->serial_fields)) { + foreach ($table_information->serial_fields as $index => $serial_field) { + $serial_key = array_search($serial_field, $this->insertFields); + if ($serial_key !== FALSE) { + $serial_value = $insert_values[$serial_key]; + + // Force $last_insert_id to the specified value. This is only done + // if $index is 0. + if ($index == 0) { + $last_insert_id = $serial_value; + } + // Set the sequence to the bigger value of either the passed + // value or the max value of the column. It can happen that another + // thread calls nextval() which could lead to a serial number being + // used twice. However, trying to insert a value into a serial + // column should only be done in very rare cases and is not thread + // safe by definition. + $this->connection->query("SELECT setval('" . $table_information->sequences[$index] . "', GREATEST(MAX(" . $serial_field . "), :serial_value)) FROM {" . $this->table . "}", array(':serial_value' => (int)$serial_value)); + } + } + } } if (!empty($this->fromQuery)) { - foreach ($this->fromQuery->getArguments() as $key => $value) { - $stmt->bindParam($key, $value); + // bindParam stores only a reference to the variable that is followed when + // the statement is executed. We pass $arguments[$key] instead of $value + // because the second argument to bindParam is passed by reference and + // the foreach statement assigns the element to the existing reference. + $arguments = $this->fromQuery->getArguments(); + foreach ($arguments as $key => $value) { + $stmt->bindParam($key, $arguments[$key]); } } @@ -62,7 +89,13 @@ class InsertQuery_pgsql extends InsertQuery { elseif ($options['return'] == Database::RETURN_INSERT_ID) { $options['return'] = Database::RETURN_NULL; } - $last_insert_id = $this->connection->query($stmt, array(), $options); + // Only use the returned last_insert_id if it is not already set. + if (!empty($last_insert_id)) { + $this->connection->query($stmt, array(), $options); + } + else { + $last_insert_id = $this->connection->query($stmt, array(), $options); + } // Re-initialize the values array so that we can re-use this query. $this->insertValues = array(); @@ -138,13 +171,13 @@ class UpdateQuery_pgsql extends UpdateQuery { // We assume that an expression will never happen on a BLOB field, // which is a fairly safe assumption to make since in most cases // it would be an invalid query anyway. - $stmt->bindParam($placeholder, $argument); + $stmt->bindParam($placeholder, $data['arguments'][$placeholder]); } } unset($fields[$field]); } - foreach ($fields as $field => &$value) { + foreach ($fields as $field => $value) { $placeholder = ':db_update_placeholder_' . ($max_placeholder++); if (isset($table_information->blob_fields[$field])) { @@ -155,7 +188,7 @@ class UpdateQuery_pgsql extends UpdateQuery { ++$blob_count; } else { - $stmt->bindParam($placeholder, $value); + $stmt->bindParam($placeholder, $fields[$field]); } } @@ -163,8 +196,8 @@ class UpdateQuery_pgsql extends UpdateQuery { $this->condition->compile($this->connection, $this); $arguments = $this->condition->arguments(); - foreach ($arguments as $placeholder => &$value) { - $stmt->bindParam($placeholder, $value); + foreach ($arguments as $placeholder => $value) { + $stmt->bindParam($placeholder, $arguments[$placeholder]); } } diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc index 44dccdc3a68e4c41476a38c999b95a235ea8aee9..ca923a50160c6169aed7923e5faa1b4a92493961 100644 --- a/includes/database/pgsql/schema.inc +++ b/includes/database/pgsql/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.34 2010/04/07 15:07:59 dries Exp $ +// $Id: schema.inc,v 1.37 2010/09/01 01:28:41 webchick Exp $ /** * @file @@ -66,6 +66,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { // return the last insert id. If there is more than 1 sequences the // first one (index 0 of the sequences array) will be used. $table_information->sequences[] = $matches[1]; + $table_information->serial_fields[] = $column->column_name; } } $this->tableInformation[$key] = $table_information; @@ -246,12 +247,6 @@ class DatabaseSchema_pgsql extends DatabaseSchema { 'blob:big' => 'bytea', 'blob:normal' => 'bytea', - 'date:normal' => 'date', - - 'datetime:normal' => 'timestamp without time zone', - - 'time:normal' => 'time without time zone', - 'serial:tiny' => 'serial', 'serial:small' => 'serial', 'serial:medium' => 'serial', @@ -475,17 +470,30 @@ class DatabaseSchema_pgsql extends DatabaseSchema { if (in_array($typecast, array('serial', 'bigserial', 'numeric'))) { $typecast = 'int'; } + $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $typecast . ' USING "' . $field . '"::' . $typecast); - - $this->connection->query("ALTER TABLE {" . $table . "} ALTER $field SET $field_new = CAST(" . $field . "_old as " . $typecast . ")"); - - $this->addField($table, "$field_new", $spec); - - $this->dropField($table, $field . '_old'); + // Map type definition to the PostgreSQL type. + $pgsql_type = $map[$spec['type'] . ':' . $spec['size']]; + if (in_array($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. + $seq = "{" . $table . "}_" . $field_new . "_seq"; + $this->connection->query("CREATE SEQUENCE " . $seq); + // Set sequence to maximal field value to not conflict with existing + // entries. + $this->connection->query("SELECT setval('" . $seq . "', MAX(\"" . $field . '")) FROM {' . $table . "}"); + $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" SET DEFAULT nextval(\'' . $seq . '\')'); + } // Rename the column if necessary. if ($field != $field_new) { - $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '_old"'); + $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '"'); + } + + // Change description if necessary. + if (!empty($spec['description'])) { + $this->connection->query('COMMENT ON COLUMN {' . $table . '}."' . $field_new . '" IS ' . $this->prepareComment($spec['description'])); } if (isset($new_keys)) { diff --git a/includes/database/query.inc b/includes/database/query.inc index 87a836e272316308bbe63bc4b7955e382a1a8229..f87ff09f8c7d1786ab3f2926f44702f841d5fb10 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -1,5 +1,5 @@ <?php -// $Id: query.inc,v 1.52 2010/06/26 01:40:05 dries Exp $ +// $Id: query.inc,v 1.55 2010/08/05 08:26:35 webchick Exp $ /** * @ingroup database @@ -486,9 +486,18 @@ class InsertQuery extends Query { // we wrap it in a transaction so that it is atomic where possible. On many // databases, such as SQLite, this is also a notable performance boost. $transaction = $this->connection->startTransaction(); - $sql = (string) $this; - foreach ($this->insertValues as $insert_values) { - $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions); + + try { + $sql = (string) $this; + foreach ($this->insertValues as $insert_values) { + $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions); + } + } + catch (Exception $e) { + // One of the INSERTs failed, rollback the whole batch. + $transaction->rollback(); + // Rethrow the exception for the calling code. + throw $e; } // Re-initialize the values array so that we can re-use this query. @@ -526,6 +535,9 @@ class InsertQuery extends Query { * * @return * TRUE if the validation was successful, FALSE if not. + * + * @throws FieldsOverlapException + * @throws NoFieldsException */ public function preExecute() { // Confirm that the user did not try to specify an identical @@ -773,6 +785,8 @@ class MergeQuery extends Query { * * @return * TRUE if the validation was successful, FALSE if not. + * + * @throws InvalidMergeQueryException */ public function preExecute() { @@ -791,6 +805,8 @@ class MergeQuery extends Query { * A status indicating the executed operation: * - MergeQuery::STATUS_INSERT for an INSERT operation. * - MergeQuery::STATUS_UPDATE for an UPDATE operation. + * + * @throws InvalidMergeQueryException */ public function execute() { // If validation fails, simply return NULL. @@ -1312,6 +1328,9 @@ class DatabaseCondition implements QueryConditionInterface, Countable { $condition['value']->compile($connection, $queryPlaceholder); $placeholders[] = (string) $condition['value']; $arguments += $condition['value']->arguments(); + // Subqueries are the actual value of the operator, we don't + // need to add another below. + $operator['use_value'] = FALSE; } // We assume that if there is a delimiter, then the value is an // array. If not, it is a scalar. For simplicity, we first convert diff --git a/includes/database/schema.inc b/includes/database/schema.inc index 7b8086d01ebd90168e36520923720aac01958387..8b3a1f99a62e49d22da122beadac3a390fac4445 100644 --- a/includes/database/schema.inc +++ b/includes/database/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.36 2010/06/28 19:57:34 dries Exp $ +// $Id: schema.inc,v 1.39 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -75,10 +75,11 @@ * - 'unique keys': An associative array of unique keys ('keyname' => * specification). Each specification is an array of one or more * key column specifiers (see below) that form a unique key on the table. - * - 'foreign keys': An associative array, each key references a column - * of the local table, each value is an array with a single key pair as - * 'tablename' => 'column' where 'column' is the foreign column to - * reference. + * - 'foreign keys': An associative array of relations ('my_relation' => + * specification). Each specification is an array containing the name of + * the referenced table ('table'), and an array of column mappings + * ('columns'). Column mappings are defined by key pairs ('source_column' => + * 'referenced_column'). * - 'indexes': An associative array of indexes ('indexname' => * specification). Each specification is an array of one or more * key column specifiers (see below) that form an index on the @@ -131,8 +132,14 @@ * 'vid' => array('vid'), * ), * 'foreign keys' => array( - * 'vid' => array('node_revision' => 'vid'), - * 'uid' => array('users' => 'uid'), + * 'node_revision' => array( + * 'table' => 'node_revision', + * 'columns' => array('vid' => 'vid'), + * ), + * 'node_author' => array( + * 'table' => 'users', + * 'columns' => array('uid' => 'uid'), + * ), * ), * 'primary key' => array('nid'), * ); @@ -175,26 +182,32 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * @param * Name of table to look prefix up for. Defaults to 'default' because thats * default key for prefix. + * @param $add_prefix + * Boolean that indicates whether the given table name should be prefixed. + * * @return * A keyed array with information about the schema, table name and prefix. */ - protected function getPrefixInfo($table = 'default') { + protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) { $info = array( 'schema' => $this->defaultSchema, 'prefix' => $this->connection->tablePrefix($table), ); + if ($add_prefix) { + $table = $info['prefix'] . $table; + } // If the prefix contains a period in it, then that means the prefix also // contains a schema reference in which case we will change the schema key // to the value before the period in the prefix. Everything after the dot // will be prefixed onto the front of the table. - if ($pos = strpos($info['prefix'], '.') !== FALSE) { + if (($pos = strpos($table, '.')) !== FALSE) { // Grab everything before the period. - $info['schema'] = substr($info['prefix'], 0, $pos); - // Grab everything after the dot, and prefix on to the table. - $info['table'] = substr($info['prefix'], ++$pos) . $table; + $info['schema'] = substr($table, 0, $pos); + // Grab everything after the dot. + $info['table'] = substr($table, ++$pos); } else { - $info['table'] = $info['prefix'] . $table; + $info['table'] = $table; } return $info; } @@ -230,15 +243,17 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The name of the table in question. * @param $operator * The operator to apply on the 'table' part of the condition. + * @param $add_prefix + * Boolean to indicate whether the table name needs to be prefixed. * * @return QueryConditionInterface * A DatabaseCondition object. */ - protected function buildTableNameCondition($table_name, $operator = '=') { + protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) { $info = $this->connection->getConnectionOptions(); // Retrive the table name and schema - $table_info = $this->getPrefixInfo($table_name); + $table_info = $this->getPrefixInfo($table_name, $add_prefix); $condition = new DatabaseCondition('AND'); $condition->condition('table_catalog', $info['database']); @@ -278,7 +293,8 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * Array, both the keys and the values are the matching tables. */ public function findTables($table_expression) { - $condition = $this->buildTableNameCondition($table_expression, 'LIKE'); + $condition = $this->buildTableNameCondition($table_expression, 'LIKE', FALSE); + $condition->compile($this->connection, $this); // Normally, we would heartily discourage the use of string // concatenation for conditionals like this however, we diff --git a/includes/database/select.inc b/includes/database/select.inc index 950c2f132cf091892e1b516686b44961186fd9e5..5a6021263dcb3fbe77d35ac18332ca8f93b3bc69 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -1,5 +1,5 @@ <?php -// $Id: select.inc,v 1.42 2010/06/01 09:24:09 dries Exp $ +// $Id: select.inc,v 1.48 2010/09/03 19:06:55 dries Exp $ /** * @ingroup database @@ -93,6 +93,24 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn */ public function &getOrderBy(); + /** + * Returns a reference to the group-by array for this query. + * + * Because this method returns by reference, alter hooks may edit the group-by + * array directly to make their changes. If just adding additional grouping + * fields, however, the use of groupBy() is preferred. + * + * Note that this method must be called by reference as well: + * + * @code + * $fields =& $query->getGroupBy(); + * @endcode + * + * @return + * A reference to the group-by array structure. + */ + public function &getGroupBy(); + /** * Returns a reference to the tables array for this query. * @@ -601,11 +619,8 @@ class SelectQueryExtender implements SelectQueryInterface { /* Implementations of QueryExtendableInterface. */ public function extend($extender_name) { - $override_class = $this->connection->driver(); - if (class_exists($override_class)) { - $extender_name = $override_class; - } - return new $extender_name($this, $this->connection); + $class = $this->connection->getDriverClass($extender_name); + return new $class($this, $this->connection); } /* Alter accessors to expose the query data to alter hooks. */ @@ -622,6 +637,10 @@ class SelectQueryExtender implements SelectQueryInterface { return $this->query->getOrderBy(); } + public function &getGroupBy() { + return $this->query->getGroupBy(); + } + public function &getTables() { return $this->query->getTables(); } @@ -1029,6 +1048,10 @@ class SelectQuery extends Query implements SelectQueryInterface { return $this->order; } + public function &getGroupBy() { + return $this->group; + } + public function &getTables() { return $this->tables; } @@ -1102,7 +1125,21 @@ class SelectQuery extends Query implements SelectQueryInterface { } drupal_alter($hooks, $query); } - return $this->prepared = TRUE; + + $this->prepared = TRUE; + + // Now also prepare any sub-queries. + foreach ($this->tables as $table) { + if ($table['table'] instanceof SelectQueryInterface) { + $table['table']->preExecute(); + } + } + + foreach ($this->union as $union) { + $union['query']->preExecute(); + } + + return $this->prepared; } public function execute() { @@ -1128,14 +1165,14 @@ class SelectQuery extends Query implements SelectQueryInterface { } // If that's already in use, try the table name and field name. - if (!empty($this->tables[$alias])) { + if (!empty($this->fields[$alias])) { $alias = $table_alias . '_' . $field; } // If that is already used, just add a counter until we find an unused alias. $alias_candidate = $alias; $count = 2; - while (!empty($this->tables[$alias_candidate])) { + while (!empty($this->fields[$alias_candidate])) { $alias_candidate = $alias . '_' . $count++; } $alias = $alias_candidate; @@ -1282,13 +1319,24 @@ class SelectQuery extends Query implements SelectQueryInterface { // Create our new query object that we will mutate into a count query. $count = clone($this); + $group_by = array_keys($count->getGroupBy()); + if (!$count->distinct) { // When not executing a distinct query, we can zero-out existing fields - // and expressions. + // and expressions that are not used by a GROUP BY. Fields listed in + // the GROUP BY clause need to be present in the query. $fields =& $count->getFields(); - $fields = array(); + foreach (array_keys($fields) as $field) { + if (!empty($group_by[$field])) { + unset($fields[$field]); + } + } $expressions =& $count->getExpressions(); - $expressions = array(); + foreach (array_keys($expressions) as $field) { + if (!empty($group_by[$field])) { + unset($fields[$field]); + } + } // Also remove 'all_fields' statements, which are expanded into tablename.* // when the query is executed. @@ -1297,22 +1345,25 @@ class SelectQuery extends Query implements SelectQueryInterface { } } + // If we've just removed all fields from the query, make sure there is at + // least one so that the query still runs. + $count->addExpression('1'); + // Ordering a count query is a waste of cycles, and breaks on some // databases anyway. $orders = &$count->getOrderBy(); $orders = array(); - if ($count->distinct) { - // If the query is distinct, we need to execute it in a subquery, - // because SQL99 does not support counting on distinct multiple fields. - $count = db_select($count); + if ($count->distinct && !empty($group_by)) { + // If the query is distinct and contains a GROUP BY, we need to remove the + // distinct because SQL99 does not support counting on distinct multiple fields. $count->distinct = FALSE; } - // COUNT() is an expression, so we add that back in. - $count->addExpression('COUNT(*)'); + $query = db_select($count); + $query->addExpression('COUNT(*)'); - return $count; + return $query; } public function __toString() { @@ -1374,6 +1425,13 @@ class SelectQuery extends Query implements SelectQueryInterface { // WHERE if (count($this->where)) { + // The following line will not generate placeholders correctly if there + // is a subquery. Fortunately, it is also called from getArguments() first + // so it's not a problem in practice... unless you try to call __toString() + // before calling getArguments(). That is a problem that we will have to + // fix in Drupal 8, because it requires more refactoring than we are + // able to do in Drupal 7. + // @todo Move away from __toString() For SelectQuery compilation at least. $this->where->compile($this->connection, $this); // There is an implicit string cast on $this->condition. $query .= "\nWHERE " . $this->where; diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index 107d0506eb3e74ff0e6046756a169af1a2eebcbc..e6f2408477366ee5c637b0d5a9a64ebd31827c2f 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -1,5 +1,5 @@ <?php -// $Id: database.inc,v 1.33 2010/07/04 02:26:10 webchick Exp $ +// $Id: database.inc,v 1.37 2010/08/27 15:45:46 webchick Exp $ /** * @file @@ -47,6 +47,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection { parent::__construct('sqlite:' . $connection_options['database'], '', '', array( // Force column names to lower case. PDO::ATTR_CASE => PDO::CASE_LOWER, + // Convert numeric values to strings when fetching. + PDO::ATTR_STRINGIFY_FETCHES => TRUE, )); $this->exec('PRAGMA encoding="UTF-8"'); @@ -111,6 +113,10 @@ class DatabaseConnection_sqlite extends DatabaseConnection { * SQLite compatibility implementation for the SUBSTRING_INDEX() SQL function. */ public function sqlFunctionSubstringIndex($string, $delimiter, $count) { + // If string is empty, simply return an empty string. + if (empty($string)) { + return ''; + } $end = 0; for ($i = 0; $i < $count; $i++) { $end = strpos($string, $delimiter, $end + 1); @@ -163,8 +169,12 @@ class DatabaseConnection_sqlite extends DatabaseConnection { } public function queryTemporary($query, array $args = array(), array $options = array()) { + // Generate a new temporary table name and protect it from prefixing. + // SQLite requires that temporary tables to be non-qualified. $tablename = $this->generateTemporaryTableName(); - $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} AS SELECT', $query), $args, $options); + $this->prefixes[$tablename] = ''; + + $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); return $tablename; } @@ -172,6 +182,10 @@ class DatabaseConnection_sqlite extends DatabaseConnection { return 'sqlite'; } + public function version() { + return $this->query('SELECT sqlite_version()')->fetchField(); + } + public function databaseType() { return 'sqlite'; } @@ -207,7 +221,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection { } // The transaction gets committed when the transaction object gets destroyed // because it gets out of scope. - return (int) $this->query('SELECT value FROM {sequences}')->fetchField(); + return $this->query('SELECT value FROM {sequences}')->fetchField(); } public function rollback($savepoint_name = 'drupal_transaction') { diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc index 432ebd1e7886e85bd369cf4f0a083f8571da75dd..13881e0edd9dde8732c9f72b18a34bbd925011ad 100644 --- a/includes/database/sqlite/schema.inc +++ b/includes/database/sqlite/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.17 2010/04/28 20:25:21 dries Exp $ +// $Id: schema.inc,v 1.19 2010/07/28 10:52:12 dries Exp $ /** * @file @@ -20,8 +20,10 @@ class DatabaseSchema_sqlite extends DatabaseSchema { protected $defaultSchema = 'main'; public function tableExists($table) { + $info = $this->getPrefixInfo($table); + // Don't use {} around sqlite_master table. - return (bool) $this->connection->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '{" . $table . "}'", array(), array())->fetchField(); + return (bool) $this->connection->query('SELECT 1 FROM ' . $info['schema'] . '.sqlite_master WHERE type = :type AND name = :name', array(':type' => 'table', ':name' => $info['table']))->fetchField(); } public function fieldExists($table, $column) { @@ -53,16 +55,12 @@ class DatabaseSchema_sqlite extends DatabaseSchema { $info = $this->getPrefixInfo($tablename); if (!empty($schema['unique keys'])) { foreach ($schema['unique keys'] as $key => $fields) { - // Normally we don't escape double quotes (we use single quotes) but - // describing the index name like this is faster and is readable. - $index = "\"{$info['schema']}\".\"{$info['table']}_$key\""; - $sql[] = 'CREATE UNIQUE INDEX ' . $index . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n"; + $sql[] = 'CREATE UNIQUE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n"; } } if (!empty($schema['indexes'])) { foreach ($schema['indexes'] as $key => $fields) { - $index = "\"{$info['schema']}\".\"{$info['table']}_$key\""; - $sql[] = 'CREATE INDEX ' . $index . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n"; + $sql[] = 'CREATE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n"; } } return $sql; @@ -218,12 +216,6 @@ class DatabaseSchema_sqlite extends DatabaseSchema { 'blob:big' => 'BLOB', 'blob:normal' => 'BLOB', - - 'date:normal' => 'DATE', - - 'time:normal' => 'TIME', - - 'datetime:normal' => 'TIMESTAMP', ); return $map; } @@ -296,10 +288,14 @@ class DatabaseSchema_sqlite extends DatabaseSchema { * * @param $table * Name of the table to be altered. + * @param $old_schema + * The old schema array for the table. * @param $new_schema * The new schema array for the table. + * @param $mapping + * An optional mapping between old fields => new fields. */ - protected function alterTable($table, $new_schema) { + protected function alterTable($table, $old_schema, $new_schema, array $mapping = array()) { $i = 0; do { $new_table = $table . '_' . $i++; @@ -307,19 +303,35 @@ class DatabaseSchema_sqlite extends DatabaseSchema { $this->createTable($new_table, $new_schema); - $select = $this->connection->select($table)->fields($table, array_keys($new_schema['fields'])); + // Complete the mapping. + $old_keys = array_keys($old_schema['fields']); + $mapping += array_combine($old_keys, $old_keys); + + $select = $this->connection->select($table); + $fields = &$select->getFields(); + + // Map the fields. + foreach ($old_keys as $key) { + if (isset($mapping[$key])) { + // Don't use ->addField() here because it messes-up with the aliases. + $fields[$mapping[$key]] = array( + 'field' => $key, + 'table' => $table, + 'alias' => $mapping[$key], + ); + } + } + + // Execute the data migration query. $this->connection->insert($new_table) ->from($select) ->execute(); + $old_count = $this->connection->query('SELECT COUNT(*) FROM {' . $table . '}')->fetchField(); $new_count = $this->connection->query('SELECT COUNT(*) FROM {' . $new_table . '}')->fetchField(); if ($old_count == $new_count) { - do { - $temp_table = $table . '_' . $i++; - } while ($this->tableExists($temp_table)); - $this->renameTable($table, $temp_table); + $this->dropTable($table); $this->renameTable($new_table, $table); - $this->dropTable($temp_table); } } @@ -338,8 +350,15 @@ class DatabaseSchema_sqlite extends DatabaseSchema { */ protected function introspectSchema($table) { $mapped_fields = array_flip($this->getFieldTypeMap()); - $schema = array(); - $result = $this->connection->query("PRAGMA table_info('{" . $table . "}')"); + $schema = array( + 'fields' => array(), + 'primary key' => array(), + 'unique keys' => array(), + 'indexes' => array(), + ); + + $info = $this->getPrefixInfo($table); + $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.table_info(' . $info['table'] . ')'); foreach ($result as $row) { if (preg_match('/^([^(]+)\((.*)\)$/', $row->type, $matches)) { $type = $matches[1]; @@ -369,7 +388,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } } $indexes = array(); - $result = $this->connection->query("PRAGMA index_list('{" . $table . "}')"); + $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.index_list(' . $info['table'] . ')'); foreach ($result as $row) { if (strpos($row->name, 'sqlite_autoindex_') !== 0) { $indexes[] = array( @@ -378,13 +397,11 @@ class DatabaseSchema_sqlite extends DatabaseSchema { ); } } - // Get length of table name and underscore. - $n = strlen($this->connection->prefixTables('{' . $table . '}')) + 1; foreach ($indexes as $index) { $name = $index['name']; // Get index name without prefix. - $index_name = substr($name, $n); - $result = $this->connection->query("PRAGMA index_info('$name')"); + $index_name = substr($name, strlen($info['table']) + 1); + $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.index_info(' . $name . ')'); foreach ($result as $row) { $schema[$index['schema_key']][$index_name][] = $row->name; } @@ -393,11 +410,15 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } public function dropField($table, $field) { - if ($this->fieldExists($table, $field)) { + if (!$this->fieldExists($table, $field)) { return FALSE; } - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + + $mapping = array($field => NULL); + unset($new_schema['fields'][$field]); foreach ($new_schema['indexes'] as $index => $fields) { foreach ($fields as $key => $field_name) { @@ -410,7 +431,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema { unset($new_schema['indexes'][$index]); } } - $this->alterTable($table, $new_schema); + $this->alterTable($table, $old_schema, $new_schema, $mapping); return TRUE; } @@ -422,19 +443,61 @@ class DatabaseSchema_sqlite extends DatabaseSchema { throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new))); } - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + + // Map the old field to the new field. + if ($field != $field_new) { + $mapping[$field] = $field_new; + } + else { + $mapping = array(); + } + + // Remove the previous definition and swap in the new one. unset($new_schema['fields'][$field]); $new_schema['fields'][$field_new] = $spec; - if (isset($keys_new['primary keys'])) { - $new_schema['primary keys'] = $keys_new['primary keys']; - $keys_new['primary keys']; + + // Map the former indexes to the new column name. + $new_schema['primary key'] = $this->mapKeyDefinition($new_schema['primary key'], $mapping); + foreach (array('unique keys', 'indexes') as $k) { + foreach ($new_schema[$k] as &$key_definition) { + $key_definition = $this->mapKeyDefinition($key_definition, $mapping); + } + } + + // Add in the keys from $keys_new. + if (isset($keys_new['primary key'])) { + $new_schema['primary key'] = $keys_new['primary key']; } foreach (array('unique keys', 'indexes') as $k) { if (!empty($keys_new[$k])) { $new_schema[$k] = $keys_new[$k] + $new_schema[$k]; } } - $this->alterTable($table, $new_schema); + + $this->alterTable($table, $old_schema, $new_schema, $mapping); + } + + /** + * Utility method: rename columns in an index definition according to a new mapping. + * + * @param $key_definition + * The key definition. + * @param $mapping + * The new mapping. + */ + protected function mapKeyDefinition(array $key_definition, array $mapping) { + foreach ($key_definition as &$field) { + // The key definition can be an array($field, $length). + if (is_array($field)) { + $field = &$field[0]; + } + if (isset($mapping[$field])) { + $field = $mapping[$field]; + } + } + return $key_definition; } public function addIndex($table, $name, $fields) { @@ -453,7 +516,9 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } public function indexExists($table, $name) { - return $this->connection->query('PRAGMA index_info({' . $table . '}_' . $name . ')')->fetchField() != ''; + $info = $this->getPrefixInfo($table); + + return $this->connection->query('PRAGMA ' . $info['schema'] . '.index_info(' . $info['table'] . '_' . $name . ')')->fetchField() != ''; } public function dropIndex($table, $name) { @@ -461,7 +526,9 @@ class DatabaseSchema_sqlite extends DatabaseSchema { return FALSE; } - $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name)); + $info = $this->getPrefixInfo($table); + + $this->connection->query('DROP INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $name); return TRUE; } @@ -485,7 +552,9 @@ class DatabaseSchema_sqlite extends DatabaseSchema { return FALSE; } - $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name)); + $info = $this->getPrefixInfo($table); + + $this->connection->query('DROP INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $name); return TRUE; } @@ -494,23 +563,27 @@ class DatabaseSchema_sqlite extends DatabaseSchema { throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table))); } - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + if (!empty($new_schema['primary key'])) { throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table))); } $new_schema['primary key'] = $fields; - $this->alterTable($table, $new_schema); + $this->alterTable($table, $old_schema, $new_schema); } public function dropPrimaryKey($table) { - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + if (empty($new_schema['primary key'])) { return FALSE; } unset($new_schema['primary key']); - $this->alterTable($table, $new_schema); + $this->alterTable($table, $old_schema, $new_schema); return TRUE; } @@ -519,9 +592,11 @@ class DatabaseSchema_sqlite extends DatabaseSchema { throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field))); } - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + $new_schema['fields'][$field]['default'] = $default; - $this->alterTable($table, $new_schema); + $this->alterTable($table, $old_schema, $new_schema); } public function fieldSetNoDefault($table, $field) { @@ -529,9 +604,11 @@ class DatabaseSchema_sqlite extends DatabaseSchema { throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field))); } - $new_schema = $this->introspectSchema($table); + $old_schema = $this->introspectSchema($table); + $new_schema = $old_schema; + unset($new_schema['fields'][$field]['default']); - $this->alterTable($table, $new_schema); + $this->alterTable($table, $old_schema, $new_schema); } public function findTables($table_expression) { diff --git a/includes/entity.inc b/includes/entity.inc index 8dd9c69bfda5202c5ed7f9a61fdf12eca6abfedd..d182a0656fd7b45713154ec70d2a3c1be7e84ed8 100644 --- a/includes/entity.inc +++ b/includes/entity.inc @@ -1,5 +1,5 @@ <?php -// $Id: entity.inc,v 1.11 2010/07/07 13:13:44 dries Exp $ +// $Id: entity.inc,v 1.14 2010/09/15 04:34:26 webchick Exp $ /** * Interface for entity controller classes. @@ -13,6 +13,7 @@ * directly. */ interface DrupalEntityControllerInterface { + /** * Constructor. * @@ -22,12 +23,12 @@ interface DrupalEntityControllerInterface { public function __construct($entityType); /** - * Reset the internal, static entity cache. + * Resets the internal, static entity cache. */ public function resetCache(); /** - * Load one or more entities. + * Loads one or more entities. * * @param $ids * An array of entity IDs, or FALSE to load all entities. @@ -48,16 +49,72 @@ interface DrupalEntityControllerInterface { */ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { + /** + * Static cache of entities. + * + * @var array + */ protected $entityCache; + + /** + * Entity type for this controller instance. + * + * @var string + */ protected $entityType; + + /** + * Array of information about the entity. + * + * @var array + * + * @see entity_get_info() + */ protected $entityInfo; + + /** + * Additional arguments to pass to hook_TYPE_load(). + * + * Set before calling DrupalDefaultEntityController::attachLoad(). + * + * @var array + */ protected $hookLoadArguments; + + /** + * Name of the entity's ID field in the entity database table. + * + * @var string + */ protected $idKey; + + /** + * Name of entity's revision database table field, if it supports revisions. + * + * Has the value FALSE if this entity does not use revisions. + * + * @var string + */ protected $revisionKey; + + /** + * The table that stores revisions, if the entity supports revisions. + * + * @var string + */ protected $revisionTable; /** - * Constructor. Set basic variables. + * Whether this entity type should use the static cache. + * + * Set by entity info. + * + * @var boolean + */ + protected $cache; + + /** + * Constructor: sets basic variables. */ public function __construct($entityType) { $this->entityType = $entityType; @@ -79,10 +136,16 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { $this->cache = !empty($this->entityInfo['static cache']); } + /** + * Implements DrupalEntityControllerInterface::resetCache(). + */ public function resetCache() { $this->entityCache = array(); } + /** + * Implements DrupalEntityControllerInterface::load(). + */ public function load($ids = array(), $conditions = array()) { $entities = array(); @@ -153,7 +216,7 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { } /** - * Build the query to load the entity. + * Builds the query to load the entity. * * This has full revision support. For entities requiring special queries, * the class can be extended, and the default query can be constructed by @@ -164,6 +227,14 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { * See CommentController::buildQuery() or TaxonomyTermController::buildQuery() * for examples. * + * @param $ids + * An array of entity IDs, or FALSE to load all entities. + * @param $conditions + * An array of conditions in the form 'field' => $value. + * @param $revision_id + * The ID of the revision to load, or FALSE if this query is asking for the + * most current revision(s). + * * @return SelectQuery * A SelectQuery object for loading the entity. */ @@ -180,11 +251,11 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { } // Add fields from the {entity} table. - $entity_fields = drupal_schema_fields_sql($this->entityInfo['base table']); + $entity_fields = $this->entityInfo['schema_fields_sql']['base table']; if ($this->revisionKey) { // Add all fields from the {entity_revision} table. - $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->revisionTable)); + $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']); // The id field is provided by entity, so remove it. unset($entity_revision_fields[$this->idKey]); @@ -213,8 +284,7 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { } /** - * Attach data to entities upon loading. - * + * Attaches data to entities upon loading. * This will attach fields, if the entity is fieldable. It calls * hook_entity_load() for modules which need to add data to all entities. * It also calls hook_TYPE_load() on the loaded entities. For example @@ -222,6 +292,12 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { * expects special parameters apart from the queried entities, you can set * $this->hookLoadArguments prior to calling the method. * See NodeController::attachLoad() for an example. + * + * @param $queried_entities + * Associative array of query results, keyed on the entity ID. + * @param $revision_id + * ID of the revision that was loaded, or FALSE if teh most current revision + * was loaded. */ protected function attachLoad(&$queried_entities, $revision_id = FALSE) { // Attach fields. @@ -249,12 +325,15 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { } /** - * Get entities from the static cache. + * Gets entities from the static cache. * * @param $ids * If not empty, return entities that match these IDs. * @param $conditions * If set, return entities that match all of these conditions. + * + * @return + * Array of entities from the entity cache. */ protected function cacheGet($ids, $conditions = array()) { $entities = array(); @@ -284,7 +363,10 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { } /** - * Store entities in the static entity cache. + * Stores entities in the static entity cache. + * + * @param $entities + * Entities to store in the cache. */ protected function cacheSet($entities) { $this->entityCache += $entities; @@ -323,6 +405,10 @@ class EntityFieldQueryException extends Exception {} * specified or if the query has field conditions or sorts that are stored in * different field storage engines. However, this logic can be overridden in * hook_entity_query(). + * + * Also note that this query does not automatically respect entity access + * restrictions. Node access control is performed by the SQL storage engine but + * other storage engines might not do this. */ class EntityFieldQuery { /** @@ -433,6 +519,24 @@ class EntityFieldQuery { */ public $age = FIELD_LOAD_CURRENT; + /** + * A list of the tags added to this query. + * + * @var array + * + * @see EntityFieldQuery::addTag() + */ + public $tags = array(); + + /** + * A list of metadata added to this query. + * + * @var array + * + * @see EntityFieldQuery::addMetaData() + */ + public $metaData = array(); + /** * The ordered results. * @@ -734,6 +838,52 @@ class EntityFieldQuery { return $this; } + /** + * Adds a tag to the query. + * + * Tags are strings that mark a query so that hook_query_alter() and + * hook_query_TAG_alter() implementations may decide if they wish to alter + * the query. A query may have any number of tags, and they must be valid PHP + * identifiers (composed of letters, numbers, and underscores). For example, + * queries involving nodes that will be displayed for a user need to add the + * tag 'node_access', so that the node module can add access restrictions to + * the query. + * + * If an entity field query has tags, it must also have an entity type + * specified, because the alter hook will need the entity base table. + * + * @param string $tag + * The tag to add. + * + * @return EntityFieldQuery + * The called object. + */ + public function addTag($tag) { + $this->tags[$tag] = $tag; + return $this; + } + + /** + * Adds additional metadata to the query. + * + * Sometimes a query may need to provide additional contextual data for the + * alter hook. The alter hook implementations may then use that information + * to decide if and how to take action. + * + * @param $key + * The unique identifier for this piece of metadata. Must be a string that + * follows the same rules as any other PHP identifier. + * @param $object + * The additional data to add to the query. May be any valid PHP variable. + * + * @return EntityFieldQuery + * The called object. + */ + public function addMetaData($key, $object) { + $this->metaData[$key] = $object; + return $this; + } + /** * Executes the query. * @@ -889,7 +1039,7 @@ class EntityFieldQuery { /** * Finishes the query. * - * Adds the range and returns the requested list. + * Adds tags, metaData, range and returns the requested list or count. * * @param SelectQuery $select_query * A SelectQuery which has entity_type, entity_id, revision_id and bundle @@ -901,6 +1051,13 @@ class EntityFieldQuery { * See EntityFieldQuery::execute(). */ function finishQuery($select_query, $id_key = 'entity_id') { + foreach ($this->tags as $tag) { + $select_query->addTag($tag); + } + foreach ($this->metaData as $key => $object) { + $select_query->addMetaData($key, $object); + } + $select_query->addMetaData('entity_field_query', $this); if ($this->range) { $select_query->range($this->range['start'], $this->range['length']); } diff --git a/includes/file.inc b/includes/file.inc index bb1f825ca356bbf8ea55427190880074e2d5a690..6a18aee6f8d9d481cf1c7135068147cb21d3af13 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -1,5 +1,5 @@ <?php -// $Id: file.inc,v 1.218 2010/07/07 01:10:35 dries Exp $ +// $Id: file.inc,v 1.229 2010/09/11 21:14:31 webchick Exp $ /** * @file @@ -202,8 +202,9 @@ function file_stream_wrapper_valid_scheme($scheme) { } } + /** - * Returns the target of a URI (e.g. a stream). + * Returns the part of an URI after the schema. * * @param $uri * A stream, referenced as "scheme://target". @@ -216,10 +217,20 @@ function file_stream_wrapper_valid_scheme($scheme) { * @see file_uri_scheme() */ function file_uri_target($uri) { - if ($scheme = file_uri_scheme($uri)) { - return file_stream_wrapper_get_instance_by_scheme($scheme)->getTarget($uri); - } - return FALSE; + $data = explode('://', $uri, 2); + + // Remove erroneous leading or trailing, forward-slashes and backslashes. + return count($data) == 2 ? trim($data[1], '\/') : FALSE; +} + +/** + * Get the default file stream implementation. + * + * @return + * 'public', 'private' or any other file scheme defined as the default. + */ +function file_default_scheme() { + return variable_get('file_default_scheme', 'public'); } /** @@ -247,6 +258,10 @@ function file_stream_wrapper_uri_normalize($uri) { $uri = $scheme . '://' . $target; } } + else { + // The default scheme is file:// + $url = 'file://' . $uri; + } return $uri; } @@ -317,11 +332,9 @@ function file_stream_wrapper_get_instance_by_scheme($scheme) { * @see http://drupal.org/node/515192 * * There are two kinds of local files: - * - "created files", i.e. those in the files directory (which is stored in - * the file_directory_path variable and can be retrieved using - * file_directory_path()). These are files that have either been uploaded by - * users or were generated automatically (for example through CSS - * aggregation). + * - "managed files", i.e. those stored by a Drupal-compatible stream wrapper. + * These are files that have either been uploaded by users or were generated + * automatically (for example through CSS aggregation). * - "shipped files", i.e. those outside of the files directory, which ship as * part of Drupal core or contributed modules or themes. * @@ -331,9 +344,9 @@ function file_stream_wrapper_get_instance_by_scheme($scheme) { * * @return * A string containing a URL that may be used to access the file. - * If the provided string already contains a preceding 'http', nothing is done - * and the same string is returned. If a valid stream wrapper could not be - * found to generate an external URL, then FALSE will be returned. + * If the provided string already contains a preceding 'http', 'https', or + * '/', nothing is done and the same string is returned. If a stream wrapper + * could not be found to generate an external URL, then FALSE is returned. */ function file_create_url($uri) { // Allow the URI to be altered, e.g. to serve a file from a CDN or static @@ -355,8 +368,8 @@ function file_create_url($uri) { } else { // If this is not a properly formatted stream, then it is a shipped file. - // Therefor, return the URI with the base URL prepended. - return $GLOBALS['base_url'] . '/' . $uri; + // Therefore, return the urlencoded URI with the base URL prepended. + return $GLOBALS['base_url'] . '/' . drupal_encode_path($uri); } } elseif ($scheme == 'http' || $scheme == 'https') { @@ -495,6 +508,7 @@ function file_load_multiple($fids = array(), $conditions = array()) { * * @param $fid * A file ID. + * * @return * A file object. * @@ -542,7 +556,130 @@ function file_save(stdClass $file) { } /** - * Copy a file to a new location and adds a file record to the database. + * Determines where a file is used. + * + * @param $file + * A file object. + * + * @return + * A nested array with usage data. The first level is keyed by module name, + * the second by object type, the third has 'id' and 'count' keys. + * + * @see file_usage_add() + * @see file_usage_delete() + */ +function file_usage_list(stdClass $file) { + $result = db_select('file_usage', 'f') + ->fields('f', array('module', 'type', 'id', 'count')) + ->condition('fid', $file->fid) + ->condition('count', 0, '>') + ->execute(); + $references = array(); + foreach ($result as $usage) { + $references[$usage->module][$usage->type] = array('id' => $usage->id, 'count' => $usage->count); + } + return $references; +} + +/** + * Records that a module is using a file. + * + * This usage information will be queried during file_delete() to ensure that + * a file is not in use before it is physically removed from disk. + * + * Examples: + * - A module that associates files with nodes, so $type would be + * 'node' and $id would be the node's nid. Files for all revisions are stored + * within a single nid. + * - The User module associates an image with a user, so $type would be 'user' + * and the $id would be the user's uid. + * + * @param $file + * A file object. + * @param $module + * The name of the module using the file. + * @param $type + * The type of the object that contains the referenced file. + * @param $id + * The unique, numeric ID of the object containing the referenced file. + * @param $count + * (optional) The number of references to add to the object. Defaults to 1. + * + * @see file_usage_list() + * @see file_usage_delete() + */ +function file_usage_add(stdClass $file, $module, $type, $id, $count = 1) { + db_merge('file_usage') + ->key(array( + 'fid' => $file->fid, + 'module' => $module, + 'type' => $type, + 'id' => $id, + )) + ->fields(array('count' => $count)) + ->expression('count', 'count + :count', array(':count' => $count)) + ->execute(); +} + +/** + * Removes a record to indicate that a module is no longer using a file. + * + * The file_delete() function is typically called after removing a file usage + * to remove the record from the file_managed table and delete the file itself. + * + * @param $file + * A file object. + * @param $module + * The name of the module using the file. + * @param $type + * (optional) The type of the object that contains the referenced file. May + * be omitted if all module references to a file are being deleted. + * @param $id + * (optional) The unique, numeric ID of the object containing the referenced + * file. May be omitted if all module references to a file are being deleted. + * @param $count + * (optional) The number of references to delete from the object. Defaults to + * 1. 0 may be specified to delete all references to the file within a + * specific object. + * + * @see file_usage_add() + * @see file_usage_list() + * @see file_delete() + */ +function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $count = 1) { + // Delete rows that have a exact or less value to prevent empty rows. + $query = db_delete('file_usage') + ->condition('module', $module) + ->condition('fid', $file->fid); + if ($type && $id) { + $query + ->condition('type', $type) + ->condition('id', $id); + } + if ($count) { + $query->condition('count', $count, '<='); + } + $result = $query->execute(); + + // If the row has more than the specified count decrement it by that number. + if (!$result) { + $query = db_update('file_usage') + ->condition('module', $module) + ->condition('fid', $file->fid); + if ($type && $id) { + $query + ->condition('type', $type) + ->condition('id', $id); + } + if ($count) { + $query->expression('count', 'count - :count', array(':count' => $count)); + } + $query->execute(); + } +} + +/** + * Copies a file to a new location and adds a file record to the database. * * This function should be used when manipulating files that have records * stored in the database. This is a powerful function that in many ways @@ -578,6 +715,12 @@ function file_save(stdClass $file) { * @see hook_file_copy() */ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { + if (!file_valid_uri($destination)) { + watchdog('file', 'File %file (%realpath) could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination)); + drupal_set_message(t('The specified file %file could not be copied, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error'); + return FALSE; + } + if ($uri = file_unmanaged_copy($source->uri, $destination, $replace)) { $file = clone $source; $file->fid = NULL; @@ -609,7 +752,29 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS } /** - * Copy a file to a new location without invoking the file API. + * Determine whether the URI has a valid scheme for file API operations. + * + * There must be a scheme and it must be a Drupal-provided scheme like + * 'public', 'private', 'temporary', or an extension provided with + * hook_stream_wrappers(). + * + * @param $uri + * The URI to be tested. + * + * @return + * TRUE if the URI is allowed. + */ +function file_valid_uri($uri) { + // Assert that the URI has an allowed scheme. Barepaths are not allowed. + $uri_scheme = file_uri_scheme($uri); + if (empty($uri_scheme) || !file_stream_wrapper_valid_scheme($uri_scheme)) { + return FALSE; + } + return TRUE; +} + +/** + * Copies a file to a new location without invoking the file API. * * This is a powerful function that in many ways performs like an advanced * version of copy(). @@ -620,11 +785,12 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS * replace the file or rename the file based on the $replace parameter. * * @param $source - * A string specifying the filepath or URI of the original file. + * A string specifying the filepath or URI of the source file. * @param $destination - * A URI containing the destination that $source should be copied to. - * This must be a stream wrapper URI. If this value is omitted, Drupal's - * default files scheme will be used, usually "public://". + * A URI containing the destination that $source should be copied to. The + * URI may be a bare filepath (without a scheme) and in that case the default + * scheme (file://) will be used. If this value is omitted, Drupal's default + * files scheme will be used, usually "public://". * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. @@ -646,6 +812,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST if (!file_exists($source)) { // @todo Replace drupal_set_message() calls with exceptions instead. drupal_set_message(t('The specified file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error'); + watchdog('file', 'File %file (%realpath) could not be copied because it does not exist.', array('%file' => $original_source, '%realpath' => drupal_realpath($original_source))); return FALSE; } @@ -654,11 +821,6 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST $destination = file_build_uri(basename($source)); } - // Assert that the destination contains a valid stream. - $destination_scheme = file_uri_scheme($destination); - if (!$destination_scheme || !file_stream_wrapper_valid_scheme($destination_scheme)) { - drupal_set_message(t('The specified file %file could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_unmanaged_copy() or a missing stream wrapper.', array('%file' => $original_source, '%destination' => $destination)), 'error'); - } // Prepare the destination directory. if (file_prepare_directory($destination)) { @@ -670,7 +832,8 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST $dirname = drupal_dirname($destination); if (!file_prepare_directory($dirname)) { // The destination is not valid. - drupal_set_message(t('The specified file %file could not be copied, because the destination %directory is not properly configured. This is often caused by a problem with file or directory permissions.', array('%file' => $original_source, '%directory' => $destination)), 'error'); + watchdog('file', 'File %file could not be copied, because the destination directory %directory is not configured correctly.', array('%file' => $original_source, '%destination' => drupal_realpath($dirname))); + drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $original_source)), 'error'); return FALSE; } } @@ -678,13 +841,15 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST // Determine whether we can perform this operation based on overwrite rules. $destination = file_destination($destination, $replace); if ($destination === FALSE) { - drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $source, '%directory' => $destination)), 'error'); + drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory.', array('%file' => $original_source)), 'error'); + watchdog('file', 'File %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $original_source, '%destination' => drupal_realpath($destination))); return FALSE; } // Assert that the source and destination filenames are not the same. if (drupal_realpath($source) == drupal_realpath($destination)) { drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error'); + watchdog('file', 'File %file could not be copied because it would overwrite itself.', array('%file' => $source)); return FALSE; } // Make sure the .htaccess files are present. @@ -692,6 +857,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST // Perform the copy operation. if (!@copy($source, $destination)) { drupal_set_message(t('The specified file %file could not be copied.', array('%file' => $source)), 'error'); + watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => drupal_realpath($destination)), WATCHDOG_ERROR); return FALSE; } @@ -705,7 +871,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST * Given a relative path, construct a URI into Drupal's default files location. */ function file_build_uri($path) { - $uri = variable_get('file_default_scheme', 'public') . '://' . $path; + $uri = file_default_scheme() . '://' . $path; return file_stream_wrapper_uri_normalize($uri); } @@ -782,6 +948,12 @@ function file_destination($destination, $replace) { * @see hook_file_move() */ function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { + if (!file_valid_uri($destination)) { + watchdog('file', 'File %file (%realpath) could not be moved, because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination)); + drupal_set_message(t('The specified file %file could not be moved, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error'); + return FALSE; + } + if ($uri = file_unmanaged_move($source->uri, $destination, $replace)) { $delete_source = FALSE; @@ -850,11 +1022,23 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST /** * Modify a filename as needed for security purposes. * - * Dangerous file names will be altered; for instance, the file name - * "exploit.php.pps" will become "exploit.php_.pps". All extensions that are + * Munging a file name prevents unknown file extensions from masking exploit + * files. When web servers such as Apache decide how to process a URL request, + * they use the file extension. If the extension is not recognized, Apache + * skips that extension and uses the previous file extension. For example, if + * the file being requested is exploit.php.pps, and Apache does not recognize + * the '.pps' extension, it treats the file as PHP and executes it. To make + * this file name safe for Apache and prevent it from executing as PHP, the + * .php extension is "munged" into .php_, making the safe file name + * exploit.php_.pps. + * + * Specifically, this function adds an underscore to all extensions that are * between 2 and 5 characters in length, internal to the file name, and not - * included in $extensions will be altered by adding an underscore. If variable - * 'allow_insecure_uploads' evaluates to TRUE, no alterations will be made. + * included in $extensions. + * + * Function behavior is also controlled by the Drupal variable + * 'allow_insecure_uploads'. If 'allow_insecure_uploads' evaluates to TRUE, no + * alterations will be made, if it evaluates to FALSE, the filename is 'munged'. * * @param $filename * File name to modify. @@ -931,6 +1115,10 @@ function file_create_filename($basename, $directory) { // Strip control characters (ASCII value < 32). Though these are allowed in // some filesystems, not many applications handle them well. $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename); + if (substr(PHP_OS, 0, 3) == 'WIN') { + // These characters are not allowed in Windows filenames + $basename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $basename); + } // A URI or path may already have a trailing slash or look like "public://". if (substr($directory, -1) == '/') { @@ -966,30 +1154,36 @@ function file_create_filename($basename, $directory) { /** * Delete a file and its database record. * - * If the $force parameter is not TRUE hook_file_references() will be called - * to determine if the file is being used by any modules. If the file is being - * used is the delete will be canceled. + * If the $force parameter is not TRUE, file_usage_list() will be called to + * determine if the file is being used by any modules. If the file is being + * used the delete will be canceled. * * @param $file * A file object. * @param $force - * Boolean indicating that the file should be deleted even if - * hook_file_references() reports that the file is in use. + * Boolean indicating that the file should be deleted even if the file is + * reported as in use by the file_usage table. * * @return mixed * TRUE for success, FALSE in the event of an error, or an array if the file - * is being used by another module. The array keys are the module's name and - * the values are the number of references. + * is being used by any modules. * * @see file_unmanaged_delete() - * @see hook_file_references() + * @see file_usage_list() + * @see file_usage_delete() * @see hook_file_delete() */ function file_delete(stdClass $file, $force = FALSE) { - // If any module returns a value from the reference hook, the file will not - // be deleted from Drupal, but file_delete will return a populated array that - // tests as TRUE. - if (!$force && ($references = module_invoke_all('file_references', $file))) { + if (!file_valid_uri($file->uri)) { + watchdog('file', 'File %file (%realpath) could not be deleted because it is not a valid URI. This may be caused by improper use of file_delete() or a missing stream wrapper.', array('%file' => $file->uri, '%realpath' => drupal_realpath($file->uri))); + drupal_set_message(t('The specified file %file could not be deleted, because it is not a valid URI. More information is available in the system log.', array('%file' => $file->uri)), 'error'); + return FALSE; + } + + // If any module still has a usage entry in the file_usage table, the file + // will not be deleted, but file_delete() will return a populated array + // that tests as TRUE. + if (!$force && ($references = file_usage_list($file))) { return $references; } @@ -1000,6 +1194,7 @@ function file_delete(stdClass $file, $force = FALSE) { // database, so UIs can still find the file in the database. if (file_unmanaged_delete($file->uri)) { db_delete('file_managed')->condition('fid', $file->fid)->execute(); + db_delete('file_usage')->condition('fid', $file->fid)->execute(); return TRUE; } return FALSE; @@ -1030,7 +1225,7 @@ function file_unmanaged_delete($path) { return FALSE; } if (is_file($path)) { - return unlink($path); + return drupal_unlink($path); } // Return TRUE for non-existent file, but log that nothing was actually // deleted, as the current state is the intended result. @@ -1078,7 +1273,8 @@ function file_unmanaged_delete_recursive($path) { file_unmanaged_delete_recursive($entry_path); } $dir->close(); - return rmdir($path); + + return drupal_rmdir($path); } return file_unmanaged_delete($path); } @@ -1090,17 +1286,15 @@ function file_unmanaged_delete_recursive($path) { * Optional. A user id, specifying NULL returns the total space used by all * non-temporary files. * @param $status - * Optional. File Status to return. Combine with a bitwise OR(|) to return - * multiple statuses. The default status is FILE_STATUS_PERMANENT. + * Optional. The file status to consider. The default is to only + * consider files in status FILE_STATUS_PERMANENT. * * @return * An integer containing the number of bytes used. */ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) { $query = db_select('file_managed', 'f'); - // Use separate placeholders for the status to avoid a bug in some versions - // of PHP. See http://drupal.org/node/352956. - $query->where('f.status & :status1 = :status2', array(':status1' => $status, ':status2' => $status)); + $query->condition('f.status', $status); $query->addExpression('SUM(f.filesize)', 'filesize'); if (!is_null($uid)) { $query->condition('f.uid', $uid); @@ -1541,6 +1735,15 @@ function file_validate_image_resolution(stdClass $file, $maximum_dimensions = 0, function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) { global $user; + if (empty($destination)) { + $destination = file_default_scheme() . '://'; + } + if (!file_valid_uri($destination)) { + watchdog('file', 'The data could not be saved because the destination %destination is invalid. This may be caused by improper use of file_save_data() or a missing stream wrapper.', array('%destination' => $destination)); + drupal_set_message(t('The data could not be saved, because the destination is invalid. More information is available in the system log.'), 'error'); + return FALSE; + } + if ($uri = file_unmanaged_save_data($data, $destination, $replace)) { // Create a file object. $file = new stdClass(); @@ -1549,7 +1752,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM $file->filename = basename($uri); $file->filemime = file_get_mimetype($file->uri); $file->uid = $user->uid; - $file->status |= FILE_STATUS_PERMANENT; + $file->status = FILE_STATUS_PERMANENT; // If we are replacing an existing file re-use its database record. if ($replace == FILE_EXISTS_REPLACE) { $existing_files = file_load_multiple(array(), array('uri' => $uri)); @@ -1660,9 +1863,19 @@ function file_download() { $uri = $scheme . '://' . $target; if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) { // Let other modules provide headers and controls access to the file. - $headers = module_invoke_all('file_download', $uri); - if (in_array(-1, $headers)) { - return drupal_access_denied(); + // module_invoke_all() uses array_merge_recursive() which merges header + // values into a new array. To avoid that and allow modules to override + // headers instead, use array_merge() to merge the returned arrays. + $headers = array(); + foreach (module_implements('file_download') as $module) { + $function = $module . '_file_download'; + $result = $function($uri); + if ($result == -1) { + return drupal_access_denied(); + } + if (isset($result) && is_array($result)) { + $headers = array_merge($headers, $result); + } } if (count($headers)) { file_transfer($uri, $headers); @@ -1748,33 +1961,6 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { return $files; } -/** - * Determines the local directory path of a given wrapper. - * - * This function will return the directory path of a stream wrapper. A stream - * is referenced as: "scheme://target". For example, a scheme of "public" - * might return "sites/default/files" or "temporary" might return "/tmp". - * - * @param $scheme - * A string representing the scheme of a stream. The default wrapper is - * is assumed if this is not provided. - * - * @return - * A string containing the directory path of a stream. FALSE is returned if - * the scheme is invalid or a wrapper could not be instantiated. - */ -function file_directory_path($scheme = NULL) { - if (empty($scheme)) { - $scheme = variable_get('file_default_scheme', 'public'); - } - if ($wrapper = file_stream_wrapper_get_instance_by_scheme($scheme)) { - return $wrapper->getDirectoryPath(); - } - else { - return FALSE; - } -} - /** * Determine the maximum file upload size by querying the PHP settings. * @@ -1786,9 +1972,15 @@ function file_upload_max_size() { static $max_size = -1; if ($max_size < 0) { + // Start with post_max_size. + $max_size = parse_size(ini_get('post_max_size')); + + // If upload_max_size is less, then reduce. Except if upload_max_size is + // zero, which indicates no limit. $upload_max = parse_size(ini_get('upload_max_filesize')); - $post_max = parse_size(ini_get('post_max_size')); - $max_size = ($upload_max < $post_max) ? $upload_max : $post_max; + if ($upload_max > 0 && $upload_max < $max_size) { + $max_size = $upload_max; + } } return $max_size; } @@ -1876,6 +2068,36 @@ function drupal_chmod($uri, $mode = NULL) { return FALSE; } +/** + * Deletes a file. + * + * PHP's unlink() is broken on Windows, as it can fail to remove a file + * when it has a read-only flag set. + * + * @param $uri + * A URI or pathname. + * @param $context + * Refer to http://php.net/manual/en/ref.stream.php + * + * @return + * Boolean TRUE on success, or FALSE on failure. + * + * @see unlink() + * @ingroup php_wrappers + */ +function drupal_unlink($uri, $context = NULL) { + $scheme = file_uri_scheme($uri); + if ((!$scheme || !file_stream_wrapper_valid_scheme($scheme)) && (substr(PHP_OS, 0, 3) == 'WIN')) { + chmod($uri, 0600); + } + if ($context) { + return unlink($uri, $context); + } + else { + return unlink($uri); + } +} + /** * Returns the absolute path of a file or directory * @@ -1972,7 +2194,6 @@ function drupal_dirname($uri) { * @ingroup php_wrappers */ function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) { - if (is_null($mode)) { $mode = variable_get('file_chmod_directory', 0775); } @@ -1985,6 +2206,36 @@ function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) { } } +/** + * Remove a directory. + * + * PHP's rmdir() is broken on Windows, as it can fail to remove a directory + * when it has a read-only flag set. + * + * @param $uri + * A URI or pathname. + * @param $context + * Refer to http://php.net/manual/en/ref.stream.php + * + * @return + * Boolean TRUE on success, or FALSE on failure. + * + * @see rmdir() + * @ingroup php_wrappers + */ +function drupal_rmdir($uri, $context = NULL) { + $scheme = file_uri_scheme($uri); + if ((!$scheme || !file_stream_wrapper_valid_scheme($scheme)) && (substr(PHP_OS, 0, 3) == 'WIN')) { + chmod($uri, 0700); + } + if ($context) { + return rmdir($uri, $context); + } + else { + return rmdir($uri); + } +} + /** * Creates a file with a unique filename in the specified directory. * @@ -2064,9 +2315,9 @@ function file_directory_temp() { } } - // if a directory has been found, use it, otherwise default to 'files/tmp' or 'files\\tmp'. if (empty($temporary_directory)) { - $temporary_directory = file_directory_path() . $path_delimiter . 'tmp'; + // If no directory has been found default to 'files/tmp' or 'files\\tmp'. + $temporary_directory = variable_get('file_public_path', conf_path() . '/files') . $path_delimiter . 'tmp'; } // Save the path of the discovered directory. variable_set('file_directory_temp', $temporary_directory); diff --git a/includes/file.mimetypes.inc b/includes/file.mimetypes.inc index ae917497baa0aed4f771a4b80095f716a8f95ee4..9f2383808d2f96b988c9befcd16e69c50d51048b 100644 --- a/includes/file.mimetypes.inc +++ b/includes/file.mimetypes.inc @@ -1,5 +1,5 @@ <?php -// $Id: file.mimetypes.inc,v 1.4 2009/08/31 05:47:33 dries Exp $ +// $Id: file.mimetypes.inc,v 1.5 2010/07/16 02:40:48 dries Exp $ /** * @file @@ -13,6 +13,7 @@ * * @return * Array of mimetypes correlated to the extensions that relate to them. + * * @see file_get_mimetype() */ function file_mimetype_mapping() { @@ -30,6 +31,7 @@ function file_mimetype_mapping() { * * @return * Array of mimetypes correlated to the extensions that relate to them. + * * @see file_get_mimetype() */ function file_default_mimetype_mapping() { diff --git a/includes/filetransfer/ftp.inc b/includes/filetransfer/ftp.inc index c3ee39b68112368f9c850de39c9c935b99e3fd7e..6c069d26dd96aae6d581511d4d78658bce16c680 100644 --- a/includes/filetransfer/ftp.inc +++ b/includes/filetransfer/ftp.inc @@ -1,5 +1,5 @@ <?php -// $Id: ftp.inc,v 1.12 2010/01/11 16:25:16 webchick Exp $ +// $Id: ftp.inc,v 1.13 2010/08/17 22:05:22 dries Exp $ /** * Base class for FTP implementations. @@ -77,7 +77,7 @@ class FileTransferFTPWrapper extends FileTransferFTP { } } closedir($dh); - if (!rmdir($this->connection . $directory)) { + if (!drupal_rmdir($this->connection . $directory)) { $exception = new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory)); throw $exception; } @@ -91,7 +91,7 @@ class FileTransferFTPWrapper extends FileTransferFTP { } function removeFileJailed($destination) { - if (!@unlink($this->connection . '/' .$destination)) { + if (!@drupal_unlink($this->connection . '/' .$destination)) { throw new FileTransferException('Cannot remove @destination', NULL, array('@destination' => $destination)); } } diff --git a/includes/filetransfer/local.inc b/includes/filetransfer/local.inc index 18b8c244fed87487054dbcff3ec46d00bb4c48db..f7d7fa84683d7bd5bdcdfb935bc43f48e592014f 100644 --- a/includes/filetransfer/local.inc +++ b/includes/filetransfer/local.inc @@ -1,5 +1,5 @@ <?php -// $Id: local.inc,v 1.3 2010/01/30 07:59:24 dries Exp $ +// $Id: local.inc,v 1.4 2010/08/17 22:05:22 dries Exp $ /** * The local connection class for copying files as the httpd user. @@ -33,23 +33,23 @@ class FileTransferLocal extends FileTransfer implements FileTransferChmodInterfa } foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory), RecursiveIteratorIterator::CHILD_FIRST) as $filename => $file) { if ($file->isDir()) { - if (@!rmdir($filename)) { + if (@!drupal_rmdir($filename)) { throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $filename)); } } elseif ($file->isFile()) { - if (@!unlink($filename)) { + if (@!drupal_unlink($filename)) { throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $filename)); } } } - if (@!rmdir($directory)) { + if (@!drupal_rmdir($directory)) { throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $directory)); } } protected function removeFileJailed($file) { - if (@!unlink($file)) { + if (@!drupal_unlink($file)) { throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $file)); } } diff --git a/includes/form.inc b/includes/form.inc index c0812bb7ce7da1f6c53c4fb04890a2d1e4704ac2..88a05544c38efde7fc1ff3980ce489a6f48c4492 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1,5 +1,5 @@ <?php -// $Id: form.inc,v 1.476 2010/07/07 17:56:42 webchick Exp $ +// $Id: form.inc,v 1.492 2010/09/14 21:42:05 dries Exp $ /** * @defgroup forms Form builder functions @@ -207,7 +207,10 @@ function drupal_get_form($form_id) { * rebuild the form from cache when the original context may no longer be * available: * - args: An array of arguments to pass to the form builder. - * - file: An optional include file that contains the form and is + * - files: An optional array defining include files that need to be loaded + * for building the form. Each array entry may be the path to a file or + * another array containing values for the parameters 'type', 'module' and + * 'name' as needed by module_load_include(). The files listed here are * automatically loaded by form_get_cache(). Defaults to the current menu * router item's 'file' definition, if existent. * - rebuild: Normally, after the entire form processing is completed and @@ -272,101 +275,79 @@ function drupal_build_form($form_id, &$form_state) { } if (isset($_SESSION['batch_form_state'])) { - // We've been redirected here after a batch processing : the form has - // already been processed, so we grab the post-process $form_state value - // and move on to form display. See _batch_finished() function. + // We've been redirected here after a batch processing. The form has + // already been processed, but needs to be rebuilt. See _batch_finished(). $form_state = $_SESSION['batch_form_state']; unset($_SESSION['batch_form_state']); - } - else { - // If the incoming input contains a form_build_id, we'll check the - // cache for a copy of the form in question. If it's there, we don't - // have to rebuild the form to proceed. In addition, if there is stored - // form_state data from a previous step, we'll retrieve it so it can - // be passed on to the form processing code. - if (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id'])) { - $form_build_id = $form_state['input']['form_build_id']; - $form = form_get_cache($form_build_id, $form_state); - } - - // If the previous bit of code didn't result in a populated $form - // object, we're hitting the form for the first time and we need - // to build it from scratch. - if (!isset($form)) { - // Record the filepath of the include file containing the original form, - // so the form builder callbacks can be loaded when the form is being - // rebuilt from cache on a different path (such as 'system/ajax'). See - // form_get_cache(). - // $menu_get_item() is not available at installation time. - if (!isset($form_state['build_info']['file']) && !defined('MAINTENANCE_MODE')) { - $item = menu_get_item(); - if (!empty($item['include_file'])) { - $form_state['build_info']['file'] = $item['include_file']; - } - } - - $form = drupal_retrieve_form($form_id, $form_state); - $form_build_id = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); - $form['#build_id'] = $form_build_id; - - // Fix the form method, if it is 'get' in $form_state, but not in $form. - if ($form_state['method'] == 'get' && !isset($form['#method'])) { - $form['#method'] = 'get'; - } - - drupal_prepare_form($form_id, $form, $form_state); - // Store a copy of the unprocessed form to cache in case - // $form_state['cache'] is set. - $original_form = $form; - } - - // Now that we know we have a form, we'll process it (validating, - // submitting, and handling the results returned by its submission - // handlers. Submit handlers accumulate data in the form_state by - // altering the $form_state variable, which is passed into them by - // reference. - drupal_process_form($form_id, $form, $form_state); - } - - // Most simple, single-step forms will be finished by this point -- - // drupal_process_form() usually redirects to another page (or to - // a 'fresh' copy of the form) once processing is complete. If one - // of the form's handlers has set $form_state['redirect'] to FALSE, - // the form will simply be re-rendered with the values still in its - // fields. - // - // If $form_state['rebuild'] has been set and input has been processed, we - // know that we're in a multi-part process of some sort and the form's - // workflow is not complete. We need to construct a fresh copy of the form, - // passing in the latest $form_state in addition to any other variables passed - // into drupal_get_form(). - if ($form_state['rebuild'] && $form_state['process_input'] && !form_get_errors()) { - $form = drupal_rebuild_form($form_id, $form_state); - } - // After processing the form, the form builder or a #process callback may - // have set $form_state['cache'] to indicate that the original form and the - // $form_state shall be cached. But the form may only be cached if the - // special 'no_cache' property is not set to TRUE and we are not rebuilding. - elseif (isset($form_build_id) && $form_state['cache'] && empty($form_state['no_cache'])) { - // Cache the original, unprocessed form upon initial build of the form. - if (isset($original_form)) { - form_set_cache($form_build_id, $original_form, $form_state); - } - // After processing a cached form, only update the cached form state. - else { - form_set_cache($form_build_id, NULL, $form_state); - } - } - - // Don't override #theme if someone already set it. - if (!isset($form['#theme'])) { - drupal_theme_initialize(); - $registry = theme_get_registry(); - if (isset($registry[$form_id])) { - $form['#theme'] = $form_id; - } - } + return drupal_rebuild_form($form_id, $form_state); + } + + // If the incoming input contains a form_build_id, we'll check the cache for a + // copy of the form in question. If it's there, we don't have to rebuild the + // form to proceed. In addition, if there is stored form_state data from a + // previous step, we'll retrieve it so it can be passed on to the form + // processing code. + $check_cache = isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id']); + if ($check_cache) { + $form = form_get_cache($form_state['input']['form_build_id'], $form_state); + } + + // If the previous bit of code didn't result in a populated $form object, we + // are hitting the form for the first time and we need to build it from + // scratch. + if (!isset($form)) { + // If we attempted to serve the form from cache, uncacheable $form_state + // keys need to be removed after retrieving and preparing the form, except + // any that were already set prior to retrieving the form. + if ($check_cache) { + $form_state_before_retrieval = $form_state; + } + + $form = drupal_retrieve_form($form_id, $form_state); + drupal_prepare_form($form_id, $form, $form_state); + + // form_set_cache() removes uncacheable $form_state keys defined in + // form_state_keys_no_cache() in order for multi-step forms to work + // properly. This means that form processing logic for single-step forms + // using $form_state['cache'] may depend on data stored in those keys + // during drupal_retrieve_form()/drupal_prepare_form(), but form + // processing should not depend on whether the form is cached or not, so + // $form_state is adjusted to match what it would be after a + // form_set_cache()/form_get_cache() sequence. These exceptions are + // allowed to survive here: + // - always_process: Does not make sense in conjunction with form caching + // in the first place, since passing form_build_id as a GET parameter is + // not desired. + // - temporary: Any assigned data is expected to survives within the same + // page request. + if ($check_cache) { + $uncacheable_keys = array_flip(array_diff(form_state_keys_no_cache(), array('always_process', 'temporary'))); + $form_state = array_diff_key($form_state, $uncacheable_keys); + $form_state += $form_state_before_retrieval; + } + } + + // Now that we have a constructed form, process it. This is where: + // - Element #process functions get called to further refine $form. + // - User input, if any, gets incorporated in the #value property of the + // corresponding elements and into $form_state['values']. + // - Validation and submission handlers are called. + // - If this submission is part of a multistep workflow, the form is rebuilt + // to contain the information of the next step. + // - If necessary, the form and form state are cached or re-cached, so that + // appropriate information persists to the next page request. + // All of the handlers in the pipeline receive $form_state by reference and + // can use it to know or update information about the state of the form. + drupal_process_form($form_id, $form, $form_state); + // If this was a successful submission of a single-step form or the last step + // of a multi-step form, then drupal_process_form() issued a redirect to + // another page, or back to this page, but as a new request. Therefore, if + // we're here, it means that this is either a form being viewed initially + // before any user input, or there was a validation error requiring the form + // to be re-displayed, or we're in a multi-step workflow and need to display + // the form's next step. In any case, we have what we need in $form, and can + // return it for rendering. return $form; } @@ -376,6 +357,7 @@ function drupal_build_form($form_id, &$form_state) { function form_state_defaults() { return array( 'rebuild' => FALSE, + 'rebuild_info' => array(), 'redirect' => NULL, 'build_info' => array('args' => array()), 'temporary' => array(), @@ -389,16 +371,19 @@ function form_state_defaults() { } /** - * Retrieves a form, caches it and processes it again. + * Constructs a new $form from the information in $form_state. + * + * This is the key function for making multi-step forms advance from step to + * step. It is called by drupal_process_form() when all user input processing, + * including calling validation and submission handlers, for the request is + * finished. If a validate or submit handler set $form_state['rebuild'] to TRUE, + * and if other conditions don't preempt a rebuild from happening, then this + * function is called to generate a new $form, the next step in the form + * workflow, to be returned for rendering. * - * If your AJAX callback simulates the pressing of a button, then your AJAX - * callback will need to do the same as what drupal_get_form() would do when the - * button is pressed: get the form from the cache, run drupal_process_form over - * it and then if it needs rebuild, run drupal_rebuild_form() over it. Then send - * back a part of the returned form. - * $form_state['triggering_element']['#array_parents'] will help you to find - * which part. - * @see ajax_form_callback() for an example. + * AJAX form submissions are almost always multi-step workflows, so that is one + * common use-case during which form rebuilding occurs. See ajax_form_callback() + * for more information about creating AJAX-enabled forms. * * @param $form_id * The unique string identifying the desired form. If a function @@ -411,21 +396,20 @@ function form_state_defaults() { * A keyed array containing the current state of the form. * @param $old_form * (optional) A previously built $form. Used to retain the #build_id and - * #action properties in AJAX callbacks and similar partial form rebuilds. - * Should not be passed for regular rebuilds, for which the entire $form - * should be rebuilt freshly. + * #action properties in AJAX callbacks and similar partial form rebuilds. The + * only properties copied from $old_form are the ones which both exist in + * $old_form and for which $form_state['rebuild_info']['copy'][PROPERTY] is + * TRUE. If $old_form is not passed, the entire $form is rebuilt freshly. + * 'rebuild_info' needs to be a separate top-level property next to + * 'build_info', since the contained data must not be cached. * * @return * The newly built form. + * + * @see drupal_process_form() + * @see ajax_form_callback() */ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { - // AJAX and other contexts may call drupal_rebuild_form() even when - // $form_state['rebuild'] isn't set, but _form_builder_handle_input_element() - // needs to distinguish a rebuild from an initial build in order to process - // user input correctly. Form constructors and form processing functions may - // also need to handle a rebuild differently than an initial build. - $form_state['rebuild'] = TRUE; - $form = drupal_retrieve_form($form_id, $form_state); // If only parts of the form will be returned to the browser (e.g. AJAX or @@ -434,20 +418,28 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { // Otherwise, a new #build_id is generated, to not clobber the previous // build's data in the form cache; also allowing the user to go back to an // earlier build, make changes, and re-submit. - $form['#build_id'] = isset($old_form['#build_id']) ? $old_form['#build_id'] : 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); + // @see drupal_prepare_form() + if (isset($old_form['#build_id']) && !empty($form_state['rebuild_info']['copy']['#build_id'])) { + $form['#build_id'] = $old_form['#build_id']; + } + else { + $form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); + } // #action defaults to request_uri(), but in case of AJAX and other partial // rebuilds, the form is submitted to an alternate URL, and the original // #action needs to be retained. - if (isset($old_form['#action'])) { + if (isset($old_form['#action']) && !empty($form_state['rebuild_info']['copy']['#action'])) { $form['#action'] = $old_form['#action']; } drupal_prepare_form($form_id, $form, $form_state); + // Caching is normally done in drupal_process_form(), but what needs to be + // cached is the $form structure before it passes through form_builder(), + // so we need to do it here. + // @todo For Drupal 8, find a way to avoid this code duplication. if (empty($form_state['no_cache'])) { - // We cache the form structure and the form state so it can be retrieved - // later for validation. form_set_cache($form['#build_id'], $form, $form_state); } @@ -455,10 +447,8 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { // re-rendering the form. $form_state['groups'] = array(); - // Do not call drupal_process_form(), since it would prevent the rebuilt form - // to submit. - $form = form_builder($form_id, $form, $form_state); - return $form; + // Return a fully built form that is ready for rendering. + return form_builder($form_id, $form, $form_state); } /** @@ -474,10 +464,17 @@ function form_get_cache($form_build_id, &$form_state) { // Re-populate $form_state for subsequent rebuilds. $form_state = $cached->data + $form_state; - // If the original form is contained in an include file, load the file. + // If the original form is contained in include files, load the files. // See drupal_build_form(). - if (!empty($form_state['build_info']['file']) && file_exists($form_state['build_info']['file'])) { - require_once DRUPAL_ROOT . '/' . $form_state['build_info']['file']; + $form_state['build_info'] += array('files' => array()); + foreach ($form_state['build_info']['files'] as $file) { + if (is_array($file)) { + $file += array('type' => 'inc', 'name' => $file['module']); + module_load_include($file['type'], $file['module'], $file['name']); + } + elseif (file_exists($file)) { + require_once DRUPAL_ROOT . '/' . $file; + } } } return $form; @@ -515,6 +512,7 @@ function form_state_keys_no_cache() { 'always_process', 'must_validate', 'rebuild', + 'rebuild_info', 'redirect', 'no_redirect', 'temporary', @@ -629,6 +627,18 @@ function drupal_form_submit($form_id, &$form_state) { function drupal_retrieve_form($form_id, &$form_state) { $forms = &drupal_static(__FUNCTION__); + // Record the filepath of the include file containing the original form, so + // the form builder callbacks can be loaded when the form is being rebuilt + // from cache on a different path (such as 'system/ajax'). See + // form_get_cache(). + // $menu_get_item() is not available at installation time. + if (!isset($form_state['build_info']['files']['menu']) && !defined('MAINTENANCE_MODE')) { + $item = menu_get_item(); + if (!empty($item['include_file'])) { + $form_state['build_info']['files']['menu'] = $item['include_file']; + } + } + // We save two copies of the incoming arguments: one for modules to use // when mapping form ids to constructor functions, and another to pass to // the constructor function itself. @@ -657,6 +667,7 @@ function drupal_retrieve_form($form_id, &$form_state) { } if (isset($form_definition['callback'])) { $callback = $form_definition['callback']; + $form_state['build_info']['base_form_id'] = $callback; } // In case $form_state['wrapper_callback'] is not defined already, we also // allow hook_forms() to define one. @@ -695,7 +706,7 @@ function drupal_retrieve_form($form_id, &$form_state) { * Processes a form submission. * * This function is the heart of form API. The form gets built, validated and in - * appropriate cases, submitted. + * appropriate cases, submitted and rebuilt. * * @param $form_id * The unique string identifying the current form. @@ -724,7 +735,11 @@ function drupal_process_form($form_id, &$form, &$form_state) { } } - // Build the form. + // form_builder() finishes building the form by calling element #process + // functions and mapping user input, if any, to #value properties, and also + // storing the values in $form_state['values']. We need to retain the + // unprocessed $form in case it needs to be cached. + $unprocessed_form = $form; $form = form_builder($form_id, $form, $form_state); // Only process the input if we have a correct form submission. @@ -784,6 +799,29 @@ function drupal_process_form($form_id, &$form, &$form_state) { // Redirect the form based on values in $form_state. drupal_redirect_form($form_state); } + + // Don't rebuild or cache form submissions invoked via drupal_form_submit(). + if (!empty($form_state['programmed'])) { + return; + } + } + + // If $form_state['rebuild'] has been set and input has been processed without + // validation errors, we're in a multi-step workflow that is not yet complete. + // We need to construct a new $form based on the changes made to $form_state + // during this request. + if ($form_state['rebuild'] && $form_state['process_input'] && !form_get_errors()) { + $form = drupal_rebuild_form($form_id, $form_state, $form); + } + // After processing the form, the form builder or a #process callback may + // have set $form_state['cache'] to indicate that the form and form state + // shall be cached. But the form may only be cached if the 'no_cache' property + // is not set to TRUE. Only cache $form as it was prior to form_builder(), + // because form_builder() must run for each request to accomodate new user + // input. We do not cache here for forms that have been rebuilt, because + // drupal_rebuild_form() takes care of that. + elseif ($form_state['cache'] && empty($form_state['no_cache'])) { + form_set_cache($form['#build_id'], $unprocessed_form, $form_state); } } @@ -807,15 +845,27 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { $form['#type'] = 'form'; $form_state['programmed'] = isset($form_state['programmed']) ? $form_state['programmed'] : FALSE; - if (isset($form['#build_id'])) { - $form['form_build_id'] = array( - '#type' => 'hidden', - '#value' => $form['#build_id'], - '#id' => $form['#build_id'], - '#name' => 'form_build_id', - ); + // Fix the form method, if it is 'get' in $form_state, but not in $form. + if ($form_state['method'] == 'get' && !isset($form['#method'])) { + $form['#method'] = 'get'; } + // Generate a new #build_id for this form, if none has been set already. The + // form_build_id is used as key to cache a particular build of the form. For + // multi-step forms, this allows the user to go back to an earlier build, make + // changes, and re-submit. + // @see drupal_build_form() + // @see drupal_rebuild_form() + if (!isset($form['#build_id'])) { + $form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); + } + $form['form_build_id'] = array( + '#type' => 'hidden', + '#value' => $form['#build_id'], + '#id' => $form['#build_id'], + '#name' => 'form_build_id', + ); + // Add a token, based on either #token or form_id, to any form displayed to // authenticated users. This ensures that any submitted form was actually // requested previously by the user and protects against cross site request @@ -856,20 +906,48 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { $form += array('#tree' => FALSE, '#parents' => array()); if (!isset($form['#validate'])) { + // Check for a handler specific to $form_id. if (function_exists($form_id . '_validate')) { - $form['#validate'] = array($form_id . '_validate'); + $form['#validate'][] = $form_id . '_validate'; + } + // Otherwise check whether this is a shared form and whether there is a + // handler for the shared $form_id. + elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) { + $form['#validate'][] = $form_state['build_info']['base_form_id'] . '_validate'; } } if (!isset($form['#submit'])) { + // Check for a handler specific to $form_id. if (function_exists($form_id . '_submit')) { - // We set submit here so that it can be altered. - $form['#submit'] = array($form_id . '_submit'); + $form['#submit'][] = $form_id . '_submit'; + } + // Otherwise check whether this is a shared form and whether there is a + // handler for the shared $form_id. + elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) { + $form['#submit'][] = $form_state['build_info']['base_form_id'] . '_submit'; } } - // Invoke hook_form_alter() and hook_form_FORM_ID_alter() implementations. - drupal_alter(array('form', 'form_' . $form_id), $form, $form_state, $form_id); + // If no #theme has been set, automatically apply theme suggestions. + // theme_form() itself is in #theme_wrappers and not #theme. Therefore, the + // #theme function only has to care for rendering the inner form elements, + // not the form itself. + if (!isset($form['#theme'])) { + $form['#theme'] = array($form_id); + if (isset($form_state['build_info']['base_form_id'])) { + $form['#theme'][] = $form_state['build_info']['base_form_id']; + } + } + + // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and + // hook_form_FORM_ID_alter() implementations. + $hooks = array('form'); + if (isset($form_state['build_info']['base_form_id'])) { + $hooks[] = 'form_' . $form_state['build_info']['base_form_id']; + } + $hooks[] = 'form_' . $form_id; + drupal_alter($hooks, $form, $form_state, $form_id); } @@ -911,12 +989,33 @@ function drupal_validate_form($form_id, &$form, &$form_state) { if (isset($form['#token'])) { if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) { // Setting this error will cause the form to fail validation. - form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.')); + form_set_error('form_token', t('This form is outdated. Reload the page and try again. Contact the site administrator if the problem persists.')); } } _form_validate($form, $form_state, $form_id); $validated_forms[$form_id] = TRUE; + + // If validation errors are limited then remove any non validated form values, + // so that only values that passed validation are left for submit callbacks. + if (isset($form_state['triggering_element']['#limit_validation_errors']) && $form_state['triggering_element']['#limit_validation_errors'] !== FALSE) { + $values = array(); + foreach ($form_state['triggering_element']['#limit_validation_errors'] as $section) { + // If the section exists within $form_state['values'], even if the value + // is NULL, copy it to $values. + $section_exists = NULL; + $value = drupal_array_get_nested_value($form_state['values'], $section, $section_exists); + if ($section_exists) { + drupal_array_set_nested_value($values, $section, $value); + } + } + // For convenience we always make the value of the pressed button available. + if (isset($form_state['triggering_element']['#button_type'])) { + $values[$form_state['triggering_element']['#name']] = $form_state['triggering_element']['#value']; + drupal_array_set_nested_value($values, $form_state['triggering_element']['#parents'], $form_state['triggering_element']['#value']); + } + $form_state['values'] = $values; + } } /** @@ -1505,6 +1604,14 @@ function form_builder($form_id, $element, &$form_state) { $element[$key]['#access'] = FALSE; } + // Make child elements inherit their parent's #disabled and #allow_focus + // values unless they specify their own. + foreach (array('#disabled', '#allow_focus') as $property) { + if (isset($element[$property]) && !isset($element[$key][$property])) { + $element[$key][$property] = $element[$property]; + } + } + // Don't squash existing parents value. if (!isset($element[$key]['#parents'])) { // Check to see if a tree of child elements is present. If so, @@ -1661,18 +1768,8 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) { if ($process_input) { // Get the input for the current element. NULL values in the input need to // be explicitly distinguished from missing input. (see below) - $input = $form_state['input']; - $input_exists = TRUE; - foreach ($element['#parents'] as $parent) { - if (is_array($input) && array_key_exists($parent, $input)) { - $input = $input[$parent]; - } - else { - $input = NULL; - $input_exists = FALSE; - break; - } - } + $input_exists = NULL; + $input = drupal_array_get_nested_value($form_state['input'], $element['#parents'], $input_exists); // For browser-submitted forms, the submitted values do not contain values // for certain elements (empty multiple select, unchecked checkbox). // During initial form processing, we add explicit NULL values for such @@ -1683,11 +1780,9 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) { // submit explicit NULL values when calling drupal_form_submit(), so we do // not modify $form_state['input'] for them. if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) { - // We leverage the internal logic of form_set_value() to change the - // input values by passing $form_state['input'] instead of the usual - // $form_state['values']. In effect, this adds the necessary parent keys - // to $form_state['input'] and sets the element's input value to NULL. - _form_set_value($form_state['input'], $element, $element['#parents'], NULL); + // Add the necessary parent keys to $form_state['input'] and sets the + // element's input value to NULL. + drupal_array_set_nested_value($form_state['input'], $element['#parents'], NULL); $input_exists = TRUE; } // If we have input for the current element, assign it to the #value @@ -1746,11 +1841,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) { // Set the element's value in $form_state['values'], but only, if its key // does not exist yet (a #value_callback may have already populated it). - $values = $form_state['values']; - foreach ($element['#parents'] as $key) { - $values = (isset($values[$key]) ? $values[$key] : NULL); - } - if (!isset($values)) { + if (!drupal_array_nested_key_exists($form_state['values'], $element['#parents'])) { form_set_value($element, $element['#value'], $form_state); } } @@ -1960,8 +2051,22 @@ function form_type_checkboxes_value($element, $input = FALSE) { } return $value; } + elseif (is_array($input)) { + // Programmatic form submissions use NULL to indicate that a checkbox + // should be unchecked; see drupal_form_submit(). We therefore remove all + // NULL elements from the array before constructing the return value, to + // simulate the behavior of web browsers (which do not send unchecked + // checkboxes to the server at all). This will not affect non-programmatic + // form submissions, since a checkbox can never legitimately be NULL. + foreach ($input as $key => $value) { + if (is_null($value)) { + unset($input[$key]); + } + } + return drupal_map_assoc($input); + } else { - return is_array($input) ? drupal_map_assoc($input) : array(); + return array(); } } @@ -2082,26 +2187,7 @@ function form_type_token_value($element, $input = FALSE) { * Form state array where the value change should be recorded. */ function form_set_value($element, $value, &$form_state) { - _form_set_value($form_state['values'], $element, $element['#parents'], $value); -} - -/** - * Helper function for form_set_value() and _form_builder_handle_input_element(). - * - * We iterate over $parents and create nested arrays for them in $form_values if - * needed. Then we insert the value into the last parent key. - */ -function _form_set_value(&$form_values, $element, $parents, $value) { - $parent = array_shift($parents); - if (empty($parents)) { - $form_values[$parent] = $value; - } - else { - if (!isset($form_values[$parent])) { - $form_values[$parent] = array(); - } - _form_set_value($form_values[$parent], $element, $parents, $value); - } + drupal_array_set_nested_value($form_state['values'], $element['#parents'], $value); } /** @@ -2310,15 +2396,16 @@ function theme_fieldset($variables) { */ function theme_radio($variables) { $element = $variables['element']; + $element['#attributes']['type'] = 'radio'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#return_value']; + if (check_plain($element['#value']) == $element['#return_value']) { + $element['#attributes']['checked'] = 'checked'; + } _form_set_class($element, array('form-radio')); - $output = '<input type="radio" '; - $output .= 'id="' . $element['#id'] . '" '; - $output .= 'name="' . $element['#name'] . '" '; - $output .= 'value="' . $element['#return_value'] . '" '; - $output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' '; - $output .= drupal_attributes($element['#attributes']) . ' />'; - return $output; + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** @@ -2522,12 +2609,8 @@ function form_process_radios($element) { '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)), + '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); - foreach (array('#ajax', '#disabled', '#allow_focus') as $property) { - if (isset($element[$property])) { - $element[$key][$property] = $element[$property]; - } - } } } } @@ -2548,19 +2631,17 @@ function form_process_radios($element) { function theme_checkbox($variables) { $element = $variables['element']; $t = get_t(); - _form_set_class($element, array('form-checkbox')); - $checkbox = '<input '; - $checkbox .= 'type="checkbox" '; - $checkbox .= 'name="' . $element['#name'] . '" '; - $checkbox .= 'id="' . $element['#id'] . '" ' ; - $checkbox .= 'value="' . $element['#return_value'] . '" '; + $element['#attributes']['type'] = 'checkbox'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#return_value']; // Unchecked checkbox has #value of integer 0. if ($element['#value'] !== 0 && $element['#value'] == $element['#return_value']) { - $checkbox .= 'checked="checked" '; + $element['#attributes']['checked'] = 'checked'; } - $checkbox .= drupal_attributes($element['#attributes']) . ' />'; + _form_set_class($element, array('form-checkbox')); - return $checkbox; + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** @@ -2623,12 +2704,8 @@ function form_process_checkboxes($element) { '#return_value' => $key, '#default_value' => isset($value[$key]) ? $key : NULL, '#attributes' => $element['#attributes'], + '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); - foreach (array('#ajax', '#disabled', '#allow_focus') as $property) { - if (isset($element[$property])) { - $element[$key][$property] = $element[$property]; - } - } } } } @@ -2746,9 +2823,14 @@ function theme_tableselect($variables) { // Add an empty header or a "Select all" checkbox to provide room for the // checkboxes/radios in the first table column. if ($element['#js_select']) { + // Add a "Select all" checkbox when checkboxes are displayed. drupal_add_js('misc/tableselect.js'); array_unshift($header, array('class' => array('select-all'))); } + else { + // Add an empty header when radio buttons are displayed. + array_unshift($header, ''); + } } return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => $element['#empty'], 'attributes' => $element['#attributes'])); } @@ -2808,13 +2890,9 @@ function form_process_tableselect($element) { '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)), + '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); } - foreach (array('#ajax', '#disabled', '#allow_focus') as $property) { - if (isset($element[$property])) { - $element[$key][$property] = $element[$property]; - } - } } } } @@ -3011,9 +3089,18 @@ function theme_submit($variables) { */ function theme_button($variables) { $element = $variables['element']; + $element['#attributes']['type'] = 'submit'; + if (!empty($element['#name'])) { + $element['#attributes']['name'] = $element['#name']; + } + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#value']; $element['#attributes']['class'][] = 'form-' . $element['#button_type']; + if (!empty($element['#attributes']['disabled'])) { + $element['#attributes']['class'][] = 'form-button-disabled'; + } - return '<input type="submit" ' . (empty($element['#name']) ? '' : 'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . '" ' . drupal_attributes($element['#attributes']) . " />\n"; + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** @@ -3028,15 +3115,24 @@ function theme_button($variables) { */ function theme_image_button($variables) { $element = $variables['element']; + $element['#attributes']['type'] = 'image'; + $element['#attributes']['name'] = $element['#name']; + if (!empty($element['#value'])) { + $element['#attributes']['value'] = $element['#value']; + } + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['src'] = file_create_url($element['#src']); + if (!empty($element['#title'])) { + $element['#attributes']['alt'] = $element['#title']; + $element['#attributes']['title'] = $element['#title']; + } + $element['#attributes']['class'][] = 'form-' . $element['#button_type']; + if (!empty($element['#attributes']['disabled'])) { + $element['#attributes']['class'][] = 'form-button-disabled'; + } - return '<input type="image" name="' . $element['#name'] . '" ' . - (!empty($element['#value']) ? ('value="' . check_plain($element['#value']) . '" ') : '') . - 'id="' . $element['#id'] . '" ' . - drupal_attributes($element['#attributes']) . - ' src="' . file_create_url($element['#src']) . '" ' . - (!empty($element['#title']) ? 'alt="' . check_plain($element['#title']) . '" title="' . check_plain($element['#title']) . '" ' : '' ) . - "/>\n"; + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** @@ -3051,7 +3147,11 @@ function theme_image_button($variables) { */ function theme_hidden($variables) { $element = $variables['element']; - return '<input type="hidden" name="' . $element['#name'] . '" id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . "\" " . drupal_attributes($element['#attributes']) . " />\n"; + $element['#attributes']['type'] = 'hidden'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#value']; + return '<input' . drupal_attributes($element['#attributes']) . " />\n"; } /** @@ -3067,20 +3167,33 @@ function theme_hidden($variables) { */ function theme_textfield($variables) { $element = $variables['element']; - $size = empty($element['#size']) ? '' : ' size="' . $element['#size'] . '"'; - $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="' . $element['#maxlength'] . '"'; - $class = array('form-text'); - $extra = ''; - $output = ''; + $element['#attributes']['type'] = 'text'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#value']; + if (!empty($element['#size'])) { + $element['#attributes']['size'] = $element['#size']; + } + if (!empty($element['#maxlength'])) { + $element['#attributes']['maxlength'] = $element['#maxlength']; + } + _form_set_class($element, array('form-text')); + $extra = ''; if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) { drupal_add_js('misc/autocomplete.js'); - $class[] = 'form-autocomplete'; - $extra = '<input class="autocomplete" type="hidden" id="' . $element['#id'] . '-autocomplete" value="' . check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) . '" disabled="disabled" />'; + $element['#attributes']['class'][] = 'form-autocomplete'; + + $attributes = array(); + $attributes['type'] = 'hidden'; + $attributes['id'] = $element['#id'] . '-autocomplete'; + $attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE)); + $attributes['disabled'] = 'disabled'; + $attributes['class'][] = 'autocomplete'; + $extra = '<input' . drupal_attributes($attributes) . ' />'; } - _form_set_class($element, $class); - $output .= '<input type="text"' . $maxlength . ' name="' . $element['#name'] . '" id="' . $element['#id'] . '"' . $size . ' value="' . check_plain($element['#value']) . '"' . drupal_attributes($element['#attributes']) . ' />'; + $output = '<input' . drupal_attributes($element['#attributes']) . ' />'; return $output . $extra; } @@ -3097,14 +3210,16 @@ function theme_textfield($variables) { */ function theme_form($variables) { $element = $variables['element']; - // Anonymous div to satisfy XHTML compliance. - $action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : ''; - + if (!empty($element['#action'])) { + $element['#attributes']['action'] = drupal_strip_dangerous_protocols($element['#action']); + } + $element['#attributes']['method'] = $element['#method']; if (empty($element['#attributes']['accept-charset'])) { $element['#attributes']['accept-charset'] = "UTF-8"; } - - return '<form '. $action .' method="'. $element['#method'] .'" id="'. $element['#id'] .'"'. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n</div></form>\n"; + $element['#attributes']['id'] = $element['#id']; + // Anonymous DIV to satisfy XHTML compliance. + return '<form' . drupal_attributes($element['#attributes']) . '><div>' . $element['#children'] . '</div></form>'; } /** @@ -3120,10 +3235,15 @@ function theme_form($variables) { */ function theme_textarea($variables) { $element = $variables['element']; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['cols'] = $element['#cols']; + $element['#attributes']['rows'] = $element['#rows']; + _form_set_class($element, array('form-textarea')); + $wrapper_attributes = array( 'class' => array('form-textarea-wrapper'), ); - $class = array('form-textarea'); // Add resizable behavior. if (!empty($element['#resizable'])) { @@ -3132,10 +3252,7 @@ function theme_textarea($variables) { } $output = '<div' . drupal_attributes($wrapper_attributes) . '>'; - - _form_set_class($element, $class); - $output .= '<textarea cols="' . $element['#cols'] . '" rows="' . $element['#rows'] . '" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>'; - + $output .= '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>'; $output .= '</div>'; return $output; } @@ -3153,12 +3270,19 @@ function theme_textarea($variables) { */ function theme_password($variables) { $element = $variables['element']; - $size = $element['#size'] ? ' size="' . $element['#size'] . '" ' : ''; - $maxlength = $element['#maxlength'] ? ' maxlength="' . $element['#maxlength'] . '" ' : ''; - + $element['#attributes']['type'] = 'password'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + $element['#attributes']['value'] = $element['#value']; + if (!empty($element['#size'])) { + $element['#attributes']['size'] = $element['#size']; + } + if (!empty($element['#maxlength'])) { + $element['#attributes']['maxlength'] = $element['#maxlength']; + } _form_set_class($element, array('form-text')); - $output = '<input type="password" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . $maxlength . $size . drupal_attributes($element['#attributes']) . ' />'; - return $output; + + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** @@ -3191,17 +3315,30 @@ function form_process_weight($element) { */ function theme_file($variables) { $element = $variables['element']; + $element['#attributes']['type'] = 'file'; + $element['#attributes']['name'] = $element['#name']; + $element['#attributes']['id'] = $element['#id']; + if (!empty($element['#size'])) { + $element['#attributes']['size'] = $element['#size']; + } _form_set_class($element, array('form-file')); - return '<input type="file" name="' . $element['#name'] . '"' . ($element['#attributes'] ? ' ' . drupal_attributes($element['#attributes']) : '') . ' id="' . $element['#id'] . '" size="' . $element['#size'] . "\" />\n"; + + return '<input' . drupal_attributes($element['#attributes']) . ' />'; } /** * Returns HTML for a form element. * - * Each form element is wrapped in a DIV with #type and #name classes. In - * addition to the element itself, the div contains a label before or after - * the element based on the optional #title_display property. After the label - * and fields this function outputs the optional element #description. + * Each form element is wrapped in a DIV container having the following CSS + * classes: + * - form-item: Generic for all form elements. + * - form-type-#type: The internal element #type. + * - form-item-#name: The internal form element #name (usually derived from the + * $form structure and set via form_builder()). + * - form-disabled: Only set if the form element is #disabled. + * + * In addition to the element itself, the DIV contains a label for the element + * based on the optional #title_display property, and an optional #description. * * The optional #title_display property can have these values: * - before: The label is output before the element. This is the default. @@ -3252,6 +3389,10 @@ function theme_form_element($variables) { if (!empty($element['#name'])) { $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => '')); } + // Add a class for disabled elements to facilitate cross-browser styling. + if (!empty($element['#attributes']['disabled'])) { + $attributes['class'][] = 'form-disabled'; + } $output = '<div' . drupal_attributes($attributes) . '>' . "\n"; // If #title is not set, we don't display any label or required marker. diff --git a/includes/image.inc b/includes/image.inc index 6896bc33133138a25f1ceacc6a0ea6d99507a1ac..394fb016331da0edb3e8afeb7e4c62387e9d98ad 100644 --- a/includes/image.inc +++ b/includes/image.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.inc,v 1.39 2010/01/25 10:38:34 dries Exp $ +// $Id: image.inc,v 1.40 2010/07/16 02:39:38 dries Exp $ /** * @file @@ -85,6 +85,7 @@ function image_get_toolkit() { * An image object returned by image_load(). * @param $params * An optional array of parameters to pass to the toolkit method. + * * @return * Mixed values (typically Boolean indicating successful operation). */ @@ -109,6 +110,7 @@ function image_toolkit_invoke($method, stdClass $image, array $params = array()) * String specifying the path of the image file. * @param $toolkit * An optional image toolkit name to override the default. + * * @return * FALSE, if the file could not be found or is not an image. Otherwise, a * keyed array containing information about the image: @@ -155,6 +157,7 @@ function image_get_info($filepath, $toolkit = FALSE) { * The target width, in pixels. * @param $height * The target height, in pixels. + * * @return * TRUE or FALSE, based on success. * @@ -189,6 +192,7 @@ function image_scale_and_crop(stdClass $image, $width, $height) { * @param $upscale * Boolean indicating that files smaller than the dimensions will be scaled * up. This generally results in a low quality image. + * * @return * TRUE or FALSE, based on success. * @@ -233,6 +237,7 @@ function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = * The target width, in pixels. * @param $height * The target height, in pixels. + * * @return * TRUE or FALSE, based on success. * @@ -259,6 +264,7 @@ function image_resize(stdClass $image, $width, $height) { * 0xff00ff for magenta, and 0xffffff for white. For images that support * transparency, this will default to transparent. Otherwise it will * be white. + * * @return * TRUE or FALSE, based on success. * @@ -282,6 +288,7 @@ function image_rotate(stdClass $image, $degrees, $background = NULL) { * The target width, in pixels. * @param $height * The target height, in pixels. + * * @return * TRUE or FALSE, based on success. * @@ -305,6 +312,7 @@ function image_crop(stdClass $image, $x, $y, $width, $height) { * * @param $image * An image object returned by image_load(). + * * @return * TRUE or FALSE, based on success. * @@ -325,6 +333,7 @@ function image_desaturate(stdClass $image) { * Path to an image file. * @param $toolkit * An optional, image toolkit name to override the default. + * * @return * An image object or FALSE if there was a problem loading the file. The * image object has the following properties: @@ -367,6 +376,7 @@ function image_load($file, $toolkit = FALSE) { * @param $destination * Destination path where the image should be saved. If it is empty the * original image file will be overwritten. + * * @return * TRUE or FALSE, based on success. * diff --git a/includes/install.core.inc b/includes/install.core.inc index 923e8668f42ff7c05c1cffd55ac83417cc70d670..fed7784b5dcbf9d810e825fd21b46a1779bb7d8b 100644 --- a/includes/install.core.inc +++ b/includes/install.core.inc @@ -1,5 +1,5 @@ <?php -// $Id: install.core.inc,v 1.25 2010/07/02 15:32:10 dries Exp $ +// $Id: install.core.inc,v 1.30 2010/09/14 21:42:05 dries Exp $ /** * @file @@ -275,12 +275,12 @@ function install_begin_request(&$install_state) { require_once DRUPAL_ROOT . '/includes/cache-install.inc'; $conf['cache_default_class'] = 'DrupalFakeCache'; - // Prepare for themed output, if necessary. We need to run this at the - // beginning of the page request to avoid a different theme accidentally - // getting set. - if ($install_state['interactive']) { - drupal_maintenance_theme(); - } + // Prepare for themed output. We need to run this at the beginning of the + // page request to avoid a different theme accidentally getting set. (We also + // need to run it even in the case of command-line installations, to prevent + // any code in the installer that happens to initialize the theme system from + // accessing the database before it is set up yet.) + drupal_maintenance_theme(); // Check existing settings.php. $install_state['settings_verified'] = install_verify_settings(); @@ -858,7 +858,7 @@ function install_settings_form($form, &$form_state, &$install_state) { ); if (count($drivers) == 1) { $form['driver']['#disabled'] = TRUE; - $form['driver']['#description'] .= ' ' . st('Your PHP configuration only supports the %driver database type so it has been automatically selected.', array('%driver' => current($drivers))); + $form['driver']['#description'] .= ' ' . st('Your PHP configuration only supports a single database type, so it has been automatically selected.'); } // Database name. @@ -1111,6 +1111,11 @@ function install_select_profile_form($form, &$form_state, $profile_files) { include_once DRUPAL_ROOT . '/' . $profile->uri; $details = install_profile_info($profile->name); + // Don't show hidden profiles. This is used by to hide the testing profile, + // which only exists to speed up test runs. + if ($details['hidden'] === TRUE) { + continue; + } $profiles[$profile->name] = $details; // Determine the name of the profile; default to file name if defined name @@ -1349,16 +1354,34 @@ function install_profile_modules(&$install_state) { $files = system_rebuild_module_data(); variable_del('install_profile_modules'); - // Install dependencies first. - $modules = array_flip($modules); - foreach ($modules as $module => $weight) { - $modules[$module] = $files[$module]->sort; + // Always install required modules first. Respect the dependencies between + // the modules. + $required = array(); + $non_required = array(); + // Although the profile module is marked as required, it needs to go after + // every dependency, including non-required ones. So clear its required + // flag for now to allow it to install late. + $files[$install_state['parameters']['profile']]->info['required'] = FALSE; + // Add modules that other modules depend on. + foreach ($modules as $module) { + if ($files[$module]->requires) { + $modules = array_merge($modules, array_keys($files[$module]->requires)); + } + } + $modules = array_unique($modules); + foreach ($modules as $module) { + if (!empty($files[$module]->info['required'])) { + $required[$module] = $files[$module]->sort; + } + else { + $non_required[$module] = $files[$module]->sort; + } } - arsort($modules); - $modules = array_keys($modules); + arsort($required); + arsort($non_required); $operations = array(); - foreach ($modules as $module) { + foreach ($required + $non_required as $module => $weight) { $operations[] = array('_install_module_batch', array($module, $files[$module]->info['name'])); } $batch = array( @@ -1545,7 +1568,7 @@ function install_check_requirements($install_state) { $exists = FALSE; // Verify that the directory exists. if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) { - // Check to make sure a settings.php already exists. + // Check if a settings.php file already exists. $file = $settings_file; if (drupal_verify_install_file($settings_file, FILE_EXIST)) { // If it does, make sure it is writable. @@ -1564,6 +1587,38 @@ function install_check_requirements($install_state) { 'description' => st('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array('@drupal' => drupal_install_profile_distribution_name(), '%default-file' => $default_settings_file)), ); } + // Otherwise, if settings.php does not exist yet, we can try to copy + // default.settings.php to create it. + elseif (!$exists) { + $copied = drupal_verify_install_file($conf_path, FILE_EXIST|FILE_WRITABLE, 'dir') && @copy($default_settings_file, $settings_file); + if ($copied) { + // If the new settings file has the same owner as default.settings.php, + // this means default.settings.php is owned by the webserver user. + // This is an inherent security weakness because it allows a malicious + // webserver process to append arbitrary PHP code and then execute it. + // However, it is also a common configuration on shared hosting, and + // there is nothing Drupal can do to prevent it. In this situation, + // having settings.php also owned by the webserver does not introduce + // any additional security risk, so we keep the file in place. + if (fileowner($default_settings_file) === fileowner($settings_file)) { + $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE); + $exists = TRUE; + } + // If settings.php and default.settings.php have different owners, this + // probably means the server is set up "securely" (with the webserver + // running as its own user, distinct from the user who owns all the + // Drupal PHP files), although with either a group or world writable + // sites directory. Keeping settings.php owned by the webserver would + // therefore introduce a security risk. It would also cause a usability + // problem, since site owners who do not have root access to the file + // system would be unable to edit their settings file later on. We + // therefore must delete the file we just created and force the + // administrator to log on to the server and create it manually. + else { + drupal_unlink($settings_file); + } + } + } // If settings.php does not exist, throw an error. if (!$exists) { diff --git a/includes/install.inc b/includes/install.inc index 4397211a93ac0a0fb3f5b05fb33ba7e45b5a16d0..7bdc0efdc70351de26af2c199b72896000f532a6 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -1,5 +1,5 @@ <?php -// $Id: install.inc,v 1.136 2010/06/21 02:27:47 webchick Exp $ +// $Id: install.inc,v 1.142 2010/08/22 15:31:18 dries Exp $ /** * Indicates that a module has not been installed yet. @@ -237,7 +237,9 @@ function drupal_detect_database_types() { require_once DRUPAL_ROOT . '/includes/database/database.inc'; spl_autoload_register('db_autoload'); foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) { - $drivers[$file->filename] = $file->uri; + if (file_exists($file->uri . '/database.inc') && file_exists($file->uri . '/install.inc')) { + $drivers[$file->filename] = $file->uri; + } } foreach ($drivers as $driver => $file) { @@ -278,7 +280,7 @@ abstract class DatabaseTasks { 'arguments' => array( 'CREATE TABLE drupal_install_test (id int NULL)', 'Drupal can use CREATE TABLE database commands.', - 'Failed to <strong>CREATE</strong> a test table on your %name database server with the command %query. %name reports the following message: %error.<p>Are you sure the configured username has the necessary %name permissions to create tables in the database?</p>', + 'Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>', TRUE, ), ), @@ -286,28 +288,28 @@ abstract class DatabaseTasks { 'arguments' => array( 'INSERT INTO drupal_install_test (id) VALUES (1)', 'Drupal can use INSERT database commands.', - 'Failed to <strong>INSERT</strong> a value into a test table on your %name database server. We tried inserting a value with the command %query and %name reported the following error: %error.', + 'Failed to <strong>INSERT</strong> a value into a test table on your database server. We tried inserting a value with the command %query and the server reported the following error: %error.', ), ), array( 'arguments' => array( 'UPDATE drupal_install_test SET id = 2', 'Drupal can use UPDATE database commands.', - 'Failed to <strong>UPDATE</strong> a value in a test table on your %name database server. We tried updating a value with the command %query and %name reported the following error: %error.', + 'Failed to <strong>UPDATE</strong> a value in a test table on your database server. We tried updating a value with the command %query and the server reported the following error: %error.', ), ), array( 'arguments' => array( 'DELETE FROM drupal_install_test', 'Drupal can use DELETE database commands.', - 'Failed to <strong>DELETE</strong> a value from a test table on your %name database server. We tried deleting a value with the command %query and %name reported the following error: %error.', + 'Failed to <strong>DELETE</strong> a value from a test table on your database server. We tried deleting a value with the command %query and the server reported the following error: %error.', ), ), array( 'arguments' => array( 'DROP TABLE drupal_install_test', 'Drupal can use DROP TABLE database commands.', - 'Failed to <strong>DROP</strong> a test table from your %name database server. We tried dropping a table with the command %query and %name reported the following error %error.', + 'Failed to <strong>DROP</strong> a test table from your database server. We tried dropping a table with the command %query and the server reported the following error %error.', ), ), ); @@ -394,7 +396,7 @@ abstract class DatabaseTasks { $this->pass('Drupal can CONNECT to the database ok.'); } catch (Exception $e) { - $this->fail(st('Failed to connect to your %name database server. %name reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li></ul>For more help, see the <a href="http://drupal.org/getting-started/install">installation handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => $e->getMessage(), '%name' => $this->name()))); + $this->fail(st('Failed to connect to your database server. The server reports the following message: %error.<ul><li>Is the database server running?</li><li>Does the database exist, and have you entered the correct database name?</li><li>Have you entered the correct username and password?</li><li>Have you entered the correct database hostname?</li></ul>', array('%error' => $e->getMessage()))); return FALSE; } return TRUE; @@ -731,19 +733,19 @@ function drupal_install_mkdir($file, $mask, $message = TRUE) { if ($mask & $m) { switch ($m) { case FILE_READABLE: - $mod += 444; + $mod |= 0444; break; case FILE_WRITABLE: - $mod += 222; + $mod |= 0222; break; case FILE_EXECUTABLE: - $mod += 111; + $mod |= 0111; break; } } } - if (@drupal_mkdir($file, intval("0$mod", 8))) { + if (@drupal_mkdir($file, $mod)) { return TRUE; } else { @@ -1020,6 +1022,7 @@ function install_profile_info($profile, $locale = 'en') { 'description' => '', 'distribution_name' => 'Drupal', 'version' => NULL, + 'hidden' => FALSE, 'php' => DRUPAL_MINIMUM_PHP, ); $info = drupal_parse_info_file("profiles/$profile/$profile.info") + $defaults; @@ -1050,5 +1053,5 @@ function db_run_tasks($driver) { $task_class = 'DatabaseTasks_' . $driver; $DatabaseTasks = new $task_class(); $DatabaseTasks->runTasks(); - return true; + return TRUE; } diff --git a/includes/locale.inc b/includes/locale.inc index 08c549288feb6b3f4a06a45d776cb69e129ffa06..8662afd8f3c197b02f9a4b476db642782a3f1993 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -1,5 +1,5 @@ <?php -// $Id: locale.inc,v 1.256 2010/06/03 13:57:41 dries Exp $ +// $Id: locale.inc,v 1.258 2010/07/30 02:47:27 dries Exp $ /** * @file @@ -407,13 +407,15 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction * Parses Gettext Portable Object file information and inserts into database * * @param $file - * Drupal file object corresponding to the PO file to import + * Drupal file object corresponding to the PO file to import. * @param $langcode - * Language code + * Language code. * @param $mode - * Should existing translations be replaced LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE + * Should existing translations be replaced LOCALE_IMPORT_KEEP or + * LOCALE_IMPORT_OVERWRITE. * @param $group - * Text group to import PO file into (eg. 'default' for interface translations) + * Text group to import PO file into (eg. 'default' for interface + * translations). */ function _locale_import_po($file, $langcode, $mode, $group = NULL) { // Try to allocate enough time to parse and import the data. @@ -460,15 +462,17 @@ function _locale_import_po($file, $langcode, $mode, $group = NULL) { * Parses Gettext Portable Object file into an array * * @param $op - * Storage operation type: db-store or mem-store + * Storage operation type: db-store or mem-store. * @param $file - * Drupal file object corresponding to the PO file to import + * Drupal file object corresponding to the PO file to import. * @param $mode - * Should existing translations be replaced LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE + * Should existing translations be replaced LOCALE_IMPORT_KEEP or + * LOCALE_IMPORT_OVERWRITE. * @param $lang - * Language code + * Language code. * @param $group - * Text group to import PO file into (eg. 'default' for interface translations) + * Text group to import PO file into (eg. 'default' for interface + * translations). */ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group = 'default') { @@ -631,11 +635,11 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group = * Sets an error message occurred during locale file parsing. * * @param $message - * The message to be translated + * The message to be translated. * @param $file - * Drupal file object corresponding to the PO file to import + * Drupal file object corresponding to the PO file to import. * @param $lineno - * An optional line number argument + * An optional line number argument. */ function _locale_import_message($message, $file, $lineno = NULL) { $vars = array('%filename' => $file->filename); @@ -650,17 +654,20 @@ function _locale_import_message($message, $file, $lineno = NULL) { * Imports a string into the database * * @param $op - * Operation to perform: 'db-store', 'db-report', 'mem-store' or 'mem-report' + * Operation to perform: 'db-store', 'db-report', 'mem-store' or 'mem-report'. * @param $value - * Details of the string stored + * Details of the string stored. * @param $mode - * Should existing translations be replaced LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE + * Should existing translations be replaced LOCALE_IMPORT_KEEP or + * LOCALE_IMPORT_OVERWRITE. * @param $lang - * Language to store the string in + * Language to store the string in. * @param $file - * Object representation of file being imported, only required when op is 'db-store' + * Object representation of file being imported, only required when op is + * 'db-store'. * @param $group - * Text group to import PO file into (eg. 'default' for interface translations) + * Text group to import PO file into (eg. 'default' for interface + * translations). */ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NULL, $file = NULL, $group = 'default') { $report = &drupal_static(__FUNCTION__, array('additions' => 0, 'updates' => 0, 'deletes' => 0, 'skips' => 0)); @@ -771,6 +778,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL * Optional plural ID to use. * @param $plural * Optional plural value to use. + * * @return * The string ID of the existing string modified or the new string added. */ @@ -868,9 +876,10 @@ function _locale_import_one_string_db(&$report, $langcode, $context, $source, $t * Parses a Gettext Portable Object file header * * @param $header - * A string containing the complete header + * A string containing the complete header. + * * @return - * An associative array of key-value pairs + * An associative array of key-value pairs. */ function _locale_import_parse_header($header) { $header_parsed = array(); @@ -888,12 +897,13 @@ function _locale_import_parse_header($header) { * Parses a Plural-Forms entry from a Gettext Portable Object file header * * @param $pluralforms - * A string containing the Plural-Forms entry + * A string containing the Plural-Forms entry. * @param $filepath - * A string containing the filepath + * A string containing the filepath. + * * @return * An array containing the number of plurals and a - * formula in PHP for computing the plural form + * formula in PHP for computing the plural form. */ function _locale_import_parse_plural_forms($pluralforms, $filepath) { // First, delete all whitespace @@ -934,9 +944,10 @@ function _locale_import_parse_plural_forms($pluralforms, $filepath) { * precedence and associativity. * * @param $string - * A string containing the arithmetic formula + * A string containing the arithmetic formula. + * * @return - * The PHP version of the formula + * The PHP version of the formula. */ function _locale_import_parse_arithmetic($string) { // Operator precedence table @@ -1033,9 +1044,10 @@ function _locale_import_parse_arithmetic($string) { * Backward compatible implementation of token_get_all() for formula parsing * * @param $string - * A string containing the arithmetic formula + * A string containing the arithmetic formula. + * * @return - * The PHP version of the formula + * The PHP version of the formula. */ function _locale_import_tokenize_formula($formula) { $formula = str_replace(" ", "", $formula); @@ -1099,9 +1111,9 @@ function _locale_import_tokenize_formula($formula) { * This is a callback function used via array_map() * * @param $entry - * An array element + * An array element. * @param $key - * Index of the array element + * Index of the array element. */ function _locale_import_append_plural($entry, $key) { // No modifications for 0, 1 @@ -1118,9 +1130,10 @@ function _locale_import_append_plural($entry, $key) { * Generate a short, one string version of the passed comment array * * @param $comment - * An array of strings containing a comment + * An array of strings containing a comment. + * * @return - * Short one string version of the comment + * Short one string version of the comment. */ function _locale_import_shorten_comments($comment) { $comm = ''; @@ -1140,9 +1153,10 @@ function _locale_import_shorten_comments($comment) { * Parses a string in quotes * * @param $string - * A string specified with enclosing quotes + * A string specified with enclosing quotes. + * * @return - * The string parsed from inside the quotes + * The string parsed from inside the quotes. */ function _locale_import_parse_quoted($string) { if (substr($string, 0, 1) != substr($string, -1, 1)) { @@ -1247,7 +1261,8 @@ function _locale_parse_js_file($filepath) { * Language object to generate the output for, or NULL if generating * translation template. * @param $group - * Text group to export PO file from (eg. 'default' for interface translations) + * Text group to export PO file from (eg. 'default' for interface + * translations). */ function _locale_export_get_strings($language = NULL, $group = 'default') { if (isset($language)) { @@ -1602,6 +1617,7 @@ function _locale_translate_seek_query() { * * @param $langcode * The language code for which the file needs to be refreshed. + * * @return * New content of the 'javascript_parsed' variable. */ @@ -1759,7 +1775,7 @@ function _locale_rebuild_js($langcode = NULL) { */ function _locale_translate_language_list($translation, $limit_language) { // Add CSS. - drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); $languages = language_list(); unset($languages['en']); @@ -1826,6 +1842,7 @@ function _locale_prepare_predefined_list() { * @param $skip * Array of component names to skip. Used in the installer for the * second pass import, when most components are already imported. + * * @return * A batch structure or FALSE if no files found. */ @@ -1854,6 +1871,7 @@ function locale_batch_by_language($langcode, $finished = NULL, $skip = array()) /** * Prepare a batch to run when installing modules or enabling themes. + * * This batch will import translations for the newly added components * in all the languages already set up on the site. * @@ -1889,13 +1907,14 @@ function locale_batch_by_component($components, $finished = '_locale_batch_syste * Build a locale batch from an array of files. * * @param $files - * Array of files to import + * Array of files to import. * @param $finished * Optional finished callback for the batch. * @param $components * Optional list of component names the batch covers. Used in the installer. + * * @return - * A batch structure + * A batch structure. */ function _locale_batch_build($files, $finished = NULL, $components = array()) { $t = get_t(); @@ -2011,6 +2030,7 @@ function locale_date_format_save($langcode, $type, $format) { * * @param $languages * An array of language codes. + * * @return * An array of date formats. */ diff --git a/includes/lock.inc b/includes/lock.inc index fe856e97f96477120d90a7a69facb15d80db4f3a..546f6bff5dcae1af120106f0a586a0923e5fd81e 100644 --- a/includes/lock.inc +++ b/includes/lock.inc @@ -1,5 +1,5 @@ <?php -// $Id: lock.inc,v 1.4 2010/05/06 15:20:18 dries Exp $ +// $Id: lock.inc,v 1.5 2010/07/16 11:19:38 dries Exp $ /** * @file @@ -91,6 +91,7 @@ function _lock_id() { * The name of the lock. * @param $timeout * A number of seconds (float) before the lock expires (minimum of 0.001). + * * @return * TRUE if the lock was acquired, FALSE if it failed. */ @@ -154,6 +155,7 @@ function lock_acquire($name, $timeout = 30.0) { * * @param $name * The name of the lock. + * * @return * TRUE if there is no lock or it was removed, FALSE otherwise. */ @@ -190,6 +192,7 @@ function lock_may_be_available($name) { * The name of the lock. * @param $delay * The maximum number of seconds to wait, as an integer. + * * @return * TRUE if the lock holds, FALSE if it is available. */ diff --git a/includes/mail.inc b/includes/mail.inc index 145b5269ab9513819e50f004ee9b21a38f61e047..6481bac9944de3a269358359e46d9753e41432d4 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -1,5 +1,5 @@ <?php -// $Id: mail.inc,v 1.32 2010/06/17 13:16:57 dries Exp $ +// $Id: mail.inc,v 1.34 2010/07/28 02:19:56 dries Exp $ /** * @file @@ -53,12 +53,14 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER * } * * function example_mail($key, &$message, $params) { - * $language = $message['language']; - * $variables = user_mail_tokens($params['account'], $language); + * $data['user'] = $params['account']; + * $options['language'] = $message['language']; + * user_mail_tokens($variables, $data, $options); * switch($key) { * case 'notice': - * $message['subject'] = t('Notification from !site', $variables, $language->language); - * $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, $language->language); + * $langcode = $message['language']->language; + * $message['subject'] = t('Notification from !site', $variables, array('langcode' => $langcode)); + * $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, array('langcode' => $langcode)); * break; * } * } @@ -87,6 +89,7 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER * @param $send * Send the message directly, without calling drupal_mail_system()->mail() * manually. + * * @return * The $message array structure containing all details of the * message. If already sent ($send = TRUE), then the 'result' element @@ -218,6 +221,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N * @param $key * A key to identify the e-mail sent. The final e-mail ID for the e-mail * alter hook in drupal_mail() would have been {$module}_{$key}. + * * @return MailSystemInterface */ function drupal_mail_system($module, $key) { @@ -287,6 +291,7 @@ interface MailSystemInterface { * - headers: Associative array containing all additional mail headers not * defined by one of the other parameters. PHP's mail() looks for Cc * and Bcc headers and sends the mail to addresses in these headers too. + * * @return * TRUE if the mail was successfully accepted for delivery, otherwise FALSE. */ @@ -354,6 +359,7 @@ function drupal_wrap_mail($text, $indent = '') { * @param $allowed_tags (optional) * If supplied, a list of tags that will be transformed. If omitted, all * all supported tags are transformed. + * * @return * The transformed string. */ diff --git a/includes/menu.inc b/includes/menu.inc index 711818895e10dbc744b28028f333efe506048655..0c698962670b118730f3ee76c819e530ac74d73c 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -1,5 +1,5 @@ <?php -// $Id: menu.inc,v 1.401 2010/07/07 08:05:01 webchick Exp $ +// $Id: menu.inc,v 1.408 2010/09/01 02:59:04 dries Exp $ /** * @file @@ -315,12 +315,16 @@ function menu_get_ancestors($parts) { } $current = ''; for ($j = $length; $j >= 0; $j--) { + // Check the bit on the $j offset. if ($i & (1 << $j)) { + // Bit one means the original value. $current .= $parts[$length - $j]; } else { + // Bit zero means means wildcard. $current .= '%'; } + // Unless we are at offset 0, add a slash. if ($j) { $current .= '/'; } @@ -572,6 +576,7 @@ function _menu_load_objects(&$item, &$map) { * A menu router or menu link item * @param $map * An array of path arguments (ex: array('node', '5')) + * * @return * $item['access'] becomes TRUE if the item is accessible, FALSE otherwise. */ @@ -864,7 +869,7 @@ function menu_get_object($type = 'node', $position = 1, $path = NULL) { } /** - * Render a menu tree based on the current path. + * Renders a menu tree based on the current path. * * The tree is expanded based on the current path and dynamic paths are also * changed according to the defined to_arg functions (for example the 'My @@ -874,7 +879,8 @@ function menu_get_object($type = 'node', $position = 1, $path = NULL) { * The name of the menu. * * @return - * The rendered HTML of that menu on the current page. + * A structured array representing the specified menu on the current page, to + * be rendered by drupal_render(). */ function menu_tree($menu_name) { $menu_output = &drupal_static(__FUNCTION__, array()); @@ -921,8 +927,10 @@ function menu_tree_output($tree) { if ($i == $num_items - 1) { $class[] = 'last'; } - // Set a class if the link has children. - if ($data['below']) { + // Set a class for the <li>-tag. Since $data['below'] may contain local + // tasks, only set 'expanded' class if the link also has children within + // the current menu. + if ($data['link']['has_children'] && $data['below']) { $class[] = 'expanded'; } elseif ($data['link']['has_children']) { @@ -953,7 +961,7 @@ function menu_tree_output($tree) { $build['#sorted'] = TRUE; // Add the theme wrapper for outer markup. // Allow menu-specific theme overrides. - $build['#theme_wrappers'][] = 'menu_tree__' . $data['link']['menu_name']; + $build['#theme_wrappers'][] = 'menu_tree__' . strtr($data['link']['menu_name'], '-', '_'); } return $build; @@ -1609,7 +1617,6 @@ function menu_list_system_menus() { 'management' => 'Management', 'user-menu' => 'User menu', 'main-menu' => 'Main menu', - 'secondary-menu' => 'Secondary menu', ); } @@ -1731,7 +1738,7 @@ function menu_local_tasks($level = 0) { return $empty; } - // Get all tabs and the root page. + // Get all tabs (also known as local tasks) and the root page. $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC)) ->fields('menu_router') ->condition('tab_root', $router_item['tab_root']) @@ -1768,12 +1775,19 @@ function menu_local_tasks($level = 0) { $tab_count = 0; $action_count = 0; foreach ($children[$path] as $item) { + // Local tasks can be normal items too, so bitmask with + // MENU_IS_LOCAL_TASK before checking. + if (!($item['type'] & MENU_IS_LOCAL_TASK)) { + // This item is not a tab, skip it. + continue; + } if ($item['access']) { $link = $item; - // The default task is always active. - if ($item['type'] == MENU_DEFAULT_LOCAL_TASK) { + // The default task is always active. As tabs can be normal items + // too, so bitmask with MENU_LINKS_TO_PARENT before checking. + if (($item['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT) { // Find the first parent which is not a default local task or action. - for ($p = $item['tab_parent']; $tasks[$p]['type'] == MENU_DEFAULT_LOCAL_TASK; $p = $tasks[$p]['tab_parent']); + for ($p = $item['tab_parent']; ($tasks[$p]['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT; $p = $tasks[$p]['tab_parent']); // Use the path of the parent instead. $link['href'] = $tasks[$p]['href']; $tabs_current[] = array( @@ -1785,19 +1799,23 @@ function menu_local_tasks($level = 0) { $tab_count++; } else { - if ($item['type'] == MENU_LOCAL_TASK) { - $tabs_current[] = array( - '#theme' => 'menu_local_task', + // Actions can be normal items too, so bitmask with + // MENU_IS_LOCAL_ACTION before checking. + if (($item['type'] & MENU_IS_LOCAL_ACTION) == MENU_IS_LOCAL_ACTION) { + // The item is an action, display it as such. + $actions_current[] = array( + '#theme' => 'menu_local_action', '#link' => $link, ); - $tab_count++; + $action_count++; } else { - $actions_current[] = array( - '#theme' => 'menu_local_action', + // Otherwise, it's a normal tab. + $tabs_current[] = array( + '#theme' => 'menu_local_task', '#link' => $link, ); - $action_count++; + $tab_count++; } } } @@ -1821,15 +1839,18 @@ function menu_local_tasks($level = 0) { $next_parent = ''; $count = 0; foreach ($children[$parent] as $item) { + // Skip local actions. if ($item['type'] & MENU_IS_LOCAL_ACTION) { continue; } if ($item['access']) { $count++; $link = $item; - if ($item['type'] == MENU_DEFAULT_LOCAL_TASK) { + // Local tasks can be normal items too, so bitmask with + // MENU_LINKS_TO_PARENT before checking. + if (($item['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT) { // Find the first parent which is not a default local task. - for ($p = $item['tab_parent']; $tasks[$p]['type'] == MENU_DEFAULT_LOCAL_TASK; $p = $tasks[$p]['tab_parent']); + for ($p = $item['tab_parent']; ($tasks[$p]['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT; $p = $tasks[$p]['tab_parent']); // Use the path of the parent instead. $link['href'] = $tasks[$p]['href']; if ($item['path'] == $router_item['path']) { @@ -2055,7 +2076,7 @@ function menu_set_active_menu_names($menu_names = NULL) { $active = $menu_names; } elseif (!isset($active)) { - $active = variable_get('menu_default_active_menus', array('management', 'navigation', 'user-menu', 'main-menu')); + $active = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus())); } return $active; } @@ -2211,7 +2232,7 @@ function menu_get_active_breadcrumb() { $end = end($active_trail); // Don't show a link to the current page in the breadcrumb trail. - if ($item['href'] == $end['href'] || ($item['type'] == MENU_DEFAULT_LOCAL_TASK && $end['href'] != '<front>')) { + if ($item['href'] == $end['href'] || (($item['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT && $end['href'] != '<front>')) { array_pop($breadcrumb); } } @@ -2530,6 +2551,7 @@ function _menu_navigation_links_rebuild($menu) { * (optional) The name of a menu that the links will be cloned for. If not * set, the cloned links will be in the same menu as the original set of * links that were passed in. + * * @return * An array of menu links with the same properties as the passed-in array, * but with the link identifiers removed so that a new link will be created @@ -2553,6 +2575,7 @@ function menu_links_clone($links, $menu_name = NULL) { * * @param $menu_name * The name of the menu whose links should be returned. + * * @return * An array of menu links. */ @@ -2926,6 +2949,7 @@ function _menu_find_router_path($link_path) { * @param $link_title * Title of the link to insert or new title to update the link to. * Unused for delete. + * * @return * The insert op returns the mlid of the new item. Others op return NULL. */ @@ -2997,25 +3021,31 @@ function _menu_link_move_children($item, $existing_item) { $query->fields(array('menu_name' => $item['menu_name'])); $p = 'p1'; + $expressions = array(); for ($i = 1; $i <= $item['depth']; $p = 'p' . ++$i) { - $query->fields(array($p => $item[$p])); + $expressions[] = array($p, ":p_$i", array(":p_$i" => $item[$p])); } $j = $existing_item['depth'] + 1; while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) { - $query->expression('p' . $i++, 'p' . $j++); + $expressions[] = array('p' . $i++, 'p' . $j++, array()); } while ($i <= MENU_MAX_DEPTH) { - $query->fields(array('p' . $i++ => 0)); + $expressions[] = array('p' . $i++, 0, array()); } $shift = $item['depth'] - $existing_item['depth']; - if ($shift < 0) { - $query->expression('depth', 'depth - :depth', array(':depth' => -$shift)); + if ($shift > 0) { + // The order of expressions must be reversed so the new values don't + // overwrite the old ones before they can be used because "Single-table + // UPDATE assignments are generally evaluated from left to right" + // see: http://dev.mysql.com/doc/refman/5.0/en/update.html + $expressions = array_reverse($expressions); } - elseif ($shift > 0) { - $query->expression('depth', 'depth + :depth', array(':depth' => $shift)); + foreach ($expressions as $expression) { + $query->expression($expression[0], $expression[1], $expression[2]); } + $query->expression('depth', 'depth + :depth', array(':depth' => $shift)); $query->condition('menu_name', $existing_item['menu_name']); $p = 'p1'; for ($i = 1; $i <= MENU_MAX_DEPTH && $existing_item[$p]; $p = 'p' . ++$i) { diff --git a/includes/module.inc b/includes/module.inc index 7398621816f85cfa4331f941db44402bcc088912..25059bfba38f4879c901f785715a46e5bdc33db5 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -1,5 +1,5 @@ <?php -// $Id: module.inc,v 1.195 2010/06/26 12:34:44 dries Exp $ +// $Id: module.inc,v 1.198 2010/09/03 19:49:55 dries Exp $ /** * @file @@ -12,6 +12,7 @@ * @param $bootstrap * Whether to load only the reduced set of modules loaded in "bootstrap mode" * for cached pages. See bootstrap.inc. + * * @return * If $bootstrap is NULL, return a boolean indicating whether all modules * have been loaded. @@ -46,6 +47,7 @@ function module_load_all($bootstrap = FALSE) { * @param $fixed_list * (Optional) Override the module list with the given modules. Stays until the * next call with $refresh = TRUE. + * * @return * An associative array whose keys and values are the names of all loaded * modules. @@ -70,7 +72,9 @@ function module_list($refresh = FALSE, $bootstrap = FALSE, $sort = FALSE, $fixed $list = system_list('bootstrap'); } else { - $list = system_list('module_enabled'); + // Not using drupal_map_assoc() here as that requires common.inc. + $list = array_keys(system_list('module_enabled')); + $list = (!empty($list) ? array_combine($list, $list) : array()); } } } @@ -94,9 +98,10 @@ function module_list($refresh = FALSE, $bootstrap = FALSE, $sort = FALSE, $fixed * - theme: All themes. * * @return - * An associative array of modules or themes, keyed by name, and having the - * respective database row as value. For $type 'module_enabled' and - * 'bootstrap', the array values equal the keys. + * An associative array of modules or themes, keyed by name. For $type + * 'bootstrap', the array values equal the keys. For $type 'module_enabled' + * or 'theme', the array values are objects representing the respective + * database row, with the 'info' property already unserialized. * * @see module_list() * @see list_themes() @@ -141,11 +146,12 @@ function system_list($type) { // Drupal installations, which might have modules installed in different // locations in the file system. The ordering here must also be // consistent with the one used in module_implements(). - $result = db_query("SELECT * FROM {system} ORDER BY weight ASC, name ASC"); + $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); foreach ($result as $record) { - if ($record->type == 'module' && $record->status) { - // Build a list of all enabled modules. - $lists['module_enabled'][$record->name] = $record->name; + $record->info = unserialize($record->info); + // Build a list of all enabled modules. + if ($record->type == 'module') { + $lists['module_enabled'][$record->name] = $record; } // Build a list of themes. if ($record->type == 'theme') { @@ -183,6 +189,7 @@ function system_list_reset() { * * @param $files * The array of filesystem objects used to rebuild the cache. + * * @return * The same array with the new keys for each module: * - requires: An array with the keys being the modules that this module @@ -192,18 +199,16 @@ function system_list_reset() { */ function _module_build_dependencies($files) { require_once DRUPAL_ROOT . '/includes/graph.inc'; - $roots = $files; foreach ($files as $filename => $file) { $graph[$file->name]['edges'] = array(); if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { foreach ($file->info['dependencies'] as $dependency) { $dependency_data = drupal_parse_dependency($dependency); $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data; - unset($roots[$dependency_data['name']]); } } } - drupal_depth_first_search($graph, array_keys($roots)); + drupal_depth_first_search($graph); foreach ($graph as $module => $data) { $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array(); $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array(); @@ -217,6 +222,7 @@ function _module_build_dependencies($files) { * * @param $module * The name of the module (without the .module extension). + * * @return * TRUE if the module is both installed and enabled. */ @@ -305,6 +311,7 @@ function module_load_all_includes($type, $name = NULL) { * - Invoke hook_enable(). * - Invoke hook_modules_installed(). * - Invoke hook_modules_enabled(). + * * @param $module_list * An array of module names. * @param $enable_dependencies @@ -543,6 +550,7 @@ function module_disable($module_list, $disable_dependents = TRUE) { * The name of the module (without the .module extension). * @param $hook * The name of the hook (e.g. "help" or "menu"). + * * @return * TRUE if the module is both installed and enabled, and the hook is * implemented in that module. @@ -711,6 +719,7 @@ function module_implements_write_cache() { * The name of the hook to invoke. * @param ... * Arguments to pass to the hook implementation. + * * @return * The return value of the hook implementation. */ @@ -730,6 +739,7 @@ function module_invoke() { * The name of the hook to invoke. * @param ... * Arguments to pass to the hook. + * * @return * An array of return values of the hook implementations. If modules return * arrays from their implementations, those are merged into one array. diff --git a/includes/pager.inc b/includes/pager.inc index d8c82e6c63403f8d15e725131d36ca5145555c8c..4934cc1ea5e30bd4d58f1a79ea79e189757e456a 100644 --- a/includes/pager.inc +++ b/includes/pager.inc @@ -1,5 +1,5 @@ <?php -// $Id: pager.inc,v 1.80 2010/05/06 05:59:30 webchick Exp $ +// $Id: pager.inc,v 1.82 2010/09/09 23:18:30 webchick Exp $ /** * @file @@ -58,7 +58,6 @@ class PagerDefault extends SelectQueryExtender { * to it. */ public function execute() { - global $pager_page_array, $pager_total, $pager_total_items, $pager_limits; // Add convenience tag to mark that this is an extended query. We have to // do this in the constructor to ensure that it is set before preExecute() @@ -73,21 +72,9 @@ class PagerDefault extends SelectQueryExtender { } $this->ensureElement(); - $page = isset($_GET['page']) ? $_GET['page'] : ''; - - // Convert comma-separated $page to an array, used by other functions. - $pager_page_array = explode(',', $page); - - if (!isset($pager_page_array[$this->element])) { - $pager_page_array[$this->element] = 0; - } - - // We calculate the total of pages as ceil(items / limit). - $pager_total_items[$this->element] = $this->getCountQuery()->execute()->fetchField(); - $pager_total[$this->element] = ceil($pager_total_items[$this->element] / $this->limit); - $pager_page_array[$this->element] = max(0, min((int) $pager_page_array[$this->element], ((int) $pager_total[$this->element]) - 1)); - $pager_limits[$this->element] = $this->limit; - $this->range($pager_page_array[$this->element] * $this->limit, $this->limit); + $total_items = $this->getCountQuery()->execute()->fetchField(); + $current_page = pager_default_initialize($total_items, $this->limit, $this->element); + $this->range($current_page * $this->limit, $this->limit); // Now that we've added our pager-based range instructions, run the query normally. return $this->query->execute(); @@ -95,16 +82,16 @@ class PagerDefault extends SelectQueryExtender { /** * Ensure that there is an element associated with this query. + * If an element was not specified previously, then the value of the + * $maxElement counter is taken, after which the counter is incremented. * - * After running this query, access $this->element to get the element for this + * After running this method, access $this->element to get the element for this * query. */ protected function ensureElement() { - if (!empty($this->element)) { - return; + if (!isset($this->element)) { + $this->element = self::$maxElement++; } - - $this->element = self::$maxElement++; } /** @@ -162,14 +149,142 @@ class PagerDefault extends SelectQueryExtender { * whatever reason you want to explicitly define an element for a given query, * you may do so here. * + * Setting the element here also increments the static $maxElement counter, + * which is used for determining the $element when there's none specified. + * + * Note that no collision detection is done when setting an element ID + * explicitly, so it is possible for two pagers to end up using the same ID + * if both are set explicitly. + * * @param $element */ public function element($element) { $this->element = $element; + if ($element >= self::$maxElement) { + self::$maxElement = $element + 1; + } return $this; } } +/** + * Returns the current page being requested for display within a pager. + * + * @param $element + * An optional integer to distinguish between multiple pagers on one page. + * + * @return + * The number of the current requested page, within the pager represented by + * $element. This is determined from the URL query parameter $_GET['page'], or + * 0 by default. Note that this number may differ from the actual page being + * displayed. For example, if a search for "example text" brings up three + * pages of results, but a users visits search/node/example+text?page=10, this + * function will return 10, even though the default pager implementation + * adjusts for this and still displays the third page of search results at + * that URL. + * + * @see pager_default_initialize() + */ +function pager_find_page($element = 0) { + $page = isset($_GET['page']) ? $_GET['page'] : ''; + $page_array = explode(',', $page); + if (!isset($page_array[$element])) { + $page_array[$element] = 0; + } + return (int) $page_array[$element]; +} + +/** + * Initializes a pager for theme('pager'). + * + * This function sets up the necessary global variables so that future calls + * to theme('pager') will render a pager that correctly corresponds to the + * items being displayed. + * + * If the items being displayed result from a database query peformed using + * Drupal's database API, and if you have control over the construction of the + * database query, you do not need to call this function directly; instead, you + * can simply extend the query object with the 'PagerDefault' extender before + * executing it. For example: + * @code + * $query = db_select('some_table')->extend('PagerDefault'); + * @endcode + * + * However, if you are using a different method for generating the items to be + * paged through, then you should call this function in preparation. + * + * The following example shows how this function can be used in a page callback + * that invokes an external datastore with an SQL-like syntax: + * @code + * // First find the total number of items and initialize the pager. + * $where = "status = 1"; + * $total = mymodule_select("SELECT COUNT(*) FROM data " . $where)->result(); + * $num_per_page = variable_get('mymodule_num_per_page', 10); + * $page = pager_default_initialize($total, $num_per_page); + * + * // Next, retrieve and display the items for the current page. + * $offset = $num_per_page * $page; + * $result = mymodule_select("SELECT * FROM data " . $where . " LIMIT %d, %d", $offset, $num_per_page)->fetchAll(); + * $output = theme('mymodule_results', array('result' => $result)); + * + * // Finally, display the pager controls, and return. + * $output .= theme('pager'); + * return $output; + * @endcode + * + * A second example involves a page callback that invokes an external search + * service where the total number of matching results is provided as part of + * the returned set (so that we do not need a separate query in order to obtain + * this information). Here, we call pager_find_page() to calculate the desired + * offset before the search is invoked: + * @code + * // Perform the query, using the requested offset from pager_find_page(). + * // This comes from a URL parameter, so here we are assuming that the URL + * // parameter corresponds to an actual page of results that will exist + * // within the set. + * $page = pager_find_page(); + * $num_per_page = variable_get('mymodule_num_per_page', 10); + * $offset = $num_per_page * $page; + * $result = mymodule_remote_search($keywords, $offset, $num_per_page); + * + * // Now that we have the total number of results, initialize the pager. + * pager_default_initialize($result->total, $num_per_page); + * + * // Display the search results. + * $output = theme('search_results', array('results' => $result->data, 'type' => 'remote')); + * + * // Finally, display the pager controls, and return. + * $output .= theme('pager'); + * return $output; + * @endcode + * + * @param $total + * The total number of items to be paged. + * @param $limit + * The number of items the calling code will display per page. + * @param $element + * An optional integer to distinguish between multiple pagers on one page. + * + * @return + * The number of the current page, within the pager represented by $element. + * This is determined from the URL query parameter $_GET['page'], or 0 by + * default. However, if a page that does not correspond to the actual range + * of the result set was requested, this function will return the closest + * page actually within the result set. + */ +function pager_default_initialize($total, $limit, $element = 0) { + global $pager_page_array, $pager_total, $pager_total_items, $pager_limits; + + $page = pager_find_page($element); + + // We calculate the total of pages as ceil(items / limit). + $pager_total_items[$element] = $total; + $pager_total[$element] = ceil($pager_total_items[$element] / $limit); + $pager_page_array[$element] = max(0, min($page, ((int) $pager_total[$element]) - 1)); + $pager_limits[$element] = $limit; + return $pager_page_array[$element]; +} + /** * Compose a URL query parameter array for pager links. * diff --git a/includes/path.inc b/includes/path.inc index 09c80cea348ae87afbcccc88c18bae4ab3f61406..139542e1b314a17648a44813bcc982b569c13e48 100644 --- a/includes/path.inc +++ b/includes/path.inc @@ -1,5 +1,5 @@ <?php -// $Id: path.inc,v 1.64 2010/06/17 13:48:53 dries Exp $ +// $Id: path.inc,v 1.71 2010/08/09 00:13:06 dries Exp $ /** * @file @@ -44,7 +44,7 @@ function drupal_path_initialize() { * found. */ function drupal_lookup_path($action, $path = '', $path_language = NULL) { - global $language_content; + global $language_url; // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { @@ -71,7 +71,11 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { } } - $path_language = $path_language ? $path_language : $language_content->language; + // If no language is explicitly specified we default to the current URL + // language. If we used a language different from the one conveyed by the + // requested URL, we might end up being unable to check if there is a path + // alias matching the URL path. + $path_language = $path_language ? $path_language : $language_url->language; if ($action == 'wipe') { $cache = array(); @@ -112,7 +116,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { return FALSE; } // For system paths which were not cached, query aliases individually. - else if (!isset($cache['no_aliases'][$path_language][$path])) { + elseif (!isset($cache['no_aliases'][$path_language][$path])) { // Get the most fitting result falling back with alias without language $alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", array( ':source' => $path, @@ -127,6 +131,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { // isn't a path that has this alias elseif ($action == 'source' && !isset($cache['no_source'][$path_language][$path])) { // Look for the value $path within the cached $map + $source = FALSE; if (!isset($cache['map'][$path_language]) || !($source = array_search($path, $cache['map'][$path_language]))) { // Get the most fitting result falling back with alias without language if ($source = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", array( @@ -135,7 +140,6 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { ':language_none' => LANGUAGE_NONE)) ->fetchField()) { $cache['map'][$path_language][$source] = $path; - return $source; } else { // We can't record anything into $map because we do not have a valid @@ -144,6 +148,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { $cache['no_source'][$path_language][$path] = TRUE; } } + return $source; } } @@ -335,7 +340,7 @@ function drupal_path_alias_whitelist_rebuild($source = NULL) { // path it corresponds to. This is the portion of the path before the first // '/', if present, otherwise the whole path itself. $whitelist = array(); - $result = db_query("SELECT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias} GROUP BY path"); + $result = db_query("SELECT DISTINCT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias}"); foreach ($result as $row) { $whitelist[$row->path] = TRUE; } @@ -392,10 +397,10 @@ function path_save(&$path) { $path += array('pid' => NULL, 'language' => LANGUAGE_NONE); // Insert or update the alias. - $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : NULL)); + $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : array())); // Verify that a record was written. - if (isset($status) && $status) { + if ($status) { if ($status === SAVED_NEW) { module_invoke_all('path_insert', $path); } @@ -436,6 +441,7 @@ function path_delete($criteria) { * * @param $path * A Drupal path. + * * @return * TRUE if the path is administrative, FALSE otherwise. * diff --git a/includes/registry.inc b/includes/registry.inc index 50d2da368e9794a2096d4b44122beed021ab98c6..9b9a2414f5603987a1d79013b6815b6ae53786e4 100644 --- a/includes/registry.inc +++ b/includes/registry.inc @@ -1,5 +1,5 @@ <?php -// $Id: registry.inc,v 1.32 2010/06/08 03:16:05 webchick Exp $ +// $Id: registry.inc,v 1.33 2010/07/10 01:57:32 dries Exp $ /** * @file @@ -65,11 +65,9 @@ function _registry_update() { // or modify attributes of a file. drupal_alter('registry_files', $files, $modules); foreach (registry_get_parsed_files() as $filename => $file) { - // Add the file creation and modification dates to those files we have - // already parsed. + // Add the hash for those files we have already parsed. if (isset($files[$filename])) { - $files[$filename]['filectime'] = $file['filectime']; - $files[$filename]['filemtime'] = $file['filemtime']; + $files[$filename]['hash'] = $file['hash']; } else { // Flush the registry of resources in files that are no longer on disc @@ -129,22 +127,15 @@ function registry_get_parsed_files() { */ function _registry_parse_files($files) { $parsed_files = array(); - $filetimes = array(); foreach ($files as $filename => $file) { if (file_exists($filename)) { - $filetimes[$filename] = array( - 'filectime' => filectime($filename), - 'filemtime' => filemtime($filename), - ); - - $modified_file = !isset($file['filectime']) || !isset($file['filemtime']) - || $filetimes[$filename]['filectime'] != $file['filectime'] - || $filetimes[$filename]['filemtime'] != $file['filemtime']; - if ($modified_file) { + $hash = hash_file('sha256', $filename); + if (empty($file['hash']) || $file['hash'] != $hash) { // Delete registry entries for this file, so we can insert the new resources. db_delete('registry') ->condition('filename', $filename) ->execute(); + $file['hash'] = $hash; $parsed_files[$filename] = $file; } } @@ -154,8 +145,7 @@ function _registry_parse_files($files) { db_merge('registry_file') ->key(array('filename' => $filename)) ->fields(array( - 'filectime' => $filetimes[$filename]['filectime'], - 'filemtime' => $filetimes[$filename]['filemtime'], + 'hash' => $file['hash'], )) ->execute(); } diff --git a/includes/session.inc b/includes/session.inc index 236baf199b52d64a0ca180e332e5cd5368768eb8..f4dd1a8f9803c407daccfbfc465337a66a80c6de 100644 --- a/includes/session.inc +++ b/includes/session.inc @@ -1,5 +1,5 @@ <?php -// $Id: session.inc,v 1.87 2010/07/07 13:52:00 dries Exp $ +// $Id: session.inc,v 1.88 2010/07/16 02:53:02 dries Exp $ /** * @file @@ -61,6 +61,7 @@ function _drupal_session_close() { * * @param $sid * Session ID. + * * @return * Either an array of the session data, or an empty string, if no data * was found or the user is anonymous. @@ -134,6 +135,7 @@ function _drupal_session_read($sid) { * Session ID. * @param $value * Serialized array of the session data. + * * @return * This function will always return TRUE. */ @@ -447,6 +449,7 @@ function _drupal_session_garbage_collection($lifetime) { * @param $status * Disables writing of session data when FALSE, (re-)enables * writing when TRUE. + * * @return * FALSE if writing session data has been disabled. Otherwise, TRUE. */ diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc index b060a8ab39b323c052062d0a2410e597abb353ae..82f44d7a5696aa36b8a5c87e6060a44ba9f97cc4 100644 --- a/includes/stream_wrappers.inc +++ b/includes/stream_wrappers.inc @@ -1,5 +1,5 @@ <?php -// $Id: stream_wrappers.inc,v 1.17 2010/06/12 08:15:15 webchick Exp $ +// $Id: stream_wrappers.inc,v 1.20 2010/08/17 22:05:22 dries Exp $ /** * @file @@ -145,24 +145,6 @@ interface DrupalStreamWrapperInterface extends StreamWrapperInterface { */ public function getExternalUrl(); - /** - * Returns the local writable target of the resource within the stream. - * - * This function should be used in place of calls to realpath() or similar - * functions when attempting to determine the location of a file. While - * functions like realpath() may return the location of a read-only file, this - * method may return a URI or path suitable for writing that is completely - * separate from the URI used for reading. - * - * @param $uri - * Optional URI. - * - * @return - * Returns a string representing a location suitable for writing of a file, - * or FALSE if unable to write to the file such as with read-only streams. - */ - public function getTarget($uri = NULL); - /** * Returns the MIME type of the resource. * @@ -286,9 +268,22 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface } /** - * Base implementation of getTarget(). + * Returns the local writable target of the resource within the stream. + * + * This function should be used in place of calls to realpath() or similar + * functions when attempting to determine the location of a file. While + * functions like realpath() may return the location of a read-only file, this + * method may return a URI or path suitable for writing that is completely + * separate from the URI used for reading. + * + * @param $uri + * Optional URI. + * + * @return + * Returns a string representing a location suitable for writing of a file, + * or FALSE if unable to write to the file such as with read-only streams. */ - function getTarget($uri = NULL) { + protected function getTarget($uri = NULL) { if (!isset($uri)) { $uri = $this->uri; } @@ -477,7 +472,9 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface * @see http://php.net/manual/en/streamwrapper.stream-seek.php */ public function stream_seek($offset, $whence) { - return fseek($this->handle, $offset, $whence); + // fseek returns 0 on success and -1 on a failure. + // stream_seek 1 on success and 0 on a failure. + return !fseek($this->handle, $offset, $whence); } /** @@ -542,7 +539,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface */ public function unlink($uri) { $this->uri = $uri; - return unlink($this->getLocalPath()); + return drupal_unlink($this->getLocalPath()); } /** @@ -639,10 +636,10 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface public function rmdir($uri, $options) { $this->uri = $uri; if ($options & STREAM_REPORT_ERRORS) { - return rmdir($this->getLocalPath()); + return drupal_rmdir($this->getLocalPath()); } else { - return @rmdir($this->getLocalPath()); + return @drupal_rmdir($this->getLocalPath()); } } @@ -711,7 +708,11 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface * @see http://php.net/manual/en/streamwrapper.dir-rewinddir.php */ public function dir_rewinddir() { - return rewinddir($this->handle); + rewinddir($this->handle); + // We do not really have a way to signal a failure as rewinddir() does not + // have a return value and there is no way to read a directory handler + // without advancing to the next file. + return TRUE; } /** @@ -723,7 +724,10 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface * @see http://php.net/manual/en/streamwrapper.dir-closedir.php */ public function dir_closedir() { - return closedir($this->handle); + closedir($this->handle); + // We do not really have a way to signal a failure as closedir() does not + // have a return value. + return TRUE; } } diff --git a/includes/theme.inc b/includes/theme.inc index 8c3aba4ef3db206ee5d2b05a1e153cb6a07f47e8..658128c1099491f6293fcd48a12223e3ca9972b9 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1,5 +1,5 @@ <?php -// $Id: theme.inc,v 1.601 2010/07/08 03:41:26 webchick Exp $ +// $Id: theme.inc,v 1.611 2010/09/14 21:42:05 dries Exp $ /** * @file @@ -95,8 +95,8 @@ function drupal_theme_initialize() { $base_theme = array(); $ancestor = $theme; while ($ancestor && isset($themes[$ancestor]->base_theme)) { - $base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme]; $ancestor = $themes[$ancestor]->base_theme; + $base_theme[] = $themes[$ancestor]; } _drupal_theme_initialize($themes[$theme], array_reverse($base_theme)); @@ -164,7 +164,7 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb // And now add the stylesheets properly foreach ($final_stylesheets as $media => $stylesheets) { foreach ($stylesheets as $stylesheet) { - drupal_add_css($stylesheet, array('weight' => CSS_THEME, 'media' => $media)); + drupal_add_css($stylesheet, array('weight' => CSS_THEME, 'media' => $media, 'preprocess' => TRUE)); } } @@ -189,7 +189,7 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb // Add scripts used by this theme. foreach ($final_scripts as $script) { - drupal_add_js($script, array('weight' => JS_THEME)); + drupal_add_js($script, array('weight' => JS_THEME, 'preprocess' => TRUE)); } $theme_engine = NULL; @@ -221,37 +221,44 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb } } - if (function_exists($registry_callback)) { - $registry_callback($theme, $base_theme, $theme_engine); + if (isset($registry_callback)) { + _theme_registry_callback($registry_callback, array($theme, $base_theme, $theme_engine)); } } /** * Get the theme registry. + * * @return * The theme registry array if it has been stored in memory, NULL otherwise. */ function theme_get_registry() { - return _theme_set_registry(); -} - -/** - * Store the theme registry in memory. - * @param $registry - * A registry array as returned by _theme_build_registry() - * @return - * The theme registry array stored in memory - */ -function _theme_set_registry($registry = NULL) { static $theme_registry = NULL; - if (isset($registry)) { - $theme_registry = $registry; + if (!isset($theme_registry)) { + list($callback, $arguments) = _theme_registry_callback(); + $theme_registry = call_user_func_array($callback, $arguments); } return $theme_registry; } +/** + * Set the callback that will be used by theme_get_registry() to fetch the registry. + * + * @param $callback + * The name of the callback function. + * @param $arguments + * The arguments to pass to the function. + */ +function _theme_registry_callback($callback = NULL, array $arguments = array()) { + static $stored; + if (isset($callback)) { + $stored = array($callback, $arguments); + } + return $stored; +} + /** * Get the theme_registry cache from the database; if it doesn't exist, build it. * @@ -268,7 +275,6 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) $cache = cache_get("theme_registry:$theme->name", 'cache'); if (isset($cache->data)) { $registry = $cache->data; - _theme_set_registry($registry); } else { // If not, build one and cache it. @@ -277,9 +283,9 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) // complete set of theme hooks. if (module_load_all(NULL)) { _theme_save_registry($theme, $registry); - _theme_set_registry($registry); } } + return $registry; } /** @@ -339,7 +345,7 @@ function drupal_theme_rebuild() { * The loaded $theme object as returned from list_themes(). * @param $path * The directory where $name is. For example, modules/system or - * themes/garland. + * themes/bartik. * * @see theme() * @see _theme_process_registry() @@ -542,6 +548,7 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) { * * @param $refresh * Whether to reload the list of themes from the database. Defaults to FALSE. + * * @return * An associative array of the currently available themes. The keys are the * names of the themes and the values are objects having the following @@ -553,7 +560,7 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) { * - 'stylesheets': A two dimensional array, using the first key for the * 'media' attribute (e.g. 'all'), the second for the name of the file * (e.g. style.css). The value is a complete filepath - * (e.g. themes/garland/style.css). + * (e.g. themes/bartik/style.css). * - 'scripts': An associative array of JavaScripts, using the filename as key * and the complete filepath as value. * - 'engine': The name of the theme engine. @@ -576,7 +583,6 @@ function list_themes($refresh = FALSE) { try { foreach (system_list('theme') as $theme) { if (file_exists($theme->filename)) { - $theme->info = unserialize($theme->info); $themes[] = $theme; } } @@ -598,9 +604,7 @@ function list_themes($refresh = FALSE) { } } foreach ($theme->info['scripts'] as $script => $path) { - if (file_exists($path)) { - $theme->scripts[$script] = $path; - } + $theme->scripts[$script] = $path; } if (isset($theme->info['engine'])) { $theme->engine = $theme->info['engine']; @@ -764,7 +768,11 @@ function theme($hook, $variables = array()) { } } if (!isset($hooks[$hook])) { - watchdog('theme', 'Theme key "@key" not found.', array('@key' => $hook), WATCHDOG_WARNING); + // Only log a message when not trying theme suggestions ($hook being an + // array). + if (!isset($candidate)) { + watchdog('theme', 'Theme key "@key" not found.', array('@key' => $hook), WATCHDOG_WARNING); + } return ''; } } @@ -1099,9 +1107,10 @@ function drupal_find_theme_templates($cache, $extension, $path) { * given for $theme. * * @param $setting_name - * The name of the setting to be retrieved. + * The name of the setting to be retrieved. * @param $theme - * The name of a given theme; defaults to the current theme. + * The name of a given theme; defaults to the current theme. + * * @return * The value of the requested setting, NULL if the setting does not exist. */ @@ -1555,6 +1564,8 @@ function theme_breadcrumb($variables) { * associative array with the following keys: * - "data": an array of cells * - Any HTML attributes, such as "class", to apply to the table row. + * - "no_striping": a boolean indicating that the row should receive no + * 'even / odd' styling. Defaults to FALSE. * Each cell can be either a string or an associative array with the * following keys: * - "data": The string to display in the table cell. @@ -1723,8 +1734,10 @@ function theme_table($variables) { } if (count($cells)) { // Add odd/even class - $class = $flip[$class]; - $attributes['class'][] = $class; + if (empty($row['no_striping'])) { + $class = $flip[$class]; + $attributes['class'][] = $class; + } // Build row $output .= ' <tr' . drupal_attributes($attributes) . '>'; @@ -1992,6 +2005,19 @@ function theme_indentation($variables) { * @} End of "ingroup themeable". */ +/** + * Returns HTML output for a single table cell for theme_table(). + * + * @param $cell + * Array of cell information, or string to display in cell. + * @param bool $header + * TRUE if this cell is a table header cell, FALSE if it is an ordinary + * table cell. If $cell is an array with element 'header' set to TRUE, that + * will override the $header parameter. + * + * @return + * HTML for the cell. + */ function _theme_table_cell($cell, $header = FALSE) { $attributes = ''; @@ -2254,6 +2280,7 @@ function template_preprocess_page(&$variables) { $variables['theme_hook_suggestions'] = $suggestions; } } + /** * Process variables for html.tpl.php * @@ -2461,9 +2488,8 @@ function template_preprocess_region(&$variables) { $variables['content'] = $variables['elements']['#children']; $variables['region'] = $variables['elements']['#region']; - $region = drupal_region_class($variables['region']); - $variables['classes_array'][] = $region; - $variables['theme_hook_suggestions'][] = 'region__' . $region; + $variables['classes_array'][] = drupal_region_class($variables['region']); + $variables['theme_hook_suggestions'][] = 'region__' . $variables['region']; } /** diff --git a/includes/theme.maintenance.inc b/includes/theme.maintenance.inc index 7fb2f40004e0b1d67610b79a1bc276253b0795a7..815e314e8ca7950b996a765b2d79b1afbba00294 100644 --- a/includes/theme.maintenance.inc +++ b/includes/theme.maintenance.inc @@ -1,5 +1,5 @@ <?php -// $Id: theme.maintenance.inc,v 1.63 2010/07/08 03:41:26 webchick Exp $ +// $Id: theme.maintenance.inc,v 1.66 2010/09/09 15:47:03 webchick Exp $ /** * @file @@ -12,7 +12,7 @@ * Used for site installs, updates and when the site is in maintenance mode. * It also applies when the database is unavailable or bootstrap was not * complete. Seven is always used for the initial install and update operations. - * In other cases, Garland is used, but this can be overridden by setting a + * In other cases, Bartik is used, but this can be overridden by setting a * "maintenance_theme" key in the $conf variable in settings.php. */ function _drupal_maintenance_theme() { @@ -45,7 +45,7 @@ function _drupal_maintenance_theme() { } // We use the default theme as the maintenance theme. If a default theme - // isn't specified in the database or in settings.php, we use Garland. + // isn't specified in the database or in settings.php, we use Bartik. $custom_theme = variable_get('maintenance_theme', variable_get('theme_default', 'bartik')); } @@ -80,6 +80,7 @@ function _drupal_maintenance_theme() { drupal_add_css(drupal_get_path('module', 'system') . '/system.css'); drupal_add_css(drupal_get_path('module', 'system') . '/system-behavior.css'); drupal_add_css(drupal_get_path('module', 'system') . '/system-menus.css'); + drupal_add_css(drupal_get_path('module', 'system') . '/system-messages.css'); drupal_add_css(drupal_get_path('module', 'system') . '/maintenance.css'); drupal_add_css(drupal_get_path('module', 'system') . '/admin.css'); } @@ -88,8 +89,7 @@ function _drupal_maintenance_theme() { * This builds the registry when the site needs to bypass any database calls. */ function _theme_load_offline_registry($theme, $base_theme = NULL, $theme_engine = NULL) { - $registry = _theme_build_registry($theme, $base_theme, $theme_engine); - _theme_set_registry($registry); + return _theme_build_registry($theme, $base_theme, $theme_engine); } /** diff --git a/includes/unicode.inc b/includes/unicode.inc index 0476ddef60975fbb599ff20b1e093870f21ca8e9..fb7f51c40bc88456a46f6593e33d84d1c71a944e 100644 --- a/includes/unicode.inc +++ b/includes/unicode.inc @@ -1,5 +1,5 @@ <?php -// $Id: unicode.inc,v 1.44 2010/06/14 12:37:15 dries Exp $ +// $Id: unicode.inc,v 1.47 2010/08/11 10:58:22 dries Exp $ /** * Indicates an error during check for PHP unicode support. @@ -172,6 +172,7 @@ function unicode_requirements() { * * @param &$data * The XML data which will be parsed later. + * * @return * An XML parser object or FALSE on error. * @@ -221,6 +222,7 @@ function drupal_xml_parser_create(&$data) { * The data to be converted. * @param $encoding * The encoding that the data is in + * * @return * Converted data or FALSE. */ @@ -257,6 +259,7 @@ function drupal_convert_to_utf8($data, $encoding) { * The string to truncate. * @param $len * An upper limit on the returned string length. + * * @return * The truncated string. */ @@ -419,77 +422,12 @@ function _mime_header_decode($matches) { * * @param $text * The text to decode entities in. - * @param $exclude - * An array of characters which should not be decoded. For example, - * array('<', '&', '"'). This affects both named and numerical entities. * * @return * The input $text, with all HTML entities decoded once. */ -function decode_entities($text, $exclude = array()) { - static $html_entities; - if (!isset($html_entities)) { - include DRUPAL_ROOT . '/includes/unicode.entities.inc'; - } - - // Flip the exclude list so that we can do quick lookups later. - $exclude = array_flip($exclude); - - // Use a regexp to select all entities in one pass, to avoid decoding - // double-escaped entities twice. The PREG_REPLACE_EVAL modifier 'e' is - // being used to allow for a callback (see - // http://php.net/manual/en/reference.pcre.pattern.modifiers). - return preg_replace('/&(#x?)?([A-Za-z0-9]+);/e', '_decode_entities("$1", "$2", "$0", $html_entities, $exclude)', $text); -} - -/** - * Helper function for decode_entities - */ -function _decode_entities($prefix, $codepoint, $original, &$html_entities, &$exclude) { - // Named entity - if (!$prefix) { - // A named entity not in the exclude list. - if (isset($html_entities[$original]) && !isset($exclude[$html_entities[$original]])) { - return $html_entities[$original]; - } - else { - return $original; - } - } - // Hexadecimal numerical entity - if ($prefix == '#x') { - $codepoint = base_convert($codepoint, 16, 10); - } - // Decimal numerical entity (strip leading zeros to avoid PHP octal notation) - else { - $codepoint = preg_replace('/^0+/', '', $codepoint); - } - // Encode codepoint as UTF-8 bytes - if ($codepoint < 0x80) { - $str = chr($codepoint); - } - elseif ($codepoint < 0x800) { - $str = chr(0xC0 | ($codepoint >> 6)) - . chr(0x80 | ($codepoint & 0x3F)); - } - elseif ($codepoint < 0x10000) { - $str = chr(0xE0 | ( $codepoint >> 12)) - . chr(0x80 | (($codepoint >> 6) & 0x3F)) - . chr(0x80 | ( $codepoint & 0x3F)); - } - elseif ($codepoint < 0x200000) { - $str = chr(0xF0 | ( $codepoint >> 18)) - . chr(0x80 | (($codepoint >> 12) & 0x3F)) - . chr(0x80 | (($codepoint >> 6) & 0x3F)) - . chr(0x80 | ( $codepoint & 0x3F)); - } - // Check for excluded characters - if (isset($exclude[$str])) { - return $original; - } - else { - return $str; - } +function decode_entities($text) { + return html_entity_decode($text, ENT_QUOTES, 'UTF-8'); } /** diff --git a/includes/update.inc b/includes/update.inc index 31b66f5b433acc7db52c34547bf716da64085905..3fe43e1d308458564257c895b5d8029197550582 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -1,5 +1,5 @@ <?php -// $Id: update.inc,v 1.60 2010/07/07 05:44:03 webchick Exp $ +// $Id: update.inc,v 1.67 2010/09/13 05:50:08 webchick Exp $ /** * @file @@ -68,16 +68,16 @@ function update_check_incompatibility($name, $type = 'module') { * the bootstrap to be successful. * * No access check has been performed when this function is called, so no - * changes to the database should be made here. + * irreversible changes to the database are made here. */ function update_prepare_d7_bootstrap() { // Allow the bootstrap to proceed even if a Drupal 6 settings.php file is // still being used. include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); - global $databases, $db_url, $update_rewrite_settings; + global $databases, $db_url, $db_prefix, $update_rewrite_settings; if (empty($databases) && !empty($db_url)) { - $databases = update_parse_db_url($db_url); + $databases = update_parse_db_url($db_url, $db_prefix); // Record the fact that the settings.php file will need to be rewritten. $update_rewrite_settings = TRUE; $settings_file = conf_path() . '/settings.php'; @@ -141,8 +141,7 @@ function update_prepare_d7_bootstrap() { $schema['registry_file'] = array( 'fields' => array( 'filename' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE), - 'filectime' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'filemtime' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'hash' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE), ), 'primary key' => array('filename'), ); @@ -221,6 +220,7 @@ function update_prepare_d7_bootstrap() { * An associative array. Keys are module names, values an associative array * mapping the old block deltas to the new block deltas for the module. * Example: + * @code * $renamed_deltas = array( * 'mymodule' => * array( @@ -228,8 +228,21 @@ function update_prepare_d7_bootstrap() { * 1 => 'mymodule-block-2', * ), * ); + * @endcode + * @param $moved_deltas + * An associative array. Keys are source module names, values an associative + * array mapping the (possibly renamed) block name to the new module name. + * Example: + * @code + * $moved_deltas = array( + * 'user' => + * array( + * 'navigation' => 'system', + * ), + * ); + * @endcode */ -function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas) { +function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas, $moved_deltas) { // Loop through each block and make changes to the block tables. // Only run this the first time through the batch update. if (!isset($sandbox['progress'])) { @@ -240,10 +253,10 @@ function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas) { foreach ($deltas as $old_delta => $new_delta) { // Only do the update if the old block actually exists. $block_exists = db_query("SELECT COUNT(*) FROM {" . $table . "} WHERE module = :module AND delta = :delta", array( - ':module' => $module, - ':delta' => $old_delta, - )) - ->fetchField(); + ':module' => $module, + ':delta' => $old_delta, + )) + ->fetchField(); if ($block_exists) { db_update($table) ->fields(array('delta' => $new_delta)) @@ -253,6 +266,23 @@ function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas) { } } } + foreach ($moved_deltas as $old_module => $deltas) { + foreach ($deltas as $delta => $new_module) { + // Only do the update if the old block actually exists. + $block_exists = db_query("SELECT COUNT(*) FROM {" . $table . "} WHERE module = :module AND delta = :delta", array( + ':module' => $old_module, + ':delta' => $delta, + )) + ->fetchField(); + if ($block_exists) { + db_update($table) + ->fields(array('module' => $new_module)) + ->condition('module', $old_module) + ->condition('delta', $delta) + ->execute(); + } + } + } } // Initialize batch update information. @@ -282,6 +312,17 @@ function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas) { } } } + foreach ($moved_deltas as $old_module => $deltas) { + foreach ($deltas as $delta => $new_module) { + if (isset($data['block'][$old_module][$delta])) { + // Transfer the old block visibility settings to the moved + // block, and mark this user for a database update. + $data['block'][$new_module][$delta] = $data['block'][$old_module][$delta]; + unset($data['block'][$old_module][$delta]); + $user_needs_update = TRUE; + } + } + } // Update the current user. if ($user_needs_update) { db_update('users') @@ -313,9 +354,9 @@ function update_fix_d7_requirements() { // Rewrite the settings.php file if necessary, see // update_prepare_d7_bootstrap(). - global $update_rewrite_settings, $db_url; + global $update_rewrite_settings, $db_url, $db_prefix; if (!empty($update_rewrite_settings)) { - $databases = update_parse_db_url($db_url); + $databases = update_parse_db_url($db_url, $db_prefix); $salt = drupal_hash_base64(drupal_random_bytes(55)); file_put_contents(conf_path() . '/settings.php', "\n" . '$databases = ' . var_export($databases, TRUE) . ";\n\$drupal_hash_salt = '$salt';", FILE_APPEND); } @@ -382,7 +423,7 @@ function update_fix_d7_requirements() { ), 'permission' => array( 'type' => 'varchar', - 'length' => 64, + 'length' => 128, 'not null' => TRUE, 'default' => '', ), @@ -675,7 +716,7 @@ function update_fix_d7_install_profile() { * @return * Drupal 7 DBTNG compatible array of database connection information. */ -function update_parse_db_url($db_url) { +function update_parse_db_url($db_url, $db_prefix) { $databases = array(); if (!is_array($db_url)) { $db_url = array('default' => $db_url); @@ -692,6 +733,9 @@ function update_parse_db_url($db_url) { 'host' => urldecode($url['host']), 'port' => isset($url['port']) ? urldecode($url['port']) : '', ); + if (isset($db_prefix)) { + $databases[$database]['default']['prefix'] = $db_prefix; + } } return $databases; } @@ -933,10 +977,13 @@ function update_get_update_list() { $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE); foreach ($modules as $module => $schema_version) { - $pending = array(); + // Skip uninstalled and incompatible modules. + if ($schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility($module)) { + continue; + } + // Otherwise, get the list of updates defined by this module. $updates = drupal_get_schema_versions($module); - // Skip incompatible module updates, otherwise test schema versions. - if (!update_check_incompatibility($module) && $updates !== FALSE && $schema_version >= 0) { + if ($updates !== FALSE) { // module_invoke returns NULL for nonexisting hooks, so if no updates // are removed, it will == 0. $last_removed = module_invoke($module, 'update_last_removed'); @@ -1268,3 +1315,16 @@ function update_retrieve_dependencies() { return $return; } +/** + * @defgroup update-api-6.x-to-7.x Update versions of API functions. + * @{ + * Functions similar to normal API function but not firing hooks. + * + * During update, it is impossible to judge the consequences of firing a hook + * as it might hit a module not yet updated. So simplified versions of some + * core APIs are provided. + */ + +/** + * @} End of "defgroup update-api-6.x-to-7.x" + */ diff --git a/includes/updater.inc b/includes/updater.inc index 56efa52bbbff30257797cd264a8ab9623adb7699..669458b9731b68a1128433c9f25be874dc3a6bd2 100644 --- a/includes/updater.inc +++ b/includes/updater.inc @@ -1,5 +1,5 @@ <?php -// $Id: updater.inc,v 1.5 2010/01/30 07:54:01 dries Exp $ +// $Id: updater.inc,v 1.7 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -23,6 +23,7 @@ interface DrupalUpdaterInterface { /** * Checks if the project is installed. + * * @return bool */ public function isInstalled(); @@ -49,6 +50,7 @@ interface DrupalUpdaterInterface { * @param string $directory * * @return bool + * TRUE if the project is installed, FALSE if not. */ public static function canUpdateDirectory($directory); @@ -105,6 +107,7 @@ class Updater { * * @param string $directory * Extracted Drupal project. + * * @return string * The class name which can work with this project type. */ @@ -129,6 +132,7 @@ class Updater { * * @param string $directory * Directory to search in. + * * @return string * Path to the info file. */ @@ -155,7 +159,9 @@ class Updater { * provide their canonical name. * * @param string $directory + * * @return string + * The name of the project. */ public static function getProjectName($directory) { return basename($directory); @@ -166,7 +172,9 @@ class Updater { * * @param string $directory * Directory to search for the info file. + * * @return string + * The title of the project. */ public static function getProjectTitle($directory) { $info_file = self::findInfoFile($directory); @@ -182,6 +190,7 @@ class Updater { * * @param array $overrides * An array of overrides for the default parameters. + * * @return array * An array of configuration parameters for an update or install operation. */ @@ -359,7 +368,7 @@ class Updater { * Return the full path to a directory where backups should be written. */ public function getBackupDir() { - return file_directory_path('temporary'); + return file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(); } /** diff --git a/includes/utility.inc b/includes/utility.inc index f2c43fcbaa49f2413557f5cec5dbb609e813da2d..38e7e40c3f22e29b7228da98a2d6818a819f8e66 100644 --- a/includes/utility.inc +++ b/includes/utility.inc @@ -1,5 +1,5 @@ <?php -// $Id: utility.inc,v 1.1 2010/06/28 02:05:47 webchick Exp $ +// $Id: utility.inc,v 1.2 2010/07/24 17:28:25 dries Exp $ /** * @file @@ -31,10 +31,10 @@ function drupal_var_export($var, $prefix = '') { $output .= ')'; } } - else if (is_bool($var)) { + elseif (is_bool($var)) { $output = $var ? 'TRUE' : 'FALSE'; } - else if (is_string($var)) { + elseif (is_string($var)) { $line_safe_var = str_replace("\n", '\n', $var); if (strpos($var, "\n") !== FALSE || strpos($var, "'") !== FALSE) { // If the string contains a line break or a single quote, use the diff --git a/includes/xmlrpc.inc b/includes/xmlrpc.inc index 334a8c3d7247e670b6c4c31f4b35a92c7c005a8f..fccc105dc70fd06dc9b6cd16d9db486f6656fed1 100644 --- a/includes/xmlrpc.inc +++ b/includes/xmlrpc.inc @@ -1,5 +1,5 @@ <?php -// $Id: xmlrpc.inc,v 1.66 2010/05/06 05:59:30 webchick Exp $ +// $Id: xmlrpc.inc,v 1.69 2010/08/14 03:15:01 dries Exp $ /** * @file @@ -13,14 +13,15 @@ */ /** - * Turn a data structure into objects with 'data' and 'type' attributes. + * Turns a data structure into objects with 'data' and 'type' attributes. * * @param $data * The data structure. * @param $type - * Optional type assign to $data. - * @return - * Object. + * Optional type to assign to $data. + * + * @return object + * An XML-RPC data object containing the input $data. */ function xmlrpc_value($data, $type = FALSE) { $xmlrpc_value = new stdClass(); @@ -44,16 +45,19 @@ function xmlrpc_value($data, $type = FALSE) { } /** - * Map PHP type to XML-RPC type. + * Maps a PHP type to an XML-RPC type. * * @param $xmlrpc_value * Variable whose type should be mapped. - * @return - * XML-RPC type as string. + * + * @return string + * The corresponding XML-RPC type. + * * @see http://www.xmlrpc.com/spec#scalars */ function xmlrpc_value_calculate_type($xmlrpc_value) { - // http://www.php.net/gettype: Never use gettype() to test for a certain type [...] Instead, use the is_* functions. + // http://www.php.net/gettype: Never use gettype() to test for a certain type + // [...] Instead, use the is_* functions. if (is_bool($xmlrpc_value->data)) { return 'boolean'; } @@ -61,7 +65,7 @@ function xmlrpc_value_calculate_type($xmlrpc_value) { return 'double'; } if (is_int($xmlrpc_value->data)) { - return 'int'; + return 'int'; } if (is_array($xmlrpc_value->data)) { // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct' @@ -82,29 +86,30 @@ function xmlrpc_value_calculate_type($xmlrpc_value) { } /** - * Generate XML representing the given value. + * Generates XML representing the given value. * * @param $xmlrpc_value * A value to be represented in XML. + * * @return - * XML representation of value. + * XML representation of $xmlrpc_value. */ function xmlrpc_value_get_xml($xmlrpc_value) { switch ($xmlrpc_value->type) { case 'boolean': return '<boolean>' . (($xmlrpc_value->data) ? '1' : '0') . '</boolean>'; - break; + case 'int': return '<int>' . $xmlrpc_value->data . '</int>'; - break; + case 'double': return '<double>' . $xmlrpc_value->data . '</double>'; - break; + case 'string': // Note: we don't escape apostrophes because of the many blogging clients // that don't support numerical entities (and XML in general) properly. return '<string>' . htmlspecialchars($xmlrpc_value->data) . '</string>'; - break; + case 'array': $return = '<array><data>' . "\n"; foreach ($xmlrpc_value->data as $item) { @@ -112,7 +117,7 @@ function xmlrpc_value_get_xml($xmlrpc_value) { } $return .= '</data></array>'; return $return; - break; + case 'struct': $return = '<struct>' . "\n"; foreach ($xmlrpc_value->data as $name => $value) { @@ -121,30 +126,35 @@ function xmlrpc_value_get_xml($xmlrpc_value) { } $return .= '</struct>'; return $return; - break; + case 'date': return xmlrpc_date_get_xml($xmlrpc_value->data); - break; + case 'base64': return xmlrpc_base64_get_xml($xmlrpc_value->data); - break; } return FALSE; } /** - * Construct an object representing an XML-RPC message. + * Constructs an object representing an XML-RPC message. * * @param $message - * String containing XML as defined at http://www.xmlrpc.com/spec - * @return - * Object + * A string containing an XML message. + * + * @return object + * An XML-RPC object containing the message. + * + * @see http://www.xmlrpc.com/spec */ function xmlrpc_message($message) { $xmlrpc_message = new stdClass(); - $xmlrpc_message->array_structs = array(); // The stack used to keep track of the current array/struct - $xmlrpc_message->array_structs_types = array(); // The stack used to keep track of if things are structs or array - $xmlrpc_message->current_struct_name = array(); // A stack as well + // The stack used to keep track of the current array/struct + $xmlrpc_message->array_structs = array(); + // The stack used to keep track of if things are structs or array + $xmlrpc_message->array_structs_types = array(); + // A stack as well + $xmlrpc_message->current_struct_name = array(); $xmlrpc_message->message = $message; return $xmlrpc_message; } @@ -156,9 +166,10 @@ function xmlrpc_message($message) { * object. * * @param $xmlrpc_message - * Object generated by xmlrpc_message() + * An object generated by xmlrpc_message(). + * * @return - * TRUE if parsing succeeded; FALSE otherwise + * TRUE if parsing succeeded; FALSE otherwise. */ function xmlrpc_message_parse($xmlrpc_message) { $xmlrpc_message->_parser = xml_parser_create(); @@ -186,12 +197,15 @@ function xmlrpc_message_parse($xmlrpc_message) { } /** - * Store a copy of the $xmlrpc_message object temporarily. + * Stores a copy of the most recent XML-RPC message object temporarily. * * @param $value - * Object - * @return - * The most recently stored $xmlrpc_message + * An XML-RPC message to store, or NULL to keep the last message. + * + * @return object + * The most recently stored message. + * + * @see xmlrpc_message_get() */ function xmlrpc_message_set($value = NULL) { static $xmlrpc_message; @@ -201,10 +215,21 @@ function xmlrpc_message_set($value = NULL) { return $xmlrpc_message; } +/** + * Returns the most recently stored XML-RPC message object. + * + * @return object + * The most recently stored message. + * + * @see xmlrpc_message_set() + */ function xmlrpc_message_get() { return xmlrpc_message_set(); } +/** + * Handles opening tags for XML parsing in xmlrpc_message_parse(). + */ function xmlrpc_message_tag_open($parser, $tag, $attr) { $xmlrpc_message = xmlrpc_message_get(); $xmlrpc_message->current_tag_contents = ''; @@ -215,11 +240,13 @@ function xmlrpc_message_tag_open($parser, $tag, $attr) { case 'fault': $xmlrpc_message->messagetype = $tag; break; + // Deal with stacks of arrays and structs case 'data': $xmlrpc_message->array_structs_types[] = 'array'; $xmlrpc_message->array_structs[] = array(); break; + case 'struct': $xmlrpc_message->array_structs_types[] = 'struct'; $xmlrpc_message->array_structs[] = array(); @@ -228,12 +255,18 @@ function xmlrpc_message_tag_open($parser, $tag, $attr) { xmlrpc_message_set($xmlrpc_message); } +/** + * Handles character data for XML parsing in xmlrpc_message_parse(). + */ function xmlrpc_message_cdata($parser, $cdata) { $xmlrpc_message = xmlrpc_message_get(); $xmlrpc_message->current_tag_contents .= $cdata; xmlrpc_message_set($xmlrpc_message); } +/** + * Handles closing tags for XML parsing in xmlrpc_message_parse(). + */ function xmlrpc_message_tag_close($parser, $tag) { $xmlrpc_message = xmlrpc_message_get(); $value_flag = FALSE; @@ -243,19 +276,23 @@ function xmlrpc_message_tag_close($parser, $tag) { $value = (int)trim($xmlrpc_message->current_tag_contents); $value_flag = TRUE; break; + case 'double': $value = (double)trim($xmlrpc_message->current_tag_contents); $value_flag = TRUE; break; + case 'string': $value = $xmlrpc_message->current_tag_contents; $value_flag = TRUE; break; + case 'dateTime.iso8601': $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents)); // $value = $iso->getTimestamp(); $value_flag = TRUE; break; + case 'value': // If no type is indicated, the type is string // We take special care for empty values @@ -265,41 +302,47 @@ function xmlrpc_message_tag_close($parser, $tag) { } unset($xmlrpc_message->last_open); break; + case 'boolean': $value = (boolean)trim($xmlrpc_message->current_tag_contents); $value_flag = TRUE; break; + case 'base64': $value = base64_decode(trim($xmlrpc_message->current_tag_contents)); $value_flag = TRUE; break; + // Deal with stacks of arrays and structs case 'data': case 'struct': - $value = array_pop($xmlrpc_message->array_structs ); + $value = array_pop($xmlrpc_message->array_structs); array_pop($xmlrpc_message->array_structs_types); $value_flag = TRUE; break; + case 'member': array_pop($xmlrpc_message->current_struct_name); break; + case 'name': $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents); break; + case 'methodName': $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents); break; } if ($value_flag) { - if (count($xmlrpc_message->array_structs ) > 0) { + if (count($xmlrpc_message->array_structs) > 0) { // Add value to struct or array - if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types)-1] == 'struct') { + if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types) - 1] == 'struct') { // Add to struct - $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name)-1]] = $value; + $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name) - 1]] = $value; } else { // Add to array - $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][] = $value; + $xmlrpc_message->array_structs[count($xmlrpc_message->array_structs) - 1][] = $value; } } else { @@ -314,14 +357,15 @@ function xmlrpc_message_tag_close($parser, $tag) { } /** - * Construct an object representing an XML-RPC request + * Constructs an object representing an XML-RPC request. * * @param $method - * The name of the method to be called + * The name of the method to be called. * @param $args * An array of parameters to send with the method. - * @return - * Object + * + * @return object + * An XML-RPC object representing the request. */ function xmlrpc_request($method, $args) { $xmlrpc_request = new stdClass(); @@ -344,7 +388,20 @@ EOD; return $xmlrpc_request; } - +/** + * Generates, temporarily saves, and returns an XML-RPC error object. + * + * @param $code + * The error code. + * @param $message + * The error message. + * @param $reset + * TRUE to empty the temporary error storage. Ignored if $code is supplied. + * + * @return object + * An XML-RPC error object representing $code and $message, or the most + * recently stored error object if omitted. + */ function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) { static $xmlrpc_error; if (isset($code)) { @@ -359,6 +416,15 @@ function xmlrpc_error($code = NULL, $message = NULL, $reset = FALSE) { return $xmlrpc_error; } +/** + * Converts an XML-RPC error object into XML. + * + * @param $xmlrpc_error + * The XML-RPC error object. + * + * @return string + * An XML representation of the error as an XML methodResponse. + */ function xmlrpc_error_get_xml($xmlrpc_error) { return <<<EOD <methodResponse> @@ -381,6 +447,15 @@ function xmlrpc_error_get_xml($xmlrpc_error) { EOD; } +/** + * Converts a PHP or ISO date/time to an XML-RPC object. + * + * @param $time + * A PHP timestamp or an ISO date-time string. + * + * @return object + * An XML-RPC time/date object. + */ function xmlrpc_date($time) { $xmlrpc_date = new stdClass(); $xmlrpc_date->is_date = TRUE; @@ -407,10 +482,28 @@ function xmlrpc_date($time) { return $xmlrpc_date; } +/** + * Converts an XML-RPC date-time object into XML. + * + * @param $xmlrpc_date + * The XML-RPC date-time object. + * + * @return string + * An XML representation of the date/time as XML. + */ function xmlrpc_date_get_xml($xmlrpc_date) { return '<dateTime.iso8601>' . $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day . 'T' . $xmlrpc_date->hour . ':' . $xmlrpc_date->minute . ':' . $xmlrpc_date->second . '</dateTime.iso8601>'; } +/** + * Returns an XML-RPC base 64 object. + * + * @param $data + * Base 64 data to store in returned object. + * + * @return object + * An XML-RPC base 64 object. + */ function xmlrpc_base64($data) { $xmlrpc_base64 = new stdClass(); $xmlrpc_base64->is_base64 = TRUE; @@ -418,56 +511,58 @@ function xmlrpc_base64($data) { return $xmlrpc_base64; } +/** + * Converts an XML-RPC base 64 object into XML. + * + * @param $xmlrpc_base64 + * The XML-RPC base 64 object. + * + * @return string + * An XML representation of the base 64 data as XML. + */ function xmlrpc_base64_get_xml($xmlrpc_base64) { return '<base64>' . base64_encode($xmlrpc_base64->data) . '</base64>'; } /** - * Performs one or more XML-RPC request(s). + * Performs one or more XML-RPC requests. * * @param $url - * An absolute URL of the XML-RPC endpoint. - * Example: - * http://www.example.com/xmlrpc.php - * @param ... - * For one request: - * The method name followed by a variable number of arguments to the - * method. - * For multiple requests (system.multicall): - * An array of call arrays. Each call array follows the pattern of the - * single request: method name followed by the arguments to the method. - * @return - * Either the return value of the method on success, or FALSE. If FALSE is - * returned, see xmlrpc_errno() and xmlrpc_error_msg(). - * - For a non-multicall request: the result just as if this had been a local - * function call. - * - For a multicall request: an array of results. Each result will either be - * a one-element array containing the result returned by the method called, - * or an xmlrpc_error object if the call failed. + * An absolute URL of the XML-RPC endpoint, e.g., + * http://example.com/xmlrpc.php + * @param $args + * An associative array whose keys are the methods to call and whose values + * are the arguments to pass to the respective method. If multiple methods + * are specified, a system.multicall is performed. + * @param $options + * (optional) An array of options to pass along to drupal_http_request(). * - * @see xmlrpc_error() + * @return + * A single response (single request) or an array of responses (multicall + * request). Each response is the return value of the method, just as if it + * has been a local function call, on success, or FALSE on failure. If FALSE + * is returned, see xmlrpc_errno() and xmlrpc_error_msg() to get more + * information. */ -function _xmlrpc() { - $args = func_get_args(); - $url = array_shift($args); +function _xmlrpc($url, $args, $options = array()) { xmlrpc_clear_error(); - if (is_array($args[0])) { - $method = 'system.multicall'; + if (count($args) > 1) { $multicall_args = array(); - foreach ($args[0] as $call) { - $multicall_args[] = array('methodName' => array_shift($call), 'params' => $call); + foreach ($args as $method => $call) { + $multicall_args[] = array('methodName' => $method, 'params' => $call); } + $method = 'system.multicall'; $args = array($multicall_args); } else { - $method = array_shift($args); + $method = key($args); + $args = $args[$method]; } $xmlrpc_request = xmlrpc_request($method, $args); - $options = array( - 'headers' => array('Content-Type' => 'text/xml'), - 'method' => 'POST', - 'data' => $xmlrpc_request->xml, - ); + // Required options which will replace any that are passed in. + $options['method'] = 'POST'; + $options['headers']['Content-Type'] = 'text/xml'; + $options['data'] = $xmlrpc_request->xml; $result = drupal_http_request($url, $options); if ($result->code != 200) { xmlrpc_error($result->code, $result->error); @@ -505,7 +600,7 @@ function _xmlrpc() { } /** - * Returns the last XML-RPC client error number + * Returns the last XML-RPC client error number. */ function xmlrpc_errno() { $error = xmlrpc_error(); @@ -513,7 +608,7 @@ function xmlrpc_errno() { } /** - * Returns the last XML-RPC client error message + * Returns the last XML-RPC client error message. */ function xmlrpc_error_msg() { $error = xmlrpc_error(); @@ -521,8 +616,11 @@ function xmlrpc_error_msg() { } /** - * Clears any previous error. + * Clears any previously-saved errors. + * + * @see xmlrpc_error() */ function xmlrpc_clear_error() { xmlrpc_error(NULL, NULL, TRUE); } + diff --git a/includes/xmlrpcs.inc b/includes/xmlrpcs.inc index a850eee48e3ddebe981646f5097598f0ba4c42f5..0976e1054737f098365661eb3a80aac598eac108 100644 --- a/includes/xmlrpcs.inc +++ b/includes/xmlrpcs.inc @@ -1,5 +1,5 @@ <?php -// $Id: xmlrpcs.inc,v 1.39 2010/04/29 05:35:21 webchick Exp $ +// $Id: xmlrpcs.inc,v 1.41 2010/07/31 04:17:34 dries Exp $ /** * @file @@ -7,38 +7,40 @@ */ /** - * The main entry point for XML-RPC requests. + * Invokes XML-RPC methods on this server. * - * @param $callbacks + * @param array $callbacks * Array of external XML-RPC method names with the callbacks they map to. */ function xmlrpc_server($callbacks) { $xmlrpc_server = new stdClass(); // Define built-in XML-RPC method names $defaults = array( - 'system.multicall' => 'xmlrpc_server_multicall', + 'system.multicall' => 'xmlrpc_server_multicall', array( 'system.methodSignature', 'xmlrpc_server_method_signature', array('array', 'string'), - 'Returns an array describing the return type and required parameters of a method.' + 'Returns an array describing the return type and required parameters of a method.', ), array( 'system.getCapabilities', 'xmlrpc_server_get_capabilities', array('struct'), - 'Returns a struct describing the XML-RPC specifications supported by this server.' + 'Returns a struct describing the XML-RPC specifications supported by this server.', ), array( 'system.listMethods', 'xmlrpc_server_list_methods', array('array'), - 'Returns an array of available methods on this server.'), + 'Returns an array of available methods on this server.', + ), array( 'system.methodHelp', 'xmlrpc_server_method_help', array('string', 'string'), - 'Returns a documentation string for the specified method.') + 'Returns a documentation string for the specified method.', + ), ); // We build an array of all method names by combining the built-ins // with those defined by modules implementing the _xmlrpc hook. @@ -86,9 +88,7 @@ function xmlrpc_server($callbacks) { <methodResponse> <params> <param> - <value>' . - xmlrpc_value_get_xml($r) - . '</value> + <value>' . xmlrpc_value_get_xml($r) . '</value> </param> </params> </methodResponse> @@ -99,12 +99,13 @@ function xmlrpc_server($callbacks) { } /** - * Throw an XML-RPC error. + * Throws an XML-RPC error. * * @param $error - * an error object OR integer error code + * An error object or integer error code. * @param $message - * description of error, used only if integer error code was passed + * (optional) The description of the error. Used only if an integer error + * code was passed in. */ function xmlrpc_server_error($error, $message = FALSE) { if ($message && !is_object($error)) { @@ -113,6 +114,12 @@ function xmlrpc_server_error($error, $message = FALSE) { xmlrpc_server_output(xmlrpc_error_get_xml($error)); } +/** + * Sends XML-RPC output to the browser. + * + * @param string $xml + * XML to send to the browser. + */ function xmlrpc_server_output($xml) { $xml = '<?xml version="1.0"?>' . "\n" . $xml; drupal_add_http_header('Content-Length', strlen($xml)); @@ -122,10 +129,16 @@ function xmlrpc_server_output($xml) { } /** - * Store a copy of the request temporarily. + * Stores a copy of an XML-RPC request temporarily. * - * @param $xmlrpc_server - * Request object created by xmlrpc_server(). + * @param object $xmlrpc_server + * (optional) Request object created by xmlrpc_server(). Omit to leave the + * previous server object saved. + * + * @return + * The latest stored request. + * + * @see xmlrpc_server_get() */ function xmlrpc_server_set($xmlrpc_server = NULL) { static $server; @@ -135,21 +148,31 @@ function xmlrpc_server_set($xmlrpc_server = NULL) { return $server; } -// Retrieve the stored request. +/** + * Retrieves the latest stored XML-RPC request. + * + * @return object + * The stored request. + * + * @see xmlrpc_server_set() + */ function xmlrpc_server_get() { return xmlrpc_server_set(); } /** - * Dispatch the request and any parameters to the appropriate handler. + * Dispatches an XML-RPC request and any parameters to the appropriate handler. + * + * @param object $xmlrpc_server + * Object containing information about this XML-RPC server, the methods it + * provides, their signatures, etc. + * @param string $methodname + * The external XML-RPC method name; e.g., 'system.methodHelp'. + * @param array $args + * Array containing any parameters that are to be sent along with the request. * - * @param $xmlrpc_server - * Contains information about this XML-RPC server, the methods it provides, - * their signatures, etc. - * @param $methodname - * The external XML-RPC method name, e.g. 'system.methodHelp' - * @param $args - * Array containing any parameters that were sent along with the request. + * @return + * The results of the call. */ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { // Make sure parameters are in an array @@ -181,23 +204,27 @@ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { $ok = FALSE; } break; + case 'base64': case 'string': if (!is_string($arg)) { $ok = FALSE; } break; + case 'boolean': if ($arg !== FALSE && $arg !== TRUE) { $ok = FALSE; } break; + case 'float': case 'double': if (!is_float($arg)) { $ok = FALSE; } break; + case 'date': case 'dateTime.iso8601': if (!$arg->is_date) { @@ -218,6 +245,20 @@ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { return call_user_func_array($method, $args); } +/** + * Dispatches multiple XML-RPC requests. + * + * @param array $methodcalls + * An array of XML-RPC requests to make. Each request is an array with the + * following elements: + * - methodName: Name of the method to invoke. + * - params: Parameters to pass to the method. + * + * @return + * An array of the results of each request. + * + * @see xmlrpc_server_call() + */ function xmlrpc_server_multicall($methodcalls) { // See http://www.xmlrpc.com/discuss/msgReader$1208 $return = array(); @@ -239,7 +280,7 @@ function xmlrpc_server_multicall($methodcalls) { if (is_object($result) && !empty($result->is_error)) { $return[] = array( 'faultCode' => $result->code, - 'faultString' => $result->message + 'faultString' => $result->message, ); } else { @@ -249,9 +290,13 @@ function xmlrpc_server_multicall($methodcalls) { return $return; } - /** + * Lists the methods available on this XML-RPC server. + * * XML-RPC method system.listMethods maps to this function. + * + * @return array + * Array of the names of methods available on this server. */ function xmlrpc_server_list_methods() { $xmlrpc_server = xmlrpc_server_get(); @@ -259,41 +304,51 @@ function xmlrpc_server_list_methods() { } /** + * Returns a list of the capabilities of this server. + * * XML-RPC method system.getCapabilities maps to this function. * + * @return array + * Array of server capabilities. + * * @see http://groups.yahoo.com/group/xml-rpc/message/2897 */ function xmlrpc_server_get_capabilities() { return array( 'xmlrpc' => array( 'specUrl' => 'http://www.xmlrpc.com/spec', - 'specVersion' => 1 + 'specVersion' => 1, ), 'faults_interop' => array( 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', - 'specVersion' => 20010516 + 'specVersion' => 20010516, ), 'system.multicall' => array( 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', - 'specVersion' => 1 + 'specVersion' => 1, ), 'introspection' => array( - 'specUrl' => 'http://scripts.incutio.com/xmlrpc/introspection.html', - 'specVersion' => 1 - ) + 'specUrl' => 'http://scripts.incutio.com/xmlrpc/introspection.html', + 'specVersion' => 1, + ), ); } /** - * XML-RPC method system.methodSignature maps to this function. + * Returns the method signature of a function. * - * @param $methodname - * Name of method for which we return a method signature. - * @return - * An array of types representing the method signature of the - * function that the methodname maps to. The methodSignature of - * this function is 'array', 'string' because it takes an array - * and returns a string. + * This is the function mapped to the XML-RPC method system.methodSignature. + * + * A method signature is an array of the input and output types of a method. For + * instance, the method signature of this function is array('array', 'string'), + * because it takes an array and returns a string. + * + * @param string $methodname + * Name of method to return a method signature for. + * + * @return array + * An array of types representing the method signature of the function that + * $methodname maps to. */ function xmlrpc_server_method_signature($methodname) { $xmlrpc_server = xmlrpc_server_get(); @@ -312,12 +367,18 @@ function xmlrpc_server_method_signature($methodname) { } /** + * Returns the help for an XML-RPC method. + * * XML-RPC method system.methodHelp maps to this function. * - * @param $method + * @param string $method * Name of method for which we return a help string. + * + * @return string + * Help text for $method. */ function xmlrpc_server_method_help($method) { $xmlrpc_server = xmlrpc_server_get(); return $xmlrpc_server->help[$method]; } + diff --git a/misc/drupal.js b/misc/drupal.js index 18064d69ca78b9b426cef1965ff7e1ea91c8ae79..7d0d0688f77e5f110f372ea9514e1f78831805d0 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -1,4 +1,4 @@ -// $Id: drupal.js,v 1.68 2010/05/24 07:22:12 dries Exp $ +// $Id: drupal.js,v 1.69 2010/07/28 01:38:28 dries Exp $ var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} }; @@ -331,7 +331,21 @@ $('html').addClass('js'); // 'js enabled' cookie. document.cookie = 'has_js=1; path=/'; -// Attach all behaviors. +/** + * Additions to jQuery.support. + */ +$(function () { + /** + * Boolean indicating whether or not position:fixed is supported. + */ + if (jQuery.support.positionFixed === undefined) { + var el = $('<div style="position:fixed; top:10px" />').appendTo(document.body); + jQuery.support.positionFixed = el[0].offsetTop === 10; + el.remove(); + } +}); + +//Attach all behaviors. $(function () { Drupal.attachBehaviors(document, Drupal.settings); }); diff --git a/misc/progress.js b/misc/progress.js index fea484f72a451bf110ec0a765116de84930dd6c3..adaa2ff6959a9c111a113b7f415855858735943e 100644 --- a/misc/progress.js +++ b/misc/progress.js @@ -1,4 +1,4 @@ -// $Id: progress.js,v 1.26 2009/11/20 05:53:40 webchick Exp $ +// $Id: progress.js,v 1.27 2010/09/09 21:05:11 dries Exp $ (function ($) { /** @@ -96,7 +96,7 @@ Drupal.progressBar.prototype.sendPing = function () { * Display errors on the page. */ Drupal.progressBar.prototype.displayError = function (string) { - var error = $('<div class="error"></div>').html(string); + var error = $('<div class="messages error"></div>').html(string); $(this.element).before(error).hide(); if (this.errorCallback) { diff --git a/misc/tabledrag.js b/misc/tabledrag.js index 4818662dd32da3af612359df93f706eba5770a89..ccb9f349fb54b242967125ff6fe11af175d2aec7 100644 --- a/misc/tabledrag.js +++ b/misc/tabledrag.js @@ -1,4 +1,4 @@ -// $Id: tabledrag.js,v 1.39 2010/06/20 17:34:51 webchick Exp $ +// $Id: tabledrag.js,v 1.42 2010/09/11 00:03:41 webchick Exp $ (function ($) { /** @@ -681,7 +681,7 @@ Drupal.tableDrag.prototype.updateFields = function (changedRow) { Drupal.tableDrag.prototype.updateField = function (changedRow, group) { var rowSettings = this.rowSettings(group, changedRow); - // Set the row as it's own target. + // Set the row as its own target. if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') { var sourceRow = changedRow; } @@ -1016,8 +1016,11 @@ Drupal.tableDrag.prototype.row.prototype.validIndentInterval = function (prevRow minIndent = nextRow ? $('.indentation', nextRow).size() : 0; // Maximum indentation: - if (!prevRow || $(this.element).is('.tabledrag-root')) { - // Do not indent the first row in the table or 'root' rows.. + if (!prevRow || $(prevRow).is(':not(.draggable)') || $(this.element).is('.tabledrag-root')) { + // Do not indent: + // - the first row in the table, + // - rows dragged below a non-draggable row, + // - 'root' rows. maxIndent = 0; } else { @@ -1167,7 +1170,7 @@ Drupal.theme.prototype.tableDragIndentation = function () { }; Drupal.theme.prototype.tableDragChangedWarning = function () { - return '<div class="warning">' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>'; + return '<div class="tabledrag-changed-warning messages warning">' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>'; }; })(jQuery); diff --git a/misc/tableheader.js b/misc/tableheader.js index 4ae90b03e21ab0b678ecc6d87502343a941b8dfe..a377bd9ce407c7e853a103e58eaabfa12d58cb4f 100644 --- a/misc/tableheader.js +++ b/misc/tableheader.js @@ -1,118 +1,111 @@ -// $Id: tableheader.js,v 1.31 2010/05/23 18:23:32 dries Exp $ +// $Id: tableheader.js,v 1.32 2010/07/28 01:38:28 dries Exp $ (function ($) { -Drupal.tableHeaderDoScroll = function () { - if ($.isFunction(Drupal.tableHeaderOnScroll)) { - Drupal.tableHeaderOnScroll(); - } -}; - +/** + * Attaches sticky table headers. + */ Drupal.behaviors.tableHeader = { attach: function (context, settings) { - // This breaks in anything less than IE 7. Prevent it from running. - if ($.browser.msie && parseInt($.browser.version, 10) < 7) { + if (!$.support.positionFixed) { return; } - $('table.sticky-enabled thead', context).once('tableheader', function () { - // Clone the table header so it inherits original jQuery properties. Hide - // the table to avoid a flash of the header clone upon page load. - var headerClone = $(this).clone(true).hide().insertBefore(this.parentNode).wrap('<table class="sticky-header"></table>').parent().css({ - position: 'fixed', - top: '0px' - }); - - headerClone = $(headerClone)[0]; + $('table.sticky-enabled', context).once('tableheader', function () { + $(this).data("drupal-tableheader", new Drupal.tableHeader(this)); + }); + } +}; - // Store parent table. - var table = $(this).parent('table')[0]; - headerClone.table = table; - // Finish initializing header positioning. - tracker(headerClone); - // We hid the header to avoid it showing up erroneously on page load; - // we need to unhide it now so that it will show up when expected. - $(headerClone).children('thead').show(); +/** + * Constructor for the tableHeader object. Provides sticky table headers. + * + * @param table + * DOM object for the table to add a sticky header to. + */ +Drupal.tableHeader = function (table) { + var self = this; - $(table).addClass('sticky-table'); - }); + this.originalTable = $(table); + this.originalHeader = $(table).children('thead'); + this.originalHeaderCells = this.originalHeader.find('> tr > th'); - // Define the anchor holding var. - var prevAnchor = ''; + // Clone the table header so it inherits original jQuery properties. Hide + // the table to avoid a flash of the header clone upon page load. + this.stickyTable = $('<table class="sticky-header"/>') + .insertBefore(this.originalTable) + .css({ position: 'fixed', top: '0px' }); + this.stickyHeader = this.originalHeader.clone(true) + .hide() + .appendTo(this.stickyTable); + this.stickyHeaderCells = this.stickyHeader.find('> tr > th'); - // Track positioning and visibility. - function tracker(e) { - // Reset top position of sticky table headers to the current top offset. - var topOffset = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; - $('.sticky-header').css('top', topOffset + 'px'); - // Save positioning data. - var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; - if (e.viewHeight != viewHeight) { - e.viewHeight = viewHeight; - e.vPosition = $(e.table).offset().top - 4 - topOffset; - e.hPosition = $(e.table).offset().left; - e.vLength = e.table.clientHeight - 100; - // Resize header and its cell widths. - var parentCell = $('th', e.table); - $('th', e).each(function (index) { - var cellWidth = parentCell.eq(index).css('width'); - // Exception for IE7. - if (cellWidth == 'auto') { - cellWidth = parentCell.get(index).clientWidth + 'px'; - } - $(this).css('width', cellWidth); - }); - $(e).css('width', $(e.table).css('width')); + this.originalTable.addClass('sticky-table'); + $(window) + .bind('scroll.drupal-tableheader', $.proxy(this, 'eventhandlerRecalculateStickyHeader')) + .bind('resize.drupal-tableheader', { calculateWidth: true }, $.proxy(this, 'eventhandlerRecalculateStickyHeader')) + // Make sure the anchor being scrolled into view is not hidden beneath the + // sticky table header. Adjust the scrollTop if it does. + .bind('drupalDisplaceAnchor.drupal-tableheader', function () { + window.scrollBy(0, -self.stickyTable.outerHeight()); + }) + // Make sure the element being focused is not hidden beneath the sticky + // table header. Adjust the scrollTop if it does. + .bind('drupalDisplaceFocus.drupal-tableheader', function (event) { + if (self.stickyVisible && event.clientY < (self.stickyOffsetTop + self.stickyTable.outerHeight()) && event.$target.closest('sticky-header').length === 0) { + window.scrollBy(0, -self.stickyTable.outerHeight()); } + }) + .triggerHandler('resize.drupal-tableheader'); - // Track horizontal positioning relative to the viewport and set visibility. - var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft; - var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - e.vPosition; - var visState = (vOffset > 0 && vOffset < e.vLength) ? 'visible' : 'hidden'; - $(e).css({ left: -hScroll + e.hPosition + 'px', visibility: visState }); + // We hid the header to avoid it showing up erroneously on page load; + // we need to unhide it now so that it will show up when expected. + this.stickyHeader.show(); +}; - // Check the previous anchor to see if we need to scroll to make room for the header. - // Get the height of the header table and scroll up that amount. - if (prevAnchor != location.hash) { - if (location.hash != '') { - var scrollLocation = $('td' + location.hash).offset().top - $(e).height(); - $('body, html').scrollTop(scrollLocation); - } - prevAnchor = location.hash; - } - } +/** + * Event handler: recalculates position of the sticky table header. + * + * @param event + * Event being triggered. + */ +Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (event) { + var self = this; + var calculateWidth = event.data && event.data.calculateWidth; - // Only attach to scrollbars once, even if Drupal.attachBehaviors is called - // multiple times. - $('body').once(function () { - $(window).scroll(Drupal.tableHeaderDoScroll); - $(document.documentElement).scroll(Drupal.tableHeaderDoScroll); - }); + // Reset top position of sticky table headers to the current top offset. + this.stickyOffsetTop = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; + this.stickyTable.css('top', this.stickyOffsetTop + 'px'); - // Track scrolling. - Drupal.tableHeaderOnScroll = function () { - $('table.sticky-header').each(function () { - tracker(this); - }); - }; + // Save positioning data. + var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; + if (calculateWidth || this.viewHeight !== viewHeight) { + this.viewHeight = viewHeight; + this.vPosition = this.originalTable.offset().top - 4 - this.stickyOffsetTop; + this.hPosition = this.originalTable.offset().left; + this.vLength = this.originalTable[0].clientHeight - 100; + calculateWidth = true; + } + + // Track horizontal positioning relative to the viewport and set visibility. + var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft; + var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - this.vPosition; + this.stickyVisible = vOffset > 0 && vOffset < this.vLength; + this.stickyTable.css({ left: (-hScroll + this.hPosition) + 'px', visibility: this.stickyVisible ? 'visible' : 'hidden' }); - // Track resizing. - var time = null; - var resize = function () { - // Ensure minimum time between adjustments. - if (time) { - return; + // Only perform expensive calculations if the sticky header is actually + // visible or when forced. + if (this.stickyVisible && (calculateWidth || !this.widthCalculated)) { + this.widthCalculated = true; + // Resize header and its cell widths. + this.stickyHeaderCells.each(function (index) { + var cellWidth = self.originalHeaderCells.eq(index).css('width'); + // Exception for IE7. + if (cellWidth == 'auto') { + cellWidth = self.originalHeaderCells.get(index).clientWidth + 'px'; } - time = setTimeout(function () { - $('table.sticky-header').each(function () { - // Force cell width calculation. - this.viewHeight = 0; - tracker(this); - }); - // Reset timer. - time = null; - }, 250); - }; - $(window).resize(resize); + $(this).css('width', cellWidth); + }); + this.stickyTable.css('width', this.originalTable.css('width')); } }; diff --git a/misc/vertical-tabs.js b/misc/vertical-tabs.js index a604c4b9fb02577fcc1ca05ebc5acdeba1291c81..7359953114148b52d02020651a09cf7232f9c7d3 100644 --- a/misc/vertical-tabs.js +++ b/misc/vertical-tabs.js @@ -1,4 +1,4 @@ -// $Id: vertical-tabs.js,v 1.13 2010/04/28 20:25:21 dries Exp $ +// $Id: vertical-tabs.js,v 1.14 2010/07/24 16:56:31 dries Exp $ (function ($) { @@ -49,7 +49,14 @@ Drupal.behaviors.verticalTabs = { $('> li:last', tab_list).addClass('last'); if (!tab_focus) { - tab_focus = $('> .vertical-tabs-pane:first', this); + // If the current URL has a fragment and one of the tabs contains an + // element that matches the URL fragment, activate that tab. + if (window.location.hash && $(window.location.hash, this).length) { + tab_focus = $(window.location.hash, this).closest('.vertical-tabs-pane'); + } + else { + tab_focus = $('> .vertical-tabs-pane:first', this); + } } if (tab_focus.length) { tab_focus.data('verticalTab').focus(); diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index 2e143ea7bfb0632ace3eb4eee1149d8ea457622c..8a960a82dde0328584da3ad2c1827ae5a9efa374 100644 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -1,4 +1,4 @@ -; $Id: aggregator.info,v 1.13 2009/11/17 21:24:18 dries Exp $ +; $Id: aggregator.info,v 1.14 2010/09/05 02:21:37 dries Exp $ name = Aggregator description = "Aggregates syndicated content (RSS, RDF, and Atom feeds)." package = Core @@ -13,9 +13,10 @@ files[] = aggregator.processor.inc files[] = aggregator.install files[] = aggregator.test configure = admin/config/services/aggregator/settings +stylesheets[all][] = aggregator.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/aggregator/aggregator.install b/modules/aggregator/aggregator.install index a1c165955d058b2a5e842a031a2f7c0494a189fe..8cc421b995aec268b7f148f880f1f1c69e92e9b7 100644 --- a/modules/aggregator/aggregator.install +++ b/modules/aggregator/aggregator.install @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.install,v 1.30 2010/05/01 08:12:22 dries Exp $ +// $Id: aggregator.install,v 1.31 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -80,7 +80,10 @@ function aggregator_schema() { 'fid' => array('fid'), ), 'foreign keys' => array( - 'cid' => array('aggregator_category' => 'cid'), + 'aggregator_category' => array( + 'table' => 'aggregator_category', + 'columns' => array('cid' => 'cid'), + ), ), ); @@ -105,7 +108,10 @@ function aggregator_schema() { 'iid' => array('iid'), ), 'foreign keys' => array( - 'cid' => array('aggregator_category' => 'cid'), + 'aggregator_category' => array( + 'table' => 'aggregator_category', + 'columns' => array('cid' => 'cid'), + ), ), ); @@ -264,7 +270,10 @@ function aggregator_schema() { 'fid' => array('fid'), ), 'foreign keys' => array( - 'fid' => array('aggregator_feed' => 'fid'), + 'aggregator_feed' => array( + 'table' => 'aggregator_feed', + 'columns' => array('fid' => 'fid'), + ), ), ); diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module index 672d078f206e7f78b2e044dff3450b3a0d2f0e03..0ebbc0a3f0840573c5b6b7c24a50da33a10508e1 100644 --- a/modules/aggregator/aggregator.module +++ b/modules/aggregator/aggregator.module @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.module,v 1.439 2010/05/01 08:12:22 dries Exp $ +// $Id: aggregator.module,v 1.443 2010/09/05 02:21:38 dries Exp $ /** * @file @@ -282,13 +282,6 @@ function _aggregator_category_title($category) { return $category['title']; } -/** - * Implements hook_init(). - */ -function aggregator_init() { - drupal_add_css(drupal_get_path('module', 'aggregator') . '/aggregator.css'); -} - /** * Find out whether there are any aggregator categories. * @@ -412,7 +405,7 @@ function aggregator_block_view($delta = '') { if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) { $block['subject'] = check_plain($feed->title); $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id)); - $read_more = theme('more_link', array('url' => url('aggregator/sources/' . $feed->fid), 'title' => t("View this feed's recent news."))); + $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news."))); } break; @@ -420,7 +413,7 @@ function aggregator_block_view($delta = '') { if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) { $block['subject'] = check_plain($category->title); $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $category->block, array(':cid' => $category->cid)); - $read_more = theme('more_link', array('url' => url('aggregator/categories/' . $category->cid), 'title' => t("View this category's recent news."))); + $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news."))); } break; } @@ -547,9 +540,9 @@ function aggregator_save_feed($edit) { if (!empty($edit['category'])) { foreach ($edit['category'] as $cid => $value) { if ($value) { - db_merge('aggregator_category_feed') - ->key(array('fid' => $edit['fid'])) + db_insert('aggregator_category_feed') ->fields(array( + 'fid' => $edit['fid'], 'cid' => $cid, )) ->execute(); diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc index 08bfa675aef787e33aa4c380857b7f341eaba6ac..9e355fc25e6d561181ee0b2102a24fd2a50b6972 100644 --- a/modules/aggregator/aggregator.pages.inc +++ b/modules/aggregator/aggregator.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.pages.inc,v 1.42 2010/04/24 14:49:13 dries Exp $ +// $Id: aggregator.pages.inc,v 1.43 2010/08/03 02:02:01 dries Exp $ /** * @file @@ -13,7 +13,7 @@ * The items HTML. */ function aggregator_page_last() { - drupal_add_feed(url('aggregator/rss'), variable_get('site_name', 'Drupal') . ' ' . t('aggregator')); + drupal_add_feed('aggregator/rss', variable_get('site_name', 'Drupal') . ' ' . t('aggregator')); $items = aggregator_feed_items_load('sum'); @@ -65,7 +65,7 @@ function aggregator_page_source_form($form, $form_state, $feed) { * The rendered list of items for a category. */ function aggregator_page_category($category) { - drupal_add_feed(url('aggregator/rss/' . $category['cid']), variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array('@title' => $category['title']))); + drupal_add_feed('aggregator/rss/' . $category['cid'], variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array('@title' => $category['title']))); // It is safe to include the cid in the query because it's loaded from the // database by aggregator_category_load. @@ -321,7 +321,7 @@ function aggregator_page_sources() { $feed->url = url('aggregator/sources/' . $feed->fid); $output .= theme('aggregator_summary_items', array('summary_items' => $summary_items, 'source' => $feed)); } - $output .= theme('feed_icon', array('url' => url('aggregator/opml'), 'title' => t('OPML feed'))); + $output .= theme('feed_icon', array('url' => 'aggregator/opml', 'title' => t('OPML feed'))); return theme('aggregator_wrapper', array('content' => $output)); } diff --git a/modules/aggregator/aggregator.parser.inc b/modules/aggregator/aggregator.parser.inc index 72842d5f5507aa9dbcc0ea488a74c8d9dad87522..2105aed7826a48c11ef0058ed22f70b7ec22009f 100644 --- a/modules/aggregator/aggregator.parser.inc +++ b/modules/aggregator/aggregator.parser.inc @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.parser.inc,v 1.9 2010/05/20 08:51:24 dries Exp $ +// $Id: aggregator.parser.inc,v 1.10 2010/07/24 17:42:22 dries Exp $ /** * @file @@ -177,7 +177,6 @@ function aggregator_element_start($parser, $name, $attributes) { switch ($name) { case 'image': case 'textinput': - case 'content': case 'summary': case 'tagline': case 'subtitle': @@ -186,16 +185,20 @@ function aggregator_element_start($parser, $name, $attributes) { $element = $name; break; case 'id': + case 'content': if ($element != 'item') { $element = $name; } case 'link': - if (!empty($attributes['rel']) && $attributes['rel'] == 'alternate') { + // According to RFC 4287, link elements in Atom feeds without a 'rel' + // attribute should be interpreted as though the relation type is + // "alternate". + if (!empty($attributes['HREF']) && (empty($attributes['REL']) || $attributes['REL'] == 'alternate')) { if ($element == 'item') { - $items[$item]['link'] = $attributes['href']; + $items[$item]['link'] = $attributes['HREF']; } else { - $channel['link'] = $attributes['href']; + $channel['link'] = $attributes['HREF']; } } break; @@ -223,12 +226,12 @@ function aggregator_element_end($parser, $name) { case 'textinput': case 'item': case 'entry': - case 'content': case 'info': $element = ''; break; case 'id': - if ($element == 'id') { + case 'content': + if ($element == $name) { $element = ''; } } diff --git a/modules/aggregator/aggregator.test b/modules/aggregator/aggregator.test index 153711d767bba96cea412388db1f5685fd18aa39..c5d17bc00809d28df78b23ff9895ac7d05f81c25 100644 --- a/modules/aggregator/aggregator.test +++ b/modules/aggregator/aggregator.test @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.test,v 1.39 2010/03/24 08:51:42 dries Exp $ +// $Id: aggregator.test,v 1.46 2010/09/07 23:17:47 dries Exp $ /** * @file @@ -149,6 +149,19 @@ class AggregatorTestCase extends DrupalWebTestCase { } } + /** + * Pull categories from aggregator_category table. + */ + function getCategories() { + $categories = array(); + $result = db_query('SELECT * FROM {aggregator_category}'); + foreach ($result as $category) { + $categories[$category->cid] = $category; + } + return $categories; + } + + /** * Check if the feed name and url is unique. * @@ -248,6 +261,12 @@ EOF; return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_rss091.xml'; } + function getAtomSample() { + // The content of this sample ATOM feed is based directly off of the + // example provided in RFC 4287. + return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_atom.xml'; + } + function createSampleNodes() { $langcode = LANGUAGE_NONE; // Post 5 articles. @@ -291,6 +310,49 @@ class AddFeedTestCase extends AggregatorTestCase { } } +class CategorizeFeedTestCase extends AggregatorTestCase { + public static function getInfo() { + return array( + 'name' => 'Categorize feed functionality', + 'description' => 'Categorize feed test.', + 'group' => 'Aggregator' + ); + } + + /** + * Create a feed and make sure you can add more than one category to it. + */ + function testCategorizeFeed() { + + // Create 2 categories. + $category_1 = array('title' => $this->randomName(10), 'description' => ''); + $this->drupalPost('admin/config/services/aggregator/add/category', $category_1, t('Save')); + $this->assertRaw(t('The category %title has been added.', array('%title' => $category_1['title'])), t('The category %title has been added.', array('%title' => $category_1['title']))); + + $category_2 = array('title' => $this->randomName(10), 'description' => ''); + $this->drupalPost('admin/config/services/aggregator/add/category', $category_2, t('Save')); + $this->assertRaw(t('The category %title has been added.', array('%title' => $category_2['title'])), t('The category %title has been added.', array('%title' => $category_2['title']))); + + // Get categories from database. + $categories = $this->getCategories(); + + // Create a feed and assign 2 categories to it. + $feed = $this->getFeedEditArray(); + $feed['block'] = 5; + foreach ($categories as $cid => $category) { + $feed['category'][$cid] = $cid; + } + + // Use aggregator_save_feed() function to save the feed. + aggregator_save_feed($feed); + $db_feed = db_query("SELECT * FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed['title'], ':url' => $feed['url']))->fetch(); + + // Assert the feed has two categories. + $this->getFeedCategories($db_feed); + $this->assertEqual(count($db_feed->categories), 2, t('Feed has 2 categories')); + } +} + class UpdateFeedTestCase extends AggregatorTestCase { public static function getInfo() { return array( @@ -686,3 +748,112 @@ class AggregatorCronTestCase extends AggregatorTestCase { $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.'); } } + +class AggregatorRenderingTestCase extends AggregatorTestCase { + public static function getInfo() { + return array( + 'name' => 'Checks display of aggregator items', + 'description' => 'Checks display of aggregator items on the page.', + 'group' => 'Aggregator' + ); + } + + /** + * Add a feed block to the page and checks its links. + * + * TODO: Test the category block as well. + */ + public function testBlockLinks() { + // Create feed. + $this->createSampleNodes(); + $feed = $this->createFeed(); + $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); + + // Place block on page (@see block.test:moveBlockToRegion()) + // Need admin user to be able to access block admin. + $this->admin_user = $this->drupalCreateUser(array( + 'administer blocks', + 'access administration pages', + 'administer news feeds', + 'access news feeds', + )); + $this->drupalLogin($this->admin_user); + + // Prepare to use the block admin form. + $block = array( + 'module' => 'aggregator', + 'delta' => 'feed-' . $feed->fid, + 'title' => $feed->title, + ); + $region = 'footer'; + $edit = array(); + $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region; + // Check the feed block is available in the block list form. + $this->drupalGet('admin/structure/block'); + $this->assertFieldByName('blocks[' . $block['module'] . '_' . $block['delta'] . '][region]', '', 'Aggregator feed block is available for positioning.'); + // Position it. + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to %region_name region.', array( '%region_name' => $region))); + // Confirm that the block is now being displayed on pages. + $this->drupalGet('node'); + $this->assertText(t($block['title']), t('Feed block is displayed on the page.')); + + // Find the expected read_more link. + $href = 'aggregator/sources/' . $feed->fid; + $links = $this->xpath('//a[@href = :href]', array(':href' => url($href))); + $this->assert(isset($links[0]), t('Link to href %href found.', array('%href' => $href))); + + // Visit that page. + $this->drupalGet($href); + $correct_titles = $this->xpath('//h1[normalize-space(text())=:title]', array(':title' => $feed->title)); + $this->assertFalse(empty($correct_titles), t('Aggregator feed page is available and has the correct title.')); + } +} + +/** + * Tests for feed parsing. + */ +class FeedParserTestCase extends AggregatorTestCase { + public static function getInfo() { + return array( + 'name' => 'Feed parser functionality', + 'description' => 'Test the built-in feed parser with valid feed samples.', + 'group' => 'Aggregator', + ); + } + + function setUp() { + parent::setUp(); + // Do not remove old aggregator items during these tests, since our sample + // feeds have hardcoded dates in them (which may be expired when this test + // is run). + variable_set('aggregator_clear', AGGREGATOR_CLEAR_NEVER); + } + + /** + * Test a feed that uses the RSS 0.91 format. + */ + function testRSS091Sample() { + $feed = $this->createFeed($this->getRSS091Sample()); + aggregator_refresh($feed); + $this->drupalGet('aggregator/sources/' . $feed->fid); + $this->assertResponse(200, t('Feed %name exists.', array('%name' => $feed->title))); + $this->assertText('First example feed item title'); + $this->assertLinkByHref('http://example.com/example-turns-one'); + $this->assertText('First example feed item description.'); + } + + /** + * Test a feed that uses the Atom format. + */ + function testAtomSample() { + $feed = $this->createFeed($this->getAtomSample()); + aggregator_refresh($feed); + $this->drupalGet('aggregator/sources/' . $feed->fid); + $this->assertResponse(200, t('Feed %name exists.', array('%name' => $feed->title))); + $this->assertText('Atom-Powered Robots Run Amok'); + $this->assertLinkByHref('http://example.org/2003/12/13/atom03'); + $this->assertText('Some text.'); + } +} + diff --git a/modules/aggregator/tests/aggregator_test.info b/modules/aggregator/tests/aggregator_test.info index 68da94c3bf771eccca87af9b794d65d14e273484..419bf4f1f00120a082818febb3263ebd336e2bdc 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/aggregator/tests/aggregator_test_atom.xml b/modules/aggregator/tests/aggregator_test_atom.xml new file mode 100644 index 0000000000000000000000000000000000000000..357b2e5a156522746face8042b1818bdf0a207d9 --- /dev/null +++ b/modules/aggregator/tests/aggregator_test_atom.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + + <title>Example Feed</title> + <link href="http://example.org/" /> + <updated>2003-12-13T18:30:02Z</updated> + <author> + <name>John Doe</name> + </author> + <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> + + <entry> + <title>Atom-Powered Robots Run Amok</title> + <link href="http://example.org/2003/12/13/atom03" /> + <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + <updated>2003-12-13T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/modules/aggregator/tests/aggregator_test_rss091.xml b/modules/aggregator/tests/aggregator_test_rss091.xml index 9576f7c164f5493d4a7e6cbdb9a029267edc7a7d..1fd5320d3e984b002c40d2d4ebc1c96b144b6906 100644 --- a/modules/aggregator/tests/aggregator_test_rss091.xml +++ b/modules/aggregator/tests/aggregator_test_rss091.xml @@ -17,14 +17,14 @@ <description>Example updates</description> </image> <item> - <title>Example turns one</title> + <title>First example feed item title</title> <link>http://example.com/example-turns-one</link> - <description>Example turns one.</description> + <description>First example feed item description.</description> </item> <item> - <title>Example turns two</title> + <title>Second example feed item title</title> <link>http://example.com/example-turns-two</link> - <description>Example turns two.</description> + <description>Second example feed item description.</description> </item> </channel> -</rss> \ No newline at end of file +</rss> diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index b0fc69bd9b8d0476c88dc6026099e9e5e794ac80..c2923305881a4cbff982d4880ebcb8c6eadda012 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: block.admin.inc,v 1.83 2010/07/08 03:41:27 webchick Exp $ +// $Id: block.admin.inc,v 1.86 2010/08/30 00:22:03 webchick Exp $ /** * @file @@ -10,7 +10,7 @@ * Menu callback for admin/structure/block/demo. */ function block_admin_demo($theme = NULL) { - drupal_add_css(drupal_get_path('module', 'block') . '/block.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'block') . '/block.css'); return ''; } @@ -32,22 +32,61 @@ function block_admin_display($theme = NULL) { } // Fetch and sort blocks. + $blocks = block_admin_display_prepare_blocks($theme); + + return drupal_get_form('block_admin_display_form', $blocks, $theme); +} + +/** + * Prepares a list of blocks for display on the blocks administration page. + * + * @param $theme + * The machine-readable name of the theme whose blocks should be returned. + * + * @return + * An array of blocks, as returned by _block_rehash(), sorted by region in + * preparation for display on the blocks administration page. + * + * @see block_admin_display_form() + */ +function block_admin_display_prepare_blocks($theme) { $blocks = _block_rehash($theme); $compare_theme = &drupal_static('_block_compare:theme'); $compare_theme = $theme; usort($blocks, '_block_compare'); - - return drupal_get_form('block_admin_display_form', $blocks, $theme); + return $blocks; } /** - * Generate main blocks administration form. + * Form builder for the main blocks administration form. + * + * @param $blocks + * An array of blocks, as returned by block_admin_display_prepare_blocks(). + * @param $theme + * A string representing the name of the theme to edit blocks for. + * @param $block_regions + * (optional) An array of regions in which the blocks will be allowed to be + * placed. Defaults to all visible regions for the theme whose blocks are + * being configured. In all cases, a dummy region for disabled blocks will + * also be displayed. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see block_admin_display_form_submit() */ -function block_admin_display_form($form, &$form_state, $blocks, $theme) { +function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_regions = NULL) { - drupal_add_css(drupal_get_path('module', 'block') . '/block.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'block') . '/block.css'); - $block_regions = system_region_list($theme, REGIONS_VISIBLE) + array(BLOCK_REGION_NONE => '<' . t('none') . '>'); + // Get a list of block regions if one was not provided. + if (!isset($block_regions)) { + $block_regions = system_region_list($theme, REGIONS_VISIBLE); + } + + // We always add a region for disabled blocks. + $block_regions += array(BLOCK_REGION_NONE => '<' . t('none') . '>'); // Weights range from -delta to +delta, so delta should be at least half // of the amount of blocks present. This makes sure all blocks in the same @@ -55,46 +94,53 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme) { $weight_delta = round(count($blocks) / 2); // Build the form tree. - $form['edited_theme'] = array('#type' => 'value', '#value' => $theme); - $form['#action'] = arg(4) ? url('admin/structure/block/list/' . $theme) : url('admin/structure/block'); + $form['edited_theme'] = array( + '#type' => 'value', + '#value' => $theme, + ); + $form['block_regions'] = array( + '#type' => 'value', + '#value' => $block_regions, + ); + $form['blocks'] = array(); $form['#tree'] = TRUE; foreach ($blocks as $i => $block) { $key = $block['module'] . '_' . $block['delta']; - $form[$key]['module'] = array( + $form['blocks'][$key]['module'] = array( '#type' => 'value', '#value' => $block['module'], ); - $form[$key]['delta'] = array( + $form['blocks'][$key]['delta'] = array( '#type' => 'value', '#value' => $block['delta'], ); - $form[$key]['info'] = array( + $form['blocks'][$key]['info'] = array( '#markup' => check_plain($block['info']), ); - $form[$key]['theme'] = array( + $form['blocks'][$key]['theme'] = array( '#type' => 'hidden', '#value' => $theme, ); - $form[$key]['weight'] = array( + $form['blocks'][$key]['weight'] = array( '#type' => 'weight', '#default_value' => $block['weight'], '#delta' => $weight_delta, '#title_display' => 'invisible', '#title' => t('Weight for @row', array('@row' => $block['info'])), ); - $form[$key]['region'] = array( + $form['blocks'][$key]['region'] = array( '#type' => 'select', '#default_value' => $block['region'], '#options' => $block_regions, ); - $form[$key]['configure'] = array( + $form['blocks'][$key]['configure'] = array( '#type' => 'link', '#title' => t('configure'), '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', ); if ($block['module'] == 'block') { - $form[$key]['delete'] = array( + $form['blocks'][$key]['delete'] = array( '#type' => 'link', '#title' => t('delete'), '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete', @@ -102,7 +148,7 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme) { } } // Do not allow disabling the main system content block. - unset($form['system_main']['region']['#options'][BLOCK_REGION_NONE]); + unset($form['blocks']['system_main']['region']['#options'][BLOCK_REGION_NONE]); $form['actions'] = array( '#tree' => FALSE, @@ -117,12 +163,14 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme) { } /** - * Process main blocks administration form submissions. + * Form submission handler for the main blocks administration form. + * + * @see block_admin_display_form() */ function block_admin_display_form_submit($form, &$form_state) { $txn = db_transaction(); - foreach ($form_state['values'] as $block) { + foreach ($form_state['values']['blocks'] as $block) { $block['status'] = (int) ($block['region'] != BLOCK_REGION_NONE); $block['region'] = $block['status'] ? $block['region'] : ''; db_update('block') @@ -184,7 +232,18 @@ function _block_compare($a, $b) { } /** - * Menu callback; displays the block configuration form. + * Form builder for the block configuration form. + * + * Also used by block_add_block_form() for adding a new custom block. + * + * @param $module + * Name of the module that implements the block to be configured. + * @param $delta + * Unique ID of the block within the context of $module. + * + * @see block_admin_configure_validate() + * @see block_admin_configure_submit() + * @ingroup forms */ function block_admin_configure($form, &$form_state, $module, $delta) { $block = block_load($module, $delta); @@ -363,6 +422,12 @@ function block_admin_configure($form, &$form_state, $module, $delta) { return $form; } +/** + * Form validation handler for the block configuration form. + * + * @see block_admin_configure() + * @see block_admin_configure_submit() + */ function block_admin_configure_validate($form, &$form_state) { if ($form_state['values']['module'] == 'block') { $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array( @@ -375,6 +440,12 @@ function block_admin_configure_validate($form, &$form_state) { } } +/** + * Form submission handler for the block configuration form. + * + * @see block_admin_configure() + * @see block_admin_configure_validate() + */ function block_admin_configure_submit($form, &$form_state) { if (!form_get_errors()) { $txn = db_transaction(); @@ -424,12 +495,22 @@ function block_admin_configure_submit($form, &$form_state) { } /** - * Menu callback: display the custom block addition form. + * Form builder for the add block form. + * + * @see block_add_block_form_validate() + * @see block_add_block_form_submit() + * @ingroup forms */ function block_add_block_form($form, &$form_state) { return block_admin_configure($form, $form_state, 'block', NULL); } +/** + * Form validation handler for the add block form. + * + * @see block_add_block_form() + * @see block_add_block_form_submit() + */ function block_add_block_form_validate($form, &$form_state) { $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE info = :info', 0, 1, array(':info' => $form_state['values']['info']))->fetchField(); @@ -439,7 +520,12 @@ function block_add_block_form_validate($form, &$form_state) { } /** - * Save the new custom block. + * Form submission handler for the add block form. + * + * Saves the new custom block. + * + * @see block_add_block_form() + * @see block_add_block_form_validate() */ function block_add_block_form_submit($form, &$form_state) { $delta = db_insert('block_custom') @@ -499,7 +585,15 @@ function block_add_block_form_submit($form, &$form_state) { } /** - * Menu callback; confirm deletion of custom blocks. + * Form builder for the custom block deletion form. + * + * @param $module + * The name of the module that implements the block to be deleted. This should + * always equal 'block' since it only allows custom blocks to be deleted. + * @param $delta + * The unique ID of the block within the context of $module. + * + * @see block_custom_block_delete_submit() */ function block_custom_block_delete($form, &$form_state, $module, $delta) { $block = block_load($module, $delta); @@ -511,7 +605,9 @@ function block_custom_block_delete($form, &$form_state, $module, $delta) { } /** - * Deletion of custom blocks. + * Form submission handler for the custom block deletion form. + * + * @see block_custom_block_delete() */ function block_custom_block_delete_submit($form, &$form_state) { db_delete('block_custom') @@ -532,7 +628,7 @@ function block_custom_block_delete_submit($form, &$form_state) { } /** - * Process variables for block-admin-display.tpl.php. + * Processes variables for block-admin-display-form.tpl.php. * * The $variables array contains the following arguments: * - $form @@ -541,11 +637,12 @@ function block_custom_block_delete_submit($form, &$form_state) { * @see theme_block_admin_display() */ function template_preprocess_block_admin_display_form(&$variables) { + $variables['block_regions'] = $variables['form']['block_regions']['#value']; + if (isset($variables['block_regions'][BLOCK_REGION_NONE])) { + $variables['block_regions'][BLOCK_REGION_NONE] = t('Disabled'); + } - $block_regions = system_region_list($variables['form']['edited_theme']['#value'], REGIONS_VISIBLE); - $variables['block_regions'] = $block_regions + array(BLOCK_REGION_NONE => t('Disabled')); - - foreach ($block_regions as $key => $value) { + foreach ($variables['block_regions'] as $key => $value) { // Initialize an empty array for the region. $variables['block_listing'][$key] = array(); } @@ -553,33 +650,28 @@ function template_preprocess_block_admin_display_form(&$variables) { // Initialize disabled blocks array. $variables['block_listing'][BLOCK_REGION_NONE] = array(); - // Set up to track previous region in loop. - $last_region = ''; - foreach (element_children($variables['form']) as $i) { - $block = &$variables['form'][$i]; - - // Only take form elements that are blocks. - if (isset($block['info'])) { - // Fetch region for current block. - $region = $block['region']['#default_value']; - - // Set special classes needed for table drag and drop. - $variables['form'][$i]['region']['#attributes']['class'] = array('block-region-select', 'block-region-' . $region); - $variables['form'][$i]['weight']['#attributes']['class'] = array('block-weight', 'block-weight-' . $region); - - $variables['block_listing'][$region][$i] = new stdClass(); - $variables['block_listing'][$region][$i]->row_class = !empty($block['#attributes']['class']) ? implode(' ', $block['#attributes']['class']) : ''; - $variables['block_listing'][$region][$i]->block_modified = !empty($block['#attributes']['class']) && in_array('block-modified', $block['#attributes']['class']); - $variables['block_listing'][$region][$i]->block_title = drupal_render($block['info']); - $variables['block_listing'][$region][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']); - $variables['block_listing'][$region][$i]->weight_select = drupal_render($block['weight']); - $variables['block_listing'][$region][$i]->configure_link = drupal_render($block['configure']); - $variables['block_listing'][$region][$i]->delete_link = !empty($block['delete']) ? drupal_render($block['delete']) : ''; - $variables['block_listing'][$region][$i]->printed = FALSE; - - $last_region = $region; - } + // Add each block in the form to the appropriate place in the block listing. + foreach (element_children($variables['form']['blocks']) as $i) { + $block = &$variables['form']['blocks'][$i]; + + // Fetch the region for the current block. + $region = $block['region']['#default_value']; + + // Set special classes needed for table drag and drop. + $block['region']['#attributes']['class'] = array('block-region-select', 'block-region-' . $region); + $block['weight']['#attributes']['class'] = array('block-weight', 'block-weight-' . $region); + + $variables['block_listing'][$region][$i] = new stdClass(); + $variables['block_listing'][$region][$i]->row_class = !empty($block['#attributes']['class']) ? implode(' ', $block['#attributes']['class']) : ''; + $variables['block_listing'][$region][$i]->block_modified = !empty($block['#attributes']['class']) && in_array('block-modified', $block['#attributes']['class']); + $variables['block_listing'][$region][$i]->block_title = drupal_render($block['info']); + $variables['block_listing'][$region][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']); + $variables['block_listing'][$region][$i]->weight_select = drupal_render($block['weight']); + $variables['block_listing'][$region][$i]->configure_link = drupal_render($block['configure']); + $variables['block_listing'][$region][$i]->delete_link = !empty($block['delete']) ? drupal_render($block['delete']) : ''; + $variables['block_listing'][$region][$i]->printed = FALSE; } $variables['form_submit'] = drupal_render_children($variables['form']); } + diff --git a/modules/block/block.api.php b/modules/block/block.api.php index 8fedbbc56d1a745ee693b9ae31209339c9c4c36d..2827af6e6e513121dce27300d354bd6070b5bd74 100644 --- a/modules/block/block.api.php +++ b/modules/block/block.api.php @@ -1,5 +1,5 @@ <?php -// $Id: block.api.php,v 1.12 2010/04/28 12:36:26 dries Exp $ +// $Id: block.api.php,v 1.13 2010/08/13 12:25:14 dries Exp $ /** * @file @@ -14,19 +14,38 @@ /** * Define all blocks provided by the module. * - * Any module can export a block (or blocks) to be displayed by defining - * the _block hook. This hook is called by theme.inc to display a block, - * and also by block.module to procure the list of available blocks. + * This hook declares to Drupal what blocks are provided by your module and can + * optionally specify initial block configuration settings. + * + * In hook_block_info(), each block your module provides is given a unique + * identifier referred to as "delta" (the array key in the return value). Delta + * values only need to be unique within your module, and they are used in the + * following ways: + * - Passed into the other block hooks in your module as an argument to + * identify the block being configured or viewed. + * - Used to construct the default HTML ID of "block-MODULE-DELTA" applied to + * each block when it is rendered (which can then be used for CSS styling or + * JavaScript programming). + * - Used to define a theming template suggestion of block__MODULE__DELTA, for + * advanced theming possibilities. + * - Used by other modules to identify your block in hook_block_info_alter() and + * other alter hooks. + * The values of delta can be strings or numbers, but because of the uses above + * it is preferable to use descriptive strings whenever possible, and only use a + * numeric identifier if you have to (for instance if your module allows users + * to create several similar blocks that you identify within your module code + * with numeric IDs). * * @return - * An associative array whose keys define the $delta - * for each block and whose values contain the block descriptions. Each - * block description is itself an associative array, with the following - * key-value pairs: - * - 'info': (required) The human-readable name of the block. - * - 'cache': A bitmask of flags describing how the block should behave with - * respect to block caching. The following shortcut bitmasks are provided - * as constants in common.inc: + * An associative array whose keys define the delta for each block and whose + * values contain the block descriptions. Each block description is itself an + * associative array, with the following key-value pairs: + * - 'info': (required) The human-readable administrative name of the block. + * This is used to identify the block on administration screens, and + * is not displayed to non-administrative users. + * - 'cache': (optional) A bitmask describing what kind of caching is + * appropriate for the block. Drupal provides the following bitmask + * constants for defining cache granularity: * - DRUPAL_CACHE_PER_ROLE (default): The block can change depending on the * roles the user viewing the page belongs to. * - DRUPAL_CACHE_PER_USER: The block can change depending on the user @@ -38,32 +57,48 @@ * - DRUPAL_CACHE_GLOBAL: The block is the same for every user on every * page where it is visible. * - DRUPAL_NO_CACHE: The block should not get cached. - * - 'weight', 'status', 'region', 'visibility', 'pages': - * You can give your blocks an explicit weight, enable them, limit them to - * given pages, etc. These settings will be registered when the block is first - * loaded at admin/block, and from there can be changed manually via block - * administration. - * Note that if you set a region that isn't available in a given theme, the - * block will be registered instead to that theme's default region (the first - * item in the _regions array). - * - * After completing your blocks, do not forget to enable them in the - * block admin menu. + * - 'weight': (optional) Initial value for the ordering weight of this block. + * Most modules do not provide an initial value, and any value provided can + * be modified by a user on the block configuration screen. + * - 'status': (optional) Initial value for block enabled status. (1 = + * enabled, 0 = disabled). Most modules do not provide an initial value, + * and any value provided can be modified by a user on the block + * configuration screen. + * - 'region': (optional) Initial value for theme region within which this + * block is set. Most modules do not provide an initial value, and + * any value provided can be modified by a user on the block configuration + * screen. Note: If you set a region that isn't available in the currently + * enabled theme, the block will be disabled. + * - 'visibility': (optional) Initial value for the visibility flag, which + * tells how to interpret the 'pages' value. Possible values are: + * - 0: Show on all pages except listed pages. 'pages' lists the paths where + * the block should not be shown. + * - 1: Show only on listed pages. 'pages' lists the paths where the block + * should be shown. + * - 2: Use custom PHP code to determine visibility. 'pages' gives the PHP + * code to use. + * Most modules do not provide an initial value for 'visibility' or 'pages', + * and any value provided can be modified by a user on the block + * configuration screen. + * - 'pages': (optional) See 'visibility' above. * * For a detailed usage example, see block_example.module. + * + * @see hook_block_configure() + * @see hook_block_save() + * @see hook_block_view() + * @see hook_block_info_alter() */ function hook_block_info() { - $blocks['exciting'] = array( - 'info' => t('An exciting block provided by Mymodule.'), - 'weight' => 0, - 'status' => 1, - 'region' => 'sidebar_first', - // DRUPAL_CACHE_PER_ROLE will be assumed for block 0. + // This example comes from node.module. + $blocks['syndicate'] = array( + 'info' => t('Syndicate'), + 'cache' => DRUPAL_NO_CACHE ); - $blocks['amazing'] = array( - 'info' => t('An amazing block provided by Mymodule.'), - 'cache' => DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE, + $blocks['recent'] = array( + 'info' => t('Recent content'), + // DRUPAL_CACHE_PER_ROLE will be assumed. ); return $blocks; @@ -73,15 +108,17 @@ function hook_block_info() { * Change block definition before saving to the database. * * @param $blocks - * A multidimensional array of blocks keyed by the defining module and delta - * the value is a block as seen in hook_block_info(). This hook is fired + * A multidimensional array of blocks keyed by the defining module and delta; + * the values are blocks returned by hook_block_info(). This hook is fired * after the blocks are collected from hook_block_info() and the database, * right before saving back to the database. * @param $theme * The theme these blocks belong to. * @param $code_blocks - * The blocks as defined in hook_block_info before overwritten by the + * The blocks as defined in hook_block_info() before being overwritten by the * database data. + * + * @see hook_block_info() */ function hook_block_info_alter(&$blocks, $theme, $code_blocks) { // Disable the login block. @@ -89,82 +126,99 @@ function hook_block_info_alter(&$blocks, $theme, $code_blocks) { } /** - * Configuration form for the block. + * Define a configuration form for a block. * * @param $delta - * Which block to return. This is a descriptive string used to identify - * blocks within each module and also within the theme system. - * The $delta for each block is defined within the array that your module - * returns when the hook_block_info() implementation is called. + * Which block is being configured. This is a unique identifier for the block + * within the module, defined in hook_block_info(). + * * @return - * Optionally return the configuration form. + * A configuration form, if one is needed for your block beyond the standard + * elements that the block module provides (block title, visibility, etc.). * * For a detailed usage example, see block_example.module. + * + * @see hook_block_info() + * @see hook_block_save() */ function hook_block_configure($delta = '') { - if ($delta == 'exciting') { - $form['items'] = array( + // This example comes from node.module. + $form = array(); + if ($delta == 'recent') { + $form['node_recent_block_count'] = array( '#type' => 'select', - '#title' => t('Number of items'), - '#default_value' => variable_get('mymodule_block_items', 0), - '#options' => array('1', '2', '3'), + '#title' => t('Number of recent content items to display'), + '#default_value' => variable_get('node_recent_block_count', 10), + '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)), ); - return $form; } + return $form; } /** - * Save the configuration options. + * Save the configuration options from hook_block_configure(). + * + * This hook allows you to save the block-specific configuration settings + * defined within your hook_block_configure(). * * @param $delta - * Which block to save the settings for. This is a descriptive string used - * to identify blocks within each module and also within the theme system. - * The $delta for each block is defined within the array that your module - * returns when the hook_block_info() implementation is called. + * Which block is being configured. This is a unique identifier for the block + * within the module, defined in hook_block_info(). * @param $edit * The submitted form data from the configuration form. * * For a detailed usage example, see block_example.module. + * + * @see hook_block_configure() + * @see hook_block_info() */ function hook_block_save($delta = '', $edit = array()) { - if ($delta == 'exciting') { - variable_set('mymodule_block_items', $edit['items']); + // This example comes from node.module. + if ($delta == 'recent') { + variable_set('node_recent_block_count', $edit['node_recent_block_count']); } } /** - * Process the block when enabled in a region in order to view its contents. + * Return a rendered or renderable view of a block. * * @param $delta - * Which block to return. This is a descriptive string used to identify - * blocks within each module and also within the theme system. - * The $delta for each block is defined within the array that your module - * returns when the hook_block_info() implementation is called. - * @return - * An array which must define a 'subject' element and a 'content' element - * defining the block indexed by $delta. + * Which block to render. This is a unique identifier for the block + * within the module, defined in hook_block_info(). * - * The functions mymodule_display_block_exciting and _amazing, as used in the - * example, should of course be defined somewhere in your module and return the - * content you want to display to your users. If the "content" element is empty, - * no block will be displayed even if "subject" is present. + * @return + * An array containing required elements 'subject' (the block's localized + * title) and 'content' (the block's body). The 'content' element may be a + * renderable array (preferable) or rendered HTML content. * * For a detailed usage example, see block_example.module. + * + * @see hook_block_info() + * @see hook_block_view_alter() + * @see hook_block_view_MODULE_DELTA_alter() */ function hook_block_view($delta = '') { + // This example comes from node.module. Note that you can also return a + // renderable array rather than rendered HTML for 'content'. + $block = array(); + switch ($delta) { - case 'exciting': - $block = array( - 'subject' => t('Default title of the exciting block'), - 'content' => mymodule_display_block_exciting(), - ); + case 'syndicate': + $block['subject'] = t('Syndicate'); + $block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate'))); break; - case 'amazing': - $block = array( - 'subject' => t('Default title of the amazing block'), - 'content' => mymodule_display_block_amazing(), - ); + case 'recent': + if (user_access('access content')) { + $block['subject'] = t('Recent content'); + if ($nodes = node_get_recent(variable_get('node_recent_block_count', 10))) { + $block['content'] = theme('node_recent_block', array( + 'nodes' => $nodes, + )); + } else { + $block['content'] = t('No content available.'); + } + } break; } return $block; @@ -189,8 +243,8 @@ function hook_block_view($delta = '') { * @param $block * The block object, as loaded from the database, having the main properties: * - module: The name of the module that defined the block. - * - delta: The identifier for the block within that module, as defined within - * hook_block_info(). + * - delta: The unique identifier for the block within that module, as defined + * in hook_block_info(). * * @see hook_block_view_MODULE_DELTA_alter() * @see hook_block_view() @@ -223,8 +277,8 @@ function hook_block_view_alter(&$data, $block) { * @param $block * The block object, as loaded from the database, having the main properties: * - module: The name of the module that defined the block. - * - delta: The identifier for the block within that module, as defined within - * hook_block_info(). + * - delta: The unique identifier for the block within that module, as defined + * in hook_block_info(). * * @see hook_block_view_alter() * @see hook_block_view() @@ -243,21 +297,22 @@ function hook_block_view_MODULE_DELTA_alter(&$data, $block) { * Act on blocks prior to rendering. * * This hook allows you to add, remove or modify blocks in the block list. The - * block list contains the block definitions not the rendered blocks. The blocks - * are rendered after the modules have had a chance to manipulate the block - * list. - * Alternatively you can set $block->content here, which will override the - * content of the block and prevent hook_block_view() from running. + * block list contains the block definitions, not the rendered blocks. The + * blocks are rendered after the modules have had a chance to manipulate the + * block list. * - * @param $blocks - * An array of $blocks, keyed by $bid + * You can also set $block->content here, which will override the content of the + * block and prevent hook_block_view() from running. * - * This example shows how to achieve language specific visibility setting for - * blocks. + * @param $blocks + * An array of $blocks, keyed by the block ID. */ function hook_block_list_alter(&$blocks) { global $language, $theme_key; + // This example shows how to achieve language specific visibility setting for + // blocks. + $result = db_query('SELECT module, delta, language FROM {my_table}'); $block_languages = array(); foreach ($result as $record) { diff --git a/modules/block/block.css b/modules/block/block.css index 90c1e4b13e5fe5b5183aa7089fff11f5aec845df..9febed825b52cf5c4814670057f435d573a12d78 100644 --- a/modules/block/block.css +++ b/modules/block/block.css @@ -1,4 +1,4 @@ -/* $Id: block.css,v 1.7 2009/10/05 02:43:01 webchick Exp $ */ +/* $Id: block.css,v 1.8 2010/07/31 12:54:58 webchick Exp $ */ #blocks tr.region-title td { font-weight: bold; @@ -16,3 +16,22 @@ margin-bottom: 4px; padding: 3px; } +a.block-demo-backlink, +a.block-demo-backlink:link, +a.block-demo-backlink:visited { + background-color: #B4D7F0; + border-radius: 0 0 10px 10px; + -moz-border-radius: 0 0 10px 10px; + -webkit-border-radius: 0 0 10px 10px; + color: #000; + font-family: "Lucida Grande", Verdana, sans-serif; + font-size: small; + line-height: 20px; + left: 20px; /*LTR*/ + padding: 5px 10px; + position: fixed; + z-index: 499; +} +a.block-demo-backlink:hover { + text-decoration: underline; +} diff --git a/modules/block/block.info b/modules/block/block.info index 893e7834af193353e461139f4a64470f77289d9e..14fd45ac3361c79a98a7b1771b3b4aba9b022813 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/block/block.install b/modules/block/block.install index dfe9fbfaba6593ac7c0b1e26d10049a49afac1bc..2328748248c70dd3718be034726d05da95b2e0f6 100644 --- a/modules/block/block.install +++ b/modules/block/block.install @@ -1,5 +1,5 @@ <?php -// $Id: block.install,v 1.44 2010/06/28 02:05:47 webchick Exp $ +// $Id: block.install,v 1.45 2010/07/28 01:40:39 dries Exp $ /** * @file @@ -346,7 +346,7 @@ function block_update_7004() { 'theme' => $theme, 'status' => 1, 'weight' => 0, - 'region' => 'highlight', + 'region' => 'highlighted', 'visibility' => BLOCK_VISIBILITY_LISTED, 'pages' => '<front>', 'cache' => DRUPAL_NO_CACHE, diff --git a/modules/block/block.module b/modules/block/block.module index be4537a2672a0c0616932ab0304bc5cee9d071e9..5c053342d815ac93f1775d697b5af51044c31301 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -1,5 +1,5 @@ <?php -// $Id: block.module,v 1.426 2010/07/08 03:41:27 webchick Exp $ +// $Id: block.module,v 1.428 2010/09/12 00:20:36 dries Exp $ /** * @file @@ -294,6 +294,15 @@ function block_page_build(&$page) { '#weight' => 15, ); } + $page['page_top']['backlink'] = array( + '#type' => 'link', + '#title' => t('Exit block region demonstration'), + '#href' => 'admin/structure/block/list' . (variable_get('theme_default', 'garland') == $theme ? '' : '/' . $theme), + // Add the "overlay-restore" class to indicate this link should restore + // the context in which the region demonstration page was opened. + '#options' => array('attributes' => array('class' => array('block-demo-backlink', 'overlay-restore'))), + '#weight' => -10, + ); } } } @@ -327,8 +336,12 @@ function _block_get_renderable_array($list = array()) { $build[$key] = $block->content; unset($block->content); - // Add contextual links for this block; skipping the system main block. - if ($key != 'system_main') { + // Add contextual links for this block; skip the main content block, since + // contextual links are basically output as tabs/local tasks already. Also + // skip the help block, since we assume that most users do not need or want + // to perform contextual actions on the help block, and the links needlessly + // draw attention on it. + if ($key != 'system_main' && $key != 'system_help') { $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta)); } @@ -980,6 +993,18 @@ function block_form_system_performance_settings_alter(&$form, &$form_state) { ); } +/** + * Implements hook_admin_paths(). + */ +function block_admin_paths() { + $paths = array( + // Exclude the block demonstration page from admin (overlay) treatment. + // This allows us to present this page in its true form, full page. + 'admin/structure/block/demo/*' => FALSE, + ); + return $paths; +} + /** * Implements hook_modules_uninstalled(). * diff --git a/modules/block/block.test b/modules/block/block.test index ec7e2d0e47d94af44833206cf39759d14d174387..db8a257f97de27c7b6032b525b5dfde7a2af02cd 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -1,5 +1,5 @@ <?php -// $Id: block.test,v 1.54 2010/07/08 03:41:27 webchick Exp $ +// $Id: block.test,v 1.58 2010/08/30 00:22:03 webchick Exp $ /** * @file @@ -120,7 +120,7 @@ class BlockTestCase extends DrupalWebTestCase { // Set the created custom block to a specific region. $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); $edit = array(); - $edit['block_' . $bid . '[region]'] = $this->regions[1]; + $edit['blocks[block_' . $bid . '][region]'] = $this->regions[1]; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the custom block is being displayed using configured text format. @@ -215,7 +215,7 @@ class BlockTestCase extends DrupalWebTestCase { // Set the block to the disabled region. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = '-1'; + $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = '-1'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block was moved to the proper region. @@ -228,7 +228,7 @@ class BlockTestCase extends DrupalWebTestCase { // For convenience of developers, put the navigation block back. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = $this->regions[1]; + $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $this->regions[1]; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to first sidebar region.')); @@ -239,7 +239,7 @@ class BlockTestCase extends DrupalWebTestCase { function moveBlockToRegion($block, $region) { // Set the created block to a specific region. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = $region; + $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block was moved to the proper region. @@ -314,7 +314,7 @@ class NewDefaultThemeBlocks extends DrupalWebTestCase { } /** - * Check the enabled Garland blocks are correctly copied over. + * Check the enabled Bartik blocks are correctly copied over. */ function testNewDefaultThemeBlocks() { // Create administrative user. @@ -418,7 +418,7 @@ class BlockCacheTestCase extends DrupalWebTestCase { variable_set('block_cache', TRUE); // Enable our test block. - $edit['block_test_test_cache[region]'] = 'sidebar_first'; + $edit['blocks[block_test_test_cache][region]'] = 'sidebar_first'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); } @@ -594,7 +594,7 @@ class BlockHTMLIdTestCase extends DrupalWebTestCase { $this->drupalLogin($this->admin_user); // Enable our test block. - $edit['block_test_test_html_id[region]'] = 'sidebar_first'; + $edit['blocks[block_test_test_html_id][region]'] = 'sidebar_first'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Make sure the block has some content so it will appear diff --git a/modules/block/tests/block_test.info b/modules/block/tests/block_test.info index c7234fe5dd5fea10497736121bc7eab667ad84de..58590722a7bea70b4056be65c1789134c7ffea3b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/blog/blog.info b/modules/blog/blog.info index 40bdc2d45648fed60c27a2ff9e0022d555d21f24..a23228ad353201b1a1c0078f6f9517647471518e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/blog/blog.module b/modules/blog/blog.module index 07bf67c7926f44524b7c6ca7620caaa2fece2ca7..3a485cbbb11bc77772b8c3aa3f266b96215adc91 100644 --- a/modules/blog/blog.module +++ b/modules/blog/blog.module @@ -1,5 +1,5 @@ <?php -// $Id: blog.module,v 1.356 2010/05/29 07:53:44 dries Exp $ +// $Id: blog.module,v 1.360 2010/08/30 05:58:46 webchick Exp $ /** * @file @@ -67,7 +67,7 @@ function blog_form($node, $form_state) { * Implements hook_view(). */ function blog_view($node, $view_mode) { - if (node_is_page($node)) { + if ($view_mode == 'full' && node_is_page($node)) { // Breadcrumb navigation. drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => format_username($node))), 'blog/' . $node->uid))); } @@ -80,16 +80,11 @@ function blog_view($node, $view_mode) { function blog_node_view($node, $view_mode) { if ($view_mode != 'rss') { if ($node->type == 'blog' && (arg(0) != 'blog' || arg(1) != $node->uid)) { - $links['blog_usernames_blog'] = array( + $node->content['links']['#links']['blog_usernames_blog'] = array( 'title' => t("!username's blog", array('!username' => format_username($node))), 'href' => "blog/$node->uid", 'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($node)))), ); - $node->content['links']['blog'] = array( - '#theme' => 'links__blog_node', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); } } } @@ -151,7 +146,7 @@ function blog_menu_local_tasks_alter(&$data, $router_item, $root_path) { } } // Provide a helper action link to the author on the 'blog/%' page. - else if ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) { + elseif ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) { $data['actions']['output']['blog'] = array( '#theme' => 'menu_local_action', ); @@ -245,7 +240,7 @@ function blog_block_view($delta = '') { $block['content']['blog_list'] = $node_title_list; $block['content']['blog_more'] = array( '#theme' => 'more_link', - '#url' => url('blog'), + '#url' => 'blog', '#title' => t('Read the latest blog entries.'), ); diff --git a/modules/blog/blog.pages.inc b/modules/blog/blog.pages.inc index 2ce639562d7f35d74cf432f25b9c5e0e6441cd3e..9270a12a23a9575858be356dc1e13316a349de0c 100644 --- a/modules/blog/blog.pages.inc +++ b/modules/blog/blog.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: blog.pages.inc,v 1.27 2010/05/24 04:51:18 webchick Exp $ +// $Id: blog.pages.inc,v 1.28 2010/08/03 02:02:02 dries Exp $ /** * @file @@ -45,7 +45,7 @@ function blog_page_user($account) { drupal_set_message(t('!author has not created any blog entries.', array('!author' => theme('username', array('account' => $account))))); } } - drupal_add_feed(url('blog/' . $account->uid . '/feed'), t('RSS - !title', array('!title' => $title))); + drupal_add_feed('blog/' . $account->uid . '/feed', t('RSS - !title', array('!title' => $title))); return $build; } @@ -80,7 +80,7 @@ function blog_page_last() { else { drupal_set_message(t('No blog entries have been created.')); } - drupal_add_feed(url('blog/feed'), t('RSS - blogs')); + drupal_add_feed('blog/feed', t('RSS - blogs')); return $build; } diff --git a/modules/blog/blog.test b/modules/blog/blog.test index 78080536af8f7af8809c667cf90c43e4070cfcd0..574e37942645b4f8cbb9b9f8831a51c5c4b6f6d2 100644 --- a/modules/blog/blog.test +++ b/modules/blog/blog.test @@ -1,5 +1,5 @@ <?php -// $Id: blog.test,v 1.27 2010/07/08 03:41:27 webchick Exp $ +// $Id: blog.test,v 1.30 2010/08/30 00:22:03 webchick Exp $ class BlogTestCase extends DrupalWebTestCase { protected $big_user; @@ -62,7 +62,7 @@ class BlogTestCase extends DrupalWebTestCase { $this->drupalLogin($this->big_user); // Enable the recent blog block. $edit = array(); - $edit['blog_recent[region]'] = 'sidebar_second'; + $edit['blocks[blog_recent][region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); // Verify ability to change number of recent blog posts in block. diff --git a/modules/book/book.info b/modules/book/book.info index 2c1d22b263bccec20a6a6e64bbe4546d44ca2aca..7222f470fbdf8d5efa7079ab6615312fd481c12e 100644 --- a/modules/book/book.info +++ b/modules/book/book.info @@ -1,4 +1,4 @@ -; $Id: book.info,v 1.13 2009/11/17 21:24:18 dries Exp $ +; $Id: book.info,v 1.14 2010/09/05 02:21:38 dries Exp $ name = Book description = Allows users to create and organize related content in an outline. package = Core @@ -10,9 +10,10 @@ files[] = book.pages.inc files[] = book.install files[] = book.test configure = admin/content/book/settings +stylesheets[all][] = book.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/book/book.module b/modules/book/book.module index 04a8f8ca2cd83db2debce6bf015f758f59b447e4..1a53dcc4158da98f00594917f09649f0ed02c656 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -1,5 +1,5 @@ <?php -// $Id: book.module,v 1.547 2010/07/07 01:07:33 dries Exp $ +// $Id: book.module,v 1.552 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -91,7 +91,7 @@ function book_node_view_link($node, $view_mode) { $links = array(); if (isset($node->book['depth'])) { - if ($view_mode == 'full') { + if ($view_mode == 'full' && node_is_page($node)) { $child_type = variable_get('book_child_type', 'book'); if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) { $links['book_add_child'] = array( @@ -112,11 +112,7 @@ function book_node_view_link($node, $view_mode) { } if (!empty($links)) { - $node->content['links']['book'] = array( - '#theme' => 'links__book_node', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); + $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links); } } @@ -218,13 +214,6 @@ function book_admin_paths() { return $paths; } -/** - * Implements hook_init(). - */ -function book_init() { - drupal_add_css(drupal_get_path('module', 'book') . '/book.css'); -} - /** * Implements hook_entity_info_alter(). */ @@ -387,35 +376,33 @@ function book_get_books() { } /** - * Implements hook_form_alter(). + * Implements hook_form_BASE_FORM_ID_alter(). * * Adds the book fieldset to the node form. + * + * @see book_pick_book_nojs_submit() */ -function book_form_alter(&$form, &$form_state, $form_id) { - if (!empty($form['#node_edit_form'])) { - // Add elements to the node form. - $node = $form['#node']; - - $access = user_access('administer book outlines'); - if (!$access) { - if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) { - // Already in the book hierarchy, or this node type is allowed. - $access = TRUE; - } +function book_form_node_form_alter(&$form, &$form_state, $form_id) { + $node = $form['#node']; + $access = user_access('administer book outlines'); + if (!$access) { + if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) { + // Already in the book hierarchy, or this node type is allowed. + $access = TRUE; } + } - if ($access) { - _book_add_form_elements($form, $form_state, $node); - // Since the "Book" dropdown can't trigger a form submission when - // JavaScript is disabled, add a submit button to do that. book.css hides - // this button when JavaScript is enabled. - $form['book']['pick-book'] = array( - '#type' => 'submit', - '#value' => t('Change book (update list of parents)'), - '#submit' => array('book_pick_book_nojs_submit'), - '#weight' => 20, - ); - } + if ($access) { + _book_add_form_elements($form, $form_state, $node); + // Since the "Book" dropdown can't trigger a form submission when + // JavaScript is disabled, add a submit button to do that. book.css hides + // this button when JavaScript is enabled. + $form['book']['pick-book'] = array( + '#type' => 'submit', + '#value' => t('Change book (update list of parents)'), + '#submit' => array('book_pick_book_nojs_submit'), + '#weight' => 20, + ); } } diff --git a/modules/book/book.test b/modules/book/book.test index 530f536da3dbb3b72e1305c6a94dbbc007b750be..c683bd5afa574963a5412fba4201bfd24cb198e7 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -1,5 +1,5 @@ <?php -// $Id: book.test,v 1.24 2010/04/22 23:31:23 webchick Exp $ +// $Id: book.test,v 1.27 2010/08/30 00:22:03 webchick Exp $ class BookTestCase extends DrupalWebTestCase { protected $book; @@ -271,7 +271,7 @@ class BookBlockTestCase extends DrupalWebTestCase { // Set the block to a region to confirm block is availble. $edit = array(); - $edit['book_navigation[region]'] = 'footer'; + $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.')); } diff --git a/modules/color/color.info b/modules/color/color.info index a6d121dd89905232f1ba10f852a4171fdf64fcb0..65d8ac8e43dc2f66777f33a7f356b9ebdea59e20 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/color/color.module b/modules/color/color.module index 5966dcf9e9c261c28bb007a2ece6cdd976c50b98..2a22a1481ea751df9607ba803346af97fc99c5d5 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -1,5 +1,5 @@ <?php -// $Id: color.module,v 1.87 2010/06/30 15:03:06 dries Exp $ +// $Id: color.module,v 1.90 2010/08/17 22:05:22 dries Exp $ /** * Implements hook_help(). @@ -73,7 +73,7 @@ function _color_theme_select_form_alter(&$form, &$form_state) { * Callback for the theme to alter the resources used. */ function _color_html_alter(&$vars) { - global $language, $theme_key; + global $theme_key; $themes = list_themes(); // Override stylesheets. @@ -102,7 +102,7 @@ function _color_html_alter(&$vars) { * Callback for the theme to alter the resources used. */ function _color_page_alter(&$vars) { - global $language, $theme_key; + global $theme_key; // Override logo. $logo = variable_get('color_' . $theme_key . '_logo'); @@ -189,7 +189,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) { ), // Add custom CSS. 'css' => array( - $base . '/color.css' => array('preprocess' => FALSE), + $base . '/color.css' => array(), ), // Add custom JavaScript. 'js' => array( @@ -312,10 +312,10 @@ function color_scheme_form_submit($form, &$form_state) { // Delete old files. foreach (variable_get('color_' . $theme . '_files', array()) as $file) { - @unlink($file); + @drupal_unlink($file); } if (isset($file) && $file = dirname($file)) { - @rmdir($file); + @drupal_rmdir($file); } // Don't render the default colorscheme, use the standard theme instead. diff --git a/modules/color/color.test b/modules/color/color.test index d7405a3b4a79a0c1b7eedec86fff910139451fc2..e0566439be4ea0730dc91a482520d3593f7a7abd 100644 --- a/modules/color/color.test +++ b/modules/color/color.test @@ -1,5 +1,5 @@ <?php -// $Id: color.test,v 1.5 2010/07/08 03:41:27 webchick Exp $ +// $Id: color.test,v 1.7 2010/08/12 16:59:04 dries Exp $ /** * @file @@ -11,11 +11,12 @@ */ class ColorTestCase extends DrupalWebTestCase { protected $big_user; + protected $themes; public static function getInfo() { return array( 'name' => 'Color functionality', - 'description' => 'Modify the garland theme color and make sure the changes are reflected on the frontend', + 'description' => 'Modify the Bartik and Garland theme colors and make sure the changes are reflected on the frontend', 'group' => 'Color', ); } @@ -26,40 +27,62 @@ class ColorTestCase extends DrupalWebTestCase { // Create users. $this->big_user = $this->drupalCreateUser(array('administer themes')); - // This test relies on Garland, the mother of all the colorable themes. - theme_enable(array('garland')); - variable_set('theme_default', 'garland'); + // This tests the color module in both Bartik and Garland. + $this->themes = array( + 'bartik' => array( + 'palette_input' => 'palette[bg]', + 'scheme' => 'slate', + 'scheme_color' => '#3b3b3b', + ), + 'garland' => array( + 'palette_input' => 'palette[link]', + 'scheme' => 'greenbeam', + 'scheme_color' => '#0c7a00', + ), + ); + theme_enable(array_keys($this->themes)); } /** * Test color module functionality. */ function testColor() { + foreach ($this->themes as $theme => $test_values) { + $this->_testColor($theme, $test_values); + } + } + + /** + * Tests color module functionality using the given theme. + */ + function _testColor($theme, $test_values) { + variable_set('theme_default', $theme); + $settings_path = 'admin/appearance/settings/' . $theme; + $this->drupalLogin($this->big_user); - $this->drupalGet('admin/appearance/settings/garland'); + $this->drupalGet($settings_path); $this->assertResponse(200); $edit['scheme'] = ''; - $edit['palette[link]'] = '#123456'; - $this->drupalPost('admin/appearance/settings/garland', $edit, t('Save configuration')); + $edit[$test_values['palette_input']] = '#123456'; + $this->drupalPost($settings_path, $edit, t('Save configuration')); $this->drupalGet('<front>'); - $stylesheets = variable_get('color_garland_stylesheets', array()); - $this->assertPattern('|' . file_create_url($stylesheets[0]) . '|', 'Make sure the color stylesheet is included in the content.'); + $stylesheets = variable_get('color_' . $theme . '_stylesheets', array()); + $this->assertPattern('|' . file_create_url($stylesheets[0]) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')'); $stylesheet_content = join("\n", file($stylesheets[0])); $matched = preg_match('/(.*color: #123456.*)/i', $stylesheet_content, $matches); - $this->assertTrue($matched == 1, 'Make sure the color we changed is in the color stylesheet.'); + $this->assertTrue($matched == 1, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')'); - $this->drupalGet('admin/appearance/settings/garland'); + $this->drupalGet($settings_path); $this->assertResponse(200); - $edit['scheme'] = 'greenbeam'; - $this->drupalPost('admin/appearance/settings/garland', $edit, t('Save configuration')); + $edit['scheme'] = $test_values['scheme']; + $this->drupalPost($settings_path, $edit, t('Save configuration')); $this->drupalGet('<front>'); - $stylesheets = variable_get('color_garland_stylesheets', array()); + $stylesheets = variable_get('color_' . $theme . '_stylesheets', array()); $stylesheet_content = join("\n", file($stylesheets[0])); - $matched = preg_match('/(.*color: #0c7a00.*)/i', $stylesheet_content, $matches); - $this->assertTrue($matched == 1, 'Make sure the color we changed is in the color stylesheet.'); + $matched = preg_match('/(.*color: ' . $test_values['scheme_color'] . '.*)/i', $stylesheet_content, $matches); + $this->assertTrue($matched == 1, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')'); } - } diff --git a/modules/comment/comment.admin.inc b/modules/comment/comment.admin.inc index e8ad4d43973c451f327e538691302ffacaa3e0b0..674053c995800242696d91295ee3fca0ddad8a4c 100644 --- a/modules/comment/comment.admin.inc +++ b/modules/comment/comment.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: comment.admin.inc,v 1.47 2010/06/25 21:24:35 dries Exp $ +// $Id: comment.admin.inc,v 1.49 2010/09/11 01:47:14 dries Exp $ /** * @file @@ -168,7 +168,7 @@ function comment_admin_overview_submit($form, &$form_state) { if ($operation == 'unpublish') { $comment->status = COMMENT_NOT_PUBLISHED; } - else if ($operation == 'publish') { + elseif ($operation == 'publish') { $comment->status = COMMENT_PUBLISHED; } comment_save($comment); @@ -273,7 +273,7 @@ function comment_confirm_delete_submit($form, &$form_state) { // Delete the comment and its replies. comment_delete($comment->cid); drupal_set_message(t('The comment and all its replies have been deleted.')); - watchdog('content', t('Deleted comment @cid and its replies.', array('@cid' => $comment->cid))); + watchdog('content', 'Deleted comment @cid and its replies.', array('@cid' => $comment->cid)); // Clear the cache so an anonymous user sees that his comment was deleted. cache_clear_all(); diff --git a/modules/comment/comment.info b/modules/comment/comment.info index 155904028d4a84f4597f4fb78029d622a2f33c02..8fe14f2f1a7e4a3605cd84c80230127b01f1fa1c 100644 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -1,9 +1,10 @@ -; $Id: comment.info,v 1.12 2009/11/17 21:24:18 dries Exp $ +; $Id: comment.info,v 1.14 2010/09/05 02:21:38 dries Exp $ name = Comment description = Allows users to comment on and discuss published content. package = Core version = VERSION core = 7.x +dependencies[] = text files[] = comment.module files[] = comment.admin.inc files[] = comment.pages.inc @@ -11,9 +12,10 @@ files[] = comment.install files[] = comment.test files[] = comment.tokens.inc configure = admin/content/comment +stylesheets[all][] = comment.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/comment/comment.install b/modules/comment/comment.install index 29449e12cc50cb660df251f1052522bf437e0ffc..358eec788d31d4d3b9b08a7c6175b2c96980ca48 100644 --- a/modules/comment/comment.install +++ b/modules/comment/comment.install @@ -1,11 +1,40 @@ <?php -// $Id: comment.install,v 1.67 2010/05/23 19:10:22 dries Exp $ +// $Id: comment.install,v 1.72 2010/09/13 05:50:09 webchick Exp $ /** * @file * Install, update and uninstall functions for the comment module. */ +/** + * Implements hook_install(). + */ +function comment_install() { + // Create comment body field. + if (!field_info_field('comment_body')) { + $field = array( + 'field_name' => 'comment_body', + 'type' => 'text_long', + 'entity_types' => array('comment'), + ); + field_create_field($field); + } + + // There is a separate comment bundle for each node type to allow for + // per-node-type customization of comment fields. Each one of these bundles + // needs a comment body field instance. A comment bundle is needed even for + // node types whose comments are disabled by default, because individual nodes + // may override that default. + // @todo This should be changed to call field_attach_create_bundle() instead, + // and a comment_field_attach_create_bundle() function should be added to + // handle the creation of the comment body field instance. + foreach (node_type_get_types() as $type => $info) { + if (!isset($info->is_new) && !isset($info->disabled) && !field_info_instance('comment', 'comment_body', 'comment_node_' . $info->type)) { + _comment_body_field_instance_create($info); + } + } +} + /** * Implements hook_uninstall(). */ @@ -46,52 +75,15 @@ function comment_enable() { db_insert('node_comment_statistics') ->from($query) ->execute(); - - // Create comment body field. - // @todo this should be done in comment_install, but causes exceptions - // in testing because the list of fields types is not available in - // hook_install(): _field_info_collate_types() returns an empty array. - if (!field_info_field('comment_body')) { - $field = array( - 'field_name' => 'comment_body', - 'type' => 'text_long', - 'entity_types' => array('comment'), - ); - field_create_field($field); - } - - // There is a separate comment bundle for each node type to allow for - // per-node-type customization of comment fields. Each one of these bundles - // needs a comment body field instance. A comment bundle is needed even for - // node types whose comments are disabled by default, because individual nodes - // may override that default. - // @todo This should be changed to call field_attach_create_bundle() instead, - // and a comment_field_attach_create_bundle() function should be added to - // handle the creation of the comment body field instance. - foreach (node_type_get_types() as $type => $info) { - if (!isset($info->is_new) && !isset($info->disabled) && !field_info_instance('comment', 'comment_body', 'comment_node_' . $info->type)) { - _comment_body_field_instance_create($info); - } - } } /** * Implements hook_update_dependencies(). */ function comment_update_dependencies() { - // Comment update 7005 creates comment Field API bundles and therefore must - // run after the Field module has been enabled, but before upgrading field - // data. + // Comment update 7005 creates the comment body field and therefore must run + // after text module has been enabled and entities have been updated. $dependencies['comment'][7005] = array( - 'system' => 7049, - ); - $dependencies['system'][7050] = array( - 'comment' => 7005, - ); - - // Comment update 7012 creates the comment body field and therefore must run - // after all entities have been updated. - $dependencies['comment'][7012] = array( 'system' => 7021, ); @@ -104,78 +96,83 @@ function comment_update_dependencies() { */ /** - * Remove comment settings for page ordering. + * Rename comment display setting variables. */ function comment_update_7000() { - $types = node_type_get_types(); - foreach ($types as $type => $object) { + $types = _update_7000_node_get_types(); + foreach ($types as $type => $type_object) { variable_del('comment_default_order' . $type); + + // Drupal 6 had four display modes: + // - COMMENT_MODE_FLAT_COLLAPSED = 1 + // - COMMENT_MODE_FLAT_EXPANDED = 2 + // - COMMENT_MODE_THREADED_COLLAPSED = 3 + // - COMMENT_MODE_THREADED_EXPANDED = 4 + // + // Drupal 7 doesn't support collapsed/expanded modes anymore, so we + // migrate all the flat modes to COMMENT_MODE_FLAT (0) and all the threaded + // modes to COMMENT_MODE_THREADED (1). + $setting = variable_get('comment_default_mode_' . $type, 4); + if ($setting == 3 || $setting == 4) { + variable_set('comment_default_mode_' . $type, 1); + } + else { + variable_set('comment_default_mode_' . $type, 0); + } + + // There were only two comment modes in the past: + // - 1 was 'required' previously, convert into DRUPAL_REQUIRED (2). + // - 0 was 'optional' previously, convert into DRUPAL_OPTIONAL (1). + $preview = variable_get('comment_preview_' . $type, 1) ? 2 : 1; + variable_set('comment_preview_' . $type, $preview); } - return t('Comment order settings removed.'); } /** * Change comment status from published being 0 to being 1 */ function comment_update_7001() { + // Choose a temporary status value different from the existing status values. + $tmp_status = db_query('SELECT MAX(status) FROM {comments}')->fetchField() + 1; + $changes = array( - 3 => 0, - 0 => 1, - 1 => 3, + 0 => $tmp_status, + 1 => 0, + $tmp_status => 1, ); foreach ($changes as $old => $new) { - db_update('comments') - ->fields(array('status' => $new)) - ->condition('status', $old) - ->execute(); + db_update('comments') + ->fields(array('status' => $new)) + ->condition('status', $old) + ->execute(); } } /** - * Rename {comments} table to {comment}. + * Rename {comments} table to {comment} and upgrade it. */ function comment_update_7002() { db_rename_table('comments', 'comment'); -} -/** - * Rename comment display setting variables. - */ -function comment_update_7004() { - $types = node_type_get_types(); - foreach ($types as $type => $object) { - $setting = variable_get('comment_default_mode_' . $type, 4); - if ($setting == 3 || $setting == 4) { - variable_set('comment_default_mode_' . $type, 1); - } - else { - variable_set('comment_default_mode_' . $type, 0); - } - } -} - -/** - * Create comment Field API bundles. - */ -function comment_update_7005() { - foreach (node_type_get_types() as $info) { - field_attach_create_bundle('comment', 'comment_node_' . $info->type); - } -} - -/** - * Create user related indexes. - */ -function comment_update_7006() { + // Add user-related indexes. db_add_index('comment', 'comment_uid', array('uid')); db_add_index('node_comment_statistics', 'last_comment_uid', array('last_comment_uid')); + + // Create a language column. + db_add_field('comment', 'language', array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + )); + db_add_index('comment', 'comment_nid_language', array('nid', 'language')); } /** * Split {comment}.timestamp into 'created' and 'changed', improve indexing on {comment}. */ -function comment_update_7007() { +function comment_update_7003() { // Drop the old indexes. db_drop_index('comment', 'status'); db_drop_index('comment', 'pid'); @@ -206,45 +203,9 @@ function comment_update_7007() { } /** - * Add language column to the {comment} table. + * Upgrade the {node_comment_statistics} table. */ -function comment_update_7008() { - // Create a language column. - db_add_field('comment', 'language', array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - )); - - // Create the index. - db_add_index('comment', 'comment_nid_language', array('nid', 'language')); -} - -/** - * Update preview setting variable to use new constants - */ -function comment_update_7009() { - foreach (node_type_get_types() as $type => $object) { - // There were only two comment modes in the past: - // - 1 was 'required' previously, convert into DRUPAL_REQUIRED (2). - // - 0 was 'optional' previously, convert into DRUPAL_OPTIONAL (1). - $original_preview = variable_get('comment_preview_' . $type, 1); - if ($original_preview) { - $preview = DRUPAL_REQUIRED; - } - else { - $preview = DRUPAL_OPTIONAL; - } - variable_set('comment_preview_' . $type, $preview); - } - return array(); -} - -/** - * Add {node_comment_statistics}.cid column. - */ -function comment_update_7010() { +function comment_update_7004() { db_add_field('node_comment_statistics', 'cid', array( 'type' => 'int', 'not null' => TRUE, @@ -252,59 +213,75 @@ function comment_update_7010() { 'description' => 'The {comment}.cid of the last comment.', )); db_add_index('node_comment_statistics', 'cid', array('cid')); -} -/** - * Add an index to node_comment_statistics on comment_count. - */ -function comment_update_7011() { + // Add an index on the comment_count. db_add_index('node_comment_statistics', 'comment_count', array('comment_count')); } /** * Create the comment_body field. */ -function comment_update_7012() { +function comment_update_7005() { // Create comment body field. $field = array( 'field_name' => 'comment_body', 'type' => 'text_long', - 'entity_types' => array('comment'), + 'module' => 'text', + 'entity_types' => array( + 'comment', + ), + 'settings' => array(), + 'cardinality' => 1, ); - field_create_field($field); + _update_7000_field_create_field($field); // Add the field to comments for all existing bundles. - $body_instance = array( - 'field_name' => 'comment_body', - 'label' => 'Comment', + $generic_instance = array( 'entity_type' => 'comment', - 'settings' => array('text_processing' => 1), + 'label' => t('Comment'), + 'settings' => array( + 'text_processing' => 1, + ), 'required' => TRUE, 'display' => array( 'default' => array( 'label' => 'hidden', 'type' => 'text_default', 'weight' => 0, + 'settings' => array(), + 'module' => 'text', + ), + ), + 'widget' => array( + 'type' => 'text_textarea', + 'settings' => array( + 'rows' => 5, ), + 'weight' => 0, + 'module' => 'text', ), + 'description' => '', ); - foreach (node_type_get_types() as $info) { - $body_instance['bundle'] = 'comment_node_' . $info->type; - field_create_instance($body_instance); + + $types = _update_7000_node_get_types(); + foreach ($types as $type => $type_object) { + $instance = $generic_instance; + $instance['bundle'] = 'comment_node_' . $type; + _update_7000_field_create_instance($field, $instance); } } /** * Migrate data from the comment field to field storage. */ -function comment_update_7013(&$sandbox) { +function comment_update_7006(&$sandbox) { // This is a multipass update. First set up some comment variables. if (empty($sandbox['total'])) { $comments = (bool) db_query_range('SELECT 1 FROM {comment}', 0, 1)->fetchField(); $sandbox['types'] = array(); if ($comments) { $sandbox['etid'] = _field_sql_storage_etid('comment'); - $sandbox['types'] = node_type_get_types(); + $sandbox['types'] = array_keys(_update_7000_node_get_types()); } $sandbox['total'] = count($sandbox['types']); } @@ -313,9 +290,9 @@ function comment_update_7013(&$sandbox) { $type = array_shift($sandbox['types']); $query = db_select('comment', 'c'); - $query->innerJoin('node', 'n', 'c.nid = n.nid AND n.type = :type', array(':type' => $type->type)); + $query->innerJoin('node', 'n', 'c.nid = n.nid AND n.type = :type', array(':type' => $type)); $query->addField('c', 'cid', 'entity_id'); - $query->addExpression("'comment_node_$type->type'", 'bundle'); + $query->addExpression("'comment_node_$type'", 'bundle'); $query->addExpression($sandbox['etid'], 'etid'); $query->addExpression('0', 'deleted'); $query->addExpression("'" . LANGUAGE_NONE . "'", 'language'); @@ -323,8 +300,7 @@ function comment_update_7013(&$sandbox) { $query->addField('c', 'comment', 'comment_body_value'); $query->addField('c', 'format', 'comment_body_format'); - $comment_body = field_info_field('comment_body'); - $comment_body_table = _field_sql_storage_tablename($comment_body); + $comment_body_table = 'field_data_comment_body'; db_insert($comment_body_table) ->from($query) @@ -456,8 +432,14 @@ function comment_schema() { ), 'primary key' => array('cid'), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), - 'uid' => array('users' => 'uid'), + 'comment_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'comment_author' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); @@ -510,8 +492,16 @@ function comment_schema() { 'last_comment_uid' => array('last_comment_uid'), ), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), - 'last_comment_uid' => array('users' => 'uid'), + 'statistics_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'last_comment_author' => array( + 'table' => 'users', + 'columns' => array( + 'last_comment_uid' => 'uid', + ), + ), ), ); diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 1d818f599cd5b54e90e4510ae91a62e23faa7db4..8b8eef6e57a8dd48cd6bc8d20fdc05e1b6f8f3fe 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -1,5 +1,5 @@ <?php -// $Id: comment.module,v 1.883 2010/06/23 02:45:35 dries Exp $ +// $Id: comment.module,v 1.898 2010/09/13 05:52:18 webchick Exp $ /** * @file @@ -103,6 +103,7 @@ function comment_entity_info() { 'entity keys' => array( 'id' => 'cid', 'bundle' => 'node_type', + 'label' => 'subject', ), 'bundles' => array(), 'view modes' => array( @@ -172,7 +173,7 @@ function comment_field_extra_fields() { 'description' => t('Author textfield'), 'weight' => -2, ), - 'title' => array( + 'subject' => array( 'label' => t('Subject'), 'description' => t('Subject textfield'), 'weight' => -1, @@ -580,7 +581,7 @@ function theme_comment_block() { $items = array(); $number = variable_get('comment_block_count', 10); foreach (comment_get_recent($number) as $comment) { - $items[] = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)) .'<span>'. t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->changed))) .'</span>'; + $items[] = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)) . ' <span>' . t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->changed))) . '</span>'; } if ($items) { @@ -681,17 +682,13 @@ function comment_node_view($node, $view_mode) { $links['comment_forbidden']['html'] = TRUE; } - $node->content['links']['comment'] = array( - '#theme' => 'links__comment_node', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); + $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links); // Only append comments when we are building a node on its own node detail // page. We compare $node and $page_node to ensure that comments are not // appended to other nodes shown on the page, for example a node_reference // displayed in 'full' view mode within another node. - if ($node->comment && node_is_page($node) && empty($node->in_preview) && user_access('access comments')) { + if ($node->comment && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview) && user_access('access comments')) { $node->content['comments'] = comment_node_page_additions($node); } } @@ -716,7 +713,6 @@ function comment_node_page_additions($node) { $comments = comment_load_multiple($cids); comment_prepare_thread($comments); $build = comment_view_multiple($comments, $node); - $build['#attached']['css'][] = drupal_get_path('module', 'comment') . '/comment.css'; $build['pager']['#theme'] = 'pager'; $additions['comments'] = $build; } @@ -724,13 +720,13 @@ function comment_node_page_additions($node) { // Append comment form if needed. if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) { - $build = drupal_get_form('comment_form', (object) array('nid' => $node->nid)); + $build = drupal_get_form("comment_node_{$node->type}_form", (object) array('nid' => $node->nid)); $additions['comment_form'] = $build; } if ($additions) { $additions += array( - '#theme' => 'comment_wrapper', + '#theme' => 'comment_wrapper__node_' . $node->type, '#node' => $node, 'comments' => array(), 'comment_form' => array(), @@ -907,32 +903,34 @@ function comment_view($comment, $node, $view_mode = 'full') { unset($comment->content); $build += array( - '#theme' => 'comment', + '#theme' => 'comment__node_' . $node->type, '#comment' => $comment, '#node' => $node, '#view_mode' => $view_mode, ); - $prefix = ''; - $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED; + if (empty($comment->in_preview)) { + $prefix = ''; + $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED; - // Add 'new' anchor if needed. - if (!empty($comment->first_new)) { - $prefix .= "<a id=\"new\"></a>\n"; - } + // Add 'new' anchor if needed. + if (!empty($comment->first_new)) { + $prefix .= "<a id=\"new\"></a>\n"; + } - // Add indentation div or close open divs as needed. - if ($is_threaded) { - $prefix .= $comment->divs <= 0 ? str_repeat('</div>', abs($comment->divs)) : "\n" . '<div class="indented">'; - } + // Add indentation div or close open divs as needed. + if ($is_threaded) { + $prefix .= $comment->divs <= 0 ? str_repeat('</div>', abs($comment->divs)) : "\n" . '<div class="indented">'; + } - // Add anchor for each comment. - $prefix .= "<a id=\"comment-$comment->cid\"></a>\n"; - $build['#prefix'] = $prefix; + // Add anchor for each comment. + $prefix .= "<a id=\"comment-$comment->cid\"></a>\n"; + $build['#prefix'] = $prefix; - // Close all open divs. - if ($is_threaded && !empty($comment->divs_final)) { - $build['#suffix'] = str_repeat('</div>', $comment->divs_final); + // Close all open divs. + if ($is_threaded && !empty($comment->divs_final)) { + $build['#suffix'] = str_repeat('</div>', $comment->divs_final); + } } // Allow modules to modify the structured comment. @@ -1081,6 +1079,12 @@ function comment_form_node_type_form_alter(&$form, $form_state) { 'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'), ), ); + $form['comment']['comment'] = array( + '#type' => 'select', + '#title' => t('Default comment setting for new content'), + '#default_value' => variable_get('comment_' . $form['#node_type']->type, COMMENT_NODE_OPEN), + '#options' => array(t('Hidden'), t('Closed'), t('Open')), + ); $form['comment']['comment_default_mode'] = array( '#type' => 'checkbox', '#title' => t('Threading'), @@ -1093,13 +1097,6 @@ function comment_form_node_type_form_alter(&$form, $form_state) { '#default_value' => variable_get('comment_default_per_page_' . $form['#node_type']->type, 50), '#options' => _comment_per_page(), ); - - $form['comment']['comment'] = array( - '#type' => 'select', - '#title' => t('Default comment setting for new content'), - '#default_value' => variable_get('comment_' . $form['#node_type']->type, COMMENT_NODE_OPEN), - '#options' => array(t('Hidden'), t('Closed'), t('Open')), - ); $form['comment']['comment_anonymous'] = array( '#type' => 'select', '#title' => t('Anonymous commenting'), @@ -1138,69 +1135,67 @@ function comment_form_node_type_form_alter(&$form, $form_state) { } /** - * Implements hook_form_alter(). + * Implements hook_form_BASE_FORM_ID_alter(). */ -function comment_form_alter(&$form, $form_state, $form_id) { - if (!empty($form['#node_edit_form'])) { - $node = $form['#node']; - $form['comment_settings'] = array( - '#type' => 'fieldset', - '#access' => user_access('administer comments'), - '#title' => t('Comment settings'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#group' => 'additional_settings', - '#attached' => array( - 'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'), - ), - '#weight' => 30, - ); - $comment_count = isset($node->nid) ? db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() : 0; - $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment; - $form['comment_settings']['comment'] = array( - '#type' => 'radios', +function comment_form_node_form_alter(&$form, $form_state) { + $node = $form['#node']; + $form['comment_settings'] = array( + '#type' => 'fieldset', + '#access' => user_access('administer comments'), + '#title' => t('Comment settings'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#group' => 'additional_settings', + '#attached' => array( + 'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'), + ), + '#weight' => 30, + ); + $comment_count = isset($node->nid) ? db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() : 0; + $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment; + $form['comment_settings']['comment'] = array( + '#type' => 'radios', + '#parents' => array('comment'), + '#default_value' => $comment_settings, + '#options' => array( + COMMENT_NODE_OPEN => t('Open'), + COMMENT_NODE_CLOSED => t('Closed'), + COMMENT_NODE_HIDDEN => t('Hidden'), + ), + COMMENT_NODE_OPEN => array( + '#type' => 'radio', + '#title' => t('Open'), + '#description' => t('Users with the "Post comments" permission can post comments.'), + '#return_value' => COMMENT_NODE_OPEN, + '#default_value' => $comment_settings, + '#id' => 'edit-comment-2', '#parents' => array('comment'), + ), + COMMENT_NODE_CLOSED => array( + '#type' => 'radio', + '#title' => t('Closed'), + '#description' => t('Users cannot post comments, but existing comments will be displayed.'), + '#return_value' => COMMENT_NODE_CLOSED, '#default_value' => $comment_settings, - '#options' => array( - COMMENT_NODE_OPEN => t('Open'), - COMMENT_NODE_CLOSED => t('Closed'), - COMMENT_NODE_HIDDEN => t('Hidden'), - ), - COMMENT_NODE_OPEN => array( - '#type' => 'radio', - '#title' => t('Open'), - '#description' => t('Users with the "Post comments" permission can post comments.'), - '#return_value' => COMMENT_NODE_OPEN, - '#default_value' => $comment_settings, - '#id' => 'edit-comment-2', - '#parents' => array('comment'), - ), - COMMENT_NODE_CLOSED => array( - '#type' => 'radio', - '#title' => t('Closed'), - '#description' => t('Users cannot post comments, but existing comments will be displayed.'), - '#return_value' => COMMENT_NODE_CLOSED, - '#default_value' => $comment_settings, - '#id' => 'edit-comment-1', - '#parents' => array('comment'), - ), - COMMENT_NODE_HIDDEN => array( - '#type' => 'radio', - '#title' => t('Hidden'), - '#description' => t('Comments are hidden from view.'), - '#return_value' => COMMENT_NODE_HIDDEN, - '#default_value' => $comment_settings, - '#id' => 'edit-comment-0', - '#parents' => array('comment'), - ), - ); - // If the node doesn't have any comments, the "hidden" option makes no - // sense, so don't even bother presenting it to the user. - if (empty($comment_count)) { - unset($form['comment_settings']['comment']['#options'][COMMENT_NODE_HIDDEN]); - unset($form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]); - $form['comment_settings']['comment'][COMMENT_NODE_CLOSED]['#description'] = t('Users cannot post comments.'); - } + '#id' => 'edit-comment-1', + '#parents' => array('comment'), + ), + COMMENT_NODE_HIDDEN => array( + '#type' => 'radio', + '#title' => t('Hidden'), + '#description' => t('Comments are hidden from view.'), + '#return_value' => COMMENT_NODE_HIDDEN, + '#default_value' => $comment_settings, + '#id' => 'edit-comment-0', + '#parents' => array('comment'), + ), + ); + // If the node doesn't have any comments, the "hidden" option makes no + // sense, so don't even bother presenting it to the user. + if (empty($comment_count)) { + unset($form['comment_settings']['comment']['#options'][COMMENT_NODE_HIDDEN]); + unset($form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]); + $form['comment_settings']['comment'][COMMENT_NODE_CLOSED]['#description'] = t('Users cannot post comments.'); } } @@ -1391,7 +1386,7 @@ function comment_access($op, $comment) { global $user; if ($op == 'edit') { - return ($user->uid && $user->uid == $comment->uid && user_access('edit own comments')) || user_access('administer comments'); + return ($user->uid && $user->uid == $comment->uid && $comment->status == COMMENT_PUBLISHED && user_access('edit own comments')) || user_access('administer comments'); } } @@ -1748,7 +1743,19 @@ function comment_get_display_page($cid, $node_type) { */ function comment_edit_page($comment) { drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject)), PASS_THROUGH); - return drupal_get_form('comment_form', $comment); + $node = node_load($comment->nid); + return drupal_get_form("comment_node_{$node->type}_form", $comment); +} + +/** + * Implements hook_forms(). + */ +function comment_forms() { + $forms = array(); + foreach (node_type_get_types() as $type) { + $forms["comment_node_{$type->type}_form"]['callback'] = 'comment_form'; + } + return $forms; } /** @@ -1791,6 +1798,11 @@ function comment_form($form, &$form_state, $comment) { $node = node_load($comment->nid); $form['#node'] = $node; + // Use #comment-form as unique jump target, regardless of node type. + $form['#id'] = drupal_html_id('comment_form'); + $form['#attributes']['class'][] = 'comment-form'; + $form['#theme'] = array('comment_form__node_' . $node->type, 'comment_form'); + $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT); $is_admin = (!empty($comment->cid) && user_access('administer comments')); @@ -1873,6 +1885,7 @@ function comment_form($form, &$form_state, $comment) { '#type' => 'textfield', '#title' => t('Your name'), '#default_value' => $author, + '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT), '#maxlength' => 60, '#size' => 30, ); @@ -1883,6 +1896,7 @@ function comment_form($form, &$form_state, $comment) { '#type' => 'textfield', '#title' => t('E-mail'), '#default_value' => $comment->mail, + '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT), '#maxlength' => 64, '#size' => 30, '#description' => t('The content of this field is kept private and will not be shown publicly.'), @@ -1896,11 +1910,6 @@ function comment_form($form, &$form_state, $comment) { '#size' => 30, '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT), ); - // Conditionally mark fields as required for anonymous users, if configured. - if (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT) { - $form['author']['name']['#required'] = TRUE; - $form['author']['mail']['#required'] = TRUE; - } // Add administrative comment publishing options. $form['author']['date'] = array( @@ -1944,14 +1953,12 @@ function comment_form($form, &$form_state, $comment) { $form['node_type'] = array('#type' => 'value', '#value' => 'comment_node_' . $node->type); // Only show the save button if comment previews are optional or if we are - // already previewing the submission. However, if there are form errors, - // we hide the save button no matter what, so that optional form elements - // (e.g., captchas) can be updated. + // already previewing the submission. $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save'), - '#access' => ($comment->cid && user_access('administer comments')) || variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['comment_preview'])), + '#access' => ($comment->cid && user_access('administer comments')) || variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || isset($form_state['comment_preview']), '#weight' => 19, ); $form['actions']['preview'] = array( @@ -2011,11 +2018,7 @@ function comment_preview($comment) { $comment->changed = REQUEST_TIME; $comment->in_preview = TRUE; $comment_build = comment_view($comment, $node); - $comment_build += array( - '#weight' => -100, - '#prefix' => '<div class="preview">', - '#suffix' => '</div>', - ); + $comment_build['#weight'] = -100; $form['comment_preview'] = $comment_build; } @@ -2054,42 +2057,29 @@ function comment_form_validate($form, &$form_state) { } } - // Check validity of name, mail and homepage (if given). - if (!$user->uid || $form_state['values']['is_anonymous']) { - $node = node_load($form_state['values']['nid']); - if (variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) > COMMENT_ANONYMOUS_MAYNOT_CONTACT) { - if ($form_state['values']['name']) { - $query = db_select('users', 'u'); - $query->addField('u', 'uid', 'uid'); - $taken = $query - ->condition('name', db_like($form_state['values']['name']), 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - if ($taken != 0) { - form_set_error('name', t('The name you used belongs to a registered user.')); - } - } - elseif (variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) { - form_set_error('name', t('You have to leave your name.')); - } - - if ($form_state['values']['mail']) { - if (!valid_email_address($form_state['values']['mail'])) { - form_set_error('mail', t('The e-mail address you specified is not valid.')); - } - } - elseif (variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) { - form_set_error('mail', t('You have to leave an e-mail address.')); - } - - if ($form_state['values']['homepage']) { - if (!valid_url($form_state['values']['homepage'], TRUE)) { - form_set_error('homepage', t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.')); - } + // Validate anonymous comment author fields (if given). + if ($form_state['values']['is_anonymous']) { + // If the (original) author of this comment was an anonymous user, verify + // that no registered user with this name exists. + if ($form_state['values']['name']) { + $query = db_select('users', 'u'); + $query->addField('u', 'uid', 'uid'); + $taken = $query + ->condition('name', db_like($form_state['values']['name']), 'LIKE') + ->countQuery() + ->execute() + ->fetchField(); + if ($taken) { + form_set_error('name', t('The name you used belongs to a registered user.')); } } } + if ($form_state['values']['mail'] && !valid_email_address($form_state['values']['mail'])) { + form_set_error('mail', t('The e-mail address you specified is not valid.')); + } + if ($form_state['values']['homepage'] && !valid_url($form_state['values']['homepage'], TRUE)) { + form_set_error('homepage', t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.')); + } } /** @@ -2109,7 +2099,7 @@ function comment_submit($comment) { $comment->created = strtotime($comment->date); $comment->changed = REQUEST_TIME; - if (!empty($comment->name) && ($account = user_load_by_name($comment->name))) { + if (!$comment->is_anonymous && !empty($comment->name) && ($account = user_load_by_name($comment->name))) { $comment->uid = $account->uid; } @@ -2220,8 +2210,6 @@ function template_preprocess_comment(&$variables) { // Preprocess fields. field_attach_preprocess('comment', $comment, $variables['elements'], $variables); - $variables['theme_hook_suggestions'][] = 'comment__' . $variables['node']->type; - // Helpful $content variable for templates. foreach (element_children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; @@ -2311,7 +2299,6 @@ function template_preprocess_comment_wrapper(&$variables) { // Provide contextual information. $variables['node'] = $variables['content']['#node']; $variables['display_mode'] = variable_get('comment_default_mode_' . $variables['node']->type, COMMENT_MODE_THREADED); - $variables['theme_hook_suggestions'][] = 'comment_wrapper__' . $variables['node']->type; } /** @@ -2630,3 +2617,12 @@ function comment_rdf_mapping() { ), ); } + +/** + * Implements hook_file_download_access(). + */ +function comment_file_download_access($field, $entity_type, $entity) { + if ($entity_type == 'comment') { + return user_access('access comments') && $entity->status == COMMENT_PUBLISHED || user_access('administer comments'); + } +} diff --git a/modules/comment/comment.pages.inc b/modules/comment/comment.pages.inc index cdfe5be4a388396a22d2ca001f24512c28cbb694..df78493b457adbd1f51cf0270154dd1b4d0dd1f5 100644 --- a/modules/comment/comment.pages.inc +++ b/modules/comment/comment.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: comment.pages.inc,v 1.39 2010/06/10 06:57:20 dries Exp $ +// $Id: comment.pages.inc,v 1.40 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -37,7 +37,7 @@ function comment_reply($node, $pid = NULL) { // The user is previewing a comment prior to submitting it. if ($op == t('Preview')) { if (user_access('post comments')) { - $build['comment_form'] = drupal_get_form('comment_form', (object) array('pid' => $pid, 'nid' => $node->nid)); + $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) array('pid' => $pid, 'nid' => $node->nid)); } else { drupal_set_message(t('You are not authorized to post comments.'), 'error'); @@ -83,7 +83,7 @@ function comment_reply($node, $pid = NULL) { } elseif (user_access('post comments')) { $edit = array('nid' => $node->nid, 'pid' => $pid); - $build['comment_form'] = drupal_get_form('comment_form', (object) $edit); + $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) $edit); } else { drupal_set_message(t('You are not authorized to post comments.'), 'error'); diff --git a/modules/comment/comment.test b/modules/comment/comment.test index a8bfa672f2bb0ae3a04c0bd8ea6e40bbb719f757..5060436abbbc44c869774175a0aa25db62d0a297 100644 --- a/modules/comment/comment.test +++ b/modules/comment/comment.test @@ -1,5 +1,5 @@ <?php -// $Id: comment.test,v 1.84 2010/07/07 17:00:42 webchick Exp $ +// $Id: comment.test,v 1.89 2010/08/30 00:22:03 webchick Exp $ class CommentHelperCase extends DrupalWebTestCase { protected $admin_user; @@ -521,6 +521,16 @@ class CommentAnonymous extends CommentHelperCase { $anonymous_comment2 = $this->postComment($this->node, $this->randomName(), $this->randomName()); $this->assertTrue($this->commentExists($anonymous_comment2), t('Anonymous comment with contact info (optional) found.')); + // Ensure anonymous users cannot post in the name of registered users. + $edit = array( + 'name' => $this->admin_user->name, + 'mail' => $this->randomName() . '@example.com', + 'subject' => $this->randomName(), + 'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(), + ); + $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save')); + $this->assertText(t('The name you used belongs to a registered user.')); + // Require contact info. $this->drupalLogin($this->admin_user); $this->setCommentAnonymous('2'); @@ -1004,7 +1014,7 @@ class CommentBlockFunctionalTest extends CommentHelperCase { // Set the block to a region to confirm block is available. $edit = array( - 'comment_recent[region]' => 'sidebar_first', + 'blocks[comment_recent][region]' => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block saved to first sidebar region.')); @@ -1022,11 +1032,16 @@ class CommentBlockFunctionalTest extends CommentHelperCase { $comment2 = $this->postComment($this->node, $this->randomName(), $this->randomName()); $comment3 = $this->postComment($this->node, $this->randomName()); - // Test that a user without the 'access comments' permission can not see the block. + // Test that a user without the 'access comments' permission cannot see the + // block. $this->drupalLogout(); + user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access comments')); $this->drupalGet(''); $this->assertNoText($block['title'], t('Block was not found.')); + user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access comments')); + // Test that a user with the 'access comments' permission can see the + // block. $this->drupalLogin($this->web_user); $this->drupalGet(''); $this->assertText($block['title'], t('Block was found.')); diff --git a/modules/contact/contact.info b/modules/contact/contact.info index 3906ba639989306eec79d648e8d24b3f4dc00424..cce79871d523309bb014a3c282948f1ee7d72e88 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/contact/contact.test b/modules/contact/contact.test index 311c7560534b7fbb38d1061dffc7ae102d92f3db..b758a083c4d04f33c589613458bdeeffd0ddd34d 100644 --- a/modules/contact/contact.test +++ b/modules/contact/contact.test @@ -1,5 +1,5 @@ <?php -// $Id: contact.test,v 1.43 2010/03/22 17:52:58 webchick Exp $ +// $Id: contact.test,v 1.45 2010/08/05 23:53:37 webchick Exp $ /** * Test the sitewide contact form. diff --git a/modules/contextual/contextual.api.php b/modules/contextual/contextual.api.php new file mode 100644 index 0000000000000000000000000000000000000000..6670322acbb7aa3834dbb74a00f51c73b843c6d6 --- /dev/null +++ b/modules/contextual/contextual.api.php @@ -0,0 +1,41 @@ +<?php +// $Id: contextual.api.php,v 1.1 2010/09/11 14:35:13 dries Exp $ + +/** + * @file + * Hooks provided by Contextual module. + */ + +/** + * @addtogroup hooks + * @{ + */ + +/** + * Alter a contextual links element before it is rendered. + * + * This hook is invoked by contextual_pre_render_links(). The renderable array + * of #type 'contextual_links', containing the entire contextual links data that + * is passed in by reference. Further links may be added or existing links can + * be altered. + * + * @param $element + * A renderable array representing the contextual links. + * @param $items + * An associative array containing the original contextual link items, as + * generated by menu_contextual_links(), which were used to build + * $element['#links']. + * + * @see hook_menu_contextual_links_alter() + * @see contextual_pre_render_links() + * @see contextual_element_info() + */ +function hook_contextual_links_view_alter(&$element, $items) { + // Add another class to all contextual link lists to facilitate custom + // styling. + $element['#attributes']['class'][] = 'custom-class'; +} + +/** + * @} End of "addtogroup hooks". + */ diff --git a/modules/contextual/contextual.info b/modules/contextual/contextual.info index 317a966f123d4ea14b75d8ace506c84ff9dca219..f5e346860d134629a792c7bacf89c52c018dfc76 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/contextual/contextual.module b/modules/contextual/contextual.module index 9d18d424936375e8e032f9c9c087e0ac18b7918e..5587a20b6182159161b6d19fe88e9f390f76d29b 100644 --- a/modules/contextual/contextual.module +++ b/modules/contextual/contextual.module @@ -1,12 +1,11 @@ <?php -// $Id: contextual.module,v 1.6 2010/06/13 05:36:58 dries Exp $ +// $Id: contextual.module,v 1.8 2010/09/11 14:35:13 dries Exp $ /** * @file * Adds contextual links to perform actions related to elements on a page. */ - /** * Implements hook_help(). */ @@ -56,10 +55,32 @@ function contextual_library() { return $libraries; } +/** + * Implements hook_element_info(). + */ +function contextual_element_info() { + $types['contextual_links'] = array( + '#pre_render' => array('contextual_pre_render_links'), + '#theme' => 'links__contextual', + '#links' => array(), + '#prefix' => '<div class="contextual-links-wrapper">', + '#suffix' => '</div>', + '#attributes' => array( + 'class' => array('contextual-links'), + ), + '#attached' => array( + 'library' => array( + array('contextual', 'contextual-links'), + ), + ), + ); + return $types; +} + /** * Template variable preprocessor for contextual links. * - * @see contextual_links_view() + * @see contextual_pre_render_links() */ function contextual_preprocess(&$variables, $hook) { static $hooks; @@ -78,7 +99,7 @@ function contextual_preprocess(&$variables, $hook) { $keys = array_keys($hooks[$hook]['variables']); $key = $keys[0]; } - else if (!empty($hooks[$hook]['render element'])) { + elseif (!empty($hooks[$hook]['render element'])) { $key = $hooks[$hook]['render element']; } if (!empty($key) && isset($variables[$key])) { @@ -86,10 +107,14 @@ function contextual_preprocess(&$variables, $hook) { } if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) { - $variables['title_suffix']['contextual_links'] = contextual_links_view($element); - if (!empty($variables['title_suffix']['contextual_links'])) { - $variables['classes_array'][] = 'contextual-links-region'; - } + // Initialize the template variable as a renderable array. + $variables['title_suffix']['contextual_links'] = array( + '#type' => 'contextual_links', + '#contextual_links' => $element['#contextual_links'], + '#element' => $element, + ); + // Mark this element as potentially having contextual links attached to it. + $variables['classes_array'][] = 'contextual-links-region'; } } @@ -113,9 +138,7 @@ function contextual_preprocess(&$variables, $hook) { * * @see menu_contextual_links() */ -function contextual_links_view($element) { - static $destination; - +function contextual_pre_render_links($element) { // Retrieve contextual menu links. $items = array(); foreach ($element['#contextual_links'] as $module => $args) { @@ -123,10 +146,6 @@ function contextual_links_view($element) { } // Transform contextual links into parameters suitable for theme_link(). - if (!isset($destination)) { - $destination = drupal_get_destination(); - } - $links = array(); foreach ($items as $class => $item) { $class = drupal_html_class($class); @@ -134,26 +153,21 @@ function contextual_links_view($element) { 'title' => $item['title'], 'href' => $item['href'], ); - // @todo theme_links() should *really* use the same parameters as l()... - if (!isset($item['localized_options']['query'])) { - $item['localized_options']['query'] = array(); - } - $item['localized_options']['query'] += $destination; + // @todo theme_links() should *really* use the same parameters as l(). + $item['localized_options'] += array('query' => array()); + $item['localized_options']['query'] += drupal_get_destination(); $links[$class] += $item['localized_options']; } - $build = array(); - if ($links) { - $build = array( - '#prefix' => '<div class="contextual-links-wrapper">', - '#suffix' => '</div>', - '#theme' => 'links__contextual', - '#links' => $links, - '#attributes' => array('class' => array('contextual-links')), - '#attached' => array( - 'library' => array(array('contextual', 'contextual-links')), - ), - ); + $element['#links'] = $links; + + // Allow modules to alter the renderable contextual links element. + drupal_alter('contextual_links_view', $element, $items); + + // If there are no links, tell drupal_render() to abort rendering. + if (empty($element['#links'])) { + $element['#printed'] = TRUE; } - return $build; + + return $element; } diff --git a/modules/dashboard/dashboard.css b/modules/dashboard/dashboard.css index 80fde4c7301cce100dc91d2861789be9c349b4b6..4b76dd530c0c0c1b33b96c37ae706582d96c5cfc 100644 --- a/modules/dashboard/dashboard.css +++ b/modules/dashboard/dashboard.css @@ -1,4 +1,4 @@ -/* $Id: dashboard.css,v 1.13 2010/04/28 20:08:38 dries Exp $ */ +/* $Id: dashboard.css,v 1.14 2010/08/30 00:22:03 webchick Exp $ */ #dashboard div.dashboard-region { float: left; @@ -46,11 +46,6 @@ padding: 10px; } -#dashboard .canvas-content a { - color: #fff; - text-decoration: underline; -} - #dashboard #disabled-blocks .ui-sortable { padding: 0; background-color: #777; diff --git a/modules/dashboard/dashboard.info b/modules/dashboard/dashboard.info index 2da7cb383ad563b9d93aba1f14122c5686d6dad9..19c1d2ed53a3d675678a4bbf40a879f87ec64dff 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/dashboard/dashboard.module b/modules/dashboard/dashboard.module index d74f800136c8ea2432cacf19e04d8864fc6e8b43..fad6a9e57115407aeaf01e2e096b3f0498ea84eb 100644 --- a/modules/dashboard/dashboard.module +++ b/modules/dashboard/dashboard.module @@ -1,5 +1,5 @@ <?php -// $Id: dashboard.module,v 1.31 2010/06/17 13:16:57 dries Exp $ +// $Id: dashboard.module,v 1.35 2010/09/14 21:51:01 webchick Exp $ /** * Implements hook_help(). @@ -18,6 +18,12 @@ function dashboard_help($path, $arg) { $output .= '<dd>' . t('By enabling blocks such as <em>Recent blog posts</em>, <em>New forum topics</em> and <em>Recent comments</em>, site users can view newly added site content at a glance.') . '</dd>'; $output .= '</dl>'; return $output; + + case 'admin/structure/dashboard': + // @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>'; + return $output; } } @@ -25,21 +31,28 @@ 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', 'page callback' => 'dashboard_admin', - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access dashboard'), + 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM, // Make this appear first, so for example, in admin menus, it shows up on // the top corner of the window as a convinient "home link". - 'weight' => -100, + 'weight' => -15, ); $items['admin/dashboard/customize'] = array( 'title' => 'Dashboard', 'description' => 'View and customize your dashboard', 'page callback' => 'dashboard_admin', 'page arguments' => array(TRUE), - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access dashboard'), 'type' => MENU_CALLBACK, ); $items['admin/dashboard/drawer'] = array( @@ -58,18 +71,22 @@ function dashboard_menu() { 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); + return $items; } /** - * Implements hook_menu_alter(). + * Implements hook_permission(). */ -function dashboard_menu_alter(&$items) { - // Make the dashboard the default local task on /admin. - $items['admin']['title'] = 'Dashboard'; - $items['admin']['page callback'] = 'dashboard_admin'; - $items['admin/dashboard']['type'] = MENU_DEFAULT_LOCAL_TASK; - $items['admin/by-task']['type'] = MENU_LOCAL_TASK; +function dashboard_permission() { + return array( + 'access dashboard' => array( + 'title' => t('View the administrative dashboard'), + 'description' => t('Customizing the dashboard requires the !permission-name permission.', array( + '!permission-name' => l(t('Administer blocks'), 'admin/people/permissions', array('fragment' => 'module-block')), + )), + ), + ); } /** @@ -188,7 +205,28 @@ function dashboard_theme() { 'dashboard_disabled_block' => array( 'variables' => array('block' => NULL), ), + 'dashboard_admin_display_form' => array( + // When building the form for configuring dashboard blocks, reuse the + // Block module's template for the main block configuration form. + 'template' => 'block-admin-display-form', + 'path' => drupal_get_path('module', 'block'), + 'file' => 'block.admin.inc', + 'render element' => 'form', + ), + ); +} + +/** + * Implements hook_forms(). + */ +function dashboard_forms() { + // Reroute the dashboard configuration form to the main blocks administration + // form. This allows us to distinguish them by form ID in hook_form_alter(). + $forms['dashboard_admin_display_form'] = array( + 'callback' => 'block_admin_display_form', ); + + return $forms; } /** @@ -221,7 +259,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="@blocks">Blocks administration page</a>, or enable JavaScript on this page to use the drag-and-drop interface.', array('@blocks' => url('admin/structure/block'))), + '#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'))), '#access' => user_access('administer blocks'), '#attached' => array( 'js' => array( @@ -234,6 +272,118 @@ function dashboard_admin($launch_customize = FALSE) { return $build; } +/** + * Menu page callback: builds the page for administering dashboard blocks. + * + * This page reuses the Block module's administration form but limits editing + * to blocks that are available to appear on the dashboard. + * + * @see block_admin_display() + * @see block_admin_display_form() + * @see dashboard_form_dashboard_admin_display_form_alter() + * @see template_preprocess_dashboard_admin_display_form() + */ +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 + // currently displayed in non-dashboard regions. + // @todo This assumes the current page is being displayed using the same + // theme that the dashboard is displayed in. + $blocks = block_admin_display_prepare_blocks($theme_key); + $dashboard_regions = dashboard_region_descriptions(); + $regions_to_remove = array_diff_key(system_region_list($theme_key, REGIONS_VISIBLE), $dashboard_regions); + foreach ($blocks as $id => $block) { + if (isset($regions_to_remove[$block['region']])) { + unset($blocks[$id]); + } + } + + // Pass in the above blocks and dashboard regions to the form, so that only + // dashboard-related regions will be displayed. + return drupal_get_form('dashboard_admin_display_form', $blocks, $theme_key, $dashboard_regions); +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function dashboard_form_block_admin_display_form_alter(&$form, &$form_state, $form_id) { + // Hide dashboard regions (and any blocks placed within them) from the block + // administration form and from the options list on that form. This + // function is called for both the dashboard block configuration form and the + // standard block configuration form so that both forms can share the same + // constructor. As a result the form_id must be checked. + if ($form_id != 'dashboard_admin_display_form') { + $dashboard_regions = dashboard_region_descriptions(); + $form['block_regions']['#value'] = array_diff_key($form['block_regions']['#value'], $dashboard_regions); + foreach (element_children($form['blocks']) as $i) { + $block = &$form['blocks'][$i]; + if (isset($block['region']['#default_value']) && isset($dashboard_regions[$block['region']['#default_value']])) { + $block['#access'] = FALSE; + } + elseif (isset($block['region']['#options'])) { + $block['region']['#options'] = array_diff_key($block['region']['#options'], $dashboard_regions); + } + } + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function dashboard_form_dashboard_admin_display_form_alter(&$form, &$form_state) { + // Inherit the submit handler from the main block administration form. + $form['#submit'][] = 'block_admin_display_form_submit'; + + // Redirect the 'configure' and 'delete' links on each block back to the + // dashboard blocks administration page. + foreach ($form['blocks'] as &$block) { + if (isset($block['configure']['#href'])) { + $block['configure']['#options']['query']['destination'] = 'admin/structure/dashboard'; + } + if (isset($block['delete']['#href'])) { + $block['delete']['#options']['query']['destination'] = 'admin/structure/dashboard'; + } + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function dashboard_form_block_admin_configure_alter(&$form, &$form_state) { + global $theme_key; + drupal_theme_initialize(); + // Hide the dashboard regions from the region select list on the block + // configuration form, for all themes except the current theme (since the + // other themes do not display the dashboard). + // @todo This assumes the current page is being displayed using the same + // theme that the dashboard is displayed in. + $dashboard_regions = dashboard_region_descriptions(); + foreach (element_children($form['regions']) as $region_name) { + $region = &$form['regions'][$region_name]; + if ($region_name != $theme_key && isset($region['#options'])) { + $region['#options'] = array_diff_key($region['#options'], $dashboard_regions); + } + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function dashboard_form_block_add_block_form_alter(&$form, &$form_state) { + dashboard_form_block_admin_configure_alter($form, $form_state); +} + +/** + * Preprocesses variables for block-admin-display-form.tpl.php. + */ +function template_preprocess_dashboard_admin_display_form(&$variables) { + template_preprocess_block_admin_display_form($variables); +} + /** * Determines if the dashboard should be displayed on the current page. * @@ -281,7 +431,7 @@ function dashboard_region_descriptions() { * Return an array of dashboard region names. */ function dashboard_regions() { - static $regions; + $regions = &drupal_static(__FUNCTION__); if (!isset($regions)) { $regions = array_keys(dashboard_region_descriptions()); } @@ -460,7 +610,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.') . '</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/structure/dashboard'))) . '</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 995b4aa101d2ad28d74a76e12278dbc1175ee3e6..ced6bce4461ff6eccd128a8df1150b595594d281 100644 --- a/modules/dashboard/dashboard.test +++ b/modules/dashboard/dashboard.test @@ -1,16 +1,16 @@ <?php -// $Id: dashboard.test,v 1.1 2010/07/02 15:24:31 webchick Exp $ +// $Id: dashboard.test,v 1.5 2010/09/14 21:51:01 webchick Exp $ /** * @file * Tests for the dashboard module. */ -class DashboardAccessTestCase extends DrupalWebTestCase { +class DashboardBlocksTestCase extends DrupalWebTestCase { public static function getInfo() { return array( - 'name' => 'Dashboard access', - 'description' => 'Test access control for the dashboard.', + 'name' => 'Dashboard blocks', + 'description' => 'Test blocks as used by the dashboard.', 'group' => 'Dashboard', ); } @@ -19,7 +19,7 @@ class DashboardAccessTestCase extends DrupalWebTestCase { parent::setUp(); // Create and log in an administrative user having access to the dashboard. - $admin_user = $this->drupalCreateUser(array('access administration pages', 'administer blocks')); + $admin_user = $this->drupalCreateUser(array('access dashboard', 'administer blocks')); $this->drupalLogin($admin_user); // Make sure that the dashboard is using the same theme as the rest of the @@ -46,15 +46,37 @@ class DashboardAccessTestCase extends DrupalWebTestCase { $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); // Ensure admin access. - $this->drupalGet('admin'); + $this->drupalGet('admin/dashboard'); $this->assertResponse(200, t('Admin has access to the dashboard.')); $this->assertRaw($custom_block['title'], t('Admin has access to a dashboard block.')); // Ensure non-admin access is denied. $normal_user = $this->drupalCreateUser(); $this->drupalLogin($normal_user); - $this->drupalGet('admin'); + $this->drupalGet('admin/dashboard'); $this->assertResponse(403, t('Non-admin has no access to the dashboard.')); $this->assertNoText($custom_block['title'], t('Non-admin has no access to a dashboard block.')); } + + /** + * Test that dashboard regions are displayed or hidden properly. + */ + function testDashboardRegions() { + $dashboard_regions = dashboard_region_descriptions(); + + // Ensure blocks can be placed in dashboard regions. + $this->drupalGet('admin/structure/dashboard'); + 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))); + } + + // Ensure blocks cannot be placed in dashboard regions on the standard + // blocks configuration page. + $this->drupalGet('admin/structure/block'); + foreach ($dashboard_regions as $region => $description) { + $elements = $this->xpath('//option[@value=:region]', array(':region' => $region)); + $this->assertTrue(empty($elements), t('%region is not an available choice on the block configuration page.', array('%region' => $region))); + } + } } diff --git a/modules/dblog/dblog.admin.inc b/modules/dblog/dblog.admin.inc index 6d3524d74e1c5a673be430bb9bf94151601e8e50..aada662c6757f67dacca7943c21f0a3aa3a85c99 100644 --- a/modules/dblog/dblog.admin.inc +++ b/modules/dblog/dblog.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: dblog.admin.inc,v 1.39 2010/04/24 14:49:13 dries Exp $ +// $Id: dblog.admin.inc,v 1.40 2010/09/09 15:47:03 webchick Exp $ /** * @file @@ -15,16 +15,6 @@ function dblog_overview() { $filter = dblog_build_filter_query(); $rows = array(); - $icons = array( - WATCHDOG_DEBUG => '', - WATCHDOG_INFO => '', - WATCHDOG_NOTICE => '', - WATCHDOG_WARNING => theme('image', array('path' => 'misc/watchdog-warning.png', 'alt' => t('warning'), 'title' => t('warning'))), - WATCHDOG_ERROR => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('error'), 'title' => t('error'))), - WATCHDOG_CRITICAL => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('critical'), 'title' => t('critical'))), - WATCHDOG_ALERT => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('alert'), 'title' => t('alert'))), - WATCHDOG_EMERGENCY => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => t('emergency'), 'title' => t('emergency'))), - ); $classes = array( WATCHDOG_DEBUG => 'dblog-debug', WATCHDOG_INFO => 'dblog-info', @@ -65,7 +55,7 @@ function dblog_overview() { $rows[] = array('data' => array( // Cells - $icons[$dblog->severity], + array('class' => 'icon'), t($dblog->type), format_date($dblog->timestamp, 'short'), theme('dblog_message', array('event' => $dblog, 'link' => TRUE)), diff --git a/modules/dblog/dblog.css b/modules/dblog/dblog.css index 954f28857166b2c92201e0e185404e30fd5e6109..e28e4956dda96e899c6f9c6fc96f7dc08f488eae 100644 --- a/modules/dblog/dblog.css +++ b/modules/dblog/dblog.css @@ -1,4 +1,4 @@ -/* $Id: dblog.css,v 1.9 2010/04/28 20:08:38 dries Exp $ */ +/* $Id: dblog.css,v 1.10 2010/09/09 15:47:03 webchick Exp $ */ .form-item-type, .form-item-severity { @@ -46,3 +46,14 @@ tr.dblog-error { tr.dblog-error .active { background: #eeb9b9; } +table#admin-dblog td.icon { + background: no-repeat center; + width: 16px; +} +table#admin-dblog tr.dblog-warning td.icon { + background-image: url(../../misc/message-16-warning.png); +} +table#admin-dblog tr.dblog-error td.icon { + background-image: url(../../misc/message-16-error.png); +} + diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info index 83df7f8a950f08048b6dca89af38efad5c3f38ff..bdf82cddd88c83989395c0ba96f4998fe65be24d 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/dblog/dblog.install b/modules/dblog/dblog.install index 6ff2b32ece1e31283c9704e84793992790274fb9..1b9fda10b24e5087e53e67295344f2d39ae4d11f 100644 --- a/modules/dblog/dblog.install +++ b/modules/dblog/dblog.install @@ -1,5 +1,5 @@ <?php -// $Id: dblog.install,v 1.22 2010/06/25 17:47:22 dries Exp $ +// $Id: dblog.install,v 1.23 2010/09/05 19:56:03 dries Exp $ /** * @file @@ -98,37 +98,37 @@ function dblog_schema() { */ /** - * Allow NULL values for links. + * Update the {watchdog} table. */ function dblog_update_7001() { - db_change_field('watchdog', 'link', 'link', array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => '')); -} + // Allow NULL values for links. + db_change_field('watchdog', 'link', 'link', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'default' => '', + 'description' => 'Link to view the result of the event.', + )); -/** - * Add index on uid. - */ -function dblog_update_7002() { + // Add an index on uid. db_add_index('watchdog', 'uid', array('uid')); -} -/** - * Allow longer type values. - */ -function dblog_update_7003() { - db_change_field('watchdog', 'type', 'type', array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => '')); -} + // Allow longer type values. + db_change_field('watchdog', 'type', 'type', array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Type of log message, for example "user" or "page not found."', + )); -/** - * Converts fields that store serialized variables from text to blob. - */ -function dblog_update_7004() { - $spec = array( + // Convert the variables field (that stores serialized variables) from text to blob. + db_change_field('watchdog', 'variables', 'variables', array( 'type' => 'blob', 'not null' => TRUE, 'size' => 'big', 'description' => 'Serialized array of variables that match the message string and that is passed into the t() function.', - ); - db_change_field('watchdog', 'variables', 'variables', $spec); + )); } /** diff --git a/modules/dblog/dblog.module b/modules/dblog/dblog.module index e6b33d3a49977abc443789384ab0ea73c02609a7..252c4011457e8d279e1390c49ac5b74d0d322347 100644 --- a/modules/dblog/dblog.module +++ b/modules/dblog/dblog.module @@ -1,5 +1,5 @@ <?php -// $Id: dblog.module,v 1.53 2010/06/29 00:39:15 dries Exp $ +// $Id: dblog.module,v 1.55 2010/09/01 03:03:05 dries Exp $ /** * @file @@ -24,13 +24,13 @@ function dblog_help($path, $arg) { $output .= '<h3>' . t('Uses') . '</h3>'; $output .= '<dl>'; $output .= '<dt>' . t('Monitoring your site') . '</dt>'; - $output .= '<dd>' . t('The Database logging module allows you to view an event log on the <a href="@dblog">Recent log entries</a> page. The log is a chronological list of recorded events containing usage data, performance data, errors, warnings and operational information. Administrators should check the log on a regular basis to ensure their site is working properly.', array('@dblog' => url('admin/reports/dblog'))) . '</dd>'; + $output .= '<dd>' . t('The Database logging module allows you to view an event log on the <a href="@dblog">Recent log messages</a> page. The log is a chronological list of recorded events containing usage data, performance data, errors, warnings and operational information. Administrators should check the log on a regular basis to ensure their site is working properly.', array('@dblog' => url('admin/reports/dblog'))) . '</dd>'; $output .= '<dt>' . t('Debugging site problems') . '</dt>'; - $output .= '<dd>' . t('In case of errors or problems with the site, the <a href="@dblog">Recent log entries</a> page can be useful for debugging, since it shows the sequence of events. The log entries include usage information, warnings, and errors.', array('@dblog' => url('admin/reports/dblog'))) . '</dd>'; + $output .= '<dd>' . t('In case of errors or problems with the site, the <a href="@dblog">Recent log messages</a> page can be useful for debugging, since it shows the sequence of events. The log messages include usage information, warnings, and errors.', array('@dblog' => url('admin/reports/dblog'))) . '</dd>'; $output .= '</dl>'; return $output; case 'admin/reports/dblog': - return '<p>' . t('The Database logging module monitors your website, capturing system events in a log (shown here) to be reviewed by an authorized individual at a later time. This log is a list of recorded events containing usage data, performance data, errors, warnings and operational information. It is vital to check the Recent log entries report on a regular basis, as it is often the only way to tell what is going on.') . '</p>'; + return '<p>' . t('The Database logging module monitors your website, capturing system events in a log (shown here) to be reviewed by an authorized individual at a later time. This log is a list of recorded events containing usage data, performance data, errors, warnings and operational information. It is vital to check the Recent log messages report on a regular basis, as it is often the only way to tell what is going on.') . '</p>'; } } @@ -39,7 +39,7 @@ function dblog_help($path, $arg) { */ function dblog_menu() { $items['admin/reports/dblog'] = array( - 'title' => 'Recent log entries', + 'title' => 'Recent log messages', 'description' => 'View events that have recently been logged.', 'page callback' => 'dblog_overview', 'access arguments' => array('access site reports'), @@ -91,7 +91,7 @@ function dblog_menu() { function dblog_init() { if (arg(0) == 'admin' && arg(1) == 'reports') { // Add the CSS for this module - drupal_add_css(drupal_get_path('module', 'dblog') . '/dblog.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'dblog') . '/dblog.css'); } } @@ -149,10 +149,10 @@ function dblog_watchdog(array $log_entry) { function dblog_form_system_logging_settings_alter(&$form, $form_state) { $form['dblog_row_limit'] = array( '#type' => 'select', - '#title' => t('Database log entries to keep'), + '#title' => t('Database log messages to keep'), '#default_value' => variable_get('dblog_row_limit', 1000), '#options' => array(0 => t('All')) + drupal_map_assoc(array(100, 1000, 10000, 100000, 1000000)), - '#description' => t('The maximum number of entries to keep in the database log. Requires a <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))) + '#description' => t('The maximum number of messages to keep in the database log. Requires a <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))) ); $form['actions']['#weight'] = 1; } diff --git a/modules/dblog/dblog.test b/modules/dblog/dblog.test index f9048f0e80524603b3a62cd649e98013d88cccba..caafa2661509cbe4563466ee11e9dfeb57205707 100644 --- a/modules/dblog/dblog.test +++ b/modules/dblog/dblog.test @@ -1,5 +1,5 @@ <?php -// $Id: dblog.test,v 1.36 2010/03/27 14:24:14 dries Exp $ +// $Id: dblog.test,v 1.39 2010/09/01 03:03:05 dries Exp $ class DBLogTestCase extends DrupalWebTestCase { protected $big_user; @@ -132,7 +132,7 @@ class DBLogTestCase extends DrupalWebTestCase { $this->drupalGet('admin/reports/dblog'); $this->assertResponse($response); if ($response == 200) { - $this->assertText(t('Recent log entries'), t('DBLog report was displayed')); + $this->assertText(t('Recent log messages'), t('DBLog report was displayed')); } // View dblog page-not-found report node. diff --git a/modules/field/field.api.php b/modules/field/field.api.php index 3136abeff41e5c622b0145cc86b0ee044ec4883e..07ff6e77c4f7992623dd61a653382ecaf9597dff 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -1,5 +1,5 @@ <?php -// $Id: field.api.php,v 1.85 2010/06/17 13:16:57 dries Exp $ +// $Id: field.api.php,v 1.90 2010/09/11 00:03:41 webchick Exp $ /** * @ingroup field_fieldable_type @@ -73,14 +73,12 @@ function hook_field_extra_fields() { * @see hook_field_extra_fields() */ function hook_field_extra_fields_alter(&$info) { - // Force node title to always be at the top of the list - // by default. + // Force node title to always be at the top of the list by default. foreach (node_type_get_types() as $bundle) { if (isset($info['node'][$bundle]['title'])) { $info['node'][$bundle]['title']['weight'] = -20; } } - } /** @@ -213,6 +211,9 @@ function hook_field_info_alter(&$info) { /** * Define the Field API schema for a field structure. * + * This hook MUST be defined in .install for it to be detected during + * installation and upgrade. + * * @param $field * A field structure. * @@ -453,7 +454,18 @@ function hook_field_presave($entity_type, $entity, $field, $instance, $langcode, * $entity->{$field['field_name']}[$langcode], or an empty array if unset. */ function hook_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { - // @todo Needs function body. + if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node' && $entity->status) { + $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created', )); + foreach ($items as $item) { + $query->values(array( + 'nid' => $entity->nid, + 'tid' => $item['tid'], + 'sticky' => $entity->sticky, + 'created' => $entity->created, + )); + } + $query->execute(); + } } /** @@ -475,7 +487,30 @@ function hook_field_insert($entity_type, $entity, $field, $instance, $langcode, * $entity->{$field['field_name']}[$langcode], or an empty array if unset. */ function hook_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { - // @todo Needs function body. + if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node') { + $first_call = &drupal_static(__FUNCTION__, array()); + + // We don't maintain data for old revisions, so clear all previous values + // from the table. Since this hook runs once per field, per object, make + // sure we only wipe values once. + if (!isset($first_call[$entity->nid])) { + $first_call[$entity->nid] = FALSE; + db_delete('taxonomy_index')->condition('nid', $entity->nid)->execute(); + } + // Only save data to the table if the node is published. + if ($entity->status) { + $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); + foreach ($items as $item) { + $query->values(array( + 'nid' => $entity->nid, + 'tid' => $item['tid'], + 'sticky' => $entity->sticky, + 'created' => $entity->created, + )); + } + $query->execute(); + } + } } /** @@ -534,7 +569,16 @@ function hook_field_storage_update_field($field, $prior_field, $has_data) { * $entity->{$field['field_name']}[$langcode], or an empty array if unset. */ function hook_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) { - // @todo Needs function body. + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + foreach ($items as $delta => $item) { + // For hook_file_references(), remember that this is being deleted. + $item['file_field_name'] = $field['field_name']; + // Pass in the ID of the object that is being removed so all references can + // be counted in hook_file_references(). + $item['file_field_type'] = $entity_type; + $item['file_field_id'] = $id; + file_field_delete_file($item, $field); + } } /** @@ -558,7 +602,13 @@ function hook_field_delete($entity_type, $entity, $field, $instance, $langcode, * $entity->{$field['field_name']}[$langcode], or an empty array if unset. */ function hook_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) { - // @todo Needs function body. + foreach ($items as $delta => $item) { + // For hook_file_references, remember that this file is being deleted. + $item['file_field_name'] = $field['field_name']; + if (file_field_delete_file($item, $field)) { + $items[$delta] = NULL; + } + } } /** @@ -892,7 +942,43 @@ function hook_field_formatter_info_alter(&$info) { * parameter by reference. */ function hook_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { - // @todo Needs function body. + $tids = array(); + + // Collect every possible term attached to any of the fieldable entities. + foreach ($entities as $id => $entity) { + foreach ($items[$id] as $delta => $item) { + // Force the array key to prevent duplicates. + $tids[$item['tid']] = $item['tid']; + } + } + + if ($tids) { + $terms = taxonomy_term_load_multiple($tids); + + // Iterate through the fieldable entities again to attach the loaded term + // data. + foreach ($entities as $id => $entity) { + $rekey = FALSE; + + foreach ($items[$id] as $delta => $item) { + // Check whether the taxonomy term field instance value could be loaded. + if (isset($terms[$item['tid']])) { + // Replace the instance value with the term data. + $items[$id][$delta]['taxonomy_term'] = $terms[$item['tid']]; + } + // Otherwise, unset the instance value, since the term does not exist. + else { + unset($items[$id][$delta]); + $rekey = TRUE; + } + } + + if ($rekey) { + // Rekey the items array. + $items[$id] = array_values($items[$id]); + } + } + } } /** @@ -993,41 +1079,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la * is provided the default site language will be used. */ function hook_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { - $tids = array(); - - // Collect every possible term attached to any of the fieldable entities. - foreach ($entities as $id => $entity) { - foreach ($items[$id] as $delta => $item) { - // Force the array key to prevent duplicates. - $tids[$item['value']] = $item['value']; - } - } - if ($tids) { - $terms = array(); - - // Avoid calling taxonomy_term_load_multiple because it could lead to - // circular references. - $query = db_select('taxonomy_term_data', 't'); - $query->fields('t'); - $query->condition('t.tid', $tids, 'IN'); - $query->addTag('term_access'); - $terms = $query->execute()->fetchAllAssoc('tid'); - - // Iterate through the fieldable entities again to attach the loaded term data. - foreach ($entities as $id => $entity) { - foreach ($items[$id] as $delta => $item) { - // Check whether the taxonomy term field instance value could be loaded. - if (isset($terms[$item['value']])) { - // Replace the instance value with the term data. - $items[$id][$delta]['taxonomy_term'] = $terms[$item['value']]; - } - // Otherwise, unset the instance value, since the term does not exist. - else { - unset($items[$id][$delta]); - } - } - } - } + // @todo Needs function body. } /** @@ -1186,7 +1238,21 @@ function hook_field_attach_purge($entity_type, $entity, $field, $instance) { * - view_mode: View mode, for example, 'full' or 'teaser'. */ function hook_field_attach_view_alter(&$output, $context) { - // @todo Needs function body. + // Append RDF term mappings on displayed taxonomy links. + foreach (element_children($output) as $field_name) { + $element = &$output[$field_name]; + if ($element['#field_type'] == 'taxonomy_term_reference' && $element['#formatter'] == 'taxonomy_term_reference_link') { + foreach ($element['#items'] as $delta => $item) { + $term = $item['taxonomy_term']; + if (!empty($term->rdf_mapping['rdftype'])) { + $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; + } + if (!empty($term->rdf_mapping['name']['predicates'])) { + $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + } + } + } + } } /** @@ -1224,7 +1290,11 @@ function hook_field_attach_prepare_translation_alter(&$entity, $context) { * - langcode: The language code $entity has to be displayed in. */ function hook_field_language_alter(&$display_language, $context) { - // @todo Needs function body. + // Do not apply core language fallback rules if they are disabled or if Locale + // is not registered as a translation handler. + if (variable_get('locale_field_language_fallback', TRUE) && field_has_translation_handler($context['entity_type'], 'locale')) { + locale_field_language_fallback($display_language, $context['entity'], $context['language']); + } } /** @@ -1241,7 +1311,12 @@ function hook_field_language_alter(&$display_language, $context) { * - field: A field data structure. */ function hook_field_available_languages_alter(&$languages, $context) { - // @todo Needs function body. + // Add an unavailable language. + $languages[] = 'xx'; + + // Remove an available language. + $index = array_search('yy', $languages); + unset($languages[$index]); } /** @@ -1252,7 +1327,9 @@ function hook_field_available_languages_alter(&$languages, $context) { * See field_attach_create_bundle() for details and arguments. */ function hook_field_attach_create_bundle($entity_type, $bundle) { - // @todo Needs function body. + // When a new bundle is created, the menu needs to be rebuilt to add the + // Field UI menu item tabs. + variable_set('menu_rebuild_needed', TRUE); } /** @@ -1263,7 +1340,15 @@ function hook_field_attach_create_bundle($entity_type, $bundle) { * See field_attach_rename_bundle() for details and arguments. */ function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { - // @todo Needs function body. + // Update the extra weights variable with new information. + if ($bundle_old !== $bundle_new) { + $extra_weights = variable_get('field_extra_weights', array()); + if (isset($info[$entity_type][$bundle_old])) { + $extra_weights[$entity_type][$bundle_new] = $extra_weights[$entity_type][$bundle_old]; + unset($extra_weights[$entity_type][$bundle_old]); + variable_set('field_extra_weights', $extra_weights); + } + } } /** @@ -1280,7 +1365,12 @@ function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) * deleted. */ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) { - // @todo Needs function body. + // Remove the extra weights variable information for this bundle. + $extra_weights = variable_get('field_extra_weights', array()); + if (isset($extra_weights[$entity_type][$bundle])) { + unset($extra_weights[$entity_type][$bundle]); + variable_set('field_extra_weights', $extra_weights); + } } /** @@ -1358,7 +1448,23 @@ function hook_field_storage_info_alter(&$info) { * @see hook_field_storage_details_alter() */ function hook_field_storage_details($field) { - // @todo Needs function body. + $details = array(); + + // Add field columns. + foreach ((array) $field['columns'] as $column_name => $attributes) { + $real_name = _field_sql_storage_columnname($field['field_name'], $column_name); + $columns[$column_name] = $real_name; + } + return array( + 'sql' => array( + FIELD_LOAD_CURRENT => array( + _field_sql_storage_tablename($field) => $columns, + ), + FIELD_LOAD_REVISION => array( + _field_sql_storage_revision_tablename($field) => $columns, + ), + ), + ); } /** @@ -1373,7 +1479,20 @@ function hook_field_storage_details($field) { * @see hook_field_storage_details() */ function hook_field_storage_details_alter(&$details, $field) { - // @todo Needs function body. + if ($field['field_name'] == 'field_of_interest') { + $columns = array(); + foreach ((array) $field['columns'] as $column_name => $attributes) { + $columns[$column_name] = $column_name; + } + $details['drupal_variables'] = array( + FIELD_LOAD_CURRENT => array( + 'moon' => $columns, + ), + FIELD_LOAD_REVISION => array( + 'mars' => $columns, + ), + ); + } } /** @@ -1404,7 +1523,49 @@ function hook_field_storage_details_alter(&$details, $field) { * loaded. */ function hook_field_storage_load($entity_type, &$entities, $age, $fields, $options) { - // @todo Needs function body. + $field_info = field_info_field_by_ids(); + $etid = _field_sql_storage_etid($entity_type); + $load_current = $age == FIELD_LOAD_CURRENT; + + foreach ($fields as $field_id => $ids) { + $field = $field_info[$field_id]; + $field_name = $field['field_name']; + $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); + + $query = db_select($table, 't') + ->fields('t') + ->condition('etid', $etid) + ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN') + ->condition('language', field_available_languages($entity_type, $field), 'IN') + ->orderBy('delta'); + + if (empty($options['deleted'])) { + $query->condition('deleted', 0); + } + + $results = $query->execute(); + + $delta_count = array(); + foreach ($results as $row) { + if (!isset($delta_count[$row->entity_id][$row->language])) { + $delta_count[$row->entity_id][$row->language] = 0; + } + + if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) { + $item = array(); + // For each column declared by the field, populate the item + // from the prefixed database column. + foreach ($field['columns'] as $column => $attributes) { + $column_name = _field_sql_storage_columnname($field_name, $column); + $item[$column] = $row->$column_name; + } + + // Add the item to the field values for the entity. + $entities[$row->entity_id]->{$field_name}[$row->language][] = $item; + $delta_count[$row->entity_id][$row->language]++; + } + } + } } /** @@ -1425,7 +1586,87 @@ function hook_field_storage_load($entity_type, &$entities, $age, $fields, $optio * array are field IDs. */ function hook_field_storage_write($entity_type, $entity, $op, $fields) { - // @todo Needs function body. + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + $etid = _field_sql_storage_etid($entity_type); + + foreach ($fields as $field_id) { + $field = field_info_field_by_id($field_id); + $field_name = $field['field_name']; + $table_name = _field_sql_storage_tablename($field); + $revision_name = _field_sql_storage_revision_tablename($field); + + $all_languages = field_available_languages($entity_type, $field); + $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name)); + + // Delete and insert, rather than update, in case a value was added. + if ($op == FIELD_STORAGE_UPDATE) { + // Delete languages present in the incoming $entity->$field_name. + // Delete all languages if $entity->$field_name is empty. + $languages = !empty($entity->$field_name) ? $field_languages : $all_languages; + if ($languages) { + db_delete($table_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->condition('language', $languages, 'IN') + ->execute(); + if (isset($vid)) { + db_delete($revision_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->condition('revision_id', $vid) + ->condition('language', $languages, 'IN') + ->execute(); + } + } + } + + // Prepare the multi-insert query. + $do_insert = FALSE; + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta', 'language'); + foreach ($field['columns'] as $column => $attributes) { + $columns[] = _field_sql_storage_columnname($field_name, $column); + } + $query = db_insert($table_name)->fields($columns); + if (isset($vid)) { + $revision_query = db_insert($revision_name)->fields($columns); + } + + foreach ($field_languages as $langcode) { + $items = (array) $entity->{$field_name}[$langcode]; + $delta_count = 0; + foreach ($items as $delta => $item) { + // We now know we have someting to insert. + $do_insert = TRUE; + $record = array( + 'etid' => $etid, + 'entity_id' => $id, + 'revision_id' => $vid, + 'bundle' => $bundle, + 'delta' => $delta, + 'language' => $langcode, + ); + foreach ($field['columns'] as $column => $attributes) { + $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL; + } + $query->values($record); + if (isset($vid)) { + $revision_query->values($record); + } + + if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) { + break; + } + } + } + + // Execute the query if we have values to insert. + if ($do_insert) { + $query->execute(); + if (isset($vid)) { + $revision_query->execute(); + } + } + } } /** @@ -1443,7 +1684,15 @@ function hook_field_storage_write($entity_type, $entity, $op, $fields) { * array are field IDs. */ function hook_field_storage_delete($entity_type, $entity, $fields) { - // @todo Needs function body. + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + $etid = _field_sql_storage_etid($entity_type); + + foreach (field_info_instances($entity_type, $bundle) as $instance) { + if (isset($fields[$instance['field_id']])) { + $field = field_info_field_by_id($instance['field_id']); + field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance); + } + } } /** @@ -1466,7 +1715,20 @@ function hook_field_storage_delete($entity_type, $entity, $fields) { * array are field IDs. */ function hook_field_storage_delete_revision($entity_type, $entity, $fields) { - // @todo Needs function body. + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + $etid = _field_sql_storage_etid($entity_type); + + if (isset($vid)) { + foreach ($fields as $field_id) { + $field = field_info_field_by_id($field_id); + $revision_name = _field_sql_storage_revision_tablename($field); + db_delete($revision_name) + ->condition('etid', $etid) + ->condition('entity_id', $id) + ->condition('revision_id', $vid) + ->execute(); + } + } } /** @@ -1484,7 +1746,126 @@ function hook_field_storage_delete_revision($entity_type, $entity, $fields) { * See EntityFieldQuery::execute() for the return values. */ function hook_field_storage_query($query) { - // @todo Needs function body + $load_current = $options['age'] == FIELD_LOAD_CURRENT; + + $field = field_info_field_by_id($field_id); + $field_name = $field['field_name']; + $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); + $field_columns = array_keys($field['columns']); + + // Build the query. + $query = db_select($table, 't'); + $query->join('field_config_entity_type', 'e', 't.etid = e.etid'); + + // Add conditions. + foreach ($conditions as $condition) { + // A condition is either a (column, value, operator) triple, or a + // (column, value) pair with implied operator. + @list($column, $value, $operator) = $condition; + // Translate operator and value if needed. + switch ($operator) { + case 'STARTS_WITH': + $operator = 'LIKE'; + $value = db_like($value) . '%'; + break; + + case 'ENDS_WITH': + $operator = 'LIKE'; + $value = '%' . db_like($value); + break; + + case 'CONTAINS': + $operator = 'LIKE'; + $value = '%' . db_like($value) . '%'; + break; + } + // Translate field columns into prefixed db columns. + if (in_array($column, $field_columns)) { + $column = _field_sql_storage_columnname($field_name, $column); + } + // Translate entity types into numeric ids. Expressing the condition on the + // local 'etid' column rather than the JOINed 'type' column avoids a + // filesort. + if ($column == 'type') { + $column = 't.etid'; + if (is_array($value)) { + foreach (array_keys($value) as $key) { + $value[$key] = _field_sql_storage_etid($value[$key]); + } + } + else { + $value = _field_sql_storage_etid($value); + } + } + // Track condition on 'deleted'. + if ($column == 'deleted') { + $condition_deleted = TRUE; + } + + $query->condition($column, $value, $operator); + } + + // Exclude deleted data unless we have a condition on it. + if (!isset($condition_deleted)) { + $query->condition('deleted', 0); + } + + // For a count query, return the count now. + if ($options['count']) { + return $query + ->fields('t', array('etid', 'entity_id', 'revision_id')) + ->distinct() + ->countQuery() + ->execute() + ->fetchField(); + } + + // For a data query, add fields. + $query + ->fields('t', array('bundle', 'entity_id', 'revision_id')) + ->fields('e', array('type')) + // We need to ensure entities arrive in a consistent order for the + // range() operation to work. + ->orderBy('t.etid') + ->orderBy('t.entity_id'); + + // Initialize results array + $return = array(); + + // Getting $count entities possibly requires reading more than $count rows + // since fields with multiple values span over several rows. We query for + // batches of $count rows until we've either read $count entities or received + // less rows than asked for. + $entity_count = 0; + do { + if ($options['limit'] != FIELD_QUERY_NO_LIMIT) { + $query->range($options['cursor'], $options['limit']); + } + $results = $query->execute(); + + $row_count = 0; + foreach ($results as $row) { + $row_count++; + $options['cursor']++; + // If querying all revisions and the entity type has revisions, we need + // to key the results by revision_ids. + $entity_type = entity_get_info($row->type); + $id = ($load_current || empty($entity_type['entity keys']['revision'])) ? $row->entity_id : $row->revision_id; + + if (!isset($return[$row->type][$id])) { + $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle)); + $entity_count++; + } + } + } while ($options['limit'] != FIELD_QUERY_NO_LIMIT && $row_count == $options['limit'] && $entity_count < $options['limit']); + + // The query is complete when the last batch returns less rows than asked + // for. + if ($row_count < $options['limit']) { + $options['cursor'] = FIELD_QUERY_COMPLETE; + } + + return $return; } /** @@ -1498,7 +1879,11 @@ function hook_field_storage_query($query) { * The field structure being created. */ function hook_field_storage_create_field($field) { - // @todo Needs function body. + $schema = _field_sql_storage_schema($field); + foreach ($schema as $name => $table) { + db_create_table($name, $table); + } + drupal_get_schema(NULL, TRUE); } /** @@ -1511,7 +1896,21 @@ function hook_field_storage_create_field($field) { * The field being deleted. */ function hook_field_storage_delete_field($field) { - // @todo Needs function body. + // Mark all data associated with the field for deletion. + $field['deleted'] = 0; + $table = _field_sql_storage_tablename($field); + $revision_table = _field_sql_storage_revision_tablename($field); + db_update($table) + ->fields(array('deleted' => 1)) + ->execute(); + + // Move the table to a unique name while the table contents are being deleted. + $field['deleted'] = 1; + $new_table = _field_sql_storage_tablename($field); + $revision_new_table = _field_sql_storage_revision_tablename($field); + db_rename_table($table, $new_table); + db_rename_table($revision_table, $revision_new_table); + drupal_get_schema(NULL, TRUE); } /** @@ -1524,7 +1923,20 @@ function hook_field_storage_delete_field($field) { * The instance being deleted. */ function hook_field_storage_delete_instance($instance) { - // @todo Needs function body. + $etid = _field_sql_storage_etid($instance['entity_type']); + $field = field_info_field($instance['field_name']); + $table_name = _field_sql_storage_tablename($field); + $revision_name = _field_sql_storage_revision_tablename($field); + db_update($table_name) + ->fields(array('deleted' => 1)) + ->condition('etid', $etid) + ->condition('bundle', $instance['bundle']) + ->execute(); + db_update($revision_name) + ->fields(array('deleted' => 1)) + ->condition('etid', $etid) + ->condition('bundle', $instance['bundle']) + ->execute(); } /** @@ -1653,6 +2065,33 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) { } } +/** + * Returns the maximum weight for the entity components handled by the module. + * + * Field API takes care of fields and 'extra_fields'. This hook is intended for + * third-party modules adding other entity components (e.g. field_group). + * + * @param $entity_type + * The type of entity; e.g. 'node' or 'user'. + * @param $bundle + * The bundle name. + * @param $context + * The context for which the maximum weight is requested. Either 'form', or + * the name of a view mode. + * @return + * The maximum weight of the entity's components, or NULL if no components + * were found. + */ +function hook_field_info_max_weight($entity_type, $bundle, $context) { + $weights = array(); + + foreach (my_module_entity_additions($entity_type, $bundle, $context) as $addition) { + $weights[] = $addition['weight']; + } + + return $weights ? max($weights) : NULL; +} + /** * Alters the display settings of a field before it gets displayed. * @@ -1829,7 +2268,8 @@ function hook_field_update_forbid($field, $prior_field, $has_data) { * Whether any data already exists for this field. */ function hook_field_update_field($field, $prior_field, $has_data) { - // @todo Needs function body. + // Reset the static value that keeps track of allowed values for list fields. + drupal_static_reset('list_allowed_values'); } /** @@ -2024,5 +2464,8 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) { * TRUE if the operation is allowed, and FALSE if the operation is denied. */ function hook_field_access($op, $field, $entity_type, $entity, $account) { - // @todo Needs function body. + if ($field['field_name'] == 'field_of_interest' && $op == 'edit') { + return user_access('edit field of interest', $account); + } + return TRUE; } diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 7434dda329e1e0269d005bebef78a72d3bf8970a..c51dfe0035715e7c17e3be43d5c995f2877e59b7 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.attach.inc,v 1.91 2010/06/17 13:44:45 dries Exp $ +// $Id: field.attach.inc,v 1.95 2010/08/01 19:49:35 dries Exp $ /** * @file @@ -273,6 +273,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b = 'language' => NULL, ); $options += $default_options; + $field_info = field_info_field_by_ids(); $fields = array(); $grouped_instances = array(); @@ -296,7 +297,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b = foreach ($instances as $instance) { $field_id = $instance['field_id']; $field_name = $instance['field_name']; - $field = field_info_field_by_id($field_id); + $field = $field_info[$field_id]; $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op; if (function_exists($function)) { // Add the field to the list of fields to invoke the hook on. @@ -507,7 +508,7 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) { * 'array_parents' => an array of keys indicating the path to the field * element within the full $form structure. This entry is populated at * form build time. - * 'errors' => the array ok field validation errors reported on the + * 'errors' => the array of field validation errors reported on the * field. This entry is populated at form validation time. * ), * ), @@ -542,7 +543,6 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod // Add custom weight handling. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); - $form['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; $form['#pre_render'][] = '_field_extra_fields_pre_render'; $form['#entity_type'] = $entity_type; $form['#bundle'] = $bundle; @@ -584,6 +584,7 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod * non-deleted fields are operated on. */ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) { + $field_info = field_info_field_by_ids(); $load_current = $age == FIELD_LOAD_CURRENT; // Merge default options. @@ -661,7 +662,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $ } // Collect the storage backend if the field has not been loaded yet. if (!isset($skip_fields[$field_id])) { - $field = field_info_field_by_id($field_id); + $field = $field_info[$field_id]; $storages[$field['storage']['type']][$field_id][] = $load_current ? $id : $vid; } } @@ -1036,10 +1037,17 @@ function field_attach_delete_revision($entity_type, $entity) { /** * Prepare field data prior to display. * - * This function must be called before field_attach_view(). It lets field - * types and formatters load additional data needed for display, and - * therefore accepts an array of entities to allow query optimisation when - * displaying lists of entities. + * This function lets field types and formatters load additional data + * needed for display that is not automatically loaded during + * field_attach_load(). It accepts an array of entities to allow query + * optimisation when displaying lists of entities. + * + * field_attach_prepare_view() and field_attach_view() are two halves + * of the same operation. It is safe to call + * field_attach_prepare_view() multiple times on the same entity + * before calling field_attach_view() on it, but calling any Field + * API operation on an entity between passing that entity to these two + * functions may yield incorrect results. * * @param $entity_type * The type of $entities; e.g. 'node' or 'user'. @@ -1077,8 +1085,12 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode) { * Each field is displayed according to the display options specified in the * $instance definition for the given $view_mode. * - * The entity must have run through field_attach_prepare_view() beforehands. - * @see field_attach_prepare_view() + * field_attach_prepare_view() and field_attach_view() are two halves + * of the same operation. It is safe to call + * field_attach_prepare_view() multiple times on the same entity + * before calling field_attach_view() on it, but calling any Field + * API operation on an entity between passing that entity to these two + * functions may yield incorrect results. * * Sample structure: * @code @@ -1129,9 +1141,6 @@ function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL) $output['#entity_type'] = $entity_type; $output['#bundle'] = $bundle; - // Include CSS styles. - $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; - // Let other modules alter the renderable array. $context = array( 'entity_type' => $entity_type, diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index cb4d5d74d1a5c91289f53df84b84d29b6b964cd9..e2ec08b29f3670219a9b6a10bff774ad5e8797cf 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.crud.inc,v 1.65 2010/06/17 13:16:57 dries Exp $ +// $Id: field.crud.inc,v 1.69 2010/09/11 00:03:42 webchick Exp $ /** * @file @@ -40,7 +40,7 @@ * - field_name (string) * The name of the field. Each field name is unique within Field API. * When a field is attached to an entity, the field's data is stored - * in $entity->$field_name. + * in $entity->$field_name. Maximum length is 32 characters. * - type (string) * The type of the field, such as 'text' or 'image'. Field types * are defined by modules that implement hook_field_info(). @@ -53,7 +53,9 @@ * - translatable (integer) * Whether the field is translatable. * - locked (integer) - * TODO: undefined. + * Whether or not the field is available for editing. If TRUE, users can't + * change field settings or create new instances of the field in the UI. + * Defaults to FALSE. * - module (string, read-only) * The name of the module that implements the field type. * - active (integer, read-only) @@ -189,9 +191,6 @@ * - ... * * Bundles are represented by two strings, an entity type and a bundle name. - * - * TODO D7 : document max length for field types, widget types, - * formatter names... */ /** * @} End of "defgroup field_structs". @@ -318,6 +317,7 @@ function field_create_field($field) { $field['storage']['module'] = $storage_type['module']; $field['storage']['active'] = 1; // Collect storage information. + module_load_install($field['module']); $schema = (array) module_invoke($field['module'], 'field_schema', $field); $schema += array('columns' => array(), 'indexes' => array()); // 'columns' are hardcoded in the field type. @@ -427,6 +427,7 @@ function field_update_field($field) { // Collect the new storage information, since what is in // $prior_field may no longer be right. + module_load_install($field['module']); $schema = (array) module_invoke($field['module'], 'field_schema', $field); $schema += array('columns' => array(), 'indexes' => array()); // 'columns' are hardcoded in the field type. @@ -553,6 +554,7 @@ function field_read_fields($params = array(), $include_additional = array()) { module_invoke_all('field_read_field', $field); // Populate storage information. + module_load_install($field['module']); $schema = (array) module_invoke($field['module'], 'field_schema', $field); $schema += array('columns' => array(), 'indexes' => array()); $field['columns'] = $schema['columns']; @@ -759,16 +761,8 @@ function _field_write_instance($instance, $update = FALSE) { ); // If no weight specified, make sure the field sinks at the bottom. if (!isset($instance['widget']['weight'])) { - $weights = array(); - foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $existing_instance) { - if ($instance['field_name'] != $existing_instance['field_name']) { - $weights[] = $existing_instance['widget']['weight']; - } - } - foreach (field_extra_fields($instance['entity_type'], $instance['bundle'], 'form') as $extra) { - $weights[] = $extra['weight']; - } - $instance['widget']['weight'] = $weights ? max($weights) + 1 : 0; + $max_weight = field_info_max_weight($instance['entity_type'], $instance['bundle'], 'form'); + $instance['widget']['weight'] = !is_null($max_weight) ? $max_weight + 1 : 0; } // Check widget module. $widget_type = field_info_widget_types($instance['widget']['type']); @@ -793,16 +787,8 @@ function _field_write_instance($instance, $update = FALSE) { } // If no weight specified, make sure the field sinks at the bottom. if (!isset($display['weight'])) { - $weights = array(); - foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $existing_instance) { - if ($instance['field_name'] != $existing_instance['field_name']) { - $weights[] = $existing_instance['display'][$view_mode]['weight']; - } - } - foreach (field_extra_fields($instance['entity_type'], $instance['bundle'], 'display') as $extra) { - $weights[] = $extra['display'][$view_mode]['weight']; - } - $display['weight'] = $weights ? max($weights) + 1 : 0; + $max_weight = field_info_max_weight($instance['entity_type'], $instance['bundle'], $view_mode); + $display['weight'] = !is_null($max_weight) ? $max_weight + 1 : 0; } $instance['display'][$view_mode] = $display; } diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc index 549ac20e86f56ddc2ca498d0163b7f5ad495c918..1353bd6495d99b7490cef867a903bbd7e4ecf795 100644 --- a/modules/field/field.default.inc +++ b/modules/field/field.default.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.default.inc,v 1.38 2010/06/17 13:44:45 dries Exp $ +// $Id: field.default.inc,v 1.39 2010/08/04 06:55:35 webchick Exp $ /** * @file @@ -65,12 +65,10 @@ function field_default_validate($entity_type, $entity, $field, $instance, $langc } function field_default_submit($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) { - // Reorder items to account for drag-n-drop reordering. - if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { - $items = _field_sort_items($field, $items); - } // Filter out empty values. $items = _field_filter_items($field, $items); + // Reorder items to account for drag-n-drop reordering. + $items = _field_sort_items($field, $items); } /** diff --git a/modules/field/field.info b/modules/field/field.info index d80ac9b995be2fc4339e8eb349902e503a11d6ab..7985e9955c13d06b756ba38786ec9dffffbc3b57 100644 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -1,4 +1,4 @@ -; $Id: field.info,v 1.8 2010/06/21 02:27:47 webchick Exp $ +; $Id: field.info,v 1.9 2010/09/05 02:21:38 dries Exp $ name = Field description = Field API to add fields to entities like nodes and users. package = Core @@ -15,9 +15,10 @@ files[] = field.form.inc files[] = tests/field.test dependencies[] = field_sql_storage required = TRUE +stylesheets[all][] = theme/field.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 29270695e4fa6796de48437ed48c1087f79d2820..a484eecdafedd14e86b9c364b5f8aea785899958 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.info.inc,v 1.47 2010/06/29 18:46:12 dries Exp $ +// $Id: field.info.inc,v 1.53 2010/09/11 00:03:42 webchick Exp $ /** * @file @@ -225,6 +225,18 @@ function _field_info_collate_fields($reset = FALSE) { // are thus not in $definitions['instances']. $info['fields'][$instance['field_id']]['bundles'][$instance['entity_type']][] = $instance['bundle']; } + + // Populate 'extra_fields'. + $extra = module_invoke_all('field_extra_fields'); + drupal_alter('field_extra_fields', $extra); + // Merge in saved settings. + foreach ($extra as $entity_type => $bundles) { + foreach ($bundles as $bundle => $extra_fields) { + $extra_fields = _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle); + $info['extra_fields'][$entity_type][$bundle] = $extra_fields; + } + } + cache_set('field_info_fields', $info, 'cache_field'); } } @@ -370,6 +382,54 @@ function _field_info_prepare_instance_widget($field, $widget) { return $widget; } +/** + * Prepares 'extra fields' for the current run-time context. + * + * @param $extra_fields + * The array of extra fields, as collected in hook_field_extra_fields(). + * @param $entity_type + * The entity type. + * @param $bundle + * The bundle name. + */ +function _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle) { + $entity_type_info = entity_get_info($entity_type); + $bundle_settings = field_bundle_settings($entity_type, $bundle); + $extra_fields += array('form' => array(), 'display' => array()); + + $result = array(); + // Extra fields in forms. + foreach ($extra_fields['form'] as $name => $field_data) { + $settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array(); + if (isset($settings['weight'])) { + $field_data['weight'] = $settings['weight']; + } + $result['form'][$name] = $field_data; + } + + // Extra fields in displayed entities. + $data = $extra_fields['display']; + foreach ($extra_fields['display'] as $name => $field_data) { + $settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array(); + $view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes'])); + foreach ($view_modes as $view_mode) { + if (isset($settings[$view_mode])) { + $field_data['display'][$view_mode] = $settings[$view_mode]; + } + else { + $field_data['display'][$view_mode] = array( + 'weight' => $field_data['weight'], + 'visible' => TRUE, + ); + } + } + unset($field_data['weight']); + $result['display'][$name] = $field_data; + } + + return $result; +} + /** * Determines the behavior of a widget with respect to an operation. * @@ -523,6 +583,7 @@ function field_info_bundles($entity_type = NULL) { * which this field belongs keyed by entity type. */ function field_info_fields() { + $fields = array(); $info = _field_info_collate_fields(); foreach ($info['fields'] as $key => $field) { if (!$field['deleted']) { @@ -574,6 +635,25 @@ function field_info_field_by_id($field_id) { } } +/** + * Returns the same data as field_info_field_by_id() for every field. + * + * This function is typically used when handling all fields of some entities + * to avoid thousands of calls to field_info_field_by_id(). + * + * @return + * An array, each key is a field ID and the values are field arrays as + * returned by field_read_fields(), with an additional element 'bundles', + * whose value is an array of all the bundle this field belongs to. + * + * @see field_info_field() + * @see field_info_field_by_id() + */ +function field_info_field_by_ids() { + $info = _field_info_collate_fields(); + return $info['fields']; +} + /** * Retrieves information about field instances. * @@ -619,6 +699,108 @@ function field_info_instance($entity_type, $field_name, $bundle_name) { } } +/** + * Returns a list and settings of pseudo-field elements in a given bundle. + * + * If $context is 'form', an array with the following structure: + * @code + * array( + * 'name_of_pseudo_field_component' => array( + * 'label' => The human readable name of the component, + * 'description' => A short description of the component content, + * 'weight' => The weight of the component in edit forms, + * ), + * 'name_of_other_pseudo_field_component' => array( + * // ... + * ), + * ); + * @endcode + * + * If $context is 'display', an array with the following structure: + * @code + * array( + * 'name_of_pseudo_field_component' => array( + * 'label' => The human readable name of the component, + * 'description' => A short description of the component content, + * // One entry per view mode, including the 'default' mode: + * 'display' => array( + * 'default' => array( + * 'weight' => The weight of the component in displayed entities in + * this view mode, + * 'visibility' => Whether the component is visible or hidden in + * displayed entities in this view mode, + * ), + * 'teaser' => array( + * // ... + * ), + * ), + * ), + * 'name_of_other_pseudo_field_component' => array( + * // ... + * ), + * ); + * @endcode + * + * @param $entity_type + * The type of entity; e.g. 'node' or 'user'. + * @param $bundle + * The bundle name. + * @param $context + * The context for which the list of pseudo-fields is requested. Either + * 'form' or 'display'. + * + * @return + * The array of pseudo-field elements in the bundle. + */ +function field_info_extra_fields($entity_type, $bundle, $context) { + $info = _field_info_collate_fields(); + if (isset($info['extra_fields'][$entity_type][$bundle][$context])) { + return $info['extra_fields'][$entity_type][$bundle][$context]; + } + return array(); +} + +/** + * Returns the maximum weight of all the components in an entity. + * + * This includes fields, 'extra_fields', and other components added by + * third-party modules (e.g. field_group). + * + * @param $entity_type + * The type of entity; e.g. 'node' or 'user'. + * @param $bundle + * The bundle name. + * @param $context + * The context for which the maximum weight is requested. Either 'form', or + * the name of a view mode. + * @return + * The maximum weight of the entity's components, or NULL if no components + * were found. + */ +function field_info_max_weight($entity_type, $bundle, $context) { + $weights = array(); + + // Collect weights for fields. + foreach (field_info_instances($entity_type, $bundle) as $instance) { + if ($context == 'form') { + $weights[] = $instance['widget']['weight']; + } + else { + $weights[] = $instance['display'][$context]['weight']; + } + } + // Collect weights for extra fields. + foreach (field_info_extra_fields($entity_type, $bundle, $context) as $extra) { + $weights[] = $extra['weight']; + } + + // Let other modules feedback about their own additions. + $weights = array_merge($weights, module_invoke_all('field_info_max_weight', $entity_type, $bundle, $context)); + $max_weight = $weights ? max($weights) : NULL; + + return $max_weight; +} + /** * Returns a field type's default settings. * diff --git a/modules/field/field.install b/modules/field/field.install index 23cff052f8ccc1f8d106340ae94ad8d32e0fc80c..d638f69382ab845e5f500eb1db436bc9732c4129 100644 --- a/modules/field/field.install +++ b/modules/field/field.install @@ -1,5 +1,5 @@ <?php -// $Id: field.install,v 1.19 2010/06/25 17:47:22 dries Exp $ +// $Id: field.install,v 1.20 2010/09/13 05:50:09 webchick Exp $ /** * @file @@ -167,3 +167,127 @@ function field_schema() { return $schema; } + +/** + * Utility function: create a field by writing directly to the database. + * + * This function is valid for a database schema version 7000. + * + * @ingroup update-api-6.x-to-7.x + */ +function _update_7000_field_create_field(&$field) { + // Merge in default values.` + $field += array( + 'entity_types' => array(), + 'cardinality' => 1, + 'translatable' => FALSE, + 'locked' => FALSE, + 'settings' => array(), + 'indexes' => array(), + 'deleted' => 0, + 'active' => 1, + ); + + // Set storage. + $field['storage'] = array( + 'type' => 'field_sql_storage', + 'settings' => array(), + 'module' => 'field_sql_storage', + 'active' => 1, + ); + + // The serialized 'data' column contains everything from $field that does not + // have its own column and is not automatically populated when the field is + // read. + $data = $field; + unset($data['columns'], $data['field_name'], $data['type'], $data['active'], $data['module'], $data['storage_type'], $data['storage_active'], $data['storage_module'], $data['locked'], $data['cardinality'], $data['deleted']); + // Additionally, do not save the 'bundles' property populated by + // field_info_field(). + unset($data['bundles']); + + // Write the field to the database. + $record = array( + 'field_name' => $field['field_name'], + 'type' => $field['type'], + 'module' => $field['module'], + 'active' => (int) $field['active'], + 'storage_type' => $field['storage']['type'], + 'storage_module' => $field['storage']['module'], + 'storage_active' => (int) $field['storage']['active'], + 'locked' => (int) $field['locked'], + 'data' => serialize($data), + 'cardinality' => $field['cardinality'], + 'translatable' => (int) $field['translatable'], + 'deleted' => (int) $field['deleted'], + ); + // We don't use drupal_write_record() here because it depends on the schema. + $field['id'] = db_insert('field_config') + ->fields($record) + ->execute(); + + // Create storage for this field, the field module is not guaranteed to be + // loaded at this point. + module_load_install($field['module']); + $schema = (array) module_invoke($field['module'], 'field_schema', $field); + $schema += array('columns' => array(), 'indexes' => array()); + // 'columns' are hardcoded in the field type. + $field['columns'] = $schema['columns']; + // 'indexes' can be both hardcoded in the field type, and specified in the + // incoming $field definition. + $field['indexes'] += $schema['indexes']; + + field_sql_storage_field_storage_create_field($field); +} + +/** + * Utility function: create a field instance directly to the database. + * + * This function is valid for a database schema version 7000. + * + * @ingroup update-api-6.x-to-7.x + */ +function _update_7000_field_create_instance($field, &$instance) { + // Merge in defaults. + $instance += array( + 'field_id' => $field['id'], + 'field_name' => $field['field_name'], + 'deleted' => 0, + ); + + // The serialized 'data' column contains everything from $instance that does + // not have its own column and is not automatically populated when the + // instance is read. + $data = $instance; + unset($data['id'], $data['field_id'], $data['field_name'], $data['entity_type'], $data['bundle'], $data['deleted']); + + $record = array( + 'field_id' => $instance['field_id'], + 'field_name' => $instance['field_name'], + 'entity_type' => $instance['entity_type'], + 'bundle' => $instance['bundle'], + 'data' => serialize($data), + 'deleted' => (int) $instance['deleted'], + ); + $instance['id'] = db_insert('field_config_instance') + ->fields($record) + ->execute(); +} + +/** + * @defgroup field-updates-6.x-to-7.x Field updates from 6.x to 7.x + * @{ + */ + +/** + * Field update version placeholder. + */ +function field_update_7000() { + // _update_7000_field_create_field() is supposed to create fields according + // to the structure of fields after running field_update_7000(). So this + // function needs to exist but as field is a new module in Drupal 7, it + // does not need to do anything. +} + +/** + * @} End of "defgroup field-updates-6.x-to-7.x" + */ diff --git a/modules/field/field.module b/modules/field/field.module index 06be61ddf67079b546bdc86296afa4dc258bcc19..c623be9dd42b8071d56de5f94a1de107528208b3 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -1,5 +1,5 @@ <?php -// $Id: field.module,v 1.76 2010/06/24 21:37:49 dries Exp $ +// $Id: field.module,v 1.84 2010/09/05 02:21:38 dries Exp $ /** * @file * Attach custom data fields to Drupal entities. @@ -69,6 +69,9 @@ require_once DRUPAL_ROOT . '/modules/field/field.form.inc'; * - @link field_purge Field API bulk data deletion @endlink. Cleans * up after bulk deletion operations such as field_delete_field() * and field_delete_instance(). + * + * - @link field_language Field language API @endlink. Provides native + * multilingual support for the Field API. */ /** @@ -212,7 +215,7 @@ function field_modules_disabled($modules) { ->condition('storage_module', $modules, 'IN') ->execute(); - field_cache_clear(TRUE); + field_cache_clear(); } /** @@ -315,24 +318,18 @@ function _field_sort_items($field, $items) { * (copied form element_sort(), which acts on #weight keys) */ function _field_sort_items_helper($a, $b) { - $a_weight = (is_array($a) && isset($a['_weight'])) ? $a['_weight'] : 0; - $b_weight = (is_array($b) && isset($b['_weight'])) ? $b['_weight'] : 0; - if ($a_weight == $b_weight) { - return 0; - } - return ($a_weight < $b_weight) ? -1 : 1; + $a_weight = (is_array($a) ? $a['_weight'] : 0); + $b_weight = (is_array($b) ? $b['_weight'] : 0); + return $a_weight - $b_weight; } /** * Same as above, using ['_weight']['#value'] */ function _field_sort_items_value_helper($a, $b) { - $a_weight = (is_array($a) && isset($a['_weight']['#value'])) ? $a['_weight']['#value'] : 0; - $b_weight = (is_array($b) && isset($b['_weight']['#value'])) ? $b['_weight']['#value'] : 0; - if ($a_weight == $b_weight) { - return 0; - } - return ($a_weight < $b_weight) ? -1 : 1; + $a_weight = (is_array($a) && isset($a['_weight']['#value']) ? $a['_weight']['#value'] : 0); + $b_weight = (is_array($b) && isset($b['_weight']['#value']) ? $b['_weight']['#value'] : 0); + return $a_weight - $b_weight; } /** @@ -373,7 +370,7 @@ function _field_sort_items_value_helper($a, $b) { * ), * ), * ), - * @encode + * @endcode * * @param $entity_type * The type of $entity; e.g. 'node' or 'user'. @@ -392,7 +389,7 @@ function field_bundle_settings($entity_type, $bundle, $settings = NULL) { $stored_settings[$entity_type][$bundle] = $settings; variable_set('field_bundle_settings', $stored_settings); drupal_static_reset('field_view_mode_settings'); - drupal_static_reset('field_extra_fields'); + field_info_cache_clear(); } else { $settings = isset($stored_settings[$entity_type][$bundle]) ? $stored_settings[$entity_type][$bundle] : array(); @@ -440,111 +437,6 @@ function field_view_mode_settings($entity_type, $bundle) { return $cache[$entity_type][$bundle]; } -/** - * Returns a list and settings of pseudo-field elements in a given bundle. - * - * If $context is 'form', an array with the following structure: - * @code - * array( - * 'name_of_pseudo_field_component' => array( - * 'label' => The human readable name of the component, - * 'description' => A short description of the component content, - * 'weight' => The weight of the component in edit forms, - * ), - * 'name_of_other_pseudo_field_component' => array( - * // ... - * ), - * ); - * @endcode - * - * If $context is 'display', an array with the following structure: - * @code - * array( - * 'name_of_pseudo_field_component' => array( - * 'label' => The human readable name of the component, - * 'description' => A short description of the component content, - * // One entry per view mode, including the 'default' mode: - * 'display' => array( - * 'default' => array( - * 'weight' => The weight of the component in displayed entities in - * this view mode, - * 'visibility' => Whether the component is visible or hidden in - * displayed entities in this view mode, - * ), - * 'teaser' => array( - * // ... - * ), - * ), - * ), - * 'name_of_other_pseudo_field_component' => array( - * // ... - * ), - * ); - * @endcode - * - * @param $entity_type - * The type of entity; e.g. 'node' or 'user'. - * @param $bundle - * The bundle name. - * @param $context - * The context for which the list of pseudo-fields is requested. Either - * 'form' or 'display'. - * - * @return - * The array of pseudo-field elements in the bundle. - */ -function field_extra_fields($entity_type, $bundle, $context) { - $extra = &drupal_static(__FUNCTION__); - - if (!isset($extra)) { - $info = (array) module_invoke_all('field_extra_fields'); - drupal_alter('field_extra_fields', $info); - - // Merge in saved settings, and make sure we have settings for all view - // modes. - foreach ($info as $entity_type_name => $bundles) { - $entity_type_info = entity_get_info($entity_type_name); - foreach ($bundles as $bundle_name => $extra_fields) { - $bundle_settings = field_bundle_settings($entity_type_name, $bundle_name); - $extra_fields += array('form' => array(), 'display' => array()); - - // Extra fields in forms. - $data = $extra_fields['form']; - foreach ($data as $name => $field_data) { - $settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array(); - if (isset($settings['weight'])) { - $data[$name]['weight'] = $settings['weight']; - } - } - $extra[$entity_type_name][$bundle_name]['form'] = $data; - - // Extra fields in displayed entities. - $data = $extra_fields['display']; - foreach ($data as $name => $field_data) { - $settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array(); - $view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes'])); - foreach ($view_modes as $view_mode) { - if (isset($settings[$view_mode])) { - $data[$name]['display'][$view_mode] = $settings[$view_mode]; - } - else { - $data[$name]['display'][$view_mode] = array( - 'weight' => $field_data['weight'], - 'visible' => TRUE, - ); - } - unset($data[$name]['weight']); - } - } - $extra[$entity_type_name][$bundle_name]['display'] = $data; - } - } - } - - return isset($extra[$entity_type][$bundle][$context]) ? $extra[$entity_type][$bundle][$context] : array(); -} - - /** * Returns the display settings to use for an instance in a given view mode. * @@ -593,7 +485,7 @@ function field_extra_fields_get_display($entity_type, $bundle, $view_mode) { // mode. $view_mode_settings = field_view_mode_settings($entity_type, $bundle); $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings'])) ? $view_mode : 'default'; - $extra_fields = field_extra_fields($entity_type, $bundle, 'display'); + $extra_fields = field_info_extra_fields($entity_type, $bundle, 'display'); $displays = array(); foreach ($extra_fields as $name => $value) { @@ -619,7 +511,7 @@ function _field_extra_fields_pre_render($elements) { $bundle = $elements['#bundle']; if (isset($elements['#type']) && $elements['#type'] == 'form') { - $extra_fields = field_extra_fields($entity_type, $bundle, 'form'); + $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form'); foreach ($extra_fields as $name => $settings) { if (isset($elements[$name])) { $elements[$name]['#weight'] = $settings['weight']; @@ -811,7 +703,6 @@ function field_view_field($entity_type, $entity, $field_name, $display = array() if (isset($result[$field_name])) { $output = $result[$field_name]; - $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; } } diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index 5ca0d9007ac89c3688d3be3dbdf308536194902d..91e31cab94a3c2f50ec80b89f85e5c3fa528c444 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -1,9 +1,63 @@ <?php -// $Id: field.multilingual.inc,v 1.11 2010/06/01 18:29:41 dries Exp $ +// $Id: field.multilingual.inc,v 1.13 2010/08/31 15:02:39 webchick Exp $ /** * @file - * Multilingual field API helper functions. + * Functions implementing Field API multilingual support. + */ + +/** + * @defgroup field_language Field language API + * @{ + * Handling of multilingual fields. + * + * Fields natively implement multilingual support, and all fields use the + * following structure: + * @code + * $entity->{$field_name}[$langcode][$delta][$column_name] + * @endcode + * Every field can hold a single or multiple value for each language belonging + * to the available languages set: + * - For untranslatable fields this set only contains LANGUAGE_NONE. + * - For translatable fields this set can contain any language code. By default + * it is the list returned by field_content_languages(), which contains all + * enabled languages with the addition of LANGUAGE_NONE. This default can be + * altered by modules implementing hook_field_available_languages_alter(). + * + * The available languages for a particular field are returned by + * field_available_languages(). Whether a field is translatable is determined by + * calling field_is_translatable(), which checks the $field['translatable'] + * property returned by field_info_field(), and whether there is at least one + * translation handler available for the field. A translation handler is a + * module registering itself via hook_entity_info() to handle field + * translations. + * + * By default, _field_invoke() and _field_invoke_multiple() are processing a + * field in all available languages, unless they are given a language + * suggestion. Based on that suggestion, _field_language_suggestion() determines + * the languages to act on. + * + * Most field_attach_*() functions act on all available languages, except for + * the following: + * - field_attach_form() only takes a single language code, specifying which + * language the field values will be submitted in. + * - field_attach_view() requires the language the entity will be displayed in. + * Since it is unknown whether a field translation exists for the requested + * language, the translation handler is responsible for performing one of the + * following actions: + * - Ignore missing translations, i.e. do not show any field values for the + * requested language. For example, see locale_field_language_alter(). + * - Provide a value in a different language as fallback. By default, the + * fallback logic is applied separately to each field to ensure that there + * is a value for each field to display. + * The field language fallback logic relies on the global language fallback + * configuration. Therefore, the displayed field values can be in the + * requested language, but may be different if no values for the requested + * language are available. The default language fallback rules inspect all the + * enabled languages ordered by their weight. This behavior can be altered or + * even disabled by modules implementing hook_field_language_alter(), making + * it possible to choose the first approach. The display language for each + * field is returned by field_language(). */ /** @@ -216,10 +270,11 @@ function field_language($entity_type, $entity, $field_name = NULL, $langcode = N if (!isset($display_languages[$entity_type][$id][$langcode])) { $display_language = array(); - // By default display language is set to LANGUAGE_NONE. It is up to - // translation handlers to implement language fallback rules. + // By default display language is set to LANGUAGE_NONE if the field + // translation is not available. It is up to translation handlers to + // implement language fallback rules. foreach (field_info_instances($entity_type, $bundle) as $instance) { - $display_language[$instance['field_name']] = LANGUAGE_NONE; + $display_language[$instance['field_name']] = isset($entity->{$instance['field_name']}[$langcode]) ? $langcode : LANGUAGE_NONE; } if (field_has_translation_handler($entity_type)) { 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 4eb08ae19476d0e25a3c58708ed221bc712e70e1..0c5c0ed1300bc5c21163ba1288322a88055caa30 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.info +++ b/modules/field/modules/field_sql_storage/field_sql_storage.info @@ -1,16 +1,17 @@ -; $Id: field_sql_storage.info,v 1.4 2010/06/21 02:27:47 webchick Exp $ +; $Id: field_sql_storage.info,v 1.5 2010/08/16 20:57:22 dries Exp $ name = Field SQL storage description = Stores field data in an SQL database. package = Core version = VERSION core = 7.x +dependencies[] = field files[] = field_sql_storage.module files[] = field_sql_storage.install files[] = field_sql_storage.test required = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.module b/modules/field/modules/field_sql_storage/field_sql_storage.module index edeedbd811c7a3c57ce0279099dc977163b7a97d..1971ef2897b2925b44387e5c5354a65969d2e79b 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -1,5 +1,5 @@ <?php -// $Id: field_sql_storage.module,v 1.48 2010/06/26 02:16:23 dries Exp $ +// $Id: field_sql_storage.module,v 1.52 2010/09/05 20:00:45 dries Exp $ /** * @file @@ -309,11 +309,12 @@ function field_sql_storage_field_storage_delete_field($field) { * Implements hook_field_storage_load(). */ function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) { + $field_info = field_info_field_by_ids(); $etid = _field_sql_storage_etid($entity_type); $load_current = $age == FIELD_LOAD_CURRENT; foreach ($fields as $field_id => $ids) { - $field = field_info_field_by_id($field_id); + $field = $field_info[$field_id]; $field_name = $field['field_name']; $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); @@ -501,6 +502,8 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) { } else { $select_query = db_select($tablename, $table_alias); + $select_query->addTag('entity_field_access'); + $select_query->addMetaData('base_table', $tablename); $select_query->fields($table_alias, array('entity_id', 'revision_id', 'bundle')); // As only a numeric ID is stored instead of the entity type add the // field_config_entity_type table to resolve the etid to a more readable @@ -546,6 +549,7 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) { if (isset($query->deleted)) { $select_query->condition("$field_base_table.deleted", (int) $query->deleted); } + if ($query->propertyConditions || $query->propertyOrder) { if (empty($query->entityConditions['entity_type']['value'])) { throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.'); @@ -561,7 +565,7 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) { } foreach ($query->entityOrder as $key => $direction) { $sql_field = $key == 'entity_type' ? 'fcet.type' : "$field_base_table.$key"; - $query->orderBy($sql_field, $direction); + $select_query->orderBy($sql_field, $direction); } return $query->finishQuery($select_query, $id_key); } @@ -677,20 +681,21 @@ function field_sql_storage_field_storage_purge_field($field) { */ function field_sql_storage_field_storage_details($field) { $details = array(); - - // Add field columns. - foreach ((array) $field['columns'] as $column_name => $attributes) { - $real_name = _field_sql_storage_columnname($field['field_name'], $column_name); - $columns[$column_name] = $real_name; - } - return array( - 'sql' => array( - FIELD_LOAD_CURRENT => array( - _field_sql_storage_tablename($field) => $columns, - ), - FIELD_LOAD_REVISION => array( - _field_sql_storage_revision_tablename($field) => $columns, + if (!empty($field['columns'])) { + // Add field columns. + foreach ($field['columns'] as $column_name => $attributes) { + $real_name = _field_sql_storage_columnname($field['field_name'], $column_name); + $columns[$column_name] = $real_name; + } + return array( + 'sql' => array( + FIELD_LOAD_CURRENT => array( + _field_sql_storage_tablename($field) => $columns, + ), + FIELD_LOAD_REVISION => array( + _field_sql_storage_revision_tablename($field) => $columns, + ), ), - ), - ); + ); + } } diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.test b/modules/field/modules/field_sql_storage/field_sql_storage.test index 552b95891835c0879b17e426170bd1e782f56a79..783dc1b574055d6e7946b5b63c41ee4b37e7ac96 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.test +++ b/modules/field/modules/field_sql_storage/field_sql_storage.test @@ -1,5 +1,5 @@ <?php -// $Id: field_sql_storage.test,v 1.19 2010/03/27 05:52:49 webchick Exp $ +// $Id: field_sql_storage.test,v 1.21 2010/08/05 23:53:37 webchick Exp $ /** * @file diff --git a/modules/field/modules/list/list.info b/modules/field/modules/list/list.info index 75562d66491bd509ad5e31d21b94740ac04a4080..37f2ff56484d682e9911b881dbdcc38283bd5916 100644 --- a/modules/field/modules/list/list.info +++ b/modules/field/modules/list/list.info @@ -1,15 +1,16 @@ -; $Id: list.info,v 1.7 2010/06/21 02:27:47 webchick Exp $ +; $Id: list.info,v 1.8 2010/08/16 20:57:22 dries Exp $ name = List description = Defines list field types. Use with Options to create selection lists. package = Core version = VERSION core = 7.x -files[]=list.module -files[]=tests/list.test +dependencies[] = field +files[] = list.module +files[] = tests/list.test required = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/list/list.install b/modules/field/modules/list/list.install new file mode 100644 index 0000000000000000000000000000000000000000..d9398a38183ccf27dfd0707a2447333990bdceb1 --- /dev/null +++ b/modules/field/modules/list/list.install @@ -0,0 +1,46 @@ +<?php +// $Id: list.install,v 1.1 2010/09/04 15:40:51 dries Exp $ + +/** + * @file + * Install, update and uninstall functions for the list module. + */ + +/** + * Implements hook_field_schema(). + */ +function list_field_schema($field) { + switch ($field['type']) { + case 'list_text': + $columns = array( + 'value' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ); + break; + case 'list_number': + $columns = array( + 'value' => array( + 'type' => 'float', + 'not null' => FALSE, + ), + ); + break; + default: + $columns = array( + 'value' => array( + 'type' => 'int', + 'not null' => FALSE, + ), + ); + break; + } + return array( + 'columns' => $columns, + 'indexes' => array( + 'value' => array('value'), + ), + ); +} \ No newline at end of file diff --git a/modules/field/modules/list/list.module b/modules/field/modules/list/list.module index d7b069e0eac2557908f8c034427eec8aad99ab70..618583e26fc51b272cb596e0ee6c2804e7bc1b3f 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.33 2010/06/14 15:41:02 dries Exp $ +// $Id: list.module,v 1.34 2010/09/04 15:40:51 dries Exp $ /** * @file @@ -55,45 +55,6 @@ function list_field_info() { ); } -/** - * Implements hook_field_schema(). - */ -function list_field_schema($field) { - switch ($field['type']) { - case 'list_text': - $columns = array( - 'value' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - ); - break; - case 'list_number': - $columns = array( - 'value' => array( - 'type' => 'float', - 'not null' => FALSE, - ), - ); - break; - default: - $columns = array( - 'value' => array( - 'type' => 'int', - 'not null' => FALSE, - ), - ); - break; - } - return array( - 'columns' => $columns, - 'indexes' => array( - 'value' => array('value'), - ), - ); -} - /** * Implements hook_field_settings_form(). * diff --git a/modules/field/modules/list/tests/list.test b/modules/field/modules/list/tests/list.test index 30db6ee31be4c57e1fb8a1184d96c3f57a447378..56b2595e565bbf1e37e224110cc11d1603f5638b 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.5 2010/03/27 12:49:32 dries Exp $ +// $Id: list.test,v 1.7 2010/08/05 23:53:37 webchick Exp $ /** * @file diff --git a/modules/field/modules/list/tests/list_test.info b/modules/field/modules/list/tests/list_test.info index bf038790ca4d0192c551e4b9c8a3357da2014fb0..c3740709ee8fa78b0ac5bb91590047fa1a9d0063 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/number/number.info b/modules/field/modules/number/number.info index 92f2e1ec312ca7919311813519ad0a9a48b873bd..f6396b074d3debc66e09989a36fb405cdcb1b712 100644 --- a/modules/field/modules/number/number.info +++ b/modules/field/modules/number/number.info @@ -1,14 +1,15 @@ -; $Id: number.info,v 1.5 2010/06/21 02:27:47 webchick Exp $ +; $Id: number.info,v 1.6 2010/08/16 20:57:22 dries Exp $ name = Number description = Defines numeric field types. package = Core version = VERSION core = 7.x -files[]=number.module +dependencies[] = field +files[] = number.module required = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/number/number.install b/modules/field/modules/number/number.install new file mode 100644 index 0000000000000000000000000000000000000000..3c7e4384994fabf0ec87900080e7655e1f8cb29c --- /dev/null +++ b/modules/field/modules/number/number.install @@ -0,0 +1,46 @@ +<?php +// $Id: number.install,v 1.1 2010/09/04 15:40:51 dries Exp $ + +/** + * @file + * Install, update and uninstall functions for the number module. + */ + +/** + * Implements hook_field_schema(). + */ +function number_field_schema($field) { + switch ($field['type']) { + case 'number_integer' : + $columns = array( + 'value' => array( + 'type' => 'int', + 'not null' => FALSE + ), + ); + break; + + case 'number_float' : + $columns = array( + 'value' => array( + 'type' => 'float', + 'not null' => FALSE + ), + ); + break; + + case 'number_decimal' : + $columns = array( + 'value' => array( + 'type' => 'numeric', + 'precision' => $field['settings']['precision'], + 'scale' => $field['settings']['scale'], + 'not null' => FALSE + ), + ); + break; + } + return array( + 'columns' => $columns, + ); +} diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module index b2d4af6ae102f9ae6a0c6dcfa745d2b0914e59d7..61d0f9dcfe3ba4fe9d53764c6d858e56f16bef11 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.40 2010/06/23 13:45:40 dries Exp $ +// $Id: number.module,v 1.43 2010/09/07 17:56:41 webchick Exp $ /** * @file @@ -50,45 +50,6 @@ function number_field_info() { ); } -/** - * Implements hook_field_schema(). - */ -function number_field_schema($field) { - switch ($field['type']) { - case 'number_integer' : - $columns = array( - 'value' => array( - 'type' => 'int', - 'not null' => FALSE - ), - ); - break; - - case 'number_float' : - $columns = array( - 'value' => array( - 'type' => 'float', - 'not null' => FALSE - ), - ); - break; - - case 'number_decimal' : - $columns = array( - 'value' => array( - 'type' => 'numeric', - 'precision' => $field['settings']['precision'], - 'scale' => $field['settings']['scale'], - 'not null' => FALSE - ), - ); - break; - } - return array( - 'columns' => $columns, - ); -} - /** * Implements hook_field_settings_form(). */ @@ -118,11 +79,7 @@ function number_field_settings_form($field, $instance, $has_data) { $form['decimal_separator'] = array( '#type' => 'select', '#title' => t('Decimal marker'), - '#options' => array( - '.' => 'decimal point', - ',' => 'comma', - ' ' => 'space', - ), + '#options' => array('.' => t('Decimal point'), ',' => t('Comma')), '#default_value' => $settings['decimal_separator'], '#description' => t('The character users will input to mark the decimal point in forms.'), ); @@ -163,7 +120,7 @@ function number_field_instance_settings_form($field, $instance) { '#title' => t('Suffix'), '#default_value' => $settings['suffix'], '#size' => 60, - '#description' => t("Define a string that should suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), + '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), ); return $form; @@ -252,6 +209,67 @@ function number_field_formatter_info() { ); } +/** + * Implements hook_field_formatter_settings_form(). + */ +function number_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $options = array( + '' => t('<none>'), + '.' => t('Decimal point'), + ',' => t('Comma'), + ' ' => t('Space'), + ); + $form['thousand_separator'] = array( + '#type' => 'select', + '#title' => t('Thousand marker'), + '#options' => $options, + '#default_value' => $settings['thousand_separator'], + ); + + if ($display['type'] == 'number_decimal' || $display['type'] == 'number_float') { + $form['decimal_separator'] = array( + '#type' => 'select', + '#title' => t('Decimal marker'), + '#options' => array('.' => t('Decimal point'), ',' => t('Comma')), + '#default_value' => $settings['decimal_separator'], + ); + $form['scale'] = array( + '#type' => 'select', + '#title' => t('Scale'), + '#options' => drupal_map_assoc(range(0, 10)), + '#default_value' => $settings['scale'], + '#description' => t('The number of digits to the right of the decimal.'), + ); + } + + $form['prefix_suffix'] = array( + '#type' => 'checkbox', + '#title' => t('Display prefix and suffix.'), + '#default_value' => $settings['prefix_suffix'], + ); + + return $form; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function number_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = array(); + $summary[] = number_format(1234.1234567890, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']); + if ($settings['prefix_suffix']) { + $summary[] = t('Display with prefix and suffix.'); + } + + return implode('<br />', $summary); +} + /** * Implements hook_field_formatter_view(). */ diff --git a/modules/field/modules/options/options.info b/modules/field/modules/options/options.info index 1a01ef3c50050edaea62fce6bd3dc5ccb7baea88..cb67005697aef1b7887af3c931facbe1272d6b89 100644 --- a/modules/field/modules/options/options.info +++ b/modules/field/modules/options/options.info @@ -1,15 +1,16 @@ -; $Id: options.info,v 1.5 2010/06/21 02:27:47 webchick Exp $ +; $Id: options.info,v 1.6 2010/08/16 20:57:22 dries Exp $ name = Options description = Defines selection, check box and radio button widgets for text and numeric fields. package = Core version = VERSION core = 7.x -files[]=options.module -files[]=options.test +dependencies[] = field +files[] = options.module +files[] = options.test required = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/options/options.module b/modules/field/modules/options/options.module index 73f8932caee663ab5614643dc20bf323223119fe..3994ba470a11bb3dfc76b37fe4d71e4739f70122 100644 --- a/modules/field/modules/options/options.module +++ b/modules/field/modules/options/options.module @@ -1,5 +1,5 @@ <?php -// $Id: options.module,v 1.26 2010/04/13 15:23:02 dries Exp $ +// $Id: options.module,v 1.27 2010/08/17 21:48:39 dries Exp $ /** * @file @@ -25,7 +25,7 @@ function options_help($path, $arg) { function options_theme() { return array( 'options_none' => array( - 'variables' => array('instance' => NULL), + 'variables' => array('instance' => NULL, 'option' => NULL), ), ); } @@ -76,7 +76,8 @@ function options_field_widget_form(&$form, &$form_state, $field, $instance, $lan $type = str_replace('options_', '', $instance['widget']['type']); $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED; $required = $element['#required']; - $properties = _options_properties($type, $multiple, $required); + $has_value = isset($items[0][$value_key]); + $properties = _options_properties($type, $multiple, $required, $has_value); // Prepare the list of options. $options = _options_get_options($field, $instance, $properties); @@ -137,6 +138,9 @@ function options_field_widget_form(&$form, &$form_state, $field, $instance, $lan * Form element validation handler for options element. */ function options_field_widget_validate($element, &$form_state) { + if ($element['#required'] && $element['#value'] == '_none') { + form_error($element, t('!name field is required.', array('!name' => $element['#title']))); + } // Transpose selections from field => delta to delta => field, turning // multiple selected options into multiple parent elements. $items = _options_form_to_storage($element); @@ -146,12 +150,12 @@ function options_field_widget_validate($element, &$form_state) { /** * Describes the preparation steps required by each widget. */ -function _options_properties($type, $multiple, $required) { +function _options_properties($type, $multiple, $required, $has_value) { $base = array( 'zero_placeholder' => FALSE, 'filter_xss' => FALSE, 'strip_tags' => FALSE, - 'empty_value' => FALSE, + 'empty_option' => FALSE, 'optgroups' => FALSE, ); @@ -162,9 +166,25 @@ function _options_properties($type, $multiple, $required) { $properties = array( // Select boxes do not support any HTML tag. 'strip_tags' => TRUE, - 'empty_value' => !$required, 'optgroups' => TRUE, ); + if ($multiple) { + // Multiple select: add a 'none' option for non-required fields. + if (!$required) { + $properties['empty_option'] = 'option_none'; + } + } + else { + // Single select: add a 'none' option for non-required fields, + // and a 'select a value' option for required fields that do not come + // with a value selected. + if (!$required) { + $properties['empty_option'] = 'option_none'; + } + else if (!$has_value) { + $properties['empty_option'] = 'option_select'; + } + } break; case 'buttons': @@ -173,9 +193,11 @@ function _options_properties($type, $multiple, $required) { // Form API 'checkboxes' do not suport 0 as an option, so we replace it with // a placeholder within the form workflow. 'zero_placeholder' => $multiple, - // Checkboxes do not need a 'none' choice. - 'empty_value' => !$required && !$multiple, ); + // Add a 'none' option for non-required radio buttons. + if (!$required && !$multiple) { + $properties['empty_option'] = 'option_none'; + } break; case 'onoff': @@ -202,8 +224,9 @@ function _options_get_options($field, $instance, $properties) { $options = options_array_flatten($options); } - if ($properties['empty_value']) { - $options = array('_none' => theme('options_none', array('instance' => $instance))) + $options; + if ($properties['empty_option']) { + $label = theme('options_none', array('instance' => $instance, 'option' => $properties['empty_option'])); + $options = array('_none' => $label) + $options; } return $options; @@ -289,7 +312,7 @@ function _options_form_to_storage($element) { // Filter out the 'none' option. Use a strict comparison, because // 0 == 'any string'. - if ($properties['empty_value']) { + if ($properties['empty_option']) { $index = array_search('_none', $values, TRUE); if ($index !== FALSE) { unset($values[$index]); @@ -368,7 +391,7 @@ function options_field_widget_error($element, $error, $form, &$form_state) { /** * Returns HTML for the label for the empty value for options that are not required. * - * The default theme will display N/A for a radio list and blank for a select. + * The default theme will display N/A for a radio list and '- None -' for a select. * * @param $variables * An associative array containing: @@ -378,12 +401,18 @@ function options_field_widget_error($element, $error, $form, &$form_state) { */ function theme_options_none($variables) { $instance = $variables['instance']; + $option = $variables['option']; + + $output = ''; switch ($instance['widget']['type']) { case 'options_buttons': - return t('N/A'); + $output = t('N/A'); + break; + case 'options_select': - return t('- None -'); - default : - return ''; + $output = ($option == 'option_none' ? t('- None -') : t('- Select a value -')); + break; } + + return $output; } diff --git a/modules/field/modules/options/options.test b/modules/field/modules/options/options.test index 42b6da57b197b01e951e2e1928a549a071e7ead8..3dfd9d910a83fcca412f5bdc7a87bb4310090ca1 100644 --- a/modules/field/modules/options/options.test +++ b/modules/field/modules/options/options.test @@ -1,5 +1,5 @@ <?php -// $Id: options.test,v 1.13 2010/04/07 17:30:43 dries Exp $ +// $Id: options.test,v 1.16 2010/08/17 21:48:39 dries Exp $ class OptionsWidgetsTestCase extends FieldTestCase { public static function getInfo() { @@ -207,6 +207,7 @@ class OptionsWidgetsTestCase extends FieldTestCase { 'field_name' => $this->card_1['field_name'], 'entity_type' => 'test_entity', 'bundle' => 'test_bundle', + 'required' => TRUE, 'widget' => array( 'type' => 'options_select', ), @@ -220,13 +221,23 @@ class OptionsWidgetsTestCase extends FieldTestCase { $entity->is_new = TRUE; field_test_entity_save($entity); - // Display form: with no field data, nothing is selected. + // Display form. $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + // A required field without any value has a "none" option. + $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), t('A required select list has a "Select a value" choice.')); + + // With no field data, nothing is selected. + $this->assertNoOptionSelected("edit-card-1-$langcode", '_none'); $this->assertNoOptionSelected("edit-card-1-$langcode", 0); $this->assertNoOptionSelected("edit-card-1-$langcode", 1); $this->assertNoOptionSelected("edit-card-1-$langcode", 2); $this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.')); + // Submit form: select invalid 'none' option. + $edit = array("card_1[$langcode]" => '_none'); + $this->drupalPost(NULL, $edit, t('Save')); + $this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), t('Cannot save a required field when selecting "none" from the select list.')); + // Submit form: select first option. $edit = array("card_1[$langcode]" => 0); $this->drupalPost(NULL, $edit, t('Save')); @@ -234,31 +245,30 @@ class OptionsWidgetsTestCase extends FieldTestCase { // Display form: check that the right options are selected. $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + // A required field with a value has no 'none' option. + $this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), t('A required select list with an actual value has no "none" choice.')); $this->assertOptionSelected("edit-card-1-$langcode", 0); $this->assertNoOptionSelected("edit-card-1-$langcode", 1); $this->assertNoOptionSelected("edit-card-1-$langcode", 2); + // Make the field non required. + $instance['required'] = FALSE; + field_update_instance($instance); + + // Display form. + $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + // A non-required field has a 'none' option. + $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), t('A non-required select list has a "None" choice.')); // Submit form: Unselect the option. $edit = array("card_1[$langcode]" => '_none'); $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_1', $langcode, array()); - // A required select list does not have an empty key. - $instance['required'] = TRUE; - field_update_instance($instance); - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); - $this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-1-' . $langcode)), t('A required select list does not have an empty key.')); - - // We do not have to test that a required select list with one option is - // auto-selected because the browser does it for us. - // Test optgroups. $this->card_1['settings']['allowed_values'] = NULL; $this->card_1['settings']['allowed_values_function'] = 'list_test_allowed_values_callback'; field_update_field($this->card_1); - $instance['required'] = FALSE; - field_update_instance($instance); // Display form: with no field data, nothing is selected $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); diff --git a/modules/field/modules/text/text.info b/modules/field/modules/text/text.info index 9e78eda5db1d9572fe4a6c65058149cd6defcd89..0fc2b1c362ee7e4c5b072799c28e9707df4588f0 100644 --- a/modules/field/modules/text/text.info +++ b/modules/field/modules/text/text.info @@ -1,15 +1,16 @@ -; $Id: text.info,v 1.7 2010/06/21 02:27:47 webchick Exp $ +; $Id: text.info,v 1.8 2010/08/16 20:57:22 dries Exp $ name = Text description = Defines simple text field types. package = Core version = VERSION core = 7.x +dependencies[] = field files[] = text.module files[] = text.test required = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/modules/text/text.install b/modules/field/modules/text/text.install new file mode 100644 index 0000000000000000000000000000000000000000..4c41e80a7b92930c2c7ce6654e51dd4df0d8bae4 --- /dev/null +++ b/modules/field/modules/text/text.install @@ -0,0 +1,60 @@ +<?php +// $Id: text.install,v 1.1 2010/09/04 15:40:51 dries Exp $ + +/** + * @file + * Install, update and uninstall functions for the text module. + */ + +/** + * Implements hook_field_schema(). + */ +function text_field_schema($field) { + switch ($field['type']) { + case 'text': + $columns = array( + 'value' => array( + 'type' => 'varchar', + 'length' => $field['settings']['max_length'], + 'not null' => FALSE, + ), + ); + break; + case 'text_long': + $columns = array( + 'value' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), + ); + break; + case 'text_with_summary': + $columns = array( + 'value' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), + 'summary' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), + ); + break; + } + $columns += array( + 'format' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + ), + ); + return array( + 'columns' => $columns, + 'indexes' => array( + 'format' => array('format'), + ), + ); +} diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 2a3604c7189fd78184ccf144c96a3105f2f848af..4bbabccbf9556d57f35a626615506d5eded90c3a 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.59 2010/07/07 13:30:06 dries Exp $ +// $Id: text.module,v 1.64 2010/09/11 04:19:15 webchick Exp $ /** * @file @@ -52,60 +52,7 @@ function text_field_info() { 'description' => t('This field stores long text in the database along with optional summary text.'), 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0), 'default_widget' => 'text_textarea_with_summary', - 'default_formatter' => 'text_summary_or_trimmed', - ), - ); -} - -/** - * Implements hook_field_schema(). - */ -function text_field_schema($field) { - switch ($field['type']) { - case 'text': - $columns = array( - 'value' => array( - 'type' => 'varchar', - 'length' => $field['settings']['max_length'], - 'not null' => FALSE, - ), - ); - break; - case 'text_long': - $columns = array( - 'value' => array( - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - ), - ); - break; - case 'text_with_summary': - $columns = array( - 'value' => array( - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - ), - 'summary' => array( - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - ), - ); - break; - } - $columns += array( - 'format' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - ), - ); - return array( - 'columns' => $columns, - 'indexes' => array( - 'format' => array('format'), + 'default_formatter' => 'text_default', ), ); } @@ -251,6 +198,7 @@ function text_field_formatter_info() { 'text_trimmed' => array( 'label' => t('Trimmed'), 'field types' => array('text', 'text_long', 'text_with_summary'), + 'settings' => array('trim_length' => 600), ), // The 'summary or trimmed' field formatter for text_with_summary @@ -260,10 +208,50 @@ function text_field_formatter_info() { 'text_summary_or_trimmed' => array( 'label' => t('Summary or trimmed'), 'field types' => array('text_with_summary'), + 'settings' => array('trim_length' => 600), ), ); } +/** + * Implements hook_field_formatter_settings_form(). + */ +function text_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $form = array(); + + if (strpos($display['type'], '_trimmed') !== FALSE) { + $form['trim_length'] = array( + '#title' => t('Trim length'), + '#type' => 'textfield', + '#size' => 10, + '#default_value' => $settings['trim_length'], + '#element_validate' => array('_element_validate_integer_positive'), + '#required' => TRUE, + ); + } + + return $form; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function text_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = ''; + + if (strpos($display['type'], '_trimmed') !== FALSE) { + $summary = t('Trim length') . ': ' . $settings['trim_length']; + } + + return $summary; +} + /** * Implements hook_field_formatter_view(). */ @@ -276,7 +264,7 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la foreach ($items as $delta => $item) { $output = _text_sanitize($instance, $langcode, $item, 'value'); if ($display['type'] == 'text_trimmed') { - $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL); + $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']); } $element[$delta] = array('#markup' => $output); } @@ -289,8 +277,7 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la } else { $output = _text_sanitize($instance, $langcode, $item, 'value'); - $size = variable_get('teaser_length_' . $instance['bundle'], 600); - $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $size); + $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']); } $element[$delta] = array('#markup' => $output); } @@ -599,11 +586,30 @@ function text_field_prepare_translation($entity_type, $entity, $field, $instance // If the translating user is not permitted to use the assigned text format, // we must not expose the source values. $field_name = $field['field_name']; - $formats = filter_formats(); - foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) { - $format_id = $item['format']; - if (!filter_access($formats[$format_id])) { - unset($items[$delta]); + if (!empty($source_entity->{$field_name}[$source_langcode])) { + $formats = filter_formats(); + foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) { + $format_id = $item['format']; + if (!empty($format_id) && !filter_access($formats[$format_id])) { + unset($items[$delta]); + } } } } + +/** + * Implements hook_filter_format_update(). + */ +function text_filter_format_update() { + field_cache_clear(); +} + +/** + * Implements hook_filter_format_delete(). + * + * @todo D8: Properly update filter format references in all fields. See + * http://drupal.org/node/556022 for details. + */ +function text_filter_format_delete() { + field_cache_clear(); +} diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index f66855339412b4d7bb6629995ea61eb588313793..1843f36b6b80caa0cba73d165fa2cd87038041e5 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.24 2010/06/28 20:58:42 dries Exp $ +// $Id: text.test,v 1.27 2010/08/17 18:25:31 dries Exp $ class TextFieldTestCase extends DrupalWebTestCase { protected $instance; @@ -401,11 +401,39 @@ class TextTranslationTestCase extends DrupalWebTestCase { $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), t('Article content type has been updated.')); } + /** + * Test that a plaintext textfield widget is correctly populated. + */ + function testTextField() { + // Disable text processing for body. + $edit = array('instance[settings][text_processing]' => 0); + $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings')); + + // Login as translator. + $this->drupalLogin($this->translator); + + // Create content. + $langcode = LANGUAGE_NONE; + $body = $this->randomName(); + $edit = array( + "title" => $this->randomName(), + "language" => 'en', + "body[$langcode][0][value]" => $body, + ); + + // Translate the article in french. + $this->drupalPost('node/add/article', $edit, t('Save')); + $node = $this->drupalGetNodeByTitle($edit['title']); + $this->drupalGet("node/$node->nid/translate"); + $this->clickLink(t('add translation')); + $this->assertFieldByXPath("//textarea[@name='body[fr][0][value]']", $body, t('The textfield widget is populated.')); + } + /** * Check that user that does not have access the field format cannot see the * source value when creating a translation. */ - function testMultipleTextField() { + function testTextFieldFormatted() { // Make node body multiple. $edit = array('field[cardinality]' => -1); $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings')); @@ -419,8 +447,9 @@ class TextTranslationTestCase extends DrupalWebTestCase { // Create an article with the first body input format set to "Full HTML". $langcode = 'en'; + $title = $this->randomName(); $edit = array( - "title" => $this->randomName(), + 'title' => $title, 'language' => $langcode, ); $this->drupalPost('node/add/article', $edit, t('Save')); @@ -438,11 +467,11 @@ class TextTranslationTestCase extends DrupalWebTestCase { } // Login as translator. - $this->drupalLogout(); $this->drupalLogin($this->translator); // Translate the article in french. - $this->drupalGet('node/1/translate'); + $node = $this->drupalGetNodeByTitle($title); + $this->drupalGet("node/$node->nid/translate"); $this->clickLink(t('add translation')); $this->assertNoText($body[0], t('The body field with delta @delta is hidden.', array('@delta' => 0))); $this->assertText($body[1], t('The body field with delta @delta is shown.', array('@delta' => 1))); diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 36d6284f5e508392f2f280a1628151745148d8e3..41fecdbf6f89778af8cbe22ee4377f9d2e5586da 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -1,5 +1,5 @@ <?php -// $Id: field.test,v 1.33 2010/06/17 13:16:57 dries Exp $ +// $Id: field.test,v 1.40 2010/09/11 06:03:11 webchick Exp $ /** * @file @@ -2076,29 +2076,45 @@ class FieldCrudTestCase extends FieldTestCase { * Test updating a field. */ function testUpdateField() { - // Create a decimal 5.2 field. - $field = array('field_name' => 'decimal53', 'type' => 'number_decimal', 'cardinality' => 3, 'settings' => array('precision' => 5, 'scale' => 2)); - $field = field_create_field($field); - $instance = array('field_name' => 'decimal53', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle'); + // Create a field with a defined cardinality, so that we can ensure it's + // respected. Since cardinality enforcement is consistent across database + // systems, it makes a good test case. + $cardinality = 4; + $field_definition = array( + 'field_name' => 'field_update', + 'type' => 'test_field', + 'cardinality' => $cardinality, + ); + $field_definition = field_create_field($field_definition); + $instance = array( + 'field_name' => 'field_update', + 'entity_type' => 'test_entity', + 'bundle' => 'test_bundle', + ); $instance = field_create_instance($instance); - // Update it to a deciaml 5.3 field. - $field['settings']['scale'] = 3; - field_update_field($field); - - // Save values with 2, 3, and 4 decimal places. - $entity = field_test_create_stub_entity(0, 0, $instance['bundle']); - $entity->decimal53[LANGUAGE_NONE][0]['value'] = '1.23'; - $entity->decimal53[LANGUAGE_NONE][1]['value'] = '1.235'; - $entity->decimal53[LANGUAGE_NONE][2]['value'] = '1.2355'; - field_attach_insert('test_entity', $entity); - $entity = field_test_create_stub_entity(0, 0, $instance['bundle']); - - // Verify that the updated 5.3 field rounds to 3 decimal places. - field_attach_load('test_entity', array(0 => $entity)); - $this->assertEqual($entity->decimal53[LANGUAGE_NONE][0]['value'], '1.23', t('2 decimal places are left alone')); - $this->assertEqual($entity->decimal53[LANGUAGE_NONE][1]['value'], '1.235', t('3 decimal places are left alone')); - $this->assertEqual($entity->decimal53[LANGUAGE_NONE][2]['value'], '1.236', t('4 decimal places are rounded to 3')); + do { + // We need a unique ID for our entity. $cardinality will do. + $id = $cardinality; + $entity = field_test_create_stub_entity($id, $id, $instance['bundle']); + // Fill in the entity with more values than $cardinality. + for ($i = 0; $i < 20; $i++) { + $entity->field_update[LANGUAGE_NONE][$i]['value'] = $i; + } + // Save the entity. + field_attach_insert('test_entity', $entity); + // Load back and assert there are $cardinality number of values. + $entity = field_test_create_stub_entity($id, $id, $instance['bundle']); + field_attach_load('test_entity', array($id => $entity)); + $this->assertEqual(count($entity->field_update[LANGUAGE_NONE]), $field_definition['cardinality'], 'Cardinality is kept'); + // Now check the values themselves. + for ($delta = 0; $delta < $cardinality; $delta++) { + $this->assertEqual($entity->field_update[LANGUAGE_NONE][$delta]['value'], $delta, 'Value is kept'); + } + // Increase $cardinality and set the field cardinality to the new value. + $field_definition['cardinality'] = ++$cardinality; + field_update_field($field_definition); + } while ($cardinality < 6); } /** @@ -2623,8 +2639,7 @@ class FieldTranslationsTestCase extends FieldTestCase { field_create_instance($instance); $entity = field_test_create_stub_entity(1, 1, $this->instance['bundle']); - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - $instances = field_info_instances($entity_type, $bundle); + $instances = field_info_instances($entity_type, $this->instance['bundle']); $enabled_languages = field_content_languages(); $languages = array(); @@ -2672,6 +2687,13 @@ class FieldTranslationsTestCase extends FieldTestCase { drupal_static_reset('field_language'); $langcode = field_language($entity_type, $entity, $this->field_name, $requested_language); $this->assertTrue(isset($entity->{$this->field_name}[$langcode]) && $langcode != $requested_language, t('The display language for the (single) field %field_name is %language.', array('%field_name' => $field_name, '%language' => $langcode))); + + // Test field_language() basic behavior without language fallback. + variable_set('field_test_language_fallback', FALSE); + $entity->{$this->field_name}[$requested_language] = mt_rand(1, 127); + drupal_static_reset('field_language'); + $display_language = field_language($entity_type, $entity, $this->field_name, $requested_language); + $this->assertEqual($display_language, $requested_language, t('Display language behave correctly when language fallback is disabled')); } /** @@ -2966,3 +2988,51 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertEqual(count($fields), 0, 'The field is purged.'); } } + +/** + * Tests entity properties. + */ +class EntityPropertiesTestCase extends FieldTestCase { + public static function getInfo() { + return array( + 'name' => 'Entity properties', + 'description'=> 'Tests entity properties.', + 'group' => 'Entity API', + ); + } + + function setUp() { + parent::setUp('field_test'); + } + + /** + * Tests label key and label callback of an entity. + */ + function testEntityLabel() { + $entity_types = array( + 'test_entity_no_label', + 'test_entity_label', + 'test_entity_label_callback', + ); + + $entity = field_test_create_stub_entity(); + + foreach ($entity_types as $entity_type) { + $label = entity_label($entity_type, $entity); + + switch ($entity_type) { + case 'test_entity_no_label': + $this->assertFalse($label, 'Entity with no label property or callback returned FALSE.'); + break; + + case 'test_entity_label': + $this->assertEqual($label, $entity->ftlabel, 'Entity with label key returned correct label.'); + break; + + case 'test_entity_label_callback': + $this->assertEqual($label, 'label callback ' . $entity->ftlabel, 'Entity with label callback returned correct label.'); + break; + } + } + } +} diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc index 34830f56b03e8e77251ca1cec4a521836b73463c..0ed6b4ffbbdc572057e46c63b9e394989258700b 100644 --- a/modules/field/tests/field_test.entity.inc +++ b/modules/field/tests/field_test.entity.inc @@ -1,5 +1,5 @@ <?php -// $Id: field_test.entity.inc,v 1.12 2010/06/17 13:44:45 dries Exp $ +// $Id: field_test.entity.inc,v 1.13 2010/09/11 06:03:11 webchick Exp $ /** * @file @@ -73,6 +73,48 @@ function field_test_entity_info() { 'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')), 'view modes' => $test_entity_modes, ), + // @see EntityPropertiesTestCase::testEntityLabel() + 'test_entity_no_label' => array( + 'name' => t('Test entity without label'), + 'fieldable' => TRUE, + 'field cache' => FALSE, + 'base table' => 'test_entity', + 'entity keys' => array( + 'id' => 'ftid', + 'revision' => 'ftvid', + 'bundle' => 'fttype', + ), + 'bundles' => $bundles, + 'view modes' => $test_entity_modes, + ), + 'test_entity_label' => array( + 'name' => t('Test entity label'), + 'fieldable' => TRUE, + 'field cache' => FALSE, + 'base table' => 'test_entity', + 'entity keys' => array( + 'id' => 'ftid', + 'revision' => 'ftvid', + 'bundle' => 'fttype', + 'label' => 'ftlabel', + ), + 'bundles' => $bundles, + 'view modes' => $test_entity_modes, + ), + 'test_entity_label_callback' => array( + 'name' => t('Test entity label callback'), + 'fieldable' => TRUE, + 'field cache' => FALSE, + 'base table' => 'test_entity', + 'label callback' => 'field_test_entity_label_callback', + 'entity keys' => array( + 'id' => 'ftid', + 'revision' => 'ftvid', + 'bundle' => 'fttype', + ), + 'bundles' => $bundles, + 'view modes' => $test_entity_modes, + ), ); } @@ -163,7 +205,7 @@ function field_test_delete_bundle($bundle) { /** * Creates a basic test_entity entity. */ -function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = 'test_bundle') { +function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $label = '') { $entity = new stdClass(); // Only set id and vid properties if they don't come as NULL (creation form). if (isset($id)) { @@ -174,6 +216,9 @@ function field_test_create_stub_entity($id = 1, $vid = 1, $bundle = 'test_bundle } $entity->fttype = $bundle; + $label = !empty($label) ? $label : $bundle . ' label'; + $entity->ftlabel = $label; + return $entity; } diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index 79212e681dd6215aee6e223d3e3c28b480efaedb..b7236192be47e6cf036456fde3cd8773ce878fd0 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.11 2010/06/14 15:41:02 dries Exp $ +// $Id: field_test.field.inc,v 1.12 2010/09/04 15:40:51 dries Exp $ /** * @file @@ -46,42 +46,6 @@ function field_test_field_info() { ); } -/** - * Implements hook_field_schema(). - */ -function field_test_field_schema($field) { - if ($field['type'] == 'test_field') { - return array( - 'columns' => array( - 'value' => array( - 'type' => 'int', - 'size' => 'medium', - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'value' => array('value'), - ), - ); - } - else { - return array( - 'columns' => array( - 'shape' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => FALSE, - ), - 'color' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => FALSE, - ), - ), - ); - } -} - /** * Implements hook_field_update_forbid(). */ diff --git a/modules/field/tests/field_test.info b/modules/field/tests/field_test.info index 4cbc63bd8eff8d44cb7906f40536fbe9b74d97cb..7ecaabca357c87d8e869fd0f60775559c5c39847 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field/tests/field_test.install b/modules/field/tests/field_test.install index d5c8ab88d555d9f214acecb524840ea7e0c1f13c..f92291282f267568dd43bc2fcaf426fa6503269d 100644 --- a/modules/field/tests/field_test.install +++ b/modules/field/tests/field_test.install @@ -1,5 +1,5 @@ <?php -// $Id: field_test.install,v 1.3 2010/06/14 15:41:02 dries Exp $ +// $Id: field_test.install,v 1.5 2010/09/11 06:03:11 webchick Exp $ /** * @file @@ -44,6 +44,13 @@ function field_test_schema() { 'not null' => TRUE, 'default' => '', ), + 'ftlabel' => array( + 'description' => 'The label of this test_entity.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), ), 'unique keys' => array( 'ftvid' => array('ftvid'), @@ -106,3 +113,39 @@ function field_test_schema() { return $schema; } + +/** + * Implements hook_field_schema(). + */ +function field_test_field_schema($field) { + if ($field['type'] == 'test_field') { + return array( + 'columns' => array( + 'value' => array( + 'type' => 'int', + 'size' => 'medium', + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'value' => array('value'), + ), + ); + } + else { + return array( + 'columns' => array( + 'shape' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => FALSE, + ), + 'color' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => FALSE, + ), + ), + ); + } +} diff --git a/modules/field/tests/field_test.module b/modules/field/tests/field_test.module index d86b7fc515b8fea18ccf7022ae089dac6828dcf0..e031a4838fb0073308e09a5ad18eda02f35aa47e 100644 --- a/modules/field/tests/field_test.module +++ b/modules/field/tests/field_test.module @@ -1,5 +1,5 @@ <?php -// $Id: field_test.module,v 1.8 2010/06/24 17:24:40 webchick Exp $ +// $Id: field_test.module,v 1.10 2010/09/11 06:03:11 webchick Exp $ /** * @file @@ -103,7 +103,9 @@ function field_test_field_available_languages_alter(&$languages, $context) { * Implements hook_field_language_alter(). */ function field_test_field_language_alter(&$display_language, $context) { - locale_field_language_fallback($display_language, $context['entity'], $context['language']); + if (variable_get('field_test_language_fallback', TRUE)) { + locale_field_language_fallback($display_language, $context['entity'], $context['language']); + } } /** @@ -212,3 +214,16 @@ function field_test_dummy_field_storage_query(EntityFieldQuery $query) { ), ); } + +/** + * Entity label callback. + * + * @param $entity + * The entity object. + * + * @return + * The label of the entity prefixed with "label callback". + */ +function field_test_entity_label_callback($entity) { + return 'label callback ' . $entity->ftlabel; +} diff --git a/modules/field_ui/field_ui-display-overview-table.tpl.php b/modules/field_ui/field_ui-display-overview-table.tpl.php deleted file mode 100644 index e06794070955fe699abcebdfa7e297b8f5cb2c54..0000000000000000000000000000000000000000 --- a/modules/field_ui/field_ui-display-overview-table.tpl.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -// $Id: field_ui-display-overview-table.tpl.php,v 1.2 2010/05/26 07:49:52 dries Exp $ - -/** - * @file - * Default theme implementation to configure field display settings. - * - * Available variables: - * - $rows: The field display settings form broken down into rendered rows for - * printing as a table. The array is separated in two entries, 'visible' and - * 'hidden'. - * - $id: The HTML id for the table. - * - * @see field_ui_display_overview_form() - * @see template_preprocess_field_ui_display_overview_table() - */ -?> -<?php if ($rows): ?> - <table id="field-display-overview" class="field-display-overview sticky-enabled"> - <thead> - <tr> - <th><?php print t('Field'); ?></th> - <th><?php print t('Weight'); ?></th> - <th><?php print t('Label'); ?></th> - <th><?php print t('Format'); ?></th> - </tr> - </thead> - <tbody> - <tr class="region-message region-visible-message <?php print empty($rows['visible']) ? 'region-empty' : 'region-populated'; ?>"> - <td colspan="4"><em><?php print t('No field is displayed'); ?></em></td> - </tr> - <?php - $count = 0; - foreach ($rows['visible'] as $row): ?> - <tr class="<?php print $count % 2 == 0 ? 'odd' : 'even'; ?> <?php print $row->class ?>"> - <td><span class="<?php print $row->label_class; ?>"><?php print $row->human_name; ?></span></td> - <td><?php print $row->weight . $row->hidden_name; ?></td> - <td><?php if (isset($row->label)) print $row->label; ?></td> - <td><?php print $row->type; ?></td> - </tr> - <?php $count++; - endforeach; ?> - <tr class="region-title region-title-hidden"> - <td colspan="4"><?php print t('Hidden'); ?></td> - </tr> - <tr class="region-message region-hidden-message <?php print empty($rows['hidden']) ? 'region-empty' : 'region-populated'; ?>"> - <td colspan="4"><em><?php print t('No field is hidden'); ?></em></td> - </tr> - <?php foreach ($rows['hidden'] as $row): ?> - <tr class="<?php print $count % 2 == 0 ? 'odd' : 'even'; ?> <?php print $row->class ?>"> - <td><span class="<?php print $row->label_class; ?>"><?php print $row->human_name; ?></span></td> - <td><?php print $row->weight . $row->hidden_name; ?></td> - <td><?php if (isset($row->label)) print $row->label; ?></td> - <td><?php print $row->type; ?></td> - </tr> - <?php $count++; - endforeach; ?> - </tbody> - </table> -<?php endif; ?> diff --git a/modules/field_ui/field_ui-rtl.css b/modules/field_ui/field_ui-rtl.css index 233390b02d4750fabb51d4c7dfd6722b0ba60c09..456d37f235a3dae0bf607089a3193439834e7a3a 100644 --- a/modules/field_ui/field_ui-rtl.css +++ b/modules/field_ui/field_ui-rtl.css @@ -1,7 +1,6 @@ -/* $Id: field_ui-rtl.css,v 1.2 2010/06/26 02:06:53 dries Exp $ */ +/* $Id: field_ui-rtl.css,v 1.3 2010/09/11 00:03:42 webchick Exp $ */ /* 'Manage fields' overview */ -#field-overview tr.add-new .label-input { +table.field-ui-overview tr.add-new .label-input { float: right; } - diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index 1bc3ba8e369b74bbf738fa778738d359cb177aa5..a4fab1dadd4037675d4374bd02510fa4fd91c7c0 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.57 2010/06/27 18:05:54 webchick Exp $ +// $Id: field_ui.admin.inc,v 1.65 2010/09/11 00:03:42 webchick Exp $ /** * @file @@ -80,18 +80,53 @@ function _field_ui_reduce_order($array, $a) { } /** - * Theme preprocess function for theme_field_ui_table(). + * Returns the region to which a row in the 'Manage fields' screen belongs. * - * @see theme_field_ui_table() + * This function is used as a #row_callback in field_ui_field_overview_form(), + * and is called during field_ui_table_pre_render(). */ -function template_preprocess_field_ui_table(&$variables) { - $elements = &$variables['elements']; +function field_ui_field_overview_row_region($row) { + switch ($row['#row_type']) { + case 'field': + case 'extra_field': + return 'main'; + case 'add_new_field': + // If no input in 'label', assume the row has not been dragged out of the + // 'add new' section. + return (!empty($row['label']['#value']) ? 'main' : 'add_new'); + } +} + +/** + * Returns the region to which a row in the 'Manage display' screen belongs. + * + * This function is used as a #row_callback in field_ui_field_overview_form(), + * and is called during field_ui_table_pre_render(). + */ +function field_ui_display_overview_row_region($row) { + switch ($row['#row_type']) { + case 'field': + case 'extra_field': + return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'visible'); + } +} + +/** + * Pre-render callback for field_ui_table elements. + */ +function field_ui_table_pre_render($elements) { + $js_settings = array(); - // Build the tree structure from the weight and parenting data contained in - // the flat form structure, to determine row order and indentation. + // For each region, build the tree structure from the weight and parenting + // data contained in the flat form structure, to determine row order and + // indentation. + $regions = $elements['#regions']; $tree = array('' => array('name' => '', 'children' => array())); + $trees = array_fill_keys(array_keys($regions), $tree); + $parents = array(); $list = drupal_map_assoc(element_children($elements)); + // Iterate on rows until we can build a known tree path for all of them. while ($list) { foreach ($list as $name) { @@ -99,12 +134,16 @@ function template_preprocess_field_ui_table(&$variables) { $parent = $row['parent_wrapper']['parent']['#value']; // Proceed if parent is known. if (empty($parent) || isset($parents[$parent])) { - // Remove from the next iteration. + // Grab parent, and remove the row from the next iteration. $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array(); unset($list[$name]); + // Determine the region for the row. + $function = $row['#region_callback']; + $region_name = $function($row); + // Add the element in the tree. - $target = &$tree['']; + $target = &$trees[$region_name]['']; foreach ($parents[$name] as $key) { $target = &$target['children'][$key]; } @@ -115,12 +154,35 @@ function template_preprocess_field_ui_table(&$variables) { $cell = current(element_children($row)); $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : ''); } + + // Add row id and associate JS settings. + $id = drupal_html_class($name); + $row['#attributes']['id'] = $id; + $row += array( + '#js_settings' => array(), + ); + $row['#js_settings'] += array( + 'rowHandler' => $row['#row_type'], + 'name' => $name, + 'region' => $region_name, + ); + $js_settings[$id] = $row['#js_settings']; } } } + // Determine rendering order from the tree structure. + foreach ($regions as $region_name => $region) { + $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), +// ); - // Determine rendering order for the tree. - $variables['row_order'] = array_reduce($tree, '_field_ui_reduce_order'); + return $elements; } /** @@ -136,26 +198,65 @@ function template_preprocess_field_ui_table(&$variables) { function theme_field_ui_table($variables) { $elements = $variables['elements']; $table = array(); + $js_settings = array(); + // Add table headers and attributes. foreach (array('header', 'attributes') as $key) { if (isset($elements["#$key"])) { $table[$key] = $elements["#$key"]; } } - foreach ($variables['row_order'] as $key) { - $element = $elements[$key]; - $row = array('data' => array()); - $row += $element['#attributes']; + // Determine the colspan to use for region rows, by checking the number of + // columns in the headers. + $colums_count = 0; + foreach ($table['header'] as $header) { + $colums_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1); + } + + // Render rows, region by region. + foreach ($elements['#regions'] as $region_name => $region) { + $region_name_class = drupal_html_class($region_name); + + // Add region rows. + if (isset($region['title'])) { + $table['rows'][] = array( + 'class' => array('region-title', 'region-' . $region_name_class . '-title'), + 'no_striping' => TRUE, + 'data' => array( + array('data' => $region['title'], 'colspan' => $colums_count), + ), + ); + } + if (isset($region['message'])) { + $class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated'); + $table['rows'][] = array( + 'class' => array('region-message', 'region-' . $region_name_class . '-message', $class), + 'no_striping' => TRUE, + 'data' => array( + array('data' => $region['message'], 'colspan' => $colums_count), + ), + ); + } + + // Add form rows, in the order determined at pre-render time. + foreach ($region['rows_order'] as $name) { + $element = $elements[$name]; - foreach (element_children($element) as $cell_key) { - $cell = array('data' => drupal_render($element[$cell_key])); - if (isset($element[$cell_key]['#cell_attributes'])) { - $cell += $element[$cell_key]['#cell_attributes']; + $row = array('data' => array()); + if (isset($element['#attributes'])) { + $row += $element['#attributes']; + } + + foreach (element_children($element) as $cell_key) { + $cell = array('data' => drupal_render($element[$cell_key])); + if (isset($element[$cell_key]['#cell_attributes'])) { + $cell += $element[$cell_key]['#cell_attributes']; + } + $row['data'][] = $cell; } - $row['data'][] = $cell; + $table['rows'][] = $row; } - $table['rows'][] = $row; } return theme('table', $table); @@ -182,10 +283,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle $field_types = field_info_field_types(); $widget_types = field_info_widget_types(); - $extra_fields = field_extra_fields($entity_type, $bundle, 'form'); - - // Store each default weight so that we can add the 'add new' rows after them. - $weights = array(); + $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form'); $form += array( '#entity_type' => $entity_type, @@ -195,7 +293,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ); $table = array( - '#type' => 'table', + '#type' => 'field_ui_table', '#tree' => TRUE, '#header' => array( t('Label'), @@ -206,35 +304,40 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle t('Widget'), array('data' => t('Operations'), 'colspan' => 2), ), - '#attributes' => array('id' => 'field-overview'), + '#parent_options' => array('' => t('<none>')), + '#regions' => array( + 'main' => array(), + 'add_new' => array('title' => ' '), + ), + '#attributes' => array( + 'class' => array('field-ui-overview'), + 'id' => 'field-overview', + ), ); - $parent_options = array('' => t('<none>')); - // Fields. foreach ($instances as $name => $instance) { $field = field_info_field($instance['field_name']); $admin_field_path = $admin_path . '/fields/' . $instance['field_name']; - $weight = $instance['widget']['weight']; - $weights[] = $weight; $table[$name] = array( - '#parents' => array($name), - '#attributes' => array('class' => array('draggable tabledrag-leaf')), + '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), + '#row_type' => 'field', + '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#markup' => check_plain($instance['label']), ), 'weight' => array( '#type' => 'textfield', - '#default_value' => $weight, + '#default_value' => $instance['widget']['weight'], '#size' => 3, '#attributes' => array('class' => array('field-weight')), ), 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', - '#options' => $parent_options, + '#options' => $table['#parent_options'], '#attributes' => array('class' => array('field-parent')), - '#parents' => array($name, 'parent'), + '#parents' => array('fields', $name, 'parent'), ), 'hidden_name' => array( '#type' => 'hidden', @@ -280,17 +383,16 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle // Non-field elements. foreach ($extra_fields as $name => $extra_field) { - $weight = $extra_field['weight']; - $weights[] = $weight; $table[$name] = array( - '#parents' => array($name), - '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'menu-disabled')), + '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), + '#row_type' => 'extra_field', + '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#markup' => check_plain($extra_field['label']), ), 'weight' => array( '#type' => 'textfield', - '#default_value' => $weight, + '#default_value' => $extra_field['weight'], '#size' => 3, '#attributes' => array('class' => array('field-weight')), '#title_display' => 'invisible', @@ -299,9 +401,9 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', - '#options' => $parent_options, + '#options' => $table['#parent_options'], '#attributes' => array('class' => array('field-parent')), - '#parents' => array($name, 'parent'), + '#parents' => array('fields', $name, 'parent'), ), 'hidden_name' => array( '#type' => 'hidden', @@ -326,7 +428,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle } // Additional row: add new field. - $weight = !empty($weights) ? max($weights) + 1 : 0; + $max_weight = field_info_max_weight($entity_type, $bundle, 'form'); $field_type_options = field_ui_field_type_options(); $widget_type_options = field_ui_widget_type_options(NULL, TRUE); if ($field_type_options && $widget_type_options) { @@ -334,8 +436,9 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle array_unshift($widget_type_options, t('- Select a widget -')); $name = '_add_new_field'; $table[$name] = array( - '#parents' => array($name), '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), + '#row_type' => 'add_new_field', + '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#type' => 'textfield', '#size' => 15, @@ -345,7 +448,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'weight' => array( '#type' => 'textfield', - '#default_value' => $weight, + '#default_value' => $max_weight + 1, '#size' => 3, '#title_display' => 'invisible', '#title' => t('Weight for new field'), @@ -355,10 +458,10 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', - '#options' => $parent_options, + '#options' => $table['#parent_options'], '#attributes' => array('class' => array('field-parent')), '#prefix' => '<div class="add-new-placeholder"> </div>', - '#parents' => array($name, 'parent'), + '#parents' => array('fields', $name, 'parent'), ), 'hidden_name' => array( '#type' => 'hidden', @@ -372,7 +475,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle '#field_prefix' => '<span dir="ltr">field_', '#field_suffix' => '</span>‎', '#attributes' => array('dir'=>'ltr'), - '#size' => 15, + '#size' => 10, '#description' => t('Field name (a-z, 0-9, _)'), '#prefix' => '<div class="add-new-placeholder"> </div>', ), @@ -397,12 +500,12 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle // Additional row: add existing field. $existing_field_options = field_ui_existing_field_options($entity_type, $bundle); if ($existing_field_options && $widget_type_options) { - $weight++; array_unshift($existing_field_options, t('- Select an existing field -')); $name = '_add_existing_field'; $table[$name] = array( - '#parents' => array($name), - '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'menu-disabled')), + '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), + '#row_type' => 'add_new_field', + '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#type' => 'textfield', '#size' => 15, @@ -413,7 +516,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'weight' => array( '#type' => 'textfield', - '#default_value' => $weight, + '#default_value' => $max_weight + 2, '#size' => 3, '#title_display' => 'invisible', '#title' => t('Weight for added field'), @@ -423,10 +526,10 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', - '#options' => $parent_options, + '#options' => $table['#parent_options'], '#attributes' => array('class' => array('field-parent')), '#prefix' => '<div class="add-new-placeholder"> </div>', - '#parents' => array($name, 'parent'), + '#parents' => array('fields', $name, 'parent'), ), 'hidden_name' => array( '#type' => 'hidden', @@ -452,14 +555,14 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), ); } - - $form['table'] = $table; + $form['fields'] = $table; $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save')); $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css'; $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js'; + // Add settings for the update selects behavior. $js_fields = array(); foreach ($existing_field_options as $field_name => $fields) { @@ -470,8 +573,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle $form['#attached']['js'][] = array( 'type' => 'setting', - 'data' => array('fields' => $js_fields, 'fieldWidgetTypes' => field_ui_widget_type_options(), - ), + 'data' => array('fields' => $js_fields, 'fieldWidgetTypes' => field_ui_widget_type_options()), ); // Add tabledrag behavior. @@ -495,18 +597,18 @@ function field_ui_field_overview_form_validate($form, &$form_state) { * Validate the 'add new field' row. */ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) { - $field = $form_state['values']['_add_new_field']; + $field = $form_state['values']['fields']['_add_new_field']; // Validate if any information was provided in the 'add new field' row. if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) { // Missing label. if (!$field['label']) { - form_set_error('_add_new_field][label', t('Add new field: you need to provide a label.')); + form_set_error('fields][_add_new_field][label', t('Add new field: you need to provide a label.')); } // Missing field name. if (!$field['field_name']) { - form_set_error('_add_new_field][field_name', t('Add new field: you need to provide a field name.')); + form_set_error('fields][_add_new_field][field_name', t('Add new field: you need to provide a field name.')); } // Field name validation. else { @@ -515,39 +617,39 @@ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) { // Add the 'field_' prefix. if (substr($field_name, 0, 6) != 'field_') { $field_name = 'field_' . $field_name; - form_set_value($form['table']['_add_new_field']['field_name'], $field_name, $form_state); + form_set_value($form['fields']['_add_new_field']['field_name'], $field_name, $form_state); } // Invalid field name. if (!preg_match('!^field_[a-z0-9_]+$!', $field_name)) { - form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name))); + form_set_error('fields][_add_new_field][field_name', t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name))); } if (strlen($field_name) > 32) { - form_set_error('_add_new_field][field_name', t("Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", array('%field_name' => $field_name))); + form_set_error('fields][_add_new_field][field_name', t("Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", array('%field_name' => $field_name))); } // Field name already exists. We need to check inactive fields as well, so // we can't use field_info_fields(). $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE)); if ($fields) { - form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name))); + form_set_error('fields][_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name))); } } // Missing field type. if (!$field['type']) { - form_set_error('_add_new_field][type', t('Add new field: you need to select a field type.')); + form_set_error('fields][_add_new_field][type', t('Add new field: you need to select a field type.')); } // Missing widget type. if (!$field['widget_type']) { - form_set_error('_add_new_field][widget_type', t('Add new field: you need to select a widget.')); + form_set_error('fields][_add_new_field][widget_type', t('Add new field: you need to select a widget.')); } // Wrong widget type. elseif ($field['type']) { $widget_types = field_ui_widget_type_options($field['type']); if (!isset($widget_types[$field['widget_type']])) { - form_set_error('_add_new_field][widget_type', t('Add new field: invalid widget.')); + form_set_error('fields][_add_new_field][widget_type', t('Add new field: invalid widget.')); } } } @@ -561,30 +663,30 @@ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) { function _field_ui_field_overview_form_validate_add_existing($form, &$form_state) { // The form element might be absent if no existing fields can be added to // this bundle. - if (isset($form_state['values']['_add_existing_field'])) { - $field = $form_state['values']['_add_existing_field']; + if (isset($form_state['values']['fields']['_add_existing_field'])) { + $field = $form_state['values']['fields']['_add_existing_field']; // Validate if any information was provided in the 'add existing field' row. if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) { // Missing label. if (!$field['label']) { - form_set_error('_add_existing_field][label', t('Add existing field: you need to provide a label.')); + form_set_error('fields][_add_existing_field][label', t('Add existing field: you need to provide a label.')); } // Missing existing field name. if (!$field['field_name']) { - form_set_error('_add_existing_field][field_name', t('Add existing field: you need to select a field.')); + form_set_error('fields][_add_existing_field][field_name', t('Add existing field: you need to select a field.')); } // Missing widget type. if (!$field['widget_type']) { - form_set_error('_add_existing_field][widget_type', t('Add existing field: you need to select a widget.')); + form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: you need to select a widget.')); } // Wrong widget type. elseif ($field['field_name'] && ($existing_field = field_info_field($field['field_name']))) { $widget_types = field_ui_widget_type_options($existing_field['type']); if (!isset($widget_types[$field['widget_type']])) { - form_set_error('_add_existing_field][widget_type', t('Add existing field: invalid widget.')); + form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: invalid widget.')); } } } @@ -595,7 +697,7 @@ function _field_ui_field_overview_form_validate_add_existing($form, &$form_state * Submit handler for the field overview form. */ function field_ui_field_overview_form_submit($form, &$form_state) { - $form_values = $form_state['values']; + $form_values = $form_state['values']['fields']; $entity_type = $form['#entity_type']; $bundle = $form['#bundle']; $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle); @@ -607,9 +709,6 @@ function field_ui_field_overview_form_submit($form, &$form_state) { if (in_array($key, $form['#fields'])) { $instance = field_read_instance($entity_type, $key, $bundle); $instance['widget']['weight'] = $values['weight']; - foreach($instance['display'] as $view_mode => $display) { - $instance['display'][$view_mode]['weight'] = $values['weight']; - } field_update_instance($instance); } elseif (in_array($key, $form['#extra'])) { @@ -695,6 +794,9 @@ function field_ui_field_overview_form_submit($form, &$form_state) { unset($_GET['destination']); $form_state['redirect'] = field_ui_get_destinations($destinations); } + else { + drupal_set_message(t('Your settings have been saved.')); + } } /** @@ -709,7 +811,11 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund // Gather type information. $instances = field_info_instances($entity_type, $bundle); $field_types = field_info_field_types(); - $extra_fields = field_extra_fields($entity_type, $bundle, 'display'); + $extra_fields = field_info_extra_fields($entity_type, $bundle, 'display'); + + $form_state += array( + 'formatter_settings_edit' => NULL, + ); $form += array( '#entity_type' => $entity_type, @@ -725,9 +831,27 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund } $table = array( - '#theme' => 'field_ui_display_overview_table', - '#field_rows' => array(), + '#type' => 'field_ui_table', '#tree' => TRUE, + '#header' => array( + t('Field'), + t('Weight'), + t('Parent'), + t('Label'), + array('data' => t('Format'), 'colspan' => 3), + ), + '#regions' => array( + 'visible' => array('message' => t('No field is displayed.')), + 'hidden' => array('title' => t('Hidden'), 'message' => t('No field is hidden.')), + ), + '#parent_options' => array('' => t('<none>')), + '#attributes' => array( + 'class' => array('field-ui-overview'), + 'id' => 'field-display-overview', + ), + // Add AJAX wrapper. + '#prefix' => '<div id="field-display-overview-wrapper">', + '#suffix' => '</div>', ); $field_label_options = array( @@ -740,65 +864,213 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund 'hidden' => t('Hidden'), ); + // Field rows. foreach ($instances as $name => $instance) { + $field = field_info_field($instance['field_name']); $display = $instance['display'][$view_mode]; - - $table[$name]['human_name'] = array( - '#markup' => check_plain($instance['label']), - ); - $table[$name]['weight'] = array( - '#type' => 'textfield', - '#default_value' => $display['weight'], - '#size' => 3, - ); - $table[$name]['hidden_name'] = array( - '#type' => 'hidden', - '#default_value' => $name, - ); - $table[$name]['label'] = array( - '#type' => 'select', - '#options' => $field_label_options, - '#default_value' => $display['label'], + $table[$name] = array( + '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), + '#row_type' => 'field', + '#region_callback' => 'field_ui_display_overview_row_region', + '#js_settings' => array( + 'rowHandler' => 'field', + 'defaultFormatter' => $field_types[$field['type']]['default_formatter'], + ), + 'human_name' => array( + '#markup' => check_plain($instance['label']), + ), + 'weight' => array( + '#type' => 'textfield', + '#default_value' => $display['weight'], + '#size' => 3, + '#attributes' => array('class' => array('field-weight')), + ), + 'parent_wrapper' => array( + 'parent' => array( + '#type' => 'select', + '#options' => $table['#parent_options'], + '#default_value' => '', + '#attributes' => array('class' => array('field-parent')), + '#parents' => array('fields', $name, 'parent'), + ), + 'hidden_name' => array( + '#type' => 'hidden', + '#default_value' => $name, + '#attributes' => array('class' => array('field-name')), + ), + ), + 'label' => array( + '#type' => 'select', + '#options' => $field_label_options, + '#default_value' => $display['label'], + ), ); - $field = field_info_field($instance['field_name']); $formatter_options = field_ui_formatter_options($field['type']); $formatter_options['hidden'] = t('<Hidden>'); - $table[$name]['type'] = array( - '#type' => 'select', - '#options' => $formatter_options, - '#default_value' => $display['type'], + $table[$name]['format'] = array( + 'type' => array( + '#type' => 'select', + '#options' => $formatter_options, + '#default_value' => $display['type'], + '#parents' => array('fields', $name, 'type'), + '#attributes' => array('class' => array('field-formatter-type')), + ), + 'settings_edit_form' => array(), + ); + + // Formatter settings. + + // Check the currently selected formatter, and merge persisted values for + // formatter settings. + if (isset($form_state['values']['fields'][$name]['type'])) { + $formatter_type = $form_state['values']['fields'][$name]['type']; + } + else { + $formatter_type = $display['type']; + } + if (isset($form_state['formatter_settings'][$name])) { + $settings = $form_state['formatter_settings'][$name]; + } + else { + $settings = $display['settings']; + } + $settings += field_info_formatter_settings($formatter_type); + + $instance['display'][$view_mode]['type'] = $formatter_type; + $formatter = field_info_formatter_types($formatter_type); + $instance['display'][$view_mode]['module'] = $formatter['module']; + $instance['display'][$view_mode]['settings'] = $settings; + + // Base button element for the various formatter settings actions. + $base_button = array( + '#submit' => array('field_ui_display_overview_multistep_submit'), + '#ajax' => array( + 'callback' => 'field_ui_display_overview_multistep_js', + 'wrapper' => 'field-display-overview-wrapper', + 'effect' => 'fade', + ), + '#field_name' => $name, ); - $table['#field_rows'][] = $name; - // Collect default formatters for the JS script. - $field_type_info = field_info_field_types($field['type']); - $default_formatters[$name] = $field_type_info['default_formatter']; + 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(); + + $settings_form = array(); + $function = $formatter['module'] . '_field_formatter_settings_form'; + if (function_exists($function)) { + $settings_form = $function($field, $instance, $view_mode, $form, $form_state); + } + + if ($settings_form) { + $table[$name]['format']['#cell_attributes'] = array('colspan' => 3); + $table[$name]['format']['settings_edit_form'] = array( + '#type' => 'container', + '#attributes' => array('class' => array('field-formatter-settings-edit-form')), + '#parents' => array('fields', $name, 'settings_edit_form'), + 'label' => array( + '#markup' => t('Format settings:') . ' <span class="formatter-name">' . $formatter['label'] . '</span>', + ), + 'settings' => $settings_form, + 'actions' => array( + '#type' => 'actions', + 'save_settings' => $base_button + array( + '#type' => 'submit', + '#name' => $name . '_formatter_settings_update', + '#value' => t('Update'), + '#op' => 'update', + ), + 'cancel_settings' => $base_button + array( + '#type' => 'submit', + '#name' => $name . '_formatter_settings_cancel', + '#value' => t('Cancel'), + '#op' => 'cancel', + // Do not check errors for the 'Cancel' button, but make sure we + // get the value of the 'formatter type' select. + '#limit_validation_errors' => array(array('fields', $name, 'type')), + ), + ), + ); + $table[$name]['#attributes']['class'][] = 'field-formatter-settings-editing'; + } + } + else { + // Display a summary of the current formatter settings. + $summary = module_invoke($formatter['module'], 'field_formatter_settings_summary', $field, $instance, $view_mode); + $table[$name]['settings_summary'] = array(); + $table[$name]['settings_edit'] = array(); + if ($summary) { + $table[$name]['settings_summary'] = array( + '#markup' => '<div class="field-formatter-summary">' . $summary . '</div>', + '#cell_attributes' => array('class' => array('field-formatter-summary-cell')), + ); + $table[$name]['settings_edit'] = $base_button + array( + '#type' => 'image_button', + '#name' => $name . '_formatter_settings_edit', + '#src' => 'misc/configure.png', + '#attributes' => array('class' => array('field-formatter-settings-edit'), 'alt' => t('Edit')), + '#op' => 'edit', + // Do not check errors for the 'Edit' button, but make sure we get + // the value of the 'formatter type' select. + '#limit_validation_errors' => array(array('fields', $name, 'type')), + '#prefix' => '<div class="field-formatter-settings-edit-wrapper">', + '#suffix' => '</div>', + ); + } + } } // Non-field elements. foreach ($extra_fields as $name => $extra_field) { $display = $extra_field['display'][$view_mode]; - $table[$name]['human_name'] = array( - '#markup' => check_plain($extra_field['label']), - ); - $table[$name]['weight'] = array( - '#type' => 'textfield', - '#default_value' => $display['weight'], - '#size' => 3, - ); - $table[$name]['hidden_name'] = array( - '#type' => 'hidden', - '#default_value' => $name, - ); - $table[$name]['type'] = array( - '#type' => 'select', - '#options' => $extra_visibility_options, - '#default_value' => $display['visible'] ? 'visible' : 'hidden', + $table[$name] = array( + '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), + '#row_type' => 'extra_field', + '#region_callback' => 'field_ui_display_overview_row_region', + '#js_settings' => array('rowHandler' => 'field'), + 'human_name' => array( + '#markup' => check_plain($extra_field['label']), + ), + 'weight' => array( + '#type' => 'textfield', + '#default_value' => $display['weight'], + '#size' => 3, + '#attributes' => array('class' => array('field-weight')), + ), + 'parent_wrapper' => array( + 'parent' => array( + '#type' => 'select', + '#options' => $table['#parent_options'], + '#default_value' => '', + '#attributes' => array('class' => array('field-parent')), + '#parents' => array('fields', $name, 'parent'), + ), + 'hidden_name' => array( + '#type' => 'hidden', + '#default_value' => $name, + '#attributes' => array('class' => array('field-name')), + ), + ), + 'empty_cell' => array( + '#markup' => ' ', + ), + 'format' => array( + 'type' => array( + '#type' => 'select', + '#options' => $extra_visibility_options, + '#default_value' => $display['visible'] ? 'visible' : 'hidden', + '#parents' => array('fields', $name, 'type'), + '#attributes' => array('class' => array('field-formatter-type')), + ), + ), + 'settings_summary' => array(), + 'settings_edit' => array(), ); - $table['#field_rows'][] = $name; } - $form['settings'] = $table; + + $form['fields'] = $table; // Custom display settings. if ($view_mode == 'default') { @@ -829,60 +1101,124 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund ); } + // In overviews involving nested rows from contributed modules (i.e + // field_group), the 'format type' selects can trigger a series of changes in + // child rows. The #ajax behavior is therefore not attached directly to the + // selects, but triggered by the client-side script through a hidden #ajax + // 'Refresh' button. A hidden 'refresh_rows' input tracks the name of + // affected rows. + $form['refresh_rows'] = array('#type' => 'hidden'); + $form['refresh'] = array( + '#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('refresh_rows'), + '#submit' => array('field_ui_display_overview_multistep_submit'), + '#ajax' => array( + 'callback' => 'field_ui_display_overview_multistep_js', + 'wrapper' => 'field-display-overview-wrapper', + 'effect' => 'fade', + // The button stays hidden, so we hide the AJAX spinner too. Ad-hoc + // spinners will be added manually by the client-side script. + 'progress' => 'none', + ), + ); + $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save')); $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js'; $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css'; - drupal_add_js(array('fieldDefaultFormatters' => $default_formatters), 'setting'); + + // 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'); return $form; } + /** - * Theme preprocess function for field_ui-display-overview-table.tpl.php. + * Form submit handler for multistep buttons on the 'Manage display' screen. */ -function template_preprocess_field_ui_display_overview_table(&$vars) { - $elements = &$vars['elements']; +function field_ui_display_overview_multistep_submit($form, &$form_state) { + $trigger = $form_state['triggering_element']; + $op = $trigger['#op']; + + switch ($op) { + case 'edit': + // Store the field whose settings are currently being edited. + $field_name = $trigger['#field_name']; + $form_state['formatter_settings_edit'] = $field_name; + break; + + case 'update': + // Store the saved settings, and set the field back to 'non edit' mode. + $field_name = $trigger['#field_name']; + $values = $form_state['values']['fields'][$field_name]['settings_edit_form']['settings']; + $form_state['formatter_settings'][$field_name] = $values; + unset($form_state['formatter_settings_edit']); + break; + + case 'cancel': + // Set the field back to 'non edit' mode. + unset($form_state['formatter_settings_edit']); + break; + + case 'refresh_table': + // If the currently edited field is one of the rows to be refreshed, set + // it back to 'non edit' mode. + $updated_rows = explode(' ', $form_state['values']['refresh_rows']); + if (isset($form_state['formatter_settings_edit']) && in_array($form_state['formatter_settings_edit'], $updated_rows)) { + unset($form_state['formatter_settings_edit']); + } + break; + } - $rows = array( - 'visible' => array(), - 'hidden' => array(), - ); + $form_state['rebuild'] = TRUE; +} - if (!empty($elements['#field_rows'])) { - drupal_add_tabledrag('field-display-overview', 'order', 'sibling', 'field-weight'); - - $order = _field_ui_overview_order($elements, $elements['#field_rows']); - foreach ($order as $key) { - $element = &$elements[$key]; - $visibility = $element['type']['#value'] == 'hidden' ? 'hidden' : 'visible'; - - // Add target classes for the tabledrag behavior. - $element['weight']['#attributes']['class'][] = 'field-weight'; - $element['hidden_name']['#attributes']['class'][] = 'field-name'; - $element['type']['#attributes']['class'][] = 'field-formatter-type'; - $element['type']['#attributes']['class'][] = "field-display-$visibility"; - $element['type']['#attributes']['class'][] = "field-name-$key"; - - $row = new stdClass(); - foreach (element_children($element) as $child) { - if (array_key_exists('label', $element[$child])) { - $row->{$child} = new stdClass(); - $row->{$child}->label = drupal_render($element[$child]['label']); - $row->{$child}->type = drupal_render($element[$child]['type']); - } - else { - $row->{$child} = drupal_render($element[$child]); - } - } - $row->class = 'draggable'; - $row->label_class = 'label-field'; - $rows[$visibility][] = $row; +/** + * AJAX handler for multistep buttons on the 'Manage display' screen. + */ +function field_ui_display_overview_multistep_js($form, &$form_state) { + $trigger = $form_state['triggering_element']; + $op = $trigger['#op']; + + // Pick the elements that need ro receive the ajax-new-content effect. + switch ($op) { + case 'edit': + $updated_rows = array($trigger['#field_name']); + $updated_columns = array('format'); + break; + + case 'update': + case 'cancel': + $updated_rows = array($trigger['#field_name']); + $updated_columns = array('format', 'settings_summary', 'settings_edit'); + break; + + case 'refresh_table': + $updated_rows = array_values(explode(' ', $form_state['values']['refresh_rows'])); + $updated_columns = array('settings_summary', 'settings_edit'); + break; + } + + foreach ($updated_rows as $name) { + foreach ($updated_columns as $key) { + $element = &$form['fields'][$name][$key]; + $element['#prefix'] = '<div class="ajax-new-content">' . (isset($element['#prefix']) ? $element['#prefix'] : ''); + $element['#suffix'] = (isset($element['#suffix']) ? $element['#suffix'] : '') . '</div>'; } } - $vars['rows'] = $rows; + // Return the whole table. + return $form['fields']; } /** @@ -897,7 +1233,29 @@ function field_ui_display_overview_form_submit($form, &$form_state) { // Save data for 'regular' fields. foreach ($form['#fields'] as $field_name) { $instance = field_info_instance($entity_type, $field_name, $bundle); - $instance['display'][$view_mode] = $form_values['settings'][$field_name]; + $values = $form_values['fields'][$field_name]; + // Get formatter settings. They lie either directly in submitted form + // values (if the whole form was submitted while some formatter + // settings were being edited), or have been persisted in + // $form_state. + $settings = $instance['display'][$view_mode]['settings']; + if (isset($values['settings_edit_form']['settings'])) { + $settings = $values['settings_edit_form']['settings']; + } + elseif (isset($form_state['formatter_settings'][$field_name])) { + $settings = $form_state['formatter_settings'][$field_name]; + } + + // Only save settings actually used by the selected formatter. + $default_settings = field_info_formatter_settings($values['type']); + $settings = array_intersect_key($settings, $default_settings); + + $instance['display'][$view_mode] = array( + 'label' => $values['label'], + 'type' => $values['type'], + 'weight' => $values['weight'], + 'settings' => $settings, + ); field_update_instance($instance); } @@ -907,8 +1265,8 @@ function field_ui_display_overview_form_submit($form, &$form_state) { // Save data for 'extra' fields. foreach ($form['#extra'] as $name) { $bundle_settings['extra_fields']['display'][$name][$view_mode] = array( - 'weight' => $form_values['settings'][$name]['weight'], - 'visible' => $form_values['settings'][$name]['type'] == 'visible', + 'weight' => $form_values['fields'][$name]['weight'], + 'visible' => $form_values['fields'][$name]['type'] == 'visible', ); } @@ -1546,25 +1904,6 @@ function field_ui_next_destination($entity_type, $bundle) { return $admin_path . '/fields'; } -/** - * Helper function to order fields when theming overview forms. - * @todo Remove when 'Manage display' screen is done. - */ -function _field_ui_overview_order(&$form, $field_rows) { - // Put weight and parenting values into a $dummy render structure and let - // drupal_render() figure out the corresponding row order. - $dummy = array(); - // Field rows: account for weight. - foreach ($field_rows as $name) { - $dummy[$name] = array( - '#markup' => $name . ' ', - '#type' => 'markup', - '#weight' => $form[$name]['weight']['#value'], - ); - } - return $dummy ? explode(' ', trim(drupal_render($dummy))) : array(); -} - /** * Helper form element validator: integer. */ diff --git a/modules/field_ui/field_ui.api.php b/modules/field_ui/field_ui.api.php index f5ed10256ad1d26fc3cb4cd1bc4224c5ecefe1dc..c8e09918a7196d7ccccb9788f0d1960e5f2d1a73 100644 --- a/modules/field_ui/field_ui.api.php +++ b/modules/field_ui/field_ui.api.php @@ -1,5 +1,5 @@ <?php -// $Id: field_ui.api.php,v 1.6 2010/05/04 16:11:08 dries Exp $ +// $Id: field_ui.api.php,v 1.8 2010/07/17 19:19:39 dries Exp $ /** * @file @@ -131,41 +131,69 @@ function hook_field_widget_settings_form($field, $instance) { return $form; } + /** - * Provide information on view mode tabs for an entity type. + * Returns form elements for a formatter's settings. * - * @param $entity_type - * The type of entity to return tabs for. + * @param $field + * The field structure being configured. + * @param $instance + * The instance structure being configured. + * @param $view_mode + * The view mode being configured. + * @param $form + * The (entire) configuration form array, which will usually have no use here. + * @param $form_state + * The form state of the (entire) configuration form. * * @return - * An array whose keys are internal-use tab names, and whose values are - * arrays of tab information, with the following elements: - * - 'title': Human-readable title of the tab. - * - 'view modes': Array of view modes for this entity type that should - * be displayed on this tab. + * The form elements for the formatter settings. + */ +function hook_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $element = array(); + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $element['trim_length'] = array( + '#title' => t('Length'), + '#type' => 'textfield', + '#size' => 20, + '#default_value' => $settings['trim_length'], + '#element_validate' => array('_element_validate_integer_positive'), + '#required' => TRUE, + ); + } + + return $element; + +} + +/** + * Returns a short summary for the current formatter settings of an instance. + * + * @param $field + * The field structure. + * @param $instance + * The instance structure. + * @param $view_mode + * The view mode for which a settings summary is requested. * - * @see field_ui_view_modes_tabs() + * @return + * A string containing a short summary of the formatter settings. */ -function hook_field_ui_view_modes_tabs($entity_type) { - $modes = array( - 'basic' => array( - 'title' => t('Basic'), - 'view modes' => array('teaser', 'full'), - ), - 'rss' => array( - 'title' => t('RSS'), - 'view modes' => array('rss'), - ), - 'print' => array( - 'title' => t('Print'), - 'view modes' => array('print'), - ), - 'search' => array( - 'title' => t('Search'), - 'view modes' => array('search_index', 'search_result'), - ), - ); - return $modes; +function hook_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = ''; + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $summary = t('Length: @chars chars', array('@chars' => $settings['trim_length'])); + } + + return $summary; } /** diff --git a/modules/field_ui/field_ui.css b/modules/field_ui/field_ui.css index ff44e878d4279b5127e6b0c60eab7fe1bb791910..3a61509d912ee9b5e397eb02660f462cc99cb28a 100644 --- a/modules/field_ui/field_ui.css +++ b/modules/field_ui/field_ui.css @@ -1,24 +1,60 @@ -/* $Id: field_ui.css,v 1.3 2010/06/26 02:06:53 dries Exp $ */ +/* $Id: field_ui.css,v 1.5 2010/09/11 00:03:42 webchick Exp $ */ -/* 'Manage fields' overview */ -#field-overview tr.add-new .label-input { +/* 'Manage fields' and 'Manage display' overviews */ +table.field-ui-overview tr.add-new .label-input { float: left; /* LTR */ } -#field-overview tr.add-new .tabledrag-changed { +table.field-ui-overview tr.add-new .tabledrag-changed { display: none; } -#field-overview tr.add-new .description { +table.field-ui-overview tr.add-new .description { margin-bottom: 0; } -#field-overview tr.add-new .add-new-placeholder { +table.field-ui-overview tr.add-new .add-new-placeholder { font-weight: bold; padding-bottom: .5em; } +table.field-ui-overview tr.region-add-new-title { + display: none; +} -/* Manage display */ -.field-display-overview tr.region-title td { +/* 'Manage display' overview */ +#field-display-overview tr.region-title td { font-weight: bold; } -.field-display-overview tr.region-populated { +#field-display-overview tr.region-message td { + font-style: italic; +} +#field-display-overview tr.region-populated { + display: none; +} +#field-display-overview .field-formatter-summary-cell { + line-height: 1em; +} +#field-display-overview .field-formatter-summary { + float: left; + font-size: 0.9em; +} +#field-display-overview td.field-formatter-summary-cell span.warning { + display: block; + float: left; + margin-right: .5em; +} +#field-display-overview .field-formatter-settings-edit-wrapper { + float: right; +} +#field-display-overview .field-formatter-settings-edit { + float: right; +} +#field-display-overview tr.field-formatter-settings-editing td { + vertical-align: top; +} +#field-display-overview tr.field-formatter-settings-editing .field-formatter-type { display: none; } +#field-display-overview .field-formatter-settings-edit-form .formatter-name{ + font-weight: bold; +} +#field-ui-display-overview-form #edit-refresh { + display:none; +} diff --git a/modules/field_ui/field_ui.info b/modules/field_ui/field_ui.info index 54685b02792e12cb7d847eb941cee2a61108939a..deefc225d69e0944f2336bafc94376a8864cfa5c 100644 --- a/modules/field_ui/field_ui.info +++ b/modules/field_ui/field_ui.info @@ -1,15 +1,16 @@ -; $Id: field_ui.info,v 1.3 2010/06/25 18:51:05 dries Exp $ +; $Id: field_ui.info,v 1.4 2010/08/16 20:57:23 dries Exp $ name = Field UI description = User interface for the Field API. package = Core version = VERSION core = 7.x +dependencies[] = field files[] = field_ui.module files[] = field_ui.admin.inc files[] = field_ui.test -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/field_ui/field_ui.js b/modules/field_ui/field_ui.js index 8ab363fada785a16f07f2ef96ad8e5413be2174c..e02d035d8bed82c1947452a1e399c2d4f3c68f46 100644 --- a/modules/field_ui/field_ui.js +++ b/modules/field_ui/field_ui.js @@ -1,62 +1,72 @@ -// $Id: field_ui.js,v 1.3 2010/05/23 19:10:23 dries Exp $ +// $Id: field_ui.js,v 1.5 2010/09/11 00:03:42 webchick Exp $ (function($) { -Drupal.behaviors.fieldManageFields = { - attach: function (context) { - attachUpdateSelects(context); +Drupal.behaviors.fieldUIFieldOverview = { + attach: function (context, settings) { + $('table#field-overview', context).once('field-overview', function () { + Drupal.fieldUIFieldOverview.attachUpdateSelects(this, settings); + }); } }; -function attachUpdateSelects(context) { - var widgetTypes = Drupal.settings.fieldWidgetTypes; - var fields = Drupal.settings.fields; +Drupal.fieldUIFieldOverview = { + /** + * Implements dependent select dropdowns on the 'Manage fields' screen. + */ + attachUpdateSelects: function(table, settings) { + var widgetTypes = settings.fieldWidgetTypes; + var fields = settings.fields; + + // Store the default text of widget selects. + $('.widget-type-select', table).each(function () { + this.initialValue = this.options[0].text; + }); - // Store the default text of widget selects. - $('#field-overview .widget-type-select', context).each(function () { - this.initialValue = this.options[0].text; - }); + // 'Field type' select updates its 'Widget' select. + $('.field-type-select', table).each(function () { + this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); - // 'Field type' select updates its 'Widget' select. - $('#field-overview .field-type-select', context).each(function () { - this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); + $(this).bind('change keyup', function () { + var selectedFieldType = this.options[this.selectedIndex].value; + var options = (selectedFieldType in widgetTypes ? widgetTypes[selectedFieldType] : []); + this.targetSelect.fieldUIPopulateOptions(options); + }); - $(this).bind('change keyup', function () { - var selectedFieldType = this.options[this.selectedIndex].value; - var options = (selectedFieldType in widgetTypes ? widgetTypes[selectedFieldType] : []); - this.targetSelect.fieldPopulateOptions(options); + // Trigger change on initial pageload to get the right widget options + // when field type comes pre-selected (on failed validation). + $(this).trigger('change', false); }); - // Trigger change on initial pageload to get the right widget options - // when field type comes pre-selected (on failed validation). - $(this).trigger('change', false); - }); + // 'Existing field' select updates its 'Widget' select and 'Label' textfield. + $('.field-select', table).each(function () { + this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); + this.targetTextfield = $('.label-textfield', $(this).parents('tr').eq(0)); + + $(this).bind('change keyup', function (e, updateText) { + var updateText = (typeof updateText == 'undefined' ? true : updateText); + var selectedField = this.options[this.selectedIndex].value; + var selectedFieldType = (selectedField in fields ? fields[selectedField].type : null); + var selectedFieldWidget = (selectedField in fields ? fields[selectedField].widget : null); + var options = (selectedFieldType && (selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : []); + this.targetSelect.fieldUIPopulateOptions(options, selectedFieldWidget); + + if (updateText) { + $(this.targetTextfield).attr('value', (selectedField in fields ? fields[selectedField].label : '')); + } + }); - // 'Existing field' select updates its 'Widget' select and 'Label' textfield. - $('#field-overview .field-select', context).each(function () { - this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); - this.targetTextfield = $('.label-textfield', $(this).parents('tr').eq(0)); - - $(this).bind('change keyup', function (e, updateText) { - var updateText = (typeof updateText == 'undefined' ? true : updateText); - var selectedField = this.options[this.selectedIndex].value; - var selectedFieldType = (selectedField in fields ? fields[selectedField].type : null); - var selectedFieldWidget = (selectedField in fields ? fields[selectedField].widget : null); - var options = (selectedFieldType && (selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : []); - this.targetSelect.fieldPopulateOptions(options, selectedFieldWidget); - - if (updateText) { - $(this.targetTextfield).attr('value', (selectedField in fields ? fields[selectedField].label : '')); - } + // Trigger change on initial pageload to get the right widget options + // and label when field type comes pre-selected (on failed validation). + $(this).trigger('change', false); }); + }, +}; - // Trigger change on initial pageload to get the right widget options - // and label when field type comes pre-selected (on failed validation). - $(this).trigger('change', false); - }); -} - -jQuery.fn.fieldPopulateOptions = function (options, selected) { +/** + * Populates options in a select input. + */ +jQuery.fn.fieldUIPopulateOptions = function (options, selected) { return this.each(function () { var disabled = false; if (options.length == 0) { @@ -81,143 +91,239 @@ jQuery.fn.fieldPopulateOptions = function (options, selected) { }); }; -/** - * Moves a field in the display settings table from visible to hidden. - * - * This behavior is dependent on the tableDrag behavior, since it uses the - * objects initialized in that behavior to update the row. - */ -Drupal.behaviors.fieldManageDisplayDrag = { +Drupal.behaviors.fieldUIDisplayOverview = { attach: function (context, settings) { - // tableDrag is required for this behavior. - if (!$('table.field-display-overview', context).length || typeof Drupal.tableDrag == 'undefined') { - return; + $('table#field-display-overview', context).once('field-display-overview', function() { + Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIDisplayOverview); + }); + } +}; + +Drupal.fieldUIOverview = { + /** + * Attaches the fieldUIOverview behavior. + */ + attach: function (table, rowsData, rowHandlers) { + var tableDrag = Drupal.tableDrag[table.id]; + + // Add custom tabledrag callbacks. + tableDrag.onDrop = this.onDrop; + tableDrag.row.prototype.onSwap = this.onSwap; + + // Create row handlers. + $('tr.draggable', table).each(function () { + // Extract server-side data for the row. + var row = this; + var data = rowsData[row.id]; + data.tableDrag = tableDrag; + + // Create the row handler, make it accessible from the DOM row element. + var rowHandler = eval('new rowHandlers.' + data.rowHandler + '(row, data);'); + $(row).data('fieldUIRowHandler', rowHandler); + }); + }, + + /** + * Event handler to be attached to form inputs triggering a region change. + */ + onChange: function () { + var $trigger = $(this); + var row = $trigger.parents('tr:first').get(0); + var rowHandler = $(row).data('fieldUIRowHandler'); + + var refreshRows = {}; + refreshRows[rowHandler.name] = $trigger.get(0); + + // Handle region change. + var region = rowHandler.getRegion(); + if (region != rowHandler.region) { + // Remove parenting. + $('select.field-parent', row).val(''); + // Let the row handler deal with the region change. + $.extend(refreshRows, rowHandler.regionChange(region)); + // Update the row region. + rowHandler.region = region; } - var defaultFormatters = Drupal.settings.fieldDefaultFormatters; - var tableDrag = Drupal.tableDrag['field-display-overview']; - - // Add a handler for when a row is swapped, update empty regions. - tableDrag.row.prototype.onSwap = function (swappedRow) { - checkEmptyRegions(this.table, this); - }; - - // Add a handler to update the formatter selector when a row is dropped in - // or out of the 'Hidden' section. - tableDrag.onDrop = function () { - var dragObject = this; - var regionRow = $(dragObject.rowObject.element).prevAll('tr.region-message').get(0); - var visibility = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); - - // Update the 'format' selector if the visibility changed. - var $select = $('select.field-formatter-type', dragObject.rowObject.element); - var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); - if (visibility != oldVisibility) { - $select.removeClass('field-display-' + oldVisibility).addClass('field-display-' + visibility); - - // Update the selected formatter if coming from an actual drag. - if (!$select.data('noUpdate')) { - if (visibility == 'visible') { - // Restore the formatter back to the previously selected one if - // available, or to the default formatter. - var value = $select.data('oldFormatter'); - if (typeof value == 'undefined') { - // Extract field name from the name of the select. - var fieldName = $select[0].className.match(/\bfield-name-(\S+)\b/)[1].replace('-', '_'); - // Pseudo-fields do not have an entry in the defaultFormatters - // array, we just return to 'visible' for those. - value = (fieldName in defaultFormatters) ? defaultFormatters[fieldName] : 'visible'; - } - $select.data('oldFormatter', value); - } - else { - var value = 'hidden'; - } - $select.val(value); + // Ajax-update the rows. + Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows); + }, + + /** + * Lets row handlers react when a row is dropped into a new region. + */ + onDrop: function () { + var dragObject = this; + var row = dragObject.rowObject.element; + var rowHandler = $(row).data('fieldUIRowHandler'); + + var regionRow = $(row).prevAll('tr.region-message').get(0); + var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); + + if (region != rowHandler.region) { + // Let the row handler deal with the region change. + refreshRows = rowHandler.regionChange(region); + // Update the row region. + rowHandler.region = region; + // AJAX-update the rows. + Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows); + } + }, + + /** + * Refreshes placeholder rows in empty regions while a row is being dragged. + * + * Copied from block.js. + * + * @param table + * The table DOM element. + * @param rowObject + * The tableDrag rowObject for the row being dragged. + */ + onSwap: function (draggedRow) { + var rowObject = this; + $('tr.region-message', rowObject.table).each(function () { + // If the dragged row is in this region, but above the message row, swap + // it down one space. + if ($(this).prev('tr').get(0) == rowObject.group[rowObject.group.length - 1]) { + // Prevent a recursion problem when using the keyboard to move rows up. + if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) { + rowObject.swap('after', this); } - $select.removeData('noUpdate'); } - }; - - // Add the behavior to each formatter select list. - $('select.field-formatter-type', context).once('field-formatter-type', function () { - // Initialize 'previously selected formatter' as the incoming value. - if ($(this).val() != 'hidden') { - $(this).data('oldFormatter', $(this).val()); + // This region has become empty. + if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) { + $(this).removeClass('region-populated').addClass('region-empty'); } + // This region has become populated. + else if ($(this).is('.region-empty')) { + $(this).removeClass('region-empty').addClass('region-populated'); + } + }); + }, + + /** + * Triigers AJAX refresh of selected rows. + * + * The 'format type' selects can trigger a series of changes in child rows. + * The #ajax behavior is therefore not attached directly to the selects, but + * triggered manually through a hidden #ajax 'Refresh' button. + * + * @param rows + * A hash object, whose keys are the names of the rows to refresh (they + * will receive the 'ajax-new-content' effect on the server side), and + * whose values are the DOM element in the row that should get an AJAX + * throbber. + */ + AJAXRefreshRows: function (rows) { + // Separate keys and values. + var rowNames = []; + var ajaxElements = []; + $.each(rows, function (rowName, ajaxElement) { + rowNames.push(rowName); + ajaxElements.push(ajaxElement); + }); - // Add change listener. - $(this).change(function (event) { - var $select = $(this); - var value = $select.val(); + if (rowNames.length) { + // Add a throbber next each of the ajaxElements. + var $throbber = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); + $(ajaxElements) + .addClass('progress-disabled') + .after($throbber); - // Keep track of the last selected formatter. - if (value != 'hidden') { - $select.data('oldFormatter', value); - } + // Fire the AJAX update. + $('input[name=refresh_rows]').val(rowNames.join(' ')); + $('input#edit-refresh').mousedown(); - var visibility = (value == 'hidden') ? 'hidden' : 'visible'; - var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); - if (visibility != oldVisibility) { - // Prevent the onDrop handler from overriding the selected option. - $select.data('noUpdate', true); - - // Make our new row and select field. - var $row = $(this).parents('tr:first'); - var $table = $(this).parents('table'); - var tableDrag = Drupal.tableDrag[$table.attr('id')]; - tableDrag.rowObject = new tableDrag.row($row); - - // Move the row at the bottom of the new section. - if (visibility == 'hidden') { - $('tr:last', tableDrag.table).after($row); - } - else { - $('tr.region-title-hidden', tableDrag.table).before($row); - } - - // Manually update weights and restripe. - tableDrag.updateFields($row.get(0)); - tableDrag.rowObject.changed = true; - if (tableDrag.oldRowElement) { - $(tableDrag.oldRowElement).removeClass('drag-previous'); - } - tableDrag.oldRowElement = $row.get(0); - tableDrag.restripeTable(); - tableDrag.rowObject.markChanged(); - tableDrag.oldRowElement = $row; - $row.addClass('drag-previous'); - - // Modify empty regions with added or removed fields. - checkEmptyRegions($table, tableDrag.rowObject); - } + // Disabled elements do not appear in POST ajax data, so we mark the + // elements disabled only after firing the request. + $(ajaxElements).attr('disabled', true); + } + }, +}; - // Remove focus from selectbox. - $select.get(0).blur(); - }); - }); - var checkEmptyRegions = function ($table, rowObject) { - $('tr.region-message', $table).each(function () { - // If the dragged row is in this region, but above the message row, swap - // it down one space. - if ($(this).prev('tr').get(0) == rowObject.element) { - // Prevent a recursion problem when using the keyboard to move rows up. - if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) { - rowObject.swap('after', this); - } - } - // This region has become empty. - if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) { - $(this).removeClass('region-populated').addClass('region-empty'); - } - // This region has become populated. - else if ($(this).is('.region-empty')) { - $(this).removeClass('region-empty').addClass('region-populated'); +/** + * Row handlers for the 'Manage display' screen. + */ +Drupal.fieldUIDisplayOverview = {}; + +/** + * Constructor for a 'field' row handler. + * + * This handler is used for both fields and 'extra fields' rows. + * + * @param row + * The row DOM element. + * @param data + * Additional data to be populated in the constructed object. + */ +Drupal.fieldUIDisplayOverview.field = function (row, data) { + this.row = row; + this.name = data.name; + this.region = data.region; + this.tableDrag = data.tableDrag; + + // Attach change listener to the 'formatter type' select. + this.$formatSelect = $('select.field-formatter-type', row); + this.$formatSelect.change(Drupal.fieldUIOverview.onChange); + + return this; +}; + +Drupal.fieldUIDisplayOverview.field.prototype = { + /** + * Returns the region corresponding to the current form values of the row. + */ + getRegion: function () { + return (this.$formatSelect.val() == 'hidden') ? 'hidden' : 'visible'; + }, + + /** + * Reacts to a row being changed regions. + * + * This function is called when the row is moved to a different region, as a + * result of either : + * - a drag-and-drop action (the row's form elements then probably need to be + * updated accordingly) + * - user input in one of the form elements watched by the + * Drupal.fieldUIOverview.onChange change listener. + * + * @param region + * The name of the new region for the row. + * @return + * A hash object indicating which rows should be AJAX-updated as a result + * of the change, in the format expected by + * Drupal.displayOverview.AJAXRefreshRows(). + */ + regionChange: function (region) { + $.debug(this.region + ' --> ' + region, this.name); + + // When triggered by a row drag, the 'format' select needs to be adjusted + // to the new region. + var currentValue = this.$formatSelect.val(); + switch (region) { + case 'visible': + if (currentValue == 'hidden') { + // Restore the formatter back to the default formatter. Pseudo-fields do + // not have default formatters, we just return to 'visible' for those. + var value = (this.defaultFormatter != undefined) ? this.defaultFormatter : 'visible'; } - }); - }; - } + break; + + default: + var value = 'hidden'; + break; + } + if (value != undefined) { + this.$formatSelect.val(value); + } + + var refreshRows = {}; + refreshRows[this.name] = this.$formatSelect.get(0); + + return refreshRows; + }, }; })(jQuery); diff --git a/modules/field_ui/field_ui.module b/modules/field_ui/field_ui.module index c3f3296c4e0a23168947e6a84003ab69cf60f31d..48690a5c83e79e35c94d2a654875ee28144341f6 100644 --- a/modules/field_ui/field_ui.module +++ b/modules/field_ui/field_ui.module @@ -1,5 +1,5 @@ <?php -// $Id: field_ui.module,v 1.31 2010/06/26 02:06:53 dries Exp $ +// $Id: field_ui.module,v 1.32 2010/09/11 00:03:42 webchick Exp $ /** * @file @@ -275,11 +275,6 @@ function _field_ui_view_mode_menu_access($entity_type, $bundle, $view_mode, $acc */ function field_ui_theme() { return array( - 'field_ui_display_overview_table' => array( - 'render element' => 'elements', - 'file' => 'field_ui.admin.inc', - 'template' => 'field_ui-display-overview-table', - ), 'field_ui_table' => array( 'render element' => 'elements', ), @@ -291,8 +286,10 @@ function field_ui_theme() { */ function field_ui_element_info() { return array( - 'table' => array( + 'field_ui_table' => array( '#theme' => 'field_ui_table', + '#pre_render' => array('field_ui_table_pre_render'), + '#regions' => array('' => array()), ), ); } diff --git a/modules/field_ui/field_ui.test b/modules/field_ui/field_ui.test index 1fb5e48b581bc58cb5fe5d9f4cdb54054aa58c0c..715fce4c7580742995cab35109ba9b3a914bb909 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.18 2010/06/27 18:05:54 webchick Exp $ +// $Id: field_ui.test,v 1.22 2010/09/11 00:03:42 webchick Exp $ /** * @file @@ -85,8 +85,8 @@ class FieldUITestCase extends DrupalWebTestCase { function createField() { // Create a test field. $edit = array( - '_add_new_field[label]' => $this->field_label, - '_add_new_field[field_name]' => $this->field_name_input, + 'fields[_add_new_field][label]' => $this->field_label, + 'fields[_add_new_field][field_name]' => $this->field_name_input, ); $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->hyphen_type, $edit); @@ -95,7 +95,7 @@ class FieldUITestCase extends DrupalWebTestCase { // should also appear in the 'taxonomy term' entity. $vocabulary = taxonomy_vocabulary_load(1); $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/fields'); - $this->assertTrue($this->xpath('//select[@name="_add_existing_field[field_name]"]//option[@value="' . $this->field_name . '"]'), t('Existing field was found in account settings.')); + $this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), t('Existing field was found in account settings.')); } /** @@ -132,12 +132,12 @@ class FieldUITestCase extends DrupalWebTestCase { // Check that the list of options respects entity type restrictions on // fields. The 'comment' field is restricted to the 'comment' entity type // and should not appear in the list. - $this->assertFalse($this->xpath('//select[@id="edit--add-existing-field-field-name"]//option[@value="comment"]'), t('The list of options respects entity type restrictions.')); + $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), t('The list of options respects entity type restrictions.')); // Add a new field based on an existing field. $edit = array( - '_add_existing_field[label]' => $this->field_label . '_2', - '_add_existing_field[field_name]' => $this->field_name, + 'fields[_add_existing_field][label]' => $this->field_label . '_2', + 'fields[_add_existing_field][field_name]' => $this->field_name, ); $this->fieldUIAddExistingField("admin/structure/types/manage/page", $edit); } @@ -224,8 +224,8 @@ class FieldUITestCase extends DrupalWebTestCase { // Create a new field. $bundle_path1 = 'admin/structure/types/manage/' . $this->hyphen_type; $edit1 = array( - '_add_new_field[label]' => $this->field_label, - '_add_new_field[field_name]' => $this->field_name, + 'fields[_add_new_field][label]' => $this->field_label, + 'fields[_add_new_field][field_name]' => $this->field_name, ); $this->fieldUIAddNewField($bundle_path1, $edit1); @@ -238,8 +238,8 @@ class FieldUITestCase extends DrupalWebTestCase { // Add an instance to the second node type. $bundle_path2 = 'admin/structure/types/manage/' . $hyphen_type2; $edit2 = array( - '_add_existing_field[label]' => $this->field_label, - '_add_existing_field[field_name]' => $this->field_name, + 'fields[_add_existing_field][label]' => $this->field_label, + 'fields[_add_existing_field][field_name]' => $this->field_name, ); $this->fieldUIAddExistingField($bundle_path2, $edit2); @@ -272,7 +272,7 @@ class FieldUITestCase extends DrupalWebTestCase { // Check that the field type is not available in the 'add new field' row. $this->drupalGet($bundle_path); - $this->assertFalse($this->xpath('//select[@id="edit--add-new-field-type"]//option[@value="hidden_test_field"]'), t("The 'add new field' select respects field types 'no_ui' property.")); + $this->assertFalse($this->xpath('//select[@id="edit-add-new-field-type"]//option[@value="hidden_test_field"]'), t("The 'add new field' select respects field types 'no_ui' property.")); // Create a field and an instance programmatically. $field_name = 'hidden_test_field'; @@ -296,7 +296,7 @@ class FieldUITestCase extends DrupalWebTestCase { // on other bundles. $bundle_path = 'admin/structure/types/manage/article/fields/'; $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.")); + $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.")); } /** @@ -317,11 +317,11 @@ class FieldUITestCase extends DrupalWebTestCase { function fieldUIAddNewField($bundle_path, $initial_edit, $field_edit = array(), $instance_edit = array()) { // Use 'test_field' field type by default. $initial_edit += array( - '_add_new_field[type]' => 'test_field', - '_add_new_field[widget_type]' => 'test_field_widget', + 'fields[_add_new_field][type]' => 'test_field', + 'fields[_add_new_field][widget_type]' => 'test_field_widget', ); - $label = $initial_edit['_add_new_field[label]']; - $field_name = $initial_edit['_add_new_field[field_name]']; + $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')); @@ -357,10 +357,10 @@ class FieldUITestCase extends DrupalWebTestCase { function fieldUIAddExistingField($bundle_path, $initial_edit, $instance_edit = array()) { // Use 'test_field_widget' by default. $initial_edit += array( - '_add_existing_field[widget_type]' => 'test_field_widget', + 'fields[_add_existing_field][widget_type]' => 'test_field_widget', ); - $label = $initial_edit['_add_existing_field[label]']; - $field_name = $initial_edit['_add_existing_field[field_name]']; + $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')); diff --git a/modules/file/file.api.php b/modules/file/file.api.php new file mode 100644 index 0000000000000000000000000000000000000000..1d0ee36226c3179328b3b4031f9a5107978bf9f3 --- /dev/null +++ b/modules/file/file.api.php @@ -0,0 +1,67 @@ +<?php +// $Id: file.api.php,v 1.1 2010/08/23 14:53:50 webchick Exp $ + +/** + * @file + * Hooks for file module. + */ + +/** + * Control download access to files. + * + * The hook is typically implemented to limit access based on the entity the + * file is referenced, e.g., only users with access to a node should be allowed + * to download files attached to that node. + * + * @param $field + * The field to which the file belongs. + * @param $entity_type + * The type of $entity; for example, 'node' or 'user'. + * @param $entity + * The $entity to which $file is referenced. + * + * @return + * TRUE is access should be allowed by this entity or FALSE if denied. Note + * that denial may be overridden by another entity controller, making this + * grant permissive rather than restrictive. + * + * @see hook_field_access(). + */ +function hook_file_download_access($field, $entity_type, $entity) { + if ($entity_type == 'node') { + return node_access('view', $entity); + } +} + +/** + * Alter the access rules applied to a file download. + * + * Entities that implement file management set the access rules for their + * individual files. Module may use this hook to create custom access rules + * for file downloads. + * + * @see hook_file_download_access(). + * + * @param &$grants + * An array of grants gathered by hook_file_download_access(). The array is + * keyed by the module that defines the entity type's access control; the + * values are Boolean grant responses for each module. + * @param $field + * The field to which the file belongs. + * @param $entity_type + * The type of $entity; for example, 'node' or 'user'. + * @param $entity + * The $entity to which $file is referenced. + * + * @return + * An array of grants, keyed by module name, each with a Boolean grant value. + * Return an empty array to assert FALSE. You may choose to return your own + * module's value in addition to other grants or to overwrite the values set by + * other modules. + */ +function hook_file_download_access_alter(&$grants, $field, $entity_type, $entity) { + // For our example module, we always enforce the rules set by node module. + if (isset($grants['node'])) { + $grants = array('node' => $grants['node']); + } +} diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index c55d3697100db3e31ecd1d59bf65c328f3050a72..b7372c412bd95be291cd1f265056742996aa986a 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -1,5 +1,5 @@ <?php -// $Id: file.field.inc,v 1.28 2010/07/02 12:37:57 dries Exp $ +// $Id: file.field.inc,v 1.34 2010/09/09 23:28:16 webchick Exp $ /** * @file @@ -17,7 +17,7 @@ function file_field_info() { 'settings' => array( 'display_field' => 0, 'display_default' => 0, - 'uri_scheme' => 'public', + 'uri_scheme' => variable_get('file_default_scheme', 'public'), ), 'instance_settings' => array( 'file_extensions' => 'txt', @@ -31,38 +31,6 @@ function file_field_info() { ); } -/** - * Implements hook_field_schema(). - */ -function file_field_schema($field) { - return array( - 'columns' => array( - 'fid' => array( - 'description' => 'The {files}.fid being referenced in this field.', - 'type' => 'int', - 'not null' => FALSE, - 'unsigned' => TRUE, - ), - 'display' => array( - 'description' => 'Flag to control whether this file should be displayed when viewing content.', - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, - ), - 'description' => array( - 'description' => 'A description of the file.', - 'type' => 'text', - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'fid' => array('fid'), - ), - ); -} - /** * Implements hook_field_settings_form(). */ @@ -193,31 +161,26 @@ function _file_generic_settings_extensions($element, &$form_state) { function _file_generic_settings_file_directory_validate($element, &$form_state) { // Strip slashes from the beginning and end of $widget['file_directory']. $value = trim($element['#value'], '\\/'); - - // Do not allow the file path to be the same as the file_directory_path(). - // This causes all sorts of problems with things like file_create_url(). - if (strpos($value, file_directory_path()) === 0) { - form_error($element, t('The file directory (@file_directory) cannot start with the system files directory (@files_directory), as this may cause conflicts when building file URLs.', array('@file_directory' => $form_state['values']['file_directory'], '@files_directory' => file_directory_path()))); - } - else { - form_set_value($element, $value, $form_state); - } + form_set_value($element, $value, $form_state); } /** * Implements hook_field_load(). */ function file_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { + + $fids = array(); foreach ($entities as $id => $entity) { // Load the files from the files table. - $fids = array(); foreach ($items[$id] as $delta => $item) { if (!empty($item['fid'])) { $fids[] = $item['fid']; } } - $files = file_load_multiple($fids); + } + $files = file_load_multiple($fids); + foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { // If the file does not exist, mark the entire item as empty. if (empty($item['fid']) || !isset($files[$item['fid']])) { @@ -255,45 +218,73 @@ function file_field_presave($entity_type, $entity, $field, $instance, $langcode, // cleaned up. foreach ($items as $item) { $file = file_load($item['fid']); - if (($file->status & FILE_STATUS_PERMANENT) == 0) { - $file->status |= FILE_STATUS_PERMANENT; + if (!$file->status) { + $file->status = FILE_STATUS_PERMANENT; file_save($file); } } } +/** + * Implements hook_field_insert(). + */ +function file_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + // Add a new usage of each uploaded file. + foreach ($items as $item) { + $file = (object) $item; + file_usage_add($file, 'file', $entity_type, $id); + } +} + /** * Implements hook_field_update(). * - * Check for files that have been removed from the object. + * Checks for files that have been removed from the object. */ function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { - // On new revisions, old files are always maintained in the previous revision. + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + // On new revisions, all files are considered to be a new usage and no + // deletion of previous file usages are necessary. if (!empty($entity->revision)) { + foreach ($items as $item) { + $file = (object) $item; + file_usage_add($file, 'file', $entity_type, $id); + } return; } // Build a display of the current FIDs. - $fids = array(); + $current_fids = array(); foreach ($items as $item) { - $fids[] = $item['fid']; + $current_fids[] = $item['fid']; } - // Get the current values in the entity, and delete files for removed items. - list($id) = entity_extract_ids($entity_type, $entity); - $original = clone $entity; + // Create a bare-bones entity so that we can load its previous values. + $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle)); field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id'])); + // Compare the original field values with the ones that are being saved. + $original_fids = array(); if (!empty($original->{$field['field_name']}[$langcode])) { foreach ($original->{$field['field_name']}[$langcode] as $original_item) { - if (isset($original_item['fid']) && !in_array($original_item['fid'], $fids)) { - // For hook_file_references, remember that this is being deleted. - $original_item['file_field_name'] = $field['field_name']; - // Delete the file if possible. - file_field_delete_file($original_item, $field); + $original_fids[] = $original_item['fid']; + if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) { + // Decrement the file usage count by 1 and delete the file if possible. + file_field_delete_file($original_item, $field, $entity_type, $id); } } } + + // Add new usage entries for newly added files. + foreach ($items as $item) { + if (!in_array($item['fid'], $original_fids)) { + $file = (object) $item; + file_usage_add($file, 'file', $entity_type, $id); + } + } } /** @@ -301,14 +292,10 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode, */ function file_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) { list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + // Delete all file usages within this entity. foreach ($items as $delta => $item) { - // For hook_file_references(), remember that this is being deleted. - $item['file_field_name'] = $field['field_name']; - // Pass in the ID of the object that is being removed so all references can - // be counted in hook_file_references(). - $item['file_field_type'] = $entity_type; - $item['file_field_id'] = $id; - file_field_delete_file($item, $field); + file_field_delete_file($item, $field, $entity_type, $id, 0); } } @@ -316,32 +303,46 @@ function file_field_delete($entity_type, $entity, $field, $instance, $langcode, * Implements hook_field_delete_revision(). */ function file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) { + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); foreach ($items as $delta => $item) { - // For hook_file_references, remember that this file is being deleted. - $item['file_field_name'] = $field['field_name']; - if (file_field_delete_file($item, $field)) { + // Decrement the file usage count by 1 and delete the file if possible. + if (file_field_delete_file($item, $field, $entity_type, $id)) { $items[$delta] = NULL; } } } /** - * Check that File controls a file before attempting to delete it. + * Decrements a file usage count and attempts to delete it. + * + * This function only has an effect if the file being deleted is used only by + * File module. + * + * @param $item + * The field item that contains a file array. + * @param $field + * The field structure for the operation. + * @param $entity_type + * The type of $entity. + * @param $id + * The entity ID which contains the file being deleted. + * @param $count + * (optional) The number of references to decrement from the object + * containing the file. Defaults to 1. + * + * @return + * Boolean TRUE if the file was deleted, or an array of remaining references + * if the file is still in use by other modules. Boolean FALSE if an error + * was encountered. */ -function file_field_delete_file($item, $field) { - // Remove the file_field_name and file_field_id properties so that references - // can be counted including the files to be deleted. - $field_name = isset($item['file_field_name']) ? $item['file_field_name'] : NULL; - $field_id = isset($item['file_field_id']) ? $item['file_field_id'] : NULL; - unset($item['file_field_name'], $item['file_field_id']); - +function file_field_delete_file($item, $field, $entity_type, $id, $count = 1) { // To prevent the file field from deleting files it doesn't know about, check // the file reference count. Temporary files can be deleted because they // are not yet associated with any content at all. $file = (object) $item; - if ($file->status == 0 || file_get_file_reference_count($file, $field) > 0) { - $file->file_field_name = $field_name; - $file->file_field_id = $field_id; + $file_usage = file_usage_list($file); + if ($file->status == 0 || !empty($file_usage['file'])) { + file_usage_delete($file, 'file', $entity_type, $id, $count); return file_delete($file); } diff --git a/modules/file/file.info b/modules/file/file.info index ec7ef4f4640c49c03f89136a6e17b4d4f875c5b4..e0dd7d9be193d475f43b27a2438f508b0e6280c7 100644 --- a/modules/file/file.info +++ b/modules/file/file.info @@ -1,16 +1,17 @@ -; $Id: file.info,v 1.1 2009/08/29 12:52:32 dries Exp $ +; $Id: file.info,v 1.2 2010/08/16 20:57:23 dries Exp $ name = File description = Defines a file field type. package = Core version = VERSION core = 7.x +dependencies[] = field files[] = file.module files[] = file.field.inc files[] = file.install files[] = tests/file.test -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/file/file.install b/modules/file/file.install index 7347d19296c799d22875360cee2ca4cf5c493cea..5a44efdc40f28d1a2d1526072f7df00536b55ba4 100644 --- a/modules/file/file.install +++ b/modules/file/file.install @@ -1,11 +1,43 @@ <?php -// $Id: file.install,v 1.2 2009/12/04 16:49:46 dries Exp $ +// $Id: file.install,v 1.3 2010/09/04 15:40:51 dries Exp $ /** * @file * Install, update and uninstall functions for File module. */ +/** + * Implements hook_field_schema(). + */ +function file_field_schema($field) { + return array( + 'columns' => array( + 'fid' => array( + 'description' => 'The {files}.fid being referenced in this field.', + 'type' => 'int', + 'not null' => FALSE, + 'unsigned' => TRUE, + ), + 'display' => array( + 'description' => 'Flag to control whether this file should be displayed when viewing content.', + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ), + 'description' => array( + 'description' => 'A description of the file.', + 'type' => 'text', + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'fid' => array('fid'), + ), + ); +} + /** * Implements hook_requirements(). * diff --git a/modules/file/file.js b/modules/file/file.js index f89ab8a794c6d8ae984cf0c81e95abc4ca1b4416..e6661cc76e1b6dfdcac387417285ec3351c0416d 100644 --- a/modules/file/file.js +++ b/modules/file/file.js @@ -1,4 +1,4 @@ -// $Id: file.js,v 1.3 2010/05/01 21:55:13 dries Exp $ +// $Id: file.js,v 1.4 2010/07/10 00:03:37 webchick Exp $ /** * @file @@ -92,17 +92,18 @@ Drupal.file = Drupal.file || { $enabledFields = $(this).parents('div.form-managed-file').find('input.form-file'); } - var $disabledFields = $('div.form-managed-file input.form-file').not($enabledFields); - - // Disable upload fields other than the one we're currently working with. - $disabledFields.attr('disabled', 'disabled'); - - // All the other mousedown handlers (like Drupal's AJAX behaviors) are - // excuted before any timeout functions will be called, so this effectively - // re-enables the file fields after other processing is complete even though - // it is only a 1 second timeout. + // Temporarily disable upload fields other than the one we're currently + // working with. Filter out fields that are already disabled so that they + // do not get enabled when we re-enable these fields at the end of behavior + // processing. Re-enable in a setTimeout set to a relatively short amount + // of time (1 second). All the other mousedown handlers (like Drupal's AJAX + // behaviors) are excuted before any timeout functions are called, so we + // don't have to worry about the fields being re-enabled too soon. + // @todo If the previous sentence is true, why not set the timeout to 0? + var $fieldsToTemporarilyDisable = $('div.form-managed-file input.form-file').not($enabledFields).not(':disabled'); + $fieldsToTemporarilyDisable.attr('disabled', 'disabled'); setTimeout(function (){ - $disabledFields.attr('disabled', ''); + $fieldsToTemporarilyDisable.attr('disabled', ''); }, 1000); }, /** diff --git a/modules/file/file.module b/modules/file/file.module index 4075d6523724fbb97931736d184e242ac0f1a8a2..fcda65bc9096c5f3cc1fd2f78bc52c28d3c5f4bc 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -1,5 +1,5 @@ <?php -// $Id: file.module,v 1.31 2010/07/02 12:37:57 dries Exp $ +// $Id: file.module,v 1.40 2010/09/13 01:09:25 dries Exp $ /** * @file @@ -88,10 +88,10 @@ function file_theme() { return array( // file.module. 'file_link' => array( - 'variables' => array('file' => NULL), + 'variables' => array('file' => NULL, 'icon_directory' => NULL), ), 'file_icon' => array( - 'variables' => array('file' => NULL), + 'variables' => array('file' => NULL, 'icon_directory' => NULL), ), 'file_managed_file' => array( 'render element' => 'element', @@ -125,55 +125,91 @@ function file_file_download($uri, $field_type = 'file') { // Get the file record based on the URI. If not in the database just return. $files = file_load_multiple(array(), array('uri' => $uri)); if (count($files)) { - $file = reset($files); + foreach ($files as $item) { + // Since some database servers sometimes use a case-insensitive comparison + // by default, double check that the filename is an exact match. + if ($item->uri === $uri) { + $file = $item; + break; + } + } } - else { + if (!isset($file)) { return; } - // Find out which (if any) file fields contain this file. + // Find out which (if any) fields of this type contain the file. $references = file_get_file_references($file, NULL, FIELD_LOAD_REVISION, $field_type); - // TODO: Check field-level access if available here. + // If there are no references, stop processing, to avoid returning headers + // for files controlled by other modules. + if (empty($references)) { + return; + } - $denied = $file->status ? NULL : FALSE; - // Check access to content containing the file fields. If access is allowed - // to any of this content, allow the download. + // Default to allow access. + $denied = FALSE; + // Loop through all references of this file. If a reference explicitly allows + // access to the field to which this file belongs, no further checks are done + // and download access is granted. If a reference denies access, eventually + // existing additional references are checked. If all references were checked + // and no reference denied access, access is granted as well. If at least one + // reference denied access, access is denied. foreach ($references as $field_name => $field_references) { foreach ($field_references as $entity_type => $type_references) { - foreach ($type_references as $reference) { - // If access is allowed to any object, immediately stop and grant - // access. If access is denied, continue through in case another object - // grants access. - // TODO: Switch this to a universal access check mechanism if available. - if ($entity_type == 'node' && ($node = node_load($reference->nid))) { - if (node_access('view', $node)) { - $denied = FALSE; - break 3; - } - else { - $denied = TRUE; + foreach ($type_references as $id => $reference) { + // Try to load $entity and $field. + $entity = reset(entity_load($entity_type, array($id))); + $field = NULL; + if ($entity) { + // Load all fields for that entity. + $field_items = field_get_items($entity_type, $entity, $field_name); + + // Find the field item with the matching URI. + foreach ($field_items as $field_item) { + if ($field_item['uri'] == $uri) { + $field = $field_item; + break; + } } } - if ($entity_type == 'user') { - if (user_access('access user profiles') || $user->uid == $reference->uid) { - $denied = FALSE; - break 3; - } - else { - $denied = TRUE; - } + + // Check that $entity and $field were loaded successfully and check if + // access to that field is not disallowed. If any of these checks fail, + // stop checking access for this reference. + if (empty($entity) || empty($field) || !field_access('view', $field, $entity_type, $entity)) { + $denied = TRUE; + break; + } + + // Invoke hook and collect grants/denies for download access. + // Default to FALSE and let entities overrule this ruling. + $grants = array('system' => FALSE); + foreach (module_implements('file_download_access') as $module) { + $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field, $entity_type, $entity))); + } + // Allow other modules to alter the returned grants/denies. + drupal_alter('file_download_access', $grants, $field, $entity_type, $entity); + + if (in_array(TRUE, $grants)) { + // If TRUE is returned, access is granted and no further checks are + // necessary. + $denied = FALSE; + break 3; + } + + if (in_array(FALSE, $grants)) { + // If an implementation returns FALSE, access to this entity is denied + // but the file could belong to another entity to which the user might + // have access. Continue with these. + $denied = TRUE; } } } } - // No access was denied or granted. - if (!isset($denied)) { - return; - } - // Access specifically denied and not granted elsewhere. - elseif ($denied == TRUE) { + // Access specifically denied. + if ($denied) { return -1; } @@ -218,7 +254,7 @@ function file_ajax_upload() { return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE); } - list($form, $form_state, $form_id, $form_build_id) = ajax_get_form(); + list($form, $form_state) = ajax_get_form(); if (!$form) { // Invalid form_build_id. @@ -235,12 +271,8 @@ function file_ajax_upload() { } $current_file_count = isset($current_element['#file_upload_delta']) ? $current_element['#file_upload_delta'] : 0; - // Build, validate and if possible, submit the form. - drupal_process_form($form_id, $form, $form_state); - - // This call recreates the form relying solely on the form_state that the - // drupal_process_form() set up. - $form = drupal_rebuild_form($form_id, $form_state, $form); + // Process user input. $form and $form_state are modified in the process. + drupal_process_form($form['#form_id'], $form, $form_state); // Retrieve the element to be rendered. foreach ($form_parents as $parent) { @@ -320,14 +352,6 @@ function file_progress_implementation() { return $implementation; } -/** - * Implements hook_file_references(). - */ -function file_file_references($file) { - $count = file_get_file_reference_count($file, NULL, 'file'); - return $count ? array('file' => $count) : NULL; -} - /** * Implements hook_file_delete(). */ @@ -520,12 +544,9 @@ function file_managed_file_validate(&$element, &$form_state) { if ($clicked_button != 'remove_button' && !empty($element['fid']['#value'])) { if ($file = file_load($element['fid']['#value'])) { if ($file->status == FILE_STATUS_PERMANENT) { - $reference_count = 0; - foreach (module_invoke_all('file_references', $file) as $module => $references) { - $reference_count += $references; - } - if ($reference_count == 0) { - form_error($element, t('Referencing to the file used in the !name field is not allowed.', array('!name' => $element['#title']))); + $references = file_usage_list($file); + if (empty($references)) { + form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title']))); } } } @@ -553,10 +574,7 @@ function file_managed_file_submit($form, &$form_state) { // and set $element to the managed_file element that contains that button. $parents = $form_state['triggering_element']['#array_parents']; $button_key = array_pop($parents); - $element = $form; - foreach ($parents as $parent) { - $element = $element[$parent]; - } + $element = drupal_array_get_nested_value($form, $parents); // No action is needed here for the upload button, because all file uploads on // the form are processed by file_managed_file_value() regardless of which @@ -574,13 +592,10 @@ function file_managed_file_submit($form, &$form_state) { // run, and for form building functions that run during the rebuild, such as // when the managed_file element is part of a field widget. // $form_state['input'] must be updated so that file_managed_file_value() - // has correct information during the rebuild. The Form API provides no - // equivalent of form_set_value() for updating $form_state['input'], so - // inline that implementation with the same logic that form_set_value() - // uses. + // has correct information during the rebuild. $values_element = $element['#extended'] ? $element['fid'] : $element; form_set_value($values_element, NULL, $form_state); - _form_set_value($form_state['input'], $values_element, $values_element['#parents'], NULL); + drupal_array_set_nested_value($form_state['input'], $values_element['#parents'], NULL); } // Set the form to rebuild so that $form is correctly updated in response to @@ -681,14 +696,17 @@ function file_managed_file_pre_render($element) { * @param $variables * An associative array containing: * - file: A file object to which the link will be created. + * - icon_directory: (optional) A path to a directory of icons to be used for + * files. Defaults to the value of the "file_icon_directory" variable. * * @ingroup themeable */ function theme_file_link($variables) { $file = $variables['file']; + $icon_directory = $variables['icon_directory']; $url = file_create_url($file->uri); - $icon = theme('file_icon', array('file' => $file)); + $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples @@ -716,14 +734,17 @@ function theme_file_link($variables) { * @param $variables * An associative array containing: * - file: A file object for which to make an icon. + * - icon_directory: (optional) A path to a directory of icons to be used for + * files. Defaults to the value of the "file_icon_directory" variable. * * @ingroup themeable */ function theme_file_icon($variables) { $file = $variables['file']; + $icon_directory = $variables['icon_directory']; $mime = check_plain($file->filemime); - $icon_url = file_icon_url($file); + $icon_url = file_icon_url($file, $icon_directory); return '<img class="file-icon" alt="" title="' . $mime . '" src="' . $icon_url . '" />'; } @@ -929,73 +950,7 @@ function file_icon_map($file) { */ /** - * Count the number of times the file is referenced. - * - * @param $file - * A file object. - * @param $field - * (optional) A CCK field array or field name as a string. If provided, - * limits the reference check to the given field. - * @param $field_type - * (optional) The name of a field type. If provided, limits the reference - * check to fields of the given type. - * @return - * An integer value. - */ -function file_get_file_reference_count($file, $field = NULL, $field_type = NULL) { - // Determine the collection of fields to check. - if (isset($field)) { - // Support $field as 'field name'. - if (is_string($field)) { - $field = field_info_field($field); - } - $fields = array($field['field_name'] => $field); - } - else { - $fields = field_info_fields(); - } - - $types = entity_get_info(); - $reference_count = 0; - - foreach ($fields as $field) { - if (empty($field_type) || $field['type'] == $field_type) { - // TODO: Use a more efficient mechanism rather than actually retrieving - // all the references themselves, such as using a COUNT() query. - $references = file_get_file_references($file, $field, FIELD_LOAD_REVISION, $field_type); - foreach ($references as $entity_type => $type_references) { - $reference_count += count($type_references); - } - - // If a field_name is present in the file object, the file is being deleted - // from this field. - if (isset($file->file_field_name) && $field['field_name'] == $file->file_field_name) { - // If deleting the entire piece of content, decrement references. - if (isset($file->file_field_type) && isset($file->file_field_id)) { - if ($file->file_field_type == $entity_type) { - $info = entity_get_info($entity_type); - $id = $types[$entity_type]['entity keys']['id']; - foreach ($type_references as $reference) { - if ($file->file_field_id == $reference->$id) { - $reference_count--; - } - } - } - } - // Otherwise we're just deleting a single reference in this field. - else { - $reference_count--; - } - } - } - } - - return $reference_count; -} - - -/** - * Get a list of references to a file. + * Gets a list of references to a file. * * @param $file * A file object. @@ -1007,8 +962,9 @@ function file_get_file_reference_count($file, $field = NULL, $field_type = NULL) * FIELD_LOAD_REVISION to retrieve all references within all revisions or * FIELD_LOAD_CURRENT to retrieve references only in the current revisions. * @param $field_type - * Optional. The name of a field type. If given, limits the reference check to - * fields of the given type. + * (optional) The name of a field type. If given, limits the reference check + * to fields of the given type. + * * @return * An integer value. */ @@ -1017,7 +973,7 @@ function file_get_file_references($file, $field = NULL, $age = FIELD_LOAD_REVISI $fields = isset($field) ? array($field['field_name'] => $field) : field_info_fields(); foreach ($fields as $field_name => $file_field) { - if ((empty($field_type) || $field['type'] == $field_type) && !isset($references[$field_name])) { + if ((empty($field_type) || $file_field['type'] == $field_type) && !isset($references[$field_name])) { // Get each time this file is used within a field. $query = new EntityFieldQuery(); $query diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 51e3b2df585037758f8f630b7d1879253c994e0a..7e1457e69e9685c5c880dbb0a3166277ce593fcf 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -1,5 +1,5 @@ <?php -// $Id: file.test,v 1.18 2010/07/02 12:37:57 dries Exp $ +// $Id: file.test,v 1.25 2010/09/11 21:14:31 webchick Exp $ /** * @file @@ -14,7 +14,7 @@ class FileFieldTestCase extends DrupalWebTestCase { function setUp() { parent::setUp('file'); - $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer content types', 'administer nodes', 'bypass node access')); + $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access')); $this->drupalLogin($this->admin_user); } @@ -96,7 +96,6 @@ class FileFieldTestCase extends DrupalWebTestCase { * Update an existing file field with new settings. */ function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) { - $field = field_info_field($name); $instance = field_info_instance('node', $name, $type_name); $instance['settings'] = array_merge($instance['settings'], $instance_settings); $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings); @@ -117,11 +116,11 @@ class FileFieldTestCase extends DrupalWebTestCase { if (is_numeric($nid_or_type)) { $node = node_load($nid_or_type); $delta = isset($node->$field_name) ? count($node->$field_name) : 0; - $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']'] = realpath($file->uri); + $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']'] = drupal_realpath($file->uri); $this->drupalPost('node/' . $nid_or_type . '/edit', $edit, t('Save')); } else { - $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = realpath($file->uri); + $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($file->uri); $type_name = str_replace('_', '-', $nid_or_type); $this->drupalPost('node/add/' . $type_name, $edit, t('Save')); } @@ -150,7 +149,7 @@ class FileFieldTestCase extends DrupalWebTestCase { */ function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) { $edit = array( - 'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => realpath($file->uri), + 'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => drupal_realpath($file->uri), 'revision' => (string) (int) $new_revision, ); @@ -170,7 +169,7 @@ class FileFieldTestCase extends DrupalWebTestCase { * Assert that a file exists in the database. */ function assertFileEntryExists($file, $message = NULL) { - drupal_static_reset('file_load_multiple'); + entity_get_controller('file')->resetCache(); $db_file = file_load($file->fid); $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri)); $this->assertEqual($db_file->uri, $file->uri, $message); @@ -188,7 +187,7 @@ class FileFieldTestCase extends DrupalWebTestCase { * Assert that a file does not exist in the database. */ function assertFileEntryNotExists($file, $message) { - drupal_static_reset('file_load_multiple'); + entity_get_controller('file')->resetCache(); $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri)); $this->assertFalse(file_load($file->fid), $message); } @@ -246,6 +245,9 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { $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. + $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 the edit page has a remove button instead of an upload button. $this->drupalGet("node/$nid/edit"); @@ -277,7 +279,90 @@ 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 private download method. + $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. + $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"); + $this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and not(@disabled)]', 'public', t('Upload destination setting enabled.')); + } + + /** + * Tests that download restrictions on private files work on comments. + */ + function testPrivateFileComment() { + $user = $this->drupalCreateUser(array('access comments')); + + // Remove access comments permission from anon user. + $edit = array( + '1[access comments]' => FALSE, + ); + $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); + + // Create a new field. + $edit = array( + 'fields[_add_new_field][label]' => $label = $this->randomName(), + 'fields[_add_new_field][field_name]' => $name = strtolower($this->randomName()), + 'fields[_add_new_field][type]' => 'file', + 'fields[_add_new_field][widget_type]' => 'file_generic', + ); + $this->drupalPost('admin/structure/types/manage/article/comment/fields', $edit, t('Save')); + $edit = array('field[settings][uri_scheme]' => 'private'); + $this->drupalPost(NULL, $edit, t('Save field settings')); + $this->drupalPost(NULL, array(), t('Save settings')); + + // Create node. + $text_file = $this->getTestFile('text'); + $edit = array( + 'title' => $this->randomName(), + ); + $this->drupalPost('node/add/article', $edit, t('Save')); + + // Add a comment with a file. + $text_file = $this->getTestFile('text'); + $edit = array( + 'files[field_' . $name . '_' . LANGUAGE_NONE . '_' . 0 . ']' => drupal_realpath($text_file->uri), + 'comment_body[' . LANGUAGE_NONE . '][0][value]' => $comment_body = $this->randomName(), + ); + $this->drupalPost(NULL, $edit, t('Save')); + + // Get the comment ID. + preg_match('/comment-([0-9]+)/', $this->getUrl(), $matches); + $cid = $matches[1]; + + // Log in as normal user. + $this->drupalLogin($user); + + $comment = comment_load($cid); + $comment_file = (object) $comment->{'field_' . $name}[LANGUAGE_NONE][0]; + $this->assertFileExists($comment_file, t('New file saved to disk on node creation.')); + // Test authenticated file download. + $url = file_create_url($comment_file->uri); + $this->assertNotEqual($url, NULL, t('Confirmed that the URL is valid')); + $this->drupalGet(file_create_url($comment_file->uri)); + $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.')); + + // Test anonymous file download. + $this->drupalLogout(); + $this->drupalGet(file_create_url($comment_file->uri)); + $this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.')); } + } /** @@ -368,7 +453,7 @@ class FileFieldRevisionTestCase extends FileFieldTestCase { // Attach the second file to a user. $user = $this->drupalCreateUser(); - $edit = array(); + $edit = (array) $user; $edit[$field_name][LANGUAGE_NONE][0] = (array) $node_file_r3; user_save($user, $edit); $this->drupalGet('user/' . $user->uid . '/edit'); @@ -481,6 +566,8 @@ class FileFieldValidateTestCase extends FileFieldTestCase { // Create a new node with the uploaded file. $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); + $this->assertTrue($nid !== FALSE, t('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name))); + $node = node_load($nid, NULL, TRUE); $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0]; diff --git a/modules/file/tests/file_module_test.info b/modules/file/tests/file_module_test.info index cf131ee800f034f6991b5ce12c7936976be4f6c2..4c4bf6e574af987badf3e5fff214023cff7937ab 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/filter/filter.admin.inc b/modules/filter/filter.admin.inc index 0e5a870e652a12c658df77fd6b2d832df252eadf..e793b69d0ba2cbc0ba41c9e6010e540a38ada5c4 100644 --- a/modules/filter/filter.admin.inc +++ b/modules/filter/filter.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: filter.admin.inc,v 1.63 2010/06/26 01:55:29 dries Exp $ +// $Id: filter.admin.inc,v 1.64 2010/08/17 13:50:52 dries Exp $ /** * @file @@ -23,8 +23,8 @@ function filter_admin_overview($form) { // to all roles and cannot be deleted via the admin interface. $form['formats'][$id]['#is_fallback'] = ($id == $fallback_format); if ($form['formats'][$id]['#is_fallback']) { - $form['formats'][$id]['name'] = array('#markup' => drupal_placeholder(array('text' => $format->name))); - $roles_markup = drupal_placeholder(array('text' => t('All roles may use this format'))); + $form['formats'][$id]['name'] = array('#markup' => drupal_placeholder($format->name)); + $roles_markup = drupal_placeholder(t('All roles may use this format')); } else { $form['formats'][$id]['name'] = array('#markup' => check_plain($format->name)); diff --git a/modules/filter/filter.css b/modules/filter/filter.css index badb039853f0076cba857ff94de585f52862e88f..d8cc5a725d1ac90aba12b8141657712f43fbfc3f 100644 --- a/modules/filter/filter.css +++ b/modules/filter/filter.css @@ -1,4 +1,4 @@ -/* $Id: filter.css,v 1.4 2010/03/09 11:45:37 dries Exp $ */ +/* $Id: filter.css,v 1.5 2010/07/20 10:11:54 dries Exp $ */ .text-format-wrapper .form-item { margin-bottom: 0; @@ -24,7 +24,7 @@ } .filter-help a { background: transparent url(../../misc/help.png) right center no-repeat; - padding-right: 20px; + padding: 0 20px; } .filter-guidelines { clear: left; diff --git a/modules/filter/filter.info b/modules/filter/filter.info index d2b3c4604e7edba7b4262d719dcd414e09969899..14a168ee6a1f2c6ca9e3d36bc86dab6a72f6956b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/filter/filter.install b/modules/filter/filter.install index ad4e65b95bb245c2b073ffc6112e0423fb9a65eb..1297c0bfe76a98e59b9f99434444342dadfe4fcf 100644 --- a/modules/filter/filter.install +++ b/modules/filter/filter.install @@ -1,5 +1,5 @@ <?php -// $Id: filter.install,v 1.42 2010/07/01 15:14:27 webchick Exp $ +// $Id: filter.install,v 1.44 2010/09/13 05:50:09 webchick Exp $ /** * @file @@ -157,22 +157,13 @@ function filter_update_dependencies() { */ /** - * Increase the size of {filters}.weight and add {filter_formats}.weight. + * Upgrade the {filter_formats} table and rename it to {filter_format}. */ function filter_update_7000() { - // The list index will be recreated by filter_update_7003(). - db_drop_index('filters', 'list'); - - // Change the weight column of the filter table to normal (ie. non tiny) int. - db_change_field('filters', 'weight', 'weight', array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Weight of filter within format.', - )); + db_rename_table('filter_formats', 'filter_format'); // Add a new filter_format.weight column. - db_add_field('filter_formats', 'weight', array( + db_add_field('filter_format', 'weight', array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, @@ -188,7 +179,7 @@ function filter_update_7000() { * Break out "escape HTML filter" option to its own filter. */ function filter_update_7001() { - $result = db_query("SELECT format FROM {filter_formats}")->fetchCol(); + $result = db_query("SELECT format FROM {filter_format}")->fetchCol(); $insert = db_insert('filters')->fields(array('format', 'module', 'delta', 'weight')); foreach ($result as $format_id) { @@ -208,23 +199,62 @@ function filter_update_7001() { } /** - * Rename {filters} table to {filter} and {filter_formats} table to {filter_format}. - */ -function filter_update_7002() { - db_rename_table('filters', 'filter'); - db_rename_table('filter_formats', 'filter_format'); -} - -/** - * Remove hardcoded numeric deltas from all filters in core. + * Upgrade the {filter} table for core filters. */ function filter_update_7003() { - // Duplicates the filter table since core cannot take care of the potential + // Duplicates the {filters} table since core cannot take care of the potential // contributed module filters. - db_rename_table('filter', 'd6_upgrade_filter'); + db_rename_table('filters', 'd6_upgrade_filter'); // Creates the Drupal 7 filter table. - $schema = filter_schema(); - db_create_table('filter', $schema['filter']); + $filter_table = array( + 'description' => 'Table that maps filters (HTML corrector) to text formats (Filtered HTML).', + 'fields' => array( + 'format' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Foreign key: The {filter_format}.format to which this filter is assigned.', + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The origin module of the filter.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Name of the filter being referenced.', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Weight of filter within format.', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Filter enabled status. (1 = enabled, 0 = disabled)', + ), + 'settings' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + 'description' => 'A serialized array of name value pairs that store the filter settings for the specific format.', + ), + ), + 'primary key' => array('format', 'name'), + 'indexes' => array( + 'list' => array('weight', 'module', 'name'), + ), + ); + db_create_table('filter', $filter_table); // Get an array of the renamed filter deltas, organized by module. $renamed_deltas = array( @@ -244,19 +274,44 @@ function filter_update_7003() { // each record from the old to the new table. foreach ($renamed_deltas as $module => $deltas) { foreach ($deltas as $old_delta => $new_name) { - $query = db_select('d6_upgrade_filter'); - $query->fields('d6_upgrade_filter', array('format', 'weight')); - $query->condition('module', $module); - $query->condition('delta', $old_delta); - $query->distinct(); - $result = $query->execute(); - foreach ($result as $record) { + $query = db_select('d6_upgrade_filter') + ->fields('d6_upgrade_filter', array('format', 'weight')) + ->condition('module', $module) + ->condition('delta', $old_delta) + ->distinct(); + + foreach ($query->execute() as $record) { + // Port the filter settings. + $settings = array(); + if ($new_name == 'filter_html') { + if ($setting = variable_get("allowed_html_{$record->format}", NULL)) { + $settings['allowed_html'] = $setting; + variable_del("allowed_html_{$record->format}"); + } + if ($setting = variable_get("filter_html_help_{$record->format}", NULL)) { + $settings['filter_html_help'] = $setting; + variable_del("filter_html_help_{$record->format}"); + } + if ($setting = variable_get("filter_html_nofollow_{$record->format}", NULL)) { + $settings['filter_html_nofollow'] = $setting; + variable_del("filter_html_nofollow_{$record->format}"); + } + } + elseif ($new_name == 'filter_url') { + if ($setting = variable_get("filter_url_length_{$record->format}", NULL)) { + $settings['filter_url_length'] = $setting; + variable_del("filter_url_length_{$record->format}"); + } + } + db_insert('filter') ->fields(array( 'format' => $record->format, 'module' => $module, 'name' => $new_name, 'weight' => $record->weight, + 'settings' => serialize($settings), + 'status' => 1, )) ->execute(); } @@ -268,49 +323,6 @@ function filter_update_7003() { } } -/** - * Move filter settings storage into {filter} table. - */ -function filter_update_7004() { - // Enable all existing filters ({filter} contained only enabled previously). - db_update('filter') - ->fields(array('status' => '1')) - ->execute(); - - // Move filter settings from system variables into {filter}.settings. - $filters = db_query("SELECT * FROM {filter} WHERE module = :name", array(':name' => 'filter')); - foreach ($filters as $filter) { - $settings = array(); - if ($filter->name == 'filter_html') { - if ($setting = variable_get("allowed_html_{$filter->format}", NULL)) { - $settings['allowed_html'] = $setting; - variable_del("allowed_html_{$filter->format}"); - } - if ($setting = variable_get("filter_html_help_{$filter->format}", NULL)) { - $settings['filter_html_help'] = $setting; - variable_del("filter_html_help_{$filter->format}"); - } - if ($setting = variable_get("filter_html_nofollow_{$filter->format}", NULL)) { - $settings['filter_html_nofollow'] = $setting; - variable_del("filter_html_nofollow_{$filter->format}"); - } - } - elseif ($filter->name == 'filter_url') { - if ($setting = variable_get("filter_url_length_{$filter->format}", NULL)) { - $settings['filter_url_length'] = $setting; - variable_del("filter_url_length_{$filter->format}"); - } - } - if (!empty($settings)) { - db_update('filter') - ->fields(array('settings' => serialize($settings))) - ->condition('format', $filter->format) - ->condition('name', $filter->name) - ->execute(); - } - } -} - /** * Integrate text formats with the user permissions system. * @@ -331,7 +343,7 @@ function filter_update_7005() { $format_roles = ($format->format == $default_format ? $all_roles : explode(',', $format->roles)); foreach ($format_roles as $format_role) { if (in_array($format_role, $all_roles)) { - user_role_grant_permissions($format_role, array(filter_permission_name($format))); + _update_7000_user_role_grant_permissions($format_role, array('use text format ' . $format->format), 'filter'); } } } @@ -348,28 +360,55 @@ function filter_update_7005() { $id = empty($id) ? 2 : $id + 1; $format_name = $start_name . ' ' . $id; } - $fallback_format = new stdClass(); - $fallback_format->name = $format_name; - $fallback_format->weight = 1; + + // Insert the filter format. + $format_id = db_insert('filter_format') + ->fields(array( + 'name' => $format_name, + 'cache' => 1, + 'weight' => 1, + )) + ->execute(); + // This format should output plain text, so we escape all HTML and apply the // line break and URL filters only. - $fallback_format->filters = array( - 'filter_html_escape' => array( + db_insert('filter') + ->fields(array( + 'format', + 'name', + 'weight', + 'status', + 'module', + 'settings', + )) + ->values(array( + 'format' => $format_id, + 'name' => 'filter_html_escape', 'weight' => 0, 'status' => 1, - ), - 'filter_url' => array( + 'module' => 'filter', + 'settings' => serialize(array()), + )) + ->values(array( + 'format' => $format_id, + 'name' => 'filter_url', 'weight' => 1, 'status' => 1, - ), - 'filter_autop' => array( + 'module' => 'filter', + 'settings' => serialize(array()), + )) + ->values(array( + 'format' => $format_id, + 'name' => 'filter_autop', 'weight' => 2, 'status' => 1, - ), - ); - filter_format_save($fallback_format); - variable_set('filter_fallback_format', $fallback_format->format); - drupal_set_message('A new <em>Plain text</em> format has been created which will be available to all users. You can configure this text format on the <a href="' . url('admin/config/content/formats/' . $fallback_format->format) . '">text format configuration page</a>.'); + 'module' => 'filter', + 'settings' => serialize(array()), + )) + ->execute(); + + variable_set('filter_fallback_format', $format_id); + drupal_set_message('A new <em>Plain text</em> format has been created which will be available to all users. You can configure this text format on the <a href="' . url('admin/config/content/formats/' . $format) . '">text format configuration page</a>.'); // Move the former site-wide default text format to the top of the list, so // that it continues to be the default text format for all users. @@ -389,9 +428,9 @@ function filter_update_7005() { function filter_update_7008() { // Build the list of permissions to grant. $permissions = array(); - foreach (filter_formats() as $format_id => $format) { - if ($permission = filter_permission_name($format)) { - $permissions[] = $permission; + foreach (db_query('SELECT format FROM {filter_format}')->fetchCol() as $format_id) { + if ($format_id != variable_get('filter_fallback_format')) { + $permissions[] = 'use text format ' . $format_id; } } // Grant text format permissions to all roles that can 'administer filters'. @@ -399,7 +438,7 @@ function filter_update_7008() { // that they do not or must not. if ($roles = user_roles(FALSE, 'administer filters')) { foreach ($roles as $rid => $name) { - user_role_grant_permissions($rid, $permissions); + _update_7000_user_role_grant_permissions($rid, $permissions, 'filter'); } } } @@ -408,15 +447,6 @@ function filter_update_7008() { * Converts fields that store serialized variables from text to blob. */ function filter_update_7009() { - $spec = array( - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - 'serialize' => TRUE, - 'description' => 'A serialized array of name value pairs that store the filter settings for the specific format.', - ); - db_change_field('filter', 'settings', 'settings', $spec); - $schema = system_schema_cache_7054(); db_drop_table('cache_filter'); db_create_table('cache_filter', $schema); diff --git a/modules/filter/filter.js b/modules/filter/filter.js index 7c09fd46d29331c32766d4a0d3f3042709a68afc..faee1190e0c76d5c8141996d68857aa731985085 100644 --- a/modules/filter/filter.js +++ b/modules/filter/filter.js @@ -1,4 +1,4 @@ -// $Id: filter.js,v 1.2 2010/04/30 07:48:07 dries Exp $ +// $Id: filter.js,v 1.3 2010/09/13 00:59:47 dries Exp $ (function ($) { /** @@ -7,7 +7,7 @@ Drupal.behaviors.filterGuidelines = { attach: function (context) { $('.filter-guidelines', context).once('filter-guidelines') - .find('label').hide() + .find(':header').hide() .parents('.filter-wrapper').find('select.filter-list') .bind('change', function () { $(this).parents('.filter-wrapper') diff --git a/modules/filter/filter.module b/modules/filter/filter.module index 92613c7e3dc950889334c227cd6631e5aaee3aad..d36253dab442735bd4505c9391648fc2e403f82c 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -1,5 +1,5 @@ <?php -// $Id: filter.module,v 1.335 2010/06/29 00:48:14 dries Exp $ +// $Id: filter.module,v 1.343 2010/09/11 03:30:03 dries Exp $ /** * @file @@ -30,7 +30,7 @@ function filter_help($path, $arg) { case 'admin/config/content/formats': $output = '<p>' . t('Text formats define the HTML tags, code, and other formatting that can be used when entering text. <strong>Improper text format configuration is a security risk</strong>. Learn more on the <a href="@filterhelp">Filter module help page</a>.', array('@filterhelp' => url('admin/help/filter'))) . '</p>'; - $output .= '<p>' . t('Text formats are presented on content editing pages in the order defined on this page.') . '</p>'; + $output .= '<p>' . t('Text formats are presented on content editing pages in the order defined on this page. The first format available to a user will be selected by default.') . '</p>'; return $output; case 'admin/config/content/formats/%': @@ -187,6 +187,10 @@ function filter_format_load($format_id) { function filter_format_save(&$format) { $format->name = trim($format->name); $format->cache = _filter_format_is_cacheable($format); + // Programmatic saves may not contain any filters. + if (!isset($format->filters)) { + $format->filters = array(); + } // Add a new text format. if (empty($format->format)) { @@ -197,10 +201,6 @@ function filter_format_save(&$format) { } $filter_info = filter_get_filters(); - // Programmatic saves may not contain any filters. - if (!isset($format->filters)) { - $format->filters = array(); - } foreach ($filter_info as $name => $filter) { // Add new filters without weight to the bottom. if (!isset($format->filters[$name]['weight'])) { @@ -280,6 +280,7 @@ function filter_format_delete($format, $fallback_id = NULL) { $fallback = filter_format_load($fallback_id); module_invoke_all('filter_format_delete', $format, $fallback); + // Clear the filter cache whenever a text format is deleted. filter_formats_reset(); cache_clear_all($format->format . ':', 'cache_filter', TRUE); } @@ -307,10 +308,10 @@ function filter_permission() { if (!empty($permission)) { // Only link to the text format configuration page if the user who is // viewing this will have access to that page. - $format_name_replacement = user_access('administer filters') ? l($format->name, 'admin/config/content/formats/' . $format->format) : drupal_placeholder(array('text' => $format->name)); + $format_name_replacement = user_access('administer filters') ? l($format->name, 'admin/config/content/formats/' . $format->format) : drupal_placeholder($format->name); $perms[$permission] = array( 'title' => t("Use the !text_format text format", array('!text_format' => $format_name_replacement,)), - 'description' => drupal_placeholder(array('text' => t('Warning: This permission may have security implications depending on how the text format is configured.'))), + 'description' => drupal_placeholder(t('Warning: This permission may have security implications depending on how the text format is configured.')), ); } } @@ -681,7 +682,11 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) if (empty($format_id)) { $format_id = filter_fallback_format(); } - $format = filter_format_load($format_id); + // If the requested text format does not exist, the text cannot be filtered. + if (!$format = filter_format_load($format_id)) { + watchdog('filter', 'Missing text format: %format.', array('%format' => $format_id), WATCHDOG_ALERT); + return ''; + } // Check for a cached version of this piece of text. $cache = $cache && !empty($format->cache); @@ -959,7 +964,9 @@ function _filter_tips($format_id, $long = FALSE) { foreach ($filters as $name => $filter) { if ($filter->status && isset($filter_info[$name]['tips callback']) && function_exists($filter_info[$name]['tips callback'])) { $tip = $filter_info[$name]['tips callback']($filter, $format, $long); - $tips[$format->name][$name] = array('tip' => $tip, 'id' => $name); + if (isset($tip)) { + $tips[$format->name][$name] = array('tip' => $tip, 'id' => $name); + } } } } @@ -1074,11 +1081,13 @@ function theme_filter_tips_more_info() { */ function theme_filter_guidelines($variables) { $format = $variables['format']; - - $name = isset($format->name) ? '<label>' . $format->name . ':</label>' : ''; $attributes['class'][] = 'filter-guidelines-item'; $attributes['class'][] = 'filter-guidelines-' . $format->format; - return '<div' . drupal_attributes($attributes) . '>' . $name . theme('filter_tips', array('tips' => _filter_tips($format->format, FALSE))) . '</div>'; + $output = '<div' . drupal_attributes($attributes) . '>'; + $output .= '<h3>' . check_plain($format->name) . '</h3>'; + $output .= theme('filter_tips', array('tips' => _filter_tips($format->format, FALSE))); + $output .= '</div>'; + return $output; } /** @@ -1291,46 +1300,202 @@ function _filter_url_settings($form, &$form_state, $filter, $format, $defaults) } /** - * URL filter. Automatically converts text web addresses (URLs, e-mail addresses, - * ftp links, etc.) into hyperlinks. + * URL filter. Automatically converts text into hyperlinks. + * + * This filter identifies and makes clickable three types of "links". + * - URLs like http://example.com. + * - E-mail addresses like name@example.com. + * - Web addresses without the "http://" protocol defined, like www.example.com. + * Each type must be processed separately, as there is no one regular + * expression that could possibly match all of the cases in one pass. */ function _filter_url($text, $filter) { - // Pass length to regexp callback + // Tags to skip and not recurse into. + $ignore_tags = 'a|script|style|code|pre'; + + // Pass length to regexp callback. _filter_url_trim(NULL, $filter->settings['filter_url_length']); - $text = ' ' . $text . ' '; + // Create an array which contains the regexps for each type of link. + // The key to the regexp is the name of a function that is used as + // callback function to process matches of the regexp. The callback function + // is to return the replacement for the match. The array is used and + // matching/replacement done below inside some loops. + $tasks = array(); + + // Prepare protocols pattern for absolute URLs. + // check_url() will replace any bad protocols with HTTP, so we need to support + // the identical list. While '//' is technically optional for MAILTO only, + // we cannot cleanly differ between protocols here without hard-coding MAILTO, + // so '//' is optional for all protocols. + // @see filter_xss_bad_protocol() + $protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal', 'rtsp')); + $protocols = implode(':(?://)?|', $protocols) . ':(?://)?'; + + // Prepare domain name pattern. + // The ICANN seems to be on track towards accepting more diverse top level + // domains, so this pattern has been "future-proofed" to allow for TLDs + // of length 2-64. + $domain = '(?:[A-Za-z0-9._+-]+\.)?[A-Za-z]{2,64}\b'; + $ip = '(?:[0-9]{1,3}\.){3}[0-9]{1,3}'; + $auth = '[a-zA-Z0-9:%_+*~#?&=.,/;-]+@'; + $trail = '[a-zA-Z0-9:%_+*~#&\[\]=/;?\.,-]*[a-zA-Z0-9:%_+*~#&\[\]=/;-]'; + + // Prepare pattern for optional trailing punctuation. + // Even these characters could have a valid meaning for the URL, such usage is + // rare compared to using a URL at the end of or within a sentence, so these + // trailing characters are optionally excluded. + $punctuation = '[\.,?!]*?'; // Match absolute URLs. - $text = preg_replace_callback("`(<p>|<li>|<br\s*/?>|[ \n\r\t\(])((http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", '_filter_url_parse_full_links', $text); + $url_pattern = "(?:$auth)?(?:$domain|$ip)/?(?:$trail)?"; + $pattern = "`((?:$protocols)(?:$url_pattern))($punctuation)`"; + $tasks['_filter_url_parse_full_links'] = $pattern; // Match e-mail addresses. - $text = preg_replace("`(<p>|<li>|<br\s*/?>|[ \n\r\t\(])([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", '\1<a href="mailto:\2">\2</a>\3', $text); + $url_pattern = "[A-Za-z0-9._-]+@(?:$domain)"; + $pattern = "`($url_pattern)`"; + $tasks['_filter_url_parse_email_links'] = $pattern; + + // Match www domains. + $url_pattern = "www\.(?:$domain)/?(?:$trail)?"; + $pattern = "`($url_pattern)($punctuation)`"; + $tasks['_filter_url_parse_partial_links'] = $pattern; + + // Each type of URL needs to be processed separately. The text is joined and + // re-split after each task, since all injected HTML tags must be correctly + // protected before the next task. + foreach ($tasks as $task => $pattern) { + // HTML comments need to be handled separately, as they may contain HTML + // markup, especially a '>'. Therefore, remove all comment contents and add + // them back later. + _filter_url_escape_comments('', TRUE); + $text = preg_replace_callback('`<!--(.*?)-->`s', '_filter_url_escape_comments', $text); + + // Split at all tags; ensures that no tags or attributes are processed. + $chunks = preg_split('/(<.+?>)/is', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + // PHP ensures that the array consists of alternating delimiters and + // literals, and begins and ends with a literal (inserting NULL as + // required). Therefore, the first chunk is always text: + $chunk_type = 'text'; + // If a tag of $ignore_tags is found, it is stored in $open_tag and only + // removed when the closing tag is found. Until the closing tag is found, + // no replacements are made. + $open_tag = ''; + + for ($i = 0; $i < count($chunks); $i++) { + if ($chunk_type == 'text') { + // Only process this text if there are no unclosed $ignore_tags. + if ($open_tag == '') { + // If there is a match, inject a link into this chunk via the callback + // function contained in $task. + $chunks[$i] = preg_replace_callback($pattern, $task, $chunks[$i]); + } + // Text chunk is done, so next chunk must be a tag. + $chunk_type = 'tag'; + } + else { + // Only process this tag if there are no unclosed $ignore_tags. + if ($open_tag == '') { + // Check whether this tag is contained in $ignore_tags. + if (preg_match("`<($ignore_tags)(?:\s|>)`i", $chunks[$i], $matches)) { + $open_tag = $matches[1]; + } + } + // Otherwise, check whether this is the closing tag for $open_tag. + else { + if (preg_match("`<\/$open_tag>`i", $chunks[$i], $matches)) { + $open_tag = ''; + } + } + // Tag chunk is done, so next chunk must be text. + $chunk_type = 'text'; + } + } - // Match www domains/addresses. - $text = preg_replace_callback("`(<p>|<li>|[ \n\r\t\(])(www\.[a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+~#\&=/;-])([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", '_filter_url_parse_partial_links', $text); - $text = substr($text, 1, -1); + $text = implode($chunks); + // Revert back to the original comment contents + _filter_url_escape_comments('', FALSE); + $text = preg_replace_callback('`<!--(.*?)-->`', '_filter_url_escape_comments', $text); + } return $text; } /** - * Make links out of absolute URLs. + * preg_replace callback to make links out of absolute URLs. */ function _filter_url_parse_full_links($match) { - $match[2] = decode_entities($match[2]); - $caption = check_plain(_filter_url_trim($match[2])); - $match[2] = check_url($match[2]); - return $match[1] . '<a href="' . $match[2] . '">' . $caption . '</a>' . $match[5]; + // The $i:th parenthesis in the regexp contains the URL. + $i = 1; + + $match[$i] = decode_entities($match[$i]); + $caption = check_plain(_filter_url_trim($match[$i])); + $match[$i] = check_plain($match[$i]); + return '<a href="' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1]; +} + +/** + * preg_replace callback to make links out of e-mail addresses. + */ +function _filter_url_parse_email_links($match) { + // The $i:th parenthesis in the regexp contains the URL. + $i = 0; + + $match[$i] = decode_entities($match[$i]); + $caption = check_plain(_filter_url_trim($match[$i])); + $match[$i] = check_plain($match[$i]); + return '<a href="mailto:' . $match[$i] . '">' . $caption . '</a>'; } /** - * Make links out of domain names starting with "www." + * preg_replace callback to make links out of domain names starting with "www." */ function _filter_url_parse_partial_links($match) { - $match[2] = decode_entities($match[2]); - $caption = check_plain(_filter_url_trim($match[2])); - $match[2] = check_plain($match[2]); - return $match[1] . '<a href="http://' . $match[2] . '">' . $caption . '</a>' . $match[3]; + // The $i:th parenthesis in the regexp contains the URL. + $i = 1; + + $match[$i] = decode_entities($match[$i]); + $caption = check_plain(_filter_url_trim($match[$i])); + $match[$i] = check_plain($match[$i]); + return '<a href="http://' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1]; +} + +/** + * preg_replace callback to escape contents of HTML comments + * + * @param $match + * An array containing matches to replace from preg_replace_callback(), + * whereas $match[1] is expected to contain the content to be filtered. + * @param $escape + * (optional) Boolean whether to escape (TRUE) or unescape comments (FALSE). + * Defaults to neither. If TRUE, statically cached $comments are reset. + */ +function _filter_url_escape_comments($match, $escape = NULL) { + static $mode, $comments = array(); + + if (isset($escape)) { + $mode = $escape; + if ($escape){ + $comments = array(); + } + return; + } + + // Replace all HTML coments with a '<!-- [hash] -->' placeholder. + if ($mode) { + $content = $match[1]; + $hash = md5($content); + $comments[$hash] = $content; + return "<!-- $hash -->"; + } + // Or replace placeholders with actual comment contents. + else { + $hash = $match[1]; + $hash = trim($hash); + $content = $comments[$hash]; + return "<!--$content-->"; + } } /** @@ -1343,7 +1508,7 @@ function _filter_url_trim($text, $length = NULL) { } // Use +3 for '...' string length. - if (strlen($text) > $_length + 3) { + if ($_length && strlen($text) > $_length + 3) { $text = substr($text, 0, $_length) . '...'; } @@ -1372,11 +1537,11 @@ function _filter_autop($text) { // All block level tags $block = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|address|p|h[1-6]|hr)'; - // Split at <pre>, <script>, <style> and </pre>, </script>, </style> tags. + // Split at opening and closing PRE, SCRIPT, STYLE, OBJECT tags and comments. // We don't apply any processing to the contents of these tags to avoid messing // up code. We look for matched pairs and allow basic nesting. For example: // "processed <pre> ignored <script> ignored </script> ignored </pre> processed" - $chunks = preg_split('@(</?(?:pre|script|style|object)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); // Note: PHP ensures the array consists of alternating delimiters and literals // and begins and ends with a literal (inserting NULL as required). $ignore = FALSE; @@ -1385,7 +1550,8 @@ function _filter_autop($text) { foreach ($chunks as $i => $chunk) { if ($i % 2) { // Opening or closing tag? - $open = ($chunk[1] != '/'); + $open = ($chunk[1] != '/' || $chunk[1] != '!'); + $comment = (substr($chunk, 0, 4) == '<!--'); list($tag) = preg_split('/[ >]/', substr($chunk, 2 - $open), 2); if (!$ignore) { if ($open) { @@ -1394,7 +1560,7 @@ function _filter_autop($text) { } } // Only allow a matching tag to close it. - elseif (!$open && $ignoretag == $tag) { + elseif ((!$open && $ignoretag == $tag) || $comment) { $ignore = FALSE; $ignoretag = ''; } diff --git a/modules/filter/filter.test b/modules/filter/filter.test index a6a1555c9486e9793ef820ab92f75586e85fc319..4a737d9f6878500e63cf2b805ea85c0c1e48b136 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -1,5 +1,5 @@ <?php -// $Id: filter.test,v 1.69 2010/07/01 19:41:18 dries Exp $ +// $Id: filter.test,v 1.75 2010/09/04 17:55:43 dries Exp $ /** * Tests for text format and filter CRUD operations. @@ -640,49 +640,129 @@ class FilterNoFormatTestCase extends DrupalWebTestCase { } /** - * Unit tests for core filters. + * Security tests for missing/vanished text formats or filters. */ -class FilterUnitTestCase extends DrupalUnitTestCase { +class FilterSecurityTestCase extends DrupalWebTestCase { public static function getInfo() { return array( - 'name' => 'Core filters', - 'description' => 'Filter each filter individually: convert line breaks, correct broken HTML.', + 'name' => 'Security', + 'description' => 'Test the behavior of check_markup() when a filter or text format vanishes.', 'group' => 'Filter', ); } + function setUp() { + parent::setUp('php', 'filter_test'); + $this->admin_user = $this->drupalCreateUser(array('administer modules', 'administer filters', 'administer site configuration')); + $this->drupalLogin($this->admin_user); + } + /** - * Test the line break filter. + * Test that filtered content is emptied when an actively used filter module is disabled. */ - function testLineBreakFilter() { - // Single line breaks should be changed to <br /> tags, while paragraphs - // separated with double line breaks should be enclosed with <p></p> tags. - $f = _filter_autop("aaa\nbbb\n\nccc"); - $this->assertEqual(str_replace("\n", '', $f), "<p>aaa<br />bbb</p><p>ccc</p>", t('Line breaking basic case.')); + function testDisableFilterModule() { + // Create a new node. + $node = $this->drupalCreateNode(array('promote' => 1)); + $body_raw = $node->body[LANGUAGE_NONE][0]['value']; + $format_id = $node->body[LANGUAGE_NONE][0]['format']; + $this->drupalGet('node/' . $node->nid); + $this->assertText($body_raw, t('Node body found.')); - // Text within some contexts should not be processed. - $f = _filter_autop("<script>aaa\nbbb\n\nccc</script>"); - $this->assertEqual($f, "<script>aaa\nbbb\n\nccc</script>", t('Line breaking -- do not break scripts.')); + // Enable the filter_test_replace filter. + $edit = array( + 'filters[filter_test_replace][status]' => 1, + ); + $this->drupalPost('admin/config/content/formats/' . $format_id, $edit, t('Save configuration')); - $f = _filter_autop('<p><div> </div></p>'); - $this->assertEqual(substr_count($f, '<p>'), substr_count($f, '</p>'), t('Make sure line breaking produces matching paragraph tags.')); + // Verify that filter_test_replace filter replaced the content. + $this->drupalGet('node/' . $node->nid); + $this->assertNoText($body_raw, t('Node body not found.')); + $this->assertText('Filter: Testing filter', t('Testing filter output found.')); + + // Delete the text format entirely. + $this->drupalPost('admin/config/content/formats/' . $format_id . '/delete', array(), t('Delete')); - $f = _filter_autop('<div><p> </p></div>'); - $this->assertEqual(substr_count($f, '<p>'), substr_count($f, '</p>'), t('Make sure line breaking produces matching paragraph tags.')); + // Verify that the content is empty, because the text format does not exist. + $this->drupalGet('node/' . $node->nid); + $this->assertNoText($body_raw, t('Node body not found.')); + } +} - $f = _filter_autop('<blockquote><pre>aaa</pre></blockquote>'); - $this->assertEqual(substr_count($f, '<p>'), substr_count($f, '</p>'), t('Make sure line breaking produces matching paragraph tags.')); +/** + * Unit tests for core filters. + */ +class FilterUnitTestCase extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'Filter module filters', + 'description' => 'Tests Filter module filters individually.', + 'group' => 'Filter', + ); + } + /** + * Test the line break filter. + */ + function testLineBreakFilter() { + // Setup dummy filter object. + $filter = new stdClass; + $filter->callback = '_filter_autop'; + + // Since the line break filter naturally needs plenty of newlines in test + // strings and expectations, we're using "\n" instead of regular newlines + // here. + $tests = array( + // Single line breaks should be changed to <br /> tags, while paragraphs + // separated with double line breaks should be enclosed with <p></p> tags. + "aaa\nbbb\n\nccc" => array( + "<p>aaa<br />\nbbb</p>\n<p>ccc</p>" => TRUE, + ), + // Skip contents of certain block tags entirely. + "<script>aaa\nbbb\n\nccc</script> +<style>aaa\nbbb\n\nccc</style> +<pre>aaa\nbbb\n\nccc</pre> +<object>aaa\nbbb\n\nccc</object> +<iframe>aaa\nbbb\n\nccc</iframe> +" => array( + "<script>aaa\nbbb\n\nccc</script>" => TRUE, + "<style>aaa\nbbb\n\nccc</style>" => TRUE, + "<pre>aaa\nbbb\n\nccc</pre>" => TRUE, + "<object>aaa\nbbb\n\nccc</object>" => TRUE, + "<iframe>aaa\nbbb\n\nccc</iframe>" => TRUE, + ), + // Skip comments entirely. + "One. <!-- comment --> Two.\n<!--\nThree.\n-->\n" => array( + '<!-- comment -->' => TRUE, + "<!--\nThree.\n-->" => TRUE, + ), + // Resulting HTML should produce matching paragraph tags. + '<p><div> </div></p>' => array( + "<p>\n<div> </div>\n</p>" => TRUE, + ), + '<div><p> </p></div>' => array( + "<div>\n</div>" => TRUE, + ), + '<blockquote><pre>aaa</pre></blockquote>' => array( + "<blockquote><pre>aaa</pre></blockquote>" => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + + // Very long string hitting PCRE limits. $limit = max(ini_get('pcre.backtrack_limit'), ini_get('pcre.recursion_limit')); - $f = _filter_autop($this->randomName($limit)); - $this->assertNotEqual($f, '', t('Make sure line breaking can process long strings.')); + $source = $this->randomName($limit); + $result = _filter_autop($source); + $success = $this->assertEqual($result, '<p>' . $source . "</p>\n", t('Line break filter can process very long strings.')); + if (!$success) { + $this->verbose("\n" . $source . "\n<hr />\n" . $result); + } } /** - * Test limiting allowed tags, XSS prevention and adding 'nofollow' to links. + * Tests limiting allowed tags and XSS prevention. * - * XSS tests assume that script is disallowed on default and src is allowed - * on default, but on* and style are disallowed. + * XSS tests assume that script is disallowed by default and src is allowed + * by default, but on* and style attributes are disallowed. * * Script injection vectors mostly adopted from http://ha.ckers.org/xss.html. * @@ -690,7 +770,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase { * - CVE-2002-1806, ~CVE-2005-0682, ~CVE-2005-2106, CVE-2005-3973, * CVE-2006-1226 (= rev. 1.112?), CVE-2008-0273, CVE-2008-3740. */ - function testHtmlFilter() { + function testFilterXSS() { // Tag stripping, different ways to work around removal of HTML tags. $f = filter_xss('<script>alert(0)</script>'); $this->assertNoNormalized($f, 'script', t('HTML tag stripping -- simple script without special characters.')); @@ -875,7 +955,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase { * @todo Class, id, name and xmlns should be added to disallowed attributes, * or better a whitelist approach should be used for that too. */ - function testFilter() { + function testHtmlFilter() { // Setup dummy filter object. $filter = new stdClass(); $filter->settings = array( @@ -943,9 +1023,6 @@ class FilterUnitTestCase extends DrupalUnitTestCase { $f = _filter_html("<\0a\0 href=\"http://www.example.com/\">text</a>", $filter); $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- some nulls.')); - $f = _filter_html('<!--[if true]><a href="http://www.example.com/">text</a><![endif]-->', $filter); - $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- link within a comment.')); - $f = _filter_html('<a href="http://www.example.com/" rel="follow">text</a>', $filter); $this->assertNoNormalized($f, 'rel="follow"', t('Spam deterrent evasion -- with rel set - rel="follow" removed.')); $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- with rel set - rel="nofollow" added.')); @@ -954,7 +1031,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase { /** * Test the loose, admin HTML filter. */ - function testAdminHtmlFilter() { + function testFilterXSSAdmin() { // DRUPAL-SA-2008-044 $f = filter_xss_admin('<object />'); $this->assertNoNormalized($f, 'object', t('Admin HTML filter -- should not allow object tag.')); @@ -967,98 +1044,373 @@ class FilterUnitTestCase extends DrupalUnitTestCase { } /** - * Test the HTML escaping filter. - */ - function testNoHtmlFilter() { - $this->_testEscapedHTML('_filter_html_escape'); - } - - /** - * Test that the check_plain() function escapes HTML correctly. + * Tests the HTML escaping filter. + * + * check_plain() is not tested here. */ - function testCheckPlain() { - $this->_testEscapedHTML('check_plain'); + function testHtmlEscapeFilter() { + // Setup dummy filter object. + $filter = new stdClass; + $filter->callback = '_filter_html_escape'; + + $tests = array( + " One. <!-- \"comment\" --> Two'.\n<p>Three.</p>\n " => array( + "One. <!-- "comment" --> Two'.\n<p>Three.</p>" => TRUE, + ' One.' => FALSE, + "</p>\n " => FALSE, + ), + ); + $this->assertFilteredString($filter, $tests); } /** - * Test the URL filter. + * Tests the URL filter. */ function testUrlFilter() { // Setup dummy filter object. - $filter = new stdClass(); + $filter = new stdClass; + $filter->callback = '_filter_url'; $filter->settings = array( 'filter_url_length' => 496, ); - - // Converting URLs. - $f = _filter_url('http://www.example.com/', $filter); - $this->assertEqual($f, '<a href="http://www.example.com/">http://www.example.com/</a>', t('Converting URLs.')); - - $f = _filter_url('http://www.example.com/?a=1&b=2', $filter); - $this->assertEqual($f, '<a href="http://www.example.com/?a=1&b=2">http://www.example.com/?a=1&b=2</a>', t('Converting URLs -- ampersands.')); - - $f = _filter_url('ftp://user:pass@ftp.example.com/dir1/dir2', $filter); - $this->assertEqual($f, '<a href="ftp://user:pass@ftp.example.com/dir1/dir2">ftp://user:pass@ftp.example.com/dir1/dir2</a>', t('Converting URLs -- FTP scheme.')); - - // Converting domain names. - $f = _filter_url('www.example.com', $filter); - $this->assertEqual($f, '<a href="http://www.example.com">www.example.com</a>', t('Converting domain names.')); - - $f = _filter_url('<li>www.example.com</li>', $filter); - $this->assertEqual($f, '<li><a href="http://www.example.com">www.example.com</a></li>', t('Converting domain names -- domain in a list.')); - - $f = _filter_url('(www.example.com/dir?a=1&b=2#a)', $filter); - $this->assertEqual($f, '(<a href="http://www.example.com/dir?a=1&b=2#a">www.example.com/dir?a=1&b=2#a</a>)', t('Converting domain names -- domain in parentheses.')); - - // Converting e-mail addresses. - $f = _filter_url('johndoe@example.com', $filter); - $this->assertEqual($f, '<a href="mailto:johndoe@example.com">johndoe@example.com</a>', t('Converting e-mail addresses.')); - - $f = _filter_url('aaa@sub.tv', $filter); - $this->assertEqual($f, '<a href="mailto:aaa@sub.tv">aaa@sub.tv</a>', t('Converting e-mail addresses -- a short e-mail from Tuvalu.')); + // @todo Possible categories: + // - absolute, mail, partial + // - characters/encoding, surrounding markup, security + + // Filter selection/pattern matching. + $tests = array( + // HTTP URLs. + ' +http://example.com or www.example.com +' => array( + '<a href="http://example.com">http://example.com</a>' => TRUE, + '<a href="http://www.example.com">www.example.com</a>' => TRUE, + ), + // MAILTO URLs. + ' +person@example.com or mailto:person2@example.com +' => array( + '<a href="mailto:person@example.com">person@example.com</a>' => TRUE, + '<a href="mailto:person2@example.com">mailto:person2@example.com</a>' => TRUE, + ), + // URI parts. + ' +http://trailingslash.com/ or www.trailingslash.com/ +http://host.com/some/path?query=foo&bar[baz]=beer#fragment or www.host.com/some/path?query=foo&bar[baz]=beer#fragment +ftp://user:pass@ftp.example.com/~home/dir1 +sftp://user@nonstandardport:222/dir +ssh://192.168.0.100/srv/git/drupal.git +' => array( + '<a href="http://trailingslash.com/">http://trailingslash.com/</a>' => TRUE, + '<a href="http://www.trailingslash.com/">www.trailingslash.com/</a>' => TRUE, + '<a href="http://host.com/some/path?query=foo&bar[baz]=beer#fragment">http://host.com/some/path?query=foo&bar[baz]=beer#fragment</a>' => TRUE, + '<a href="http://www.host.com/some/path?query=foo&bar[baz]=beer#fragment">www.host.com/some/path?query=foo&bar[baz]=beer#fragment</a>' => TRUE, + '<a href="ftp://user:pass@ftp.example.com/~home/dir1">ftp://user:pass@ftp.example.com/~home/dir1</a>' => TRUE, + '<a href="sftp://user@nonstandardport:222/dir">sftp://user@nonstandardport:222/dir</a>' => TRUE, + '<a href="ssh://192.168.0.100/srv/git/drupal.git">ssh://192.168.0.100/srv/git/drupal.git</a>' => TRUE, + ), + // Encoding. + ' +http://ampersand.com/?a=1&b=2 +http://encoded.com/?a=1&b=2 +' => array( + '<a href="http://ampersand.com/?a=1&b=2">http://ampersand.com/?a=1&b=2</a>' => TRUE, + '<a href="http://encoded.com/?a=1&b=2">http://encoded.com/?a=1&b=2</a>' => TRUE, + ), + // Domain name length. + ' +www.ex.ex or www.example.example or www.toolongdomainexampledomainexampledomainexampledomainexampledomain or +me@me.tv +' => array( + '<a href="http://www.ex.ex">www.ex.ex</a>' => TRUE, + '<a href="http://www.example.example">www.example.example</a>' => TRUE, + 'http://www.toolong' => FALSE, + '<a href="mailto:me@me.tv">me@me.tv</a>' => TRUE, + ), + // Absolute URL protocols. + // The list to test is found in the beginning of _filter_url() at + // $protocols = variable_get('filter_allowed_protocols'... (approx line 1325). + ' +https://example.com, +ftp://ftp.example.com, +news://example.net, +telnet://example, +irc://example.host, +ssh://odd.geek, +sftp://secure.host?, +webcal://calendar, +rtsp://127.0.0.1, +not foo://disallowed.com. +' => array( + 'href="https://example.com"' => TRUE, + 'href="ftp://ftp.example.com"' => TRUE, + 'href="news://example.net"' => TRUE, + 'href="telnet://example"' => TRUE, + 'href="irc://example.host"' => TRUE, + 'href="ssh://odd.geek"' => TRUE, + 'href="sftp://secure.host"' => TRUE, + 'href="webcal://calendar"' => TRUE, + 'href="rtsp://127.0.0.1"' => TRUE, + 'href="foo://disallowed.com"' => FALSE, + 'not foo://disallowed.com.' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + + // Surrounding text/punctuation. + $tests = array( + ' +Partial URL with trailing period www.partial.com. +E-mail with trailing comma person@example.com, +Absolute URL with trailing question http://www.absolute.com? +Query string with trailing exclamation www.query.com/index.php?a=! +Partial URL with 3 trailing www.partial.periods... +E-mail with 3 trailing exclamations@example.com!!! +Absolute URL and query string with 2 different punctuation characters (http://www.example.com/q=abc). +' => array( + 'period <a href="http://www.partial.com">www.partial.com</a>.' => TRUE, + 'comma <a href="mailto:person@example.com">person@example.com</a>,' => TRUE, + 'question <a href="http://www.absolute.com">http://www.absolute.com</a>?' => TRUE, + 'exclamation <a href="http://www.query.com/index.php?a=">www.query.com/index.php?a=</a>!' => TRUE, + 'trailing <a href="http://www.partial.periods">www.partial.periods</a>...' => TRUE, + 'trailing <a href="mailto:exclamations@example.com">exclamations@example.com</a>!!!' => TRUE, + 'characters (<a href="http://www.example.com/q=abc">http://www.example.com/q=abc</a>).' => TRUE, + ), + ' +(www.parenthesis.com/dir?a=1&b=2#a) +' => array( + '(<a href="http://www.parenthesis.com/dir?a=1&b=2#a">www.parenthesis.com/dir?a=1&b=2#a</a>)' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + + // Surrounding markup. + $tests = array( + ' +<p xmlns="www.namespace.com" /> +<p xmlns="http://namespace.com"> +An <a href="http://example.com" title="Read more at www.example.info...">anchor</a>. +</p> +' => array( + '<p xmlns="www.namespace.com" />' => TRUE, + '<p xmlns="http://namespace.com">' => TRUE, + 'href="http://www.namespace.com"' => FALSE, + 'href="http://namespace.com"' => FALSE, + 'An <a href="http://example.com" title="Read more at www.example.info...">anchor</a>.' => TRUE, + ), + ' +Not <a href="foo">www.relative.com</a> or <a href="http://absolute.com">www.absolute.com</a> +but <strong>http://www.strong.net</strong> or <em>www.emphasis.info</em> +' => array( + '<a href="foo">www.relative.com</a>' => TRUE, + 'href="http://www.relative.com"' => FALSE, + '<a href="http://absolute.com">www.absolute.com</a>' => TRUE, + '<strong><a href="http://www.strong.net">http://www.strong.net</a></strong>' => TRUE, + '<em><a href="http://www.emphasis.info">www.emphasis.info</a></em>' => TRUE, + ), + ' +Test <code>using www.example.com the code tag</code>. +' => array( + 'href' => FALSE, + 'http' => FALSE, + ), + ' +Intro. +<blockquote> +Quoted text linking to www.example.com, written by person@example.com, originating from http://origin.example.com. <code>@see www.usage.example.com or <em>www.example.info</em> bla bla</code>. +</blockquote> + +Outro. +' => array( + 'href="http://www.example.com"' => TRUE, + 'href="mailto:person@example.com"' => TRUE, + 'href="http://origin.example.com"' => TRUE, + 'http://www.usage.example.com' => FALSE, + 'http://www.example.info' => FALSE, + 'Intro.' => TRUE, + 'Outro.' => TRUE, + ), + ' +Unknown tag <x>containing x and www.example.com</x>? And a tag <pooh>beginning with p and containing www.example.pooh with p?</pooh> +' => array( + 'href="http://www.example.com"' => TRUE, + 'href="http://www.example.pooh"' => TRUE, + ), + ' +<p>Test <br/>: This is a www.example17.com example <strong>with</strong> various http://www.example18.com tags. *<br/> + It is important www.example19.com to *<br/>test different URLs and http://www.example20.com in the same paragraph. *<br> +HTML www.example21.com soup by person@example22.com can litererally http://www.example23.com contain *img*<img> anything. Just a www.example24.com with http://www.example25.com thrown in. www.example26.com from person@example27.com with extra http://www.example28.com. +' => array( + 'href="http://www.example17.com"' => TRUE, + 'href="http://www.example18.com"' => TRUE, + 'href="http://www.example19.com"' => TRUE, + 'href="http://www.example20.com"' => TRUE, + 'href="http://www.example21.com"' => TRUE, + 'href="mailto:person@example22.com"' => TRUE, + 'href="http://www.example23.com"' => TRUE, + 'href="http://www.example24.com"' => TRUE, + 'href="http://www.example25.com"' => TRUE, + 'href="http://www.example26.com"' => TRUE, + 'href="mailto:person@example27.com"' => TRUE, + 'href="http://www.example28.com"' => TRUE, + ), + ' +<script> +<!-- + // @see www.example.com + var exampleurl = "http://example.net"; +--> +<!--//--><![CDATA[//><!-- + // @see www.example.com + var exampleurl = "http://example.net"; +//--><!]]> +</script> +' => array( + 'href="http://www.example.com"' => FALSE, + 'href="http://example.net"' => FALSE, + ), + ' +<style>body { + background: url(http://example.com/pixel.gif); +}</style> +' => array( + 'href' => FALSE, + ), + ' +<!-- Skip any URLs like www.example.com in comments --> +' => array( + 'href' => FALSE, + ), + ' +<!-- Skip any URLs like +www.example.com with a newline in comments --> +' => array( + 'href' => FALSE, + ), + ' +<!-- Skip any URLs like www.comment.com in comments. <p>Also ignore http://commented.out/markup.</p> --> +' => array( + 'href' => FALSE, + ), + ' +<dl> +<dt>www.example.com</dt> +<dd>http://example.com</dd> +<dd>person@example.com</dd> +<dt>Check www.example.net</dt> +<dd>Some text around http://www.example.info by person@example.info?</dd> +</dl> +' => array( + 'href="http://www.example.com"' => TRUE, + 'href="http://example.com"' => TRUE, + 'href="mailto:person@example.com"' => TRUE, + 'href="http://www.example.net"' => TRUE, + 'href="http://www.example.info"' => TRUE, + 'href="mailto:person@example.info"' => TRUE, + ), + ' +<div>www.div.com</div> +<ul> +<li>http://listitem.com</li> +<li class="odd">www.class.listitem.com</li> +</ul> +' => array( + '<div><a href="http://www.div.com">www.div.com</a></div>' => TRUE, + '<li><a href="http://listitem.com">http://listitem.com</a></li>' => TRUE, + '<li class="odd"><a href="http://www.class.listitem.com">www.class.listitem.com</a></li>' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); // URL trimming. - $filter->settings['filter_url_length'] = 28; - - $f = _filter_url('http://www.example.com/d/ff.ext?a=1&b=2#a1', $filter); - $this->assertNormalized($f, 'http://www.example.com/d/ff....', t('URL trimming.')); - - // Not breaking existing links. - $f = _filter_url('<a href="http://www.example.com">www.example.com</a>', $filter); - $this->assertEqual($f, '<a href="http://www.example.com">www.example.com</a>', t('Converting URLs -- do not break existing links.')); - - $f = _filter_url('<a href="foo">http://www.example.com</a>', $filter); - $this->assertEqual($f, '<a href="foo">http://www.example.com</a>', t('Converting URLs -- do not break existing, relative links.')); - - // Addresses within some tags such as code or script should not be converted. - $f = _filter_url('<code>http://www.example.com</code>', $filter); - $this->assertEqual($f, '<code>http://www.example.com</code>', t('Converting URLs -- skip code contents.')); - - $f = _filter_url('<code><em>http://www.example.com</em></code>', $filter); - $this->assertEqual($f, '<code><em>http://www.example.com</em></code>', t('Converting URLs -- really skip code contents.')); - - $f = _filter_url('<script>http://www.example.com</script>', $filter); - $this->assertEqual($f, '<script>http://www.example.com</script>', t('Converting URLs -- do not process scripts.')); - - // Addresses in attributes should not be converted. - $f = _filter_url('<p xmlns="http://www.example.com" />', $filter); - $this->assertEqual($f, '<p xmlns="http://www.example.com" />', t('Converting URLs -- do not convert addresses in attributes.')); - - $f = _filter_url('<a title="Go to www.example.com" href="http://www.example.com">text</a>', $filter); - $this->assertEqual($f, '<a title="Go to www.example.com" href="http://www.example.com">text</a>', t('Converting URLs -- do not break existing links with custom title attribute.')); + $filter->settings['filter_url_length'] = 20; + $tests = array( + 'www.trimmed.com/d/ff.ext?a=1&b=2#a1' => array( + '<a href="http://www.trimmed.com/d/ff.ext?a=1&b=2#a1">www.trimmed.com/d/ff...</a>' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + } - // Even though a dot at the end of a URL can indicate a fully qualified - // domain name, such usage is rare compared to using a link at the end - // of a sentence, so remove the dot from the link. - // @todo It can also be used at the end of a filename or a query string. - $f = _filter_url('www.example.com.', $filter); - $this->assertEqual($f, '<a href="http://www.example.com">www.example.com</a>.', t('Converting URLs -- do not recognize a dot at the end of a domain name (FQDNs).')); + /** + * Asserts multiple filter output expectations for multiple input strings. + * + * @param $filter + * A input filter object. + * @param $tests + * An associative array, whereas each key is an arbitrary input string and + * each value is again an associative array whose keys are filter output + * strings and whose values are Booleans indicating whether the output is + * expected or not. + * + * For example: + * @code + * $tests = array( + * 'Input string' => array( + * '<p>Input string</p>' => TRUE, + * 'Input string<br' => FALSE, + * ), + * ); + * @endcode + */ + function assertFilteredString($filter, $tests) { + foreach ($tests as $source => $tasks) { + $function = $filter->callback; + $result = $function($source, $filter); + foreach ($tasks as $value => $is_expected) { + // Not using assertIdentical, since combination with strpos() is hard to grok. + if ($is_expected) { + $success = $this->assertTrue(strpos($result, $value) !== FALSE, t('@source: @value found.', array( + '@source' => var_export($source, TRUE), + '@value' => var_export($value, TRUE), + ))); + } + else { + $success = $this->assertTrue(strpos($result, $value) === FALSE, t('@source: @value not found.', array( + '@source' => var_export($source, TRUE), + '@value' => var_export($value, TRUE), + ))); + } + if (!$success) { + $this->verbose('Source:<pre>' . check_plain(var_export($source, TRUE)) . '</pre>' + . '<hr />' . 'Result:<pre>' . check_plain(var_export($result, TRUE)) . '</pre>' + . '<hr />' . ($is_expected ? 'Found:' : 'Not found:') + . '<pre>' . check_plain(var_export($value, TRUE)) . '</pre>' + ); + } + } + } + } - $f = _filter_url('http://www.example.com.', $filter); - $this->assertEqual($f, '<a href="http://www.example.com">http://www.example.com</a>.', t('Converting URLs -- do not recognize a dot at the end of an URL (FQDNs).')); + /** + * Tests URL filter on longer content. + * + * Filters based on regular expressions should also be tested with a more + * complex content than just isolated test lines. + * The most common errors are: + * - accidental '*' (greedy) match instead of '*?' (minimal) match. + * - only matching first occurrence instead of all. + * - newlines not matching '.*'. + * + * This test covers: + * - Document with multiple newlines and paragraphs (two newlines). + * - Mix of several HTML tags, invalid non-HTML tags, tags to ignore and HTML + * comments. + * - Empty HTML tags (BR, IMG). + * - Mix of absolute and partial URLs, and e-mail addresses in one content. + */ + function testUrlFilterContent() { + // Setup dummy filter object. + $filter = new stdClass; + $filter->settings = array( + 'filter_url_length' => 496, + ); + $path = drupal_get_path('module', 'filter') . '/tests'; - $f = _filter_url('www.example.com/index.php?a=.', $filter); - $this->assertEqual($f, '<a href="http://www.example.com/index.php?a=">www.example.com/index.php?a=</a>.', t('Converting URLs -- do forget about a dot at the end of a query string.')); + $input = file_get_contents($path . '/filter.url-input.txt'); + $expected = file_get_contents($path . '/filter.url-output.txt'); + $result = _filter_url($input, $filter); + $this->assertIdentical($result, $expected, 'Complex HTML document was correctly processed.'); } /** @@ -1226,31 +1578,6 @@ alert("test") function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') { return $this->assertTrue(strpos(strtolower(decode_entities($haystack)), $needle) === FALSE, $message, $group); } - - /** - * Helper method to test functions that are intended to escape HTML. - * - * @param $function - * The name of the function to test. - */ - function _testEscapedHTML($function) { - // Define string replacements for the assertion messages. - $replacements = array('@function' => $function); - - // Test that characters that have special meaning in XML are changed into - // entities. - $f = $function('<>&"'); - $this->assertEqual($f, '<>&"', t('The @function() function correctly filters basic HTML entities.', $replacements)); - - // A single quote can also be used for evil things in some contexts. - $f = $function('\''); - $this->assertEqual($f, ''', t('The @function() function correctly filters single quotes.', $replacements)); - - // Test that the filter is not fooled by different evasion techniques. - // Ignore PHP 5.3+ invalid multibyte sequence warning. - $f = @$function("\xc2\""); - $this->assertEqual($f, '', t('The @function() function correctly filters invalid UTF-8.', $replacements)); - } } /** diff --git a/modules/filter/tests/filter.url-input.txt b/modules/filter/tests/filter.url-input.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b33af56ca94c370c599130cfdabccd55e135b9a --- /dev/null +++ b/modules/filter/tests/filter.url-input.txt @@ -0,0 +1,36 @@ +This is just a www.test.com. paragraph with person@test.com. some http://www.test.com. urls thrown in and also <code>using www.test.com the code tag</code>. + +<blockquote> +This is just a www.test.com. paragraph with person@test.com. some http://www.test.com. urls thrown in and also <code>using www.test.com the code tag</code>. +</blockquote> + +<code>Testing code tag http://www.test.com abc</code> + +http://www.test.com +www.test.com +person@test.com +<code>www.test.com</code> + +What about tags that don't exist <x>like x say www.test.com</x>? And what about tag <pooh>beginning www.test.com with p?</pooh> + +Test <br/>: This is just a www.test.com. paragraph <strong>with</strong> some http://www.test.com urls thrown in. *<br/> This is just a www.test.com paragraph *<br/> with some http://www.test.com urls thrown in. *<br/>This is just a www.test.com paragraph person@test.com with some http://www.test.com urls *img*<img/> thrown in. This is just a www.test.com paragraph with some http://www.test.com urls thrown in. This is just a www.test.com paragraph person@test.com with some http://www.test.com urls thrown in. + +This is just a www.test.com paragraph <strong>with</strong> some http://www.test.com urls thrown in. <br /> This is just a www.test.com paragraph with some http://www.test.com urls thrown in. This is just a www.test.com paragraph person@test.com with some http://www.test.com urls thrown in. This is just a www.test.com paragraph with some http://www.test.com urls thrown in. This is just a www.test.com paragraph person@test.com with some http://www.test.com urls thrown in. + +The old URL filter has problems with <a title="kind of link www.example.com with text" href="http://www.example.com">this kind of link</a> with www address as part of text in title. www.test.com + +<!-- This url www.test.com is inside a comment --> + +<dl> +<dt>www.test.com</dt> +<dd>http://www.test.com</dd> +<dd>person@test.com</dd> +<dt>check www.test.com</dt> +<dd>this with some text around: http://www.test.com not so easy person@test.com now?</dd> +</dl> + +<!-- <p>This url http://www.test.com is + inside a comment containing newlines and +<em>html</em> tags.</p> --> + +This is the end! \ No newline at end of file diff --git a/modules/filter/tests/filter.url-output.txt b/modules/filter/tests/filter.url-output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cc507308864710ee321c8a225b54f5bcbd7ad0a --- /dev/null +++ b/modules/filter/tests/filter.url-output.txt @@ -0,0 +1,36 @@ +This is just a <a href="http://www.test.com">www.test.com</a>. paragraph with <a href="mailto:person@test.com">person@test.com</a>. some <a href="http://www.test.com">http://www.test.com</a>. urls thrown in and also <code>using www.test.com the code tag</code>. + +<blockquote> +This is just a <a href="http://www.test.com">www.test.com</a>. paragraph with <a href="mailto:person@test.com">person@test.com</a>. some <a href="http://www.test.com">http://www.test.com</a>. urls thrown in and also <code>using www.test.com the code tag</code>. +</blockquote> + +<code>Testing code tag http://www.test.com abc</code> + +<a href="http://www.test.com">http://www.test.com</a> +<a href="http://www.test.com">www.test.com</a> +<a href="mailto:person@test.com">person@test.com</a> +<code>www.test.com</code> + +What about tags that don't exist <x>like x say <a href="http://www.test.com">www.test.com</a></x>? And what about tag <pooh>beginning <a href="http://www.test.com">www.test.com</a> with p?</pooh> + +Test <br/>: This is just a <a href="http://www.test.com">www.test.com</a>. paragraph <strong>with</strong> some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. *<br/> This is just a <a href="http://www.test.com">www.test.com</a> paragraph *<br/> with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. *<br/>This is just a <a href="http://www.test.com">www.test.com</a> paragraph <a href="mailto:person@test.com">person@test.com</a> with some <a href="http://www.test.com">http://www.test.com</a> urls *img*<img/> thrown in. This is just a <a href="http://www.test.com">www.test.com</a> paragraph with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. This is just a <a href="http://www.test.com">www.test.com</a> paragraph <a href="mailto:person@test.com">person@test.com</a> with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. + +This is just a <a href="http://www.test.com">www.test.com</a> paragraph <strong>with</strong> some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. <br /> This is just a <a href="http://www.test.com">www.test.com</a> paragraph with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. This is just a <a href="http://www.test.com">www.test.com</a> paragraph <a href="mailto:person@test.com">person@test.com</a> with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. This is just a <a href="http://www.test.com">www.test.com</a> paragraph with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. This is just a <a href="http://www.test.com">www.test.com</a> paragraph <a href="mailto:person@test.com">person@test.com</a> with some <a href="http://www.test.com">http://www.test.com</a> urls thrown in. + +The old URL filter has problems with <a title="kind of link www.example.com with text" href="http://www.example.com">this kind of link</a> with www address as part of text in title. <a href="http://www.test.com">www.test.com</a> + +<!-- This url www.test.com is inside a comment --> + +<dl> +<dt><a href="http://www.test.com">www.test.com</a></dt> +<dd><a href="http://www.test.com">http://www.test.com</a></dd> +<dd><a href="mailto:person@test.com">person@test.com</a></dd> +<dt>check <a href="http://www.test.com">www.test.com</a></dt> +<dd>this with some text around: <a href="http://www.test.com">http://www.test.com</a> not so easy <a href="mailto:person@test.com">person@test.com</a> now?</dd> +</dl> + +<!-- <p>This url http://www.test.com is + inside a comment containing newlines and +<em>html</em> tags.</p> --> + +This is the end! \ No newline at end of file diff --git a/modules/forum/forum.admin.inc b/modules/forum/forum.admin.inc index cefe406307d6822e08ed886d38f667a37d5f3cfb..84a891d22a620e47b112eb88668a9ea97a29ba51 100644 --- a/modules/forum/forum.admin.inc +++ b/modules/forum/forum.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: forum.admin.inc,v 1.34 2010/06/26 21:32:20 dries Exp $ +// $Id: forum.admin.inc,v 1.36 2010/08/08 19:21:25 dries Exp $ /** * @file @@ -89,6 +89,7 @@ function forum_form_submit($form, &$form_state) { $containers[] = $term->tid; variable_set('forum_containers', $containers); } + $form_state['values']['tid'] = $term->tid; drupal_set_message(t('Created new @type %term.', array('%term' => $form_state['values']['name'], '@type' => $type))); break; case SAVED_UPDATED: @@ -257,7 +258,7 @@ function forum_overview($form, &$form_state) { } // Remove the alphabetical reset. - unset($form['reset_alphabetical']); + unset($form['actions']['reset_alphabetical']); // The form needs to have submit and validate handlers set explicitly. $form['#theme'] = 'taxonomy_overview_terms'; diff --git a/modules/forum/forum.info b/modules/forum/forum.info index c8baba56eb599badaaf57c8b7756c806b79c6e16..c54a2c0704797a76c781f9d3a29b8e54bca3bbae 100644 --- a/modules/forum/forum.info +++ b/modules/forum/forum.info @@ -1,4 +1,4 @@ -; $Id: forum.info,v 1.13 2009/11/17 21:24:18 dries Exp $ +; $Id: forum.info,v 1.14 2010/09/05 02:21:38 dries Exp $ name = Forum description = Provides discussion forums. dependencies[] = taxonomy @@ -12,9 +12,10 @@ files[] = forum.pages.inc files[] = forum.install files[] = forum.test configure = admin/structure/forum +stylesheets[all][] = forum.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/forum/forum.install b/modules/forum/forum.install index 22b8d4643fcfedd6a42ab9bf7b1c207a200b3049..4a4dd32cb4c5dcc4a86e8bd352d5f2917121b262 100644 --- a/modules/forum/forum.install +++ b/modules/forum/forum.install @@ -1,5 +1,5 @@ <?php -// $Id: forum.install,v 1.48 2010/06/24 12:59:22 webchick Exp $ +// $Id: forum.install,v 1.50 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -24,6 +24,10 @@ function forum_install() { * Implements hook_enable(). */ function forum_enable() { + // If we enable forum at the same time as taxonomy we need to call + // field_associate_fields() as otherwise the field won't be enabled until + // hook modules_enabled is called which takes place after hook_enable events. + field_associate_fields('taxonomy'); // Create the forum vocabulary if it does not exist. $vocabulary = taxonomy_vocabulary_load(variable_get('forum_nav_vocabulary', 0)); if (!$vocabulary) { @@ -56,28 +60,6 @@ function forum_enable() { ); field_create_field($field); - $instance = array( - 'field_name' => 'taxonomy_' . $vocabulary->machine_name, - 'entity_type' => 'node', - 'label' => $vocabulary->name, - 'bundle' => 'forum', - 'required' => TRUE, - 'widget' => array( - 'type' => 'options_select', - ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - 'weight' => 10, - ), - 'teaser' => array( - 'type' => 'taxonomy_term_reference_link', - 'weight' => 10, - ), - ), - ); - field_create_instance($instance); - variable_set('forum_nav_vocabulary', $vocabulary->vid); // Create a default forum so forum posts can be created. @@ -90,6 +72,30 @@ function forum_enable() { $term = (object) $edit; taxonomy_term_save($term); } + + // Create the instance on the bundle. + $instance = array( + 'field_name' => 'taxonomy_' . $vocabulary->machine_name, + 'entity_type' => 'node', + 'label' => $vocabulary->name, + 'bundle' => 'forum', + 'required' => TRUE, + 'widget' => array( + 'type' => 'options_select', + ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), + ); + field_create_instance($instance); + // Ensure the forum node type is available. node_types_rebuild(); $types = node_type_get_types(); @@ -147,8 +153,13 @@ function forum_schema() { ), 'primary key' => array('vid'), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), - 'vid' => array('node' => 'vid'), + 'forum_node' => array( + 'table' => 'node', + 'columns' => array( + 'nid' => 'nid', + 'vid' => 'vid', + ), + ), ), ); @@ -208,8 +219,16 @@ function forum_schema() { 'forum_topics' => array('tid', 'sticky', 'last_comment_timestamp'), ), 'foreign keys' => array( - 'node' => 'nid', - 'taxonomy_term_data' => 'tid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'term' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array( + 'tid' => 'tid', + ), + ), ), ); @@ -285,8 +304,16 @@ function forum_update_7001() { 'forum_topics' => array('tid', 'sticky', 'last_comment_timestamp'), ), 'foreign keys' => array( - 'node' => 'nid', - 'taxonomy_term_data' => 'tid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'term' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array( + 'tid' => 'tid', + ), + ), ), ); db_create_table('forum_index', $forum_index); diff --git a/modules/forum/forum.module b/modules/forum/forum.module index eaae2744ab3804ad82c67a4cceb072bc0455e932..4f5ca81df1af9e2a5d4c61f99bd83f8808b51ecf 100644 --- a/modules/forum/forum.module +++ b/modules/forum/forum.module @@ -1,5 +1,5 @@ <?php -// $Id: forum.module,v 1.567 2010/06/20 23:55:08 webchick Exp $ +// $Id: forum.module,v 1.575 2010/09/07 23:39:08 dries Exp $ /** * @file @@ -41,7 +41,7 @@ function forum_help($path, $arg) { return $output; case 'admin/structure/forum': $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>'; - $output .= theme('more_help_link', array('url' => url('admin/help/forum'))); + $output .= theme('more_help_link', array('url' => 'admin/help/forum')); return $output; case 'admin/structure/forum/add/container': return '<p>' . t('Use containers to group related forums.') . '</p>'; @@ -217,13 +217,6 @@ function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) { } } -/** - * Implements hook_init(). - */ -function forum_init() { - drupal_add_css(drupal_get_path('module', 'forum') . '/forum.css'); -} - /** * Implements hook_entity_info_alter(). */ @@ -277,7 +270,7 @@ function forum_node_view($node, $view_mode) { $vid = variable_get('forum_nav_vocabulary', 0); $vocabulary = taxonomy_vocabulary_load($vid); if (_forum_node_check_node_type($node)) { - if (node_is_page($node)) { + if ($view_mode == 'full' && node_is_page($node)) { // Breadcrumb navigation $breadcrumb[] = l(t('Home'), NULL); $breadcrumb[] = l($vocabulary->name, 'forum'); @@ -700,7 +693,7 @@ function forum_block_view_pre_render($elements) { $result = $elements['#query']->execute(); if ($node_title_list = node_title_list($result)) { $elements['forum_list'] = $node_title_list; - $elements['forum_more'] = array('#theme' => 'more_link', '#url' => url('forum'), '#title' => t('Read the latest forum topics.')); + $elements['forum_more'] = array('#theme' => 'more_link', '#url' => 'forum', '#title' => t('Read the latest forum topics.')); } return $elements; } @@ -761,7 +754,7 @@ function forum_forum_load($tid = NULL) { } } // If $tid is 0, create an empty object to hold the child terms. - else if ($tid === 0) { + elseif ($tid === 0) { $forum_term = (object) array( 'tid' => 0, ); @@ -852,7 +845,7 @@ function forum_forum_load($tid = NULL) { function _forum_topics_unread($term, $uid) { $query = db_select('node', 'n'); $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term)); - $query->join('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid)); + $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid)); $query->addExpression('COUNT(n.nid)', 'count'); return $query ->condition('status', 1) @@ -989,7 +982,7 @@ function template_preprocess_forums(&$variables) { if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) { $variables['topics'] = theme('forum_topic_list', $variables); - drupal_add_feed(url('taxonomy/term/' . $variables['tid'] . '/0/feed'), 'RSS - ' . $title); + drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/0/feed', 'RSS - ' . $title); } else { $variables['topics'] = ''; diff --git a/modules/forum/forum.test b/modules/forum/forum.test index 45e49481dcd514f00c7007e983d4cb7d3cf15ab0..267dec7dee5791275ecb67b092f390977436572e 100644 --- a/modules/forum/forum.test +++ b/modules/forum/forum.test @@ -1,5 +1,5 @@ <?php -// $Id: forum.test,v 1.57 2010/07/08 03:41:27 webchick Exp $ +// $Id: forum.test,v 1.62 2010/08/30 00:22:03 webchick Exp $ class ForumTestCase extends DrupalWebTestCase { protected $admin_user; @@ -71,8 +71,25 @@ class ForumTestCase extends DrupalWebTestCase { // Verify the topic and post counts on the forum page. $this->drupalGet('forum'); - $this->assertRaw("<td class=\"topics\">\n 6 </td>"); - $this->assertRaw('<td class="posts">6</td>'); + + // Verify row for testing forum. + $forum_arg = array(':forum' => 'forum-list-' . $this->forum['tid']); + + // Topics cell contains number of topics and number of unread topics. + $xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="topics"]', $forum_arg); + $topics = $this->xpath($xpath); + $topics = trim($topics[0]); + $this->assertEqual($topics, '6', t('Number of topics found.')); + + // Verify the number of unread topics. + $unread_topics = _forum_topics_unread($this->forum['tid'], $this->edit_any_topics_user->uid); + $unread_topics = format_plural($unread_topics, '1 new', '@count new'); + $xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="topics"]//a', $forum_arg); + $this->assertFieldByXPath($xpath, $unread_topics, t('Number of unread topics found.')); + + // Verify total number of posts in forum. + $xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="posts"]', $forum_arg); + $this->assertFieldByXPath($xpath, '6', t('Number of posts found.')); // Test loading multiple forum nodes on the front page. $this->drupalLogin($this->drupalCreateUser(array('administer content types', 'create forum content'))); @@ -128,14 +145,14 @@ class ForumTestCase extends DrupalWebTestCase { // Enable the active forum block. $edit = array(); - $edit['forum_active[region]'] = 'sidebar_second'; + $edit['blocks[forum_active][region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('Active forum topics forum block was enabled')); // Enable the new forum block. $edit = array(); - $edit['forum_new[region]'] = 'sidebar_second'; + $edit['blocks[forum_new][region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('[New forum topics] Forum block was enabled')); diff --git a/modules/help/help.admin.inc b/modules/help/help.admin.inc index ce7f5d2cf21dc14722cdf40251f5e9797d9748b5..697eb8ed22c94e0df97f5a4f061bde268554424a 100644 --- a/modules/help/help.admin.inc +++ b/modules/help/help.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: help.admin.inc,v 1.11 2009/10/13 05:26:57 webchick Exp $ +// $Id: help.admin.inc,v 1.12 2010/07/30 02:47:28 dries Exp $ /** * @file @@ -11,7 +11,7 @@ */ function help_main() { // Add CSS - drupal_add_css(drupal_get_path('module', 'help') . '/help.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'help') . '/help.css'); $output = '<h2>' . t('Help topics') . '</h2><p>' . t('Help is available on the following items:') . '</p>' . help_links_as_list(); return $output; } diff --git a/modules/help/help.api.php b/modules/help/help.api.php index c32bca6dd86e189c7d8afd02e2862a1bc2f487c1..7229285309967d1e597489bc1e423bee71b8dfa7 100644 --- a/modules/help/help.api.php +++ b/modules/help/help.api.php @@ -1,5 +1,5 @@ <?php -// $Id: help.api.php,v 1.8 2010/01/30 07:59:25 dries Exp $ +// $Id: help.api.php,v 1.10 2010/08/11 01:06:44 dries Exp $ /** * @file @@ -50,7 +50,7 @@ function hook_help($path, $arg) { switch ($path) { // Main module help for the block module case 'admin/help#block': - return '<p>' . t('Blocks are boxes of content rendered into an area, or region, of a web page. The default theme Garland, for example, implements the regions "left sidebar", "right sidebar", "content", "header", and "footer", and a block may appear in any one of these areas. The <a href="@blocks">blocks administration page</a> provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions.', array('@blocks' => url('admin/structure/block'))) . '</p>'; + return '<p>' . t('Blocks are boxes of content rendered into an area, or region, of a web page. The default theme Bartik, for example, implements the regions "Sidebar first", "Sidebar second", "Featured", "Content", "Header", "Footer", etc., and a block may appear in any one of these areas. The <a href="@blocks">blocks administration page</a> provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions.', array('@blocks' => url('admin/structure/block'))) . '</p>'; // Help for another path in the block module case 'admin/structure/block': diff --git a/modules/help/help.info b/modules/help/help.info index e8075b14a2e2097992de6a3ac53cc2642b7cf22e..96adf2c9c129064f591ffa2043a487b40031fdcc 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/help/help.test b/modules/help/help.test index fc80ca540cab96b00a4a7c322426b129731ba02b..532ce6446ef0350edc2c22c229e2a68e5e7dc368 100644 --- a/modules/help/help.test +++ b/modules/help/help.test @@ -1,5 +1,5 @@ <?php -// $Id: help.test,v 1.19 2010/04/01 14:52:21 dries Exp $ +// $Id: help.test,v 1.21 2010/08/05 23:53:38 webchick Exp $ class HelpTestCase extends DrupalWebTestCase { protected $big_user; diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc index d41c86a1a3b698878af58dcab7419541cba1abc1..cb42324df13fcf0a75c278f4a6c6f93124da37f3 100644 --- a/modules/image/image.admin.inc +++ b/modules/image/image.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.admin.inc,v 1.20 2010/04/24 14:49:14 dries Exp $ +// $Id: image.admin.inc,v 1.21 2010/07/30 02:47:28 dries Exp $ /** * @file @@ -16,7 +16,7 @@ function image_style_list() { $page['image_style_list'] = array( '#markup' => theme('image_style_list', array('styles' => $styles)), '#attached' => array( - 'css' => array(drupal_get_path('module', 'image') . '/image.admin.css' => array('preprocess' => FALSE)), + 'css' => array(drupal_get_path('module', 'image') . '/image.admin.css' => array()), ), ); @@ -48,7 +48,7 @@ function image_style_form($form, &$form_state, $style) { $form_state['image_style'] = $style; $form['#tree'] = TRUE; - $form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array('preprocess' => FALSE); + $form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array(); // Show the thumbnail preview. $form['preview'] = array( @@ -383,7 +383,7 @@ function image_effect_form($form, &$form_state, $style, $effect) { } $form['#tree'] = TRUE; - $form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array('preprocess' => FALSE); + $form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array(); if (function_exists($effect['form callback'])) { $form['data'] = call_user_func($effect['form callback'], $effect['data']); } diff --git a/modules/image/image.api.php b/modules/image/image.api.php index 95ee61e52e34a6b63bdd952cd451d2d1470fa1fc..67018e5765a3cc8c55d3e28323ba16855fa0fecf 100644 --- a/modules/image/image.api.php +++ b/modules/image/image.api.php @@ -1,5 +1,5 @@ <?php -// $Id: image.api.php,v 1.4 2010/02/01 07:07:57 webchick Exp $ +// $Id: image.api.php,v 1.5 2010/09/11 01:54:43 dries Exp $ /** * @file @@ -29,6 +29,8 @@ * $form array providing a configuration form for this image effect. * - "summary theme": (optional) The name of a theme function that will output * a summary of this image effect's configuration. + * + * @see hook_image_effect_info_alter() */ function hook_image_effect_info() { $effects = array(); @@ -44,6 +46,20 @@ function hook_image_effect_info() { return $effects; } +/** + * Alter the information provided in hook_image_effect_info(). + * + * @param $effects + * The array of image effects, keyed on the machine-readable effect name. + * + * @see hook_image_effect_info() + */ +function hook_image_effect_info_alter(&$effects) { + // Override the Image module's crop effect with more options. + $effect['image_crop']['effect callback'] = 'mymodule_crop_effect'; + $effect['image_crop']['form callback'] = 'mymodule_crop_form'; +} + /** * Respond to image style updating. * diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index 43f5a2dbca59c07bdf130ab0b9a2775966536486..f5f4d02bf7af65869b57d9fe24233f9dba8ba591 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.field.inc,v 1.21 2010/04/30 12:53:47 dries Exp $ +// $Id: image.field.inc,v 1.27 2010/09/09 23:28:16 webchick Exp $ /** * @file @@ -15,7 +15,7 @@ function image_field_info() { 'label' => t('Image'), 'description' => t('This field stores the ID of an image file as an integer value.'), 'settings' => array( - 'uri_scheme' => 'public', + 'uri_scheme' => variable_get('file_default_scheme', 'public'), 'default_image' => 0, ), 'instance_settings' => array( @@ -33,37 +33,6 @@ function image_field_info() { ); } -/** - * Implements hook_field_schema(). - */ -function image_field_schema($field) { - return array( - 'columns' => array( - 'fid' => array( - 'description' => 'The {files}.fid being referenced in this field.', - 'type' => 'int', - 'not null' => FALSE, - 'unsigned' => TRUE, - ), - 'alt' => array( - 'description' => "Alternative image text, for the image's 'alt' attribute.", - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'title' => array( - 'description' => "Image title text, for the image's 'title' attribute.", - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'fid' => array('fid'), - ), - ); -} - /** * Implements hook_field_settings_form(). */ @@ -107,10 +76,12 @@ function image_field_instance_settings_form($field, $instance) { // Add maximum and minimum resolution settings. $max_resolution = explode('x', $settings['max_resolution']) + array('', ''); $form['max_resolution'] = array( + '#type' => 'item', '#title' => t('Maximum image resolution'), '#element_validate' => array('_image_field_resolution_validate'), - '#theme_wrappers' => array('form_element'), '#weight' => 4.1, + '#field_prefix' => '<div class="container-inline">', + '#field_suffix' => '</div>', '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Leave blank for no restriction. If a larger image is uploaded, it will be resized to reflect the given width and height. Resizing images on upload will cause the loss of <a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format">EXIF data</a> in the image.'), ); $form['max_resolution']['x'] = array( @@ -119,7 +90,6 @@ function image_field_instance_settings_form($field, $instance) { '#size' => 5, '#maxlength' => 5, '#field_suffix' => ' x ', - '#theme_wrappers' => array(), ); $form['max_resolution']['y'] = array( '#type' => 'textfield', @@ -127,15 +97,16 @@ function image_field_instance_settings_form($field, $instance) { '#size' => 5, '#maxlength' => 5, '#field_suffix' => ' ' . t('pixels'), - '#theme_wrappers' => array(), ); $min_resolution = explode('x', $settings['min_resolution']) + array('', ''); $form['min_resolution'] = array( + '#type' => 'item', '#title' => t('Minimum image resolution'), '#element_validate' => array('_image_field_resolution_validate'), - '#theme_wrappers' => array('form_element'), '#weight' => 4.2, + '#field_prefix' => '<div class="container-inline">', + '#field_suffix' => '</div>', '#description' => t('The minimum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Leave blank for no restriction. If a smaller image is uploaded, it will be rejected.'), ); $form['min_resolution']['x'] = array( @@ -144,7 +115,6 @@ function image_field_instance_settings_form($field, $instance) { '#size' => 5, '#maxlength' => 5, '#field_suffix' => ' x ', - '#theme_wrappers' => array(), ); $form['min_resolution']['y'] = array( '#type' => 'textfield', @@ -152,7 +122,6 @@ function image_field_instance_settings_form($field, $instance) { '#size' => 5, '#maxlength' => 5, '#field_suffix' => ' ' . t('pixels'), - '#theme_wrappers' => array(), ); // Remove the description option. @@ -232,6 +201,13 @@ function image_field_presave($entity_type, $entity, $field, $instance, $langcode file_field_presave($entity_type, $entity, $field, $instance, $langcode, $items); } +/** + * Implements hook_field_insert(). + */ +function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + file_field_insert($entity_type, $entity, $field, $instance, $langcode, $items); +} + /** * Implements hook_field_update(). */ @@ -428,33 +404,74 @@ function image_field_formatter_info() { 'image' => array( 'label' => t('Image'), 'field types' => array('image'), - ), - 'image_link_content' => array( - 'label' => t('Image linked to content'), - 'field types' => array('image'), - ), - 'image_link_file' => array( - 'label' => t('Image linked to file'), - 'field types' => array('image'), + 'settings' => array('image_style' => '', 'image_link' => ''), ), ); - foreach (image_styles() as $style) { - $formatters['image__' . $style['name']] = array( - 'label' => t('Image "@style"', array('@style' => $style['name'])), - 'field types' => array('image'), - ); - $formatters['image_link_content__' . $style['name']] = array( - 'label' => t('Image "@style" linked to content', array('@style' => $style['name'])), - 'field types' => array('image'), - ); - $formatters['image_link_file__' . $style['name']] = array( - 'label' => t('Image "@style" linked to file', array('@style' => $style['name'])), - 'field types' => array('image'), - ); + return $formatters; +} + +/** + * Implements hook_field_formatter_settings_form(). + */ +function image_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $image_styles = array('' => t('None (original image)')) + image_style_options(FALSE); + $form['image_style'] = array( + '#title' => t('Image style'), + '#type' => 'select', + '#default_value' => $settings['image_style'], + '#options' => $image_styles, + ); + + $link_types = array( + '' => t('<none>'), + 'content' => t('Content'), + 'file' => t('File'), + ); + $form['image_link'] = array( + '#title' => t('Link image to'), + '#type' => 'select', + '#default_value' => $settings['image_link'], + '#options' => $link_types, + ); + + return $form; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function image_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = array(); + + $image_styles = image_style_options(FALSE); + // Unset possible 'No defined styles' option. + unset($image_styles['']); + // Styles could be lost because of enabled/disabled modules that defines + // their styles in code. + if (isset($image_styles[$settings['image_style']])) { + $summary[] = t('Image style: @style', array('@style' => $image_styles[$settings['image_style']])); + } + else { + $summary[] = t('Original image'); } - return $formatters; + $link_types = array( + 'content' => t('Linked to content'), + 'file' => t('Linked to file'), + ); + // Display this setting only if image is linked. + if (isset($link_types[$settings['image_link']])) { + $summary[] = $link_types[$settings['image_link']]; + } + + return implode('<br />', $summary); } /** @@ -463,17 +480,11 @@ function image_field_formatter_info() { function image_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $element = array(); - // Check if the formatter involves a particular image style. - $matches = array(); - if (preg_match('/__([a-z0-9_]+)/', $display['type'], $matches)) { - $image_style = $matches[1]; - } - // Check if the formatter involves a link. - if (strpos($display['type'], 'image_link_content') === 0) { + if ($display['settings']['image_link'] == 'content') { $uri = entity_uri($entity_type, $entity); } - elseif (strpos($display['type'], 'image_link_file') === 0) { + elseif ($display['settings']['image_link'] == 'file') { $link_file = TRUE; } @@ -487,7 +498,7 @@ function image_field_formatter_view($entity_type, $entity, $field, $instance, $l $element[$delta] = array( '#theme' => 'image_formatter', '#item' => $item, - '#image_style' => isset($image_style) ? $image_style : '', + '#image_style' => $display['settings']['image_style'], '#path' => isset($uri) ? $uri : '', ); } diff --git a/modules/image/image.info b/modules/image/image.info index 6c4bc710334b1844c0526c5a49c59e23d983bb3b..10b53813034d18b0f1e9148164e5ae050aad9a48 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/image/image.install b/modules/image/image.install index fae977a6ecdb5aae38afe3eff7cf4c54547d4b63..53123887f9dbd04e57f260b6fdaf1944000ccf57 100644 --- a/modules/image/image.install +++ b/modules/image/image.install @@ -1,5 +1,5 @@ <?php -// $Id: image.install,v 1.10 2010/06/25 17:47:22 dries Exp $ +// $Id: image.install,v 1.13 2010/09/04 15:40:51 dries Exp $ /** * @file @@ -11,8 +11,8 @@ */ function image_install() { // Create the styles directory and ensure it's writable. - $path = file_directory_path() . '/styles'; - file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $directory = file_default_scheme() . '://styles'; + file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); } /** @@ -20,8 +20,7 @@ function image_install() { */ function image_uninstall() { // Remove the styles directory and generated images. - $path = file_directory_path() . '/styles'; - file_unmanaged_delete_recursive($path); + file_unmanaged_delete_recursive(file_default_scheme() . '://styles'); } /** @@ -98,13 +97,47 @@ function image_schema() { 'weight' => array('weight'), ), 'foreign keys' => array( - 'isid' => array('image_styles' => 'isid'), + 'image_style' => array( + 'table' => 'image_styles', + 'columns' => array('isid' => 'isid'), + ), ), ); return $schema; } +/** + * Implements hook_field_schema(). + */ +function image_field_schema($field) { + return array( + 'columns' => array( + 'fid' => array( + 'description' => 'The {files}.fid being referenced in this field.', + 'type' => 'int', + 'not null' => FALSE, + 'unsigned' => TRUE, + ), + 'alt' => array( + 'description' => "Alternative image text, for the image's 'alt' attribute.", + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), + 'title' => array( + 'description' => "Image title text, for the image's 'title' attribute.", + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'fid' => array('fid'), + ), + ); +} + /** * Install the schema for users upgrading from the contributed module. */ @@ -180,7 +213,10 @@ function image_update_7000() { 'weight' => array('weight'), ), 'foreign keys' => array( - 'isid' => array('image_styles' => 'isid'), + 'image_style' => array( + 'table' => 'image_styles', + 'columns' => array('isid' => 'isid'), + ), ), ); diff --git a/modules/image/image.module b/modules/image/image.module index 50be71c902a4968867e2ee37fc4d6febfd5f89d3..71c96e3b55fc3f9d59bf2e2525d8e1366880e45b 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -1,5 +1,5 @@ <?php -// $Id: image.module,v 1.43 2010/05/31 11:42:11 dries Exp $ +// $Id: image.module,v 1.50 2010/09/11 05:07:22 webchick Exp $ /** * @file @@ -71,10 +71,25 @@ function image_help($path, $arg) { function image_menu() { $items = array(); - $items['image/generate/%image_style'] = array( + // Generate image derivatives of publicly available files. + // If clean URLs are disabled, image derivatives will always be served + // through the menu system. + // If clean URLs are enabled and the image derivative already exists, + // PHP will be bypassed. + $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(); + $items[$directory_path . '/styles/%image_style'] = array( 'title' => 'Generate image style', - 'page callback' => 'image_style_generate', - 'page arguments' => array(2), + 'page callback' => 'image_style_deliver', + 'page arguments' => array(count(explode('/', $directory_path)) + 1), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + // Generate and deliver image derivatives of private files. + // These image derivatives are always delivered through the menu system. + $items['system/files/styles/%image_style'] = array( + 'title' => 'Generate image style', + 'page callback' => 'image_style_deliver', + 'page arguments' => array(3), 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); @@ -232,6 +247,25 @@ function image_permission() { ); } +/** + * Implements hook_form_FORM_ID_alter(). + */ +function image_form_system_file_system_settings_alter(&$form, &$form_state) { + $form['#submit'][] = 'image_system_file_system_settings_submit'; +} + +/** + * Submit handler for the file system settings form. + * + * Adds a menu rebuild after the public file path has been changed, so that the + * menu router item depending on that file path will be regenerated. + */ +function image_system_file_system_settings_submit($form, &$form_state) { + if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) { + variable_set('menu_rebuild_needed', TRUE); + } +} + /** * Implements hook_flush_caches(). */ @@ -254,6 +288,9 @@ function image_file_download($uri) { array_shift($args); // Get the style name from the second part. $style_name = array_shift($args); + // Remove the scheme from the path. + array_shift($args); + // Then the remaining parts are the path to the image. $original_uri = file_uri_scheme($uri) . '://' . implode('/', $args); @@ -306,14 +343,6 @@ function image_file_delete($file) { image_path_flush($file->uri); } -/** - * Implements hook_file_references(). - */ -function image_file_references($file) { - $count = file_get_file_reference_count($file, NULL, 'image'); - return $count ? array('image' => $count) : NULL; -} - /** * Implements hook_image_default_styles(). */ @@ -344,7 +373,7 @@ function image_image_default_styles() { 'effects' => array( array( 'name' => 'image_scale', - 'data' => array('width' => 640, 'height' => 640, 'upscale' => 0), + 'data' => array('width' => 480, 'height' => 480, 'upscale' => 0), 'weight' => 0, ), ) @@ -365,14 +394,11 @@ function image_image_style_save($style) { $instance_changed = FALSE; foreach ($instance['display'] as $view_mode => $display) { // Check if the formatter involves an image style. - $matches = array(); - if (preg_match('/__([a-z0-9_]+)/', $display['type'], $matches)) { + if ($display['type'] == 'image' && $display['settings']['image_style'] == $style['old_name']) { // Update display information for any instance using the image // style that was just deleted. - if ($style['old_name'] == $matches[1]) { - $instance['display'][$view_mode]['type'] = str_replace($style['old_name'], $style['name'], $display['type']); - $instance_changed = TRUE; - } + $instance['display'][$view_mode]['settings']['image_style'] = $style['name']; + $instance_changed = TRUE; } } if ($instance['widget']['settings']['preview_image_style'] == $style['old_name']) { @@ -620,32 +646,48 @@ function image_style_options($include_empty = TRUE) { /** * Menu callback; Given a style and image path, generate a derivative. * - * This menu callback is always served after checking a token to prevent - * generation of unnecessary images. After generating an image transfer it to - * the requesting agent via file_transfer(). + * After generating an image, transfer it to the requesting agent. + * + * @param $style + * The image style */ -function image_style_generate() { - $args = func_get_args(); - $style = array_shift($args); - $style_name = $style['name']; - $scheme = array_shift($args); - $path = implode('/', $args); - - $path = $scheme . '://' . $path; - $path_hash = drupal_hash_base64($path); - $destination = image_style_path($style['name'], $path); - - // Check that it's a defined style and that access was granted by - // image_style_url(). - if (!$style || !cache_get('access:' . $style_name . ':' . $path_hash, 'cache_image')) { - drupal_access_denied(); +function image_style_deliver($style, $scheme) { + // Check that the style is defined and the scheme is valid. + if (!$style || !file_stream_wrapper_valid_scheme($scheme)) { drupal_exit(); } + + $args = func_get_args(); + array_shift($args); + array_shift($args); + $target = implode('/', $args); + + $image_uri = $scheme . '://' . $target; + $derivative_uri = image_style_path($style['name'], $image_uri); + + // If using the private scheme, let other modules provide headers and + // control access to the file. + if ($scheme == 'private') { + if (file_exists($derivative_uri)) { + file_download($scheme, file_uri_target($derivative_uri)); + } + else { + $headers = module_invoke_all('file_download', $image_uri); + if (in_array(-1, $headers) || empty($headers)) { + return drupal_access_denied(); + } + if (count($headers)) { + foreach ($headers as $name => $value) { + drupal_add_http_header($name, $value); + } + } + } + } - // Don't start generating the image if the derivate already exists or if + // Don't start generating the image if the derivative already exists or if // generation is in progress in another thread. - $lock_name = 'image_style_generate:' . $style_name . ':' . $path_hash; - if (!file_exists($destination)) { + $lock_name = 'image_style_deliver:' . $style['name'] . ':' . drupal_hash_base64($image_uri); + if (!file_exists($derivative_uri)) { $lock_acquired = lock_acquire($lock_name); if (!$lock_acquired) { // Tell client to retry again in 3 seconds. Currently no browsers are known @@ -659,18 +701,18 @@ function image_style_generate() { // Try to generate the image, unless another thread just did it while we were // acquiring the lock. - $success = file_exists($destination) || image_style_create_derivative($style, $path, $destination); + $success = file_exists($derivative_uri) || image_style_create_derivative($style, $image_uri, $derivative_uri); if (!empty($lock_acquired)) { lock_release($lock_name); } if ($success) { - $image = image_load($destination); + $image = image_load($derivative_uri); file_transfer($image->source, array('Content-Type' => $image->info['mime_type'], 'Content-Length' => $image->info['file_size'])); } else { - watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $destination)); + watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $derivative_uri)); drupal_add_http_header('Status', '500 Internal Server Error'); print t('Error generating image.'); drupal_exit(); @@ -725,7 +767,7 @@ function image_style_create_derivative($style, $source, $destination) { * An image style array. */ function image_style_flush($style) { - $style_directory = drupal_realpath(variable_get('file_default_scheme', 'public') . '://styles/' . $style['name']); + $style_directory = drupal_realpath(file_default_scheme() . '://styles/' . $style['name']); if (is_dir($style_directory)) { file_unmanaged_delete_recursive($style_directory); } @@ -753,13 +795,6 @@ function image_style_flush($style) { /** * Return the URL for an image derivative given a style and image path. * - * This function is the default image generation method. It returns a URL for - * an image that can be used in an <img> tag. When the browser requests the - * image at image/generate/[style_name]/[scheme]/[path] the image is generated - * if it does not already exist and then served to the browser. This allows - * each image to have its own PHP instance (and memory limit) for generation of - * the new image. - * * @param $style_name * The name of the style to be used with this image. * @param $path @@ -767,29 +802,18 @@ function image_style_flush($style) { * @return * The absolute URL where a style image can be downloaded, suitable for use * in an <img> tag. Requesting the URL will cause the image to be created. - * @see image_style_generate() + * @see image_style_deliver() */ function image_style_url($style_name, $path) { - $destination = image_style_path($style_name, $path); - - // If the image already exists use that rather than regenerating it. - if (file_exists($destination)) { - return file_create_url($destination); - } - - // Disable page cache for this request. This prevents anonymous users from - // needlessly hitting the image generation URL when the image already exists. - drupal_page_is_cacheable(FALSE); - - // Set a cache entry to grant access to this style/image path. This will be - // checked by image_style_generate(). - cache_set('access:' . $style_name . ':' . drupal_hash_base64($path), 1, 'cache_image', REQUEST_TIME + 600); - $scheme = file_uri_scheme($path); - $target = file_uri_target($path); - - // Generate a callback path for the image. - $url = url('image/generate/' . $style_name . '/' . $scheme . '/' . $target, array('absolute' => TRUE)); + if ($scheme === 'private') { + $target = file_uri_target($path); + $url = url('system/files/styles/' . $style_name . '/' . $scheme . '/' . $target, array('absolute' => TRUE)); + } + else { + $destination = image_style_path($style_name, $path); + $url = url(file_stream_wrapper_get_instance_by_scheme($scheme)->getDirectoryPath() . '/' . file_uri_target($destination), array('absolute' => TRUE)); + } return $url; } @@ -814,9 +838,9 @@ function image_style_path($style_name, $uri) { } else { $path = $uri; - $scheme = variable_get('file_default_scheme', 'public'); + $scheme = file_default_scheme(); } - return $scheme . '://styles/' . $style_name . '/' . $path; + return $scheme . '://styles/' . $style_name . '/' . $scheme . '/' . $path; } /** @@ -891,6 +915,7 @@ function image_effect_definitions() { } } uasort($effects, '_image_effect_definitions_sort'); + drupal_alter('image_effect_info', $effects); cache_set("image_effects:$langcode", $effects); } } @@ -1079,7 +1104,7 @@ function theme_image_style($variables) { if (!file_exists($style_path)) { $style_path = image_style_url($style_name, $path); } - $variables['path'] = file_create_url($style_path); + $variables['path'] = $style_path; $variables['getsize'] = FALSE; return theme('image', $variables); } diff --git a/modules/image/image.test b/modules/image/image.test index 473d1c525e6fa0edc5c305d7faf8219fe49006f3..2691f63184079dac1045d05084e98710ab2082d9 100644 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -1,5 +1,5 @@ <?php -// $Id: image.test,v 1.23 2010/06/30 22:37:49 webchick Exp $ +// $Id: image.test,v 1.30 2010/09/11 21:14:31 webchick Exp $ /** * @file @@ -94,7 +94,7 @@ class ImageFieldTestCase extends DrupalWebTestCase { $edit = array( 'title' => $this->randomName(), ); - $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = realpath($image->uri); + $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($image->uri); $this->drupalPost('node/add/' . $type, $edit, t('Save')); // Retrieve ID of the newly created node from the current URL. @@ -121,7 +121,7 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { } function setUp() { - parent::setUp(); + parent::setUp('image_module_test'); $this->style_name = 'style_foo'; image_style_save(array('name' => $this->style_name)); @@ -131,12 +131,13 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { * Test image_style_path(). */ function testImageStylePath() { - $actual = image_style_path($this->style_name, 'public://foo/bar.gif'); - $expected = 'public://styles/' . $this->style_name . '/foo/bar.gif'; + $scheme = 'public'; + $actual = image_style_path($this->style_name, "$scheme://foo/bar.gif"); + $expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif"; $this->assertEqual($actual, $expected, t('Got the path for a file URI.')); $actual = image_style_path($this->style_name, 'foo/bar.gif'); - $expected = 'public://styles/' . $this->style_name . '/foo/bar.gif'; + $expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif"; $this->assertEqual($actual, $expected, t('Got the path for a relative file path.')); } @@ -161,12 +162,10 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { // Make the default scheme neither "public" nor "private" to verify the // functions work for other than the default scheme. variable_set('file_default_scheme', 'temporary'); - $d = 'temporary://'; - file_prepare_directory($d, FILE_CREATE_DIRECTORY); // Create the directories for the styles. - $d = $scheme . '://styles/' . $this->style_name; - $status = file_prepare_directory($d, FILE_CREATE_DIRECTORY); + $directory = $scheme . '://styles/' . $this->style_name; + $status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY); $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images for the test style.')); // Create a working copy of the file. @@ -174,46 +173,27 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { $file = reset($files); $image_info = image_get_info($file->uri); $original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME); + // Let the image_module_test module know about this file, so it can claim + // ownership in hook_file_download(). + variable_set('image_module_test_file_download', $original_uri); $this->assertNotIdentical(FALSE, $original_uri, t('Created the generated image file.')); - // Get the URL of a file that has not been generated yet and try to access - // it before image_style_url has been called. - $generated_uri = $scheme . '://styles/' . $this->style_name . '/' . basename($original_uri); + // Get the URL of a file that has not been generated and try to create it. + $generated_uri = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. basename($original_uri); $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); - $expected_generate_url = url('image/generate/' . $this->style_name . '/' . $scheme . '/' . basename($original_uri), array('absolute' => TRUE)); - $this->drupalGet($expected_generate_url); - $this->assertResponse(403, t('Access to generate URL was denied.')); - - // Check that a generate URL is returned. - $actual_generate_url = image_style_url($this->style_name, $original_uri); - $this->assertEqual($actual_generate_url, $expected_generate_url, t('Got the generate URL for a non-existent file.')); - - // Fetch the URL that generates the file while another process appears to - // be generating the same file (this is signaled using a lock). - $lock_name = 'image_style_generate:' . $this->style_name . ':' . drupal_hash_base64($original_uri); - $this->assertTrue(lock_acquire($lock_name), t('Lock was acquired.')); - $this->drupalGet($expected_generate_url); - $this->assertResponse(503, t('Service Unavailable response received.')); - $this->assertTrue($this->drupalGetHeader('Retry-After'), t('Retry-After header received.')); - lock_release($lock_name); + $generate_url = image_style_url($this->style_name, $original_uri); // Fetch the URL that generates the file. - $this->drupalGet($expected_generate_url); - $this->assertTrue(file_exists($generated_uri), t('Generated file was created.')); + $this->drupalGet($generate_url); + $this->assertResponse(200, t('Image was generated at the URL.')); + $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); $this->assertRaw(file_get_contents($generated_uri), t('URL returns expected file.')); $generated_image_info = image_get_info($generated_uri); $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], t('Expected Content-Type was reported.')); $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], t('Expected Content-Length was reported.')); - $this->assertTrue(lock_may_be_available($lock_name), t('Lock was released.')); - - // Check that the URL points directly to the generated file. - $expected_generated_url = file_create_url($generated_uri); - $actual_generated_url = image_style_url($this->style_name, $original_uri); - $this->drupalGet($expected_generated_url); - $this->assertEqual($actual_generated_url, $expected_generated_url, t('Got the download URL for an existing file.')); - $this->assertRaw(file_get_contents($generated_uri), t('URL returns expected file.')); - $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], t('Expected Content-Type was reported.')); - $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], t('Expected Content-Length was reported.')); + if ($scheme == 'private') { + $this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', t('Expected custom header has been added.')); + } } } @@ -354,8 +334,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase { * Count the number of images currently create for a style. */ function getImageCount($style) { - $directory = file_directory_path() . '/styles/' . $style['name']; - return count(file_scan_directory($directory, '/.*/')); + return count(file_scan_directory('public://styles/' . $style['name'], '/.*/')); } /** @@ -502,7 +481,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase { $this->drupalPost('admin/config/media/image-styles/delete/' . $style_name, array(), t('Delete')); // Confirm the style directory has been removed. - $directory = file_directory_path() . '/styles/' . $style_name; + $directory = file_default_scheme() . '://styles/' . $style_name; $this->assertFalse(is_dir($directory), t('Image style %style directory removed on style deletion.', array('%style' => $style['name']))); drupal_static_reset('image_styles'); @@ -592,7 +571,8 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase { // Create an image field that uses the new style. $field_name = strtolower($this->randomName(10)); $instance = $this->createImageField($field_name, 'article'); - $instance['display']['default']['type'] = 'image__' . $style_name; + $instance['display']['default']['type'] = 'image'; + $instance['display']['default']['settings']['image_style'] = $style_name; field_update_instance($instance); // Create a new node with an image attached. @@ -639,13 +619,28 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { ); } + /** + * Test image formatters on node display for public files. + */ + function testImageFieldFormattersPublic() { + $this->_testImageFieldFormatters('public'); + } + + /** + * Test image formatters on node display for private files. + */ + function testImageFieldFormattersPrivate() { + // Remove access content permission from anonymous users. + user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array('access content' => FALSE)); + $this->_testImageFieldFormatters('private'); + } + /** * Test image formatters on node display. */ - function testImageFieldFormatters() { + function _testImageFieldFormatters($scheme) { $field_name = strtolower($this->randomName()); - $this->createImageField($field_name, 'article'); - + $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme)); // Create a new node with an image attached. $test_image = current($this->drupalGetTestFiles('image')); $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); @@ -662,30 +657,56 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { // Test the image linked to file formatter. $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['default']['type'] = 'image_link_file'; + $instance['display']['default']['type'] = 'image'; + $instance['display']['default']['settings']['image_link'] = 'file'; field_update_instance($instance); $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE)); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image linked to file formatter displaying correctly on full node view.')); + // Verify that the image can be downloaded. + $this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), t('File was downloaded successfully.')); + if ($scheme == 'private') { + // Only verify HTTP headers when using private scheme and the headers are + // sent by Drupal. + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png; name="' . $test_image->filename . '"', t('Content-Type header was sent.')); + $this->assertEqual($this->drupalGetHeader('Content-Disposition'), 'inline; filename="' . $test_image->filename . '"', t('Content-Disposition header was sent.')); + $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'private', t('Cache-Control header was sent.')); + + // Log out and try to access the file. + $this->drupalLogout(); + $this->drupalGet(file_create_url($image_uri)); + $this->assertResponse('403', t('Access denied to original image as anonymous user.')); + + // Log in again. + $this->drupalLogin($this->admin_user); + } // Test the image linked to content formatter. - $instance['display']['default']['type'] = 'image_link_content'; + $instance['display']['default']['settings']['image_link'] = 'content'; field_update_instance($instance); $default_output = l(theme('image', $image_info), 'node/' . $nid, array('html' => TRUE, 'attributes' => array('class' => 'active'))); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image linked to content formatter displaying correctly on full node view.')); // Test the image style 'thumbnail' formatter. - $instance['display']['default']['type'] = 'image__thumbnail'; + $instance['display']['default']['settings']['image_link'] = ''; + $instance['display']['default']['settings']['image_style'] = 'thumbnail'; field_update_instance($instance); - // Ensure the derrivative image is generated so we do not have to deal with + // Ensure the derivative image is generated so we do not have to deal with // image style callback paths. $this->drupalGet(image_style_url('thumbnail', $image_uri)); - $image_info['path'] = image_style_url('thumbnail', $image_uri); + $image_info['path'] = image_style_path('thumbnail', $image_uri); $image_info['getsize'] = FALSE; $default_output = theme('image', $image_info); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image style thumbnail formatter displaying correctly on full node view.')); + + if ($scheme == 'private') { + // Log out and try to access the file. + $this->drupalLogout(); + $this->drupalGet(image_style_url('thumbnail', $image_uri)); + $this->assertResponse('403', t('Access denied to image style thumbnail as anonymous user.')); + } } /** @@ -765,7 +786,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { // Add a default image to the imagefield instance. $images = $this->drupalGetTestFiles('image'); $edit = array( - 'files[field_settings_default_image]' => realpath($images[0]->uri), + 'files[field_settings_default_image]' => drupal_realpath($images[0]->uri), ); $this->drupalPost('admin/structure/types/manage/article/fields/' . $field_name, $edit, t('Save settings')); // Clear field info cache so the new default image is detected. diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info new file mode 100644 index 0000000000000000000000000000000000000000..8f9512050e430fcee3d52616e17a9fbc00805a9c --- /dev/null +++ b/modules/image/tests/image_module_test.info @@ -0,0 +1,14 @@ +; $Id: image_module_test.info,v 1.1 2010/08/23 09:04:57 dries Exp $ +name = Image test +description = Provides hook implementations for testing Image module functionality. +package = Core +version = VERSION +core = 7.x +files[] = image_module_test.module +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" +project = "drupal" +datestamp = "1284599761" + diff --git a/modules/image/tests/image_module_test.module b/modules/image/tests/image_module_test.module new file mode 100644 index 0000000000000000000000000000000000000000..474bc1a56a67385e02826b6a252a7db7c1def094 --- /dev/null +++ b/modules/image/tests/image_module_test.module @@ -0,0 +1,14 @@ +<?php +// $Id: image_module_test.module,v 1.1 2010/08/23 09:04:57 dries Exp $ + +/** + * @file + * Provides Image module hook implementations for testing purposes. + */ + +function image_module_test_file_download($uri) { + if (variable_get('image_module_test_file_download', FALSE) == $uri) { + return array('X-Image-Owned-By' => 'image_module_test'); + } + return -1; +} diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index 3b79afe27f53fcb8e57eada81b91bddcc199abf2..d3a75c5de36b0d81598eea7597269f4aedd2e12c 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: locale.admin.inc,v 1.16 2010/06/26 21:32:20 dries Exp $ +// $Id: locale.admin.inc,v 1.17 2010/07/30 02:47:28 dries Exp $ /** * @file @@ -784,7 +784,7 @@ function locale_translate_overview_screen() { */ function locale_translate_seek_screen() { // Add CSS. - drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); $elements = drupal_get_form('locale_translation_filter_form'); $output = drupal_render($elements); diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php index a09535b01fa94477e9dc996e2bc03d40aca3f2df..fad2754c0209842be0a364264f0366252e06cbfd 100644 --- a/modules/locale/locale.api.php +++ b/modules/locale/locale.api.php @@ -1,5 +1,5 @@ <?php -// $Id: locale.api.php,v 1.11 2010/05/12 08:26:14 dries Exp $ +// $Id: locale.api.php,v 1.12 2010/07/16 02:37:06 dries Exp $ /** * @file @@ -68,8 +68,8 @@ function hook_language_init() { function hook_language_switch_links_alter(array &$links, $type, $path) { global $language; - if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language])) { - foreach ($links[$language] as $link) { + if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language->language])) { + foreach ($links[$language->language] as $link) { $link['attributes']['class'][] = 'active-language'; } } diff --git a/modules/locale/locale.info b/modules/locale/locale.info index 8e0957baf42a5a33106f4fd8aa9014092c12d35f..e1335db92fefa82ad53a371a7cd416e2514a36ee 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/locale/locale.install b/modules/locale/locale.install index a398df0af96727aecc3c6ba088d04e91dcb2bc64..630659925b6b48ad65fd1239cff94e8151666395 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -1,5 +1,5 @@ <?php -// $Id: locale.install,v 1.61 2010/07/07 05:44:03 webchick Exp $ +// $Id: locale.install,v 1.65 2010/08/27 11:07:36 webchick Exp $ /** * @file @@ -46,6 +46,7 @@ function locale_update_7000() { */ function locale_update_7001() { require_once DRUPAL_ROOT . '/includes/language.inc'; + require_once DRUPAL_ROOT . '/modules/locale/locale.module'; switch (variable_get('language_negotiation', 0)) { // LANGUAGE_NEGOTIATION_NONE. @@ -129,7 +130,7 @@ function locale_uninstall() { } // Delete the JavaScript translations directory if empty. if (!file_scan_directory($locale_js_directory, '/.*/')) { - rmdir($locale_js_directory); + drupal_rmdir($locale_js_directory); } } @@ -145,6 +146,7 @@ function locale_uninstall() { variable_del('locale_js_directory'); variable_del('javascript_parsed'); variable_del('locale_field_language_fallback'); + variable_del('locale_cache_length'); foreach (language_types() as $type) { variable_del("language_negotiation_$type"); @@ -334,7 +336,10 @@ function locale_schema() { ), 'primary key' => array('language', 'lid', 'plural'), 'foreign keys' => array( - 'lid' => array('locales_source' => 'lid'), + 'locales_source' => array( + 'table' => 'locales_source', + 'columns' => array('lid' => 'lid'), + ), ), 'indexes' => array( 'lid' => array('lid'), diff --git a/modules/locale/locale.module b/modules/locale/locale.module index e396cefbe784ad942ac30ff20ae01949af4269ad..162faa01d9f979cf24ebc0b94363330308cb827e 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -1,5 +1,5 @@ <?php -// $Id: locale.module,v 1.293 2010/05/12 08:26:14 dries Exp $ +// $Id: locale.module,v 1.298 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -377,7 +377,7 @@ function locale_multilingual_node_type($type_name) { /** * Implements hook_form_alter(). * - * Adds language fields to forms. + * Adds language fields to user forms. */ function locale_form_alter(&$form, &$form_state, $form_id) { // Only alter user forms if there is more than one language. @@ -388,25 +388,29 @@ function locale_form_alter(&$form, &$form_state, $form_id) { locale_language_selector_form($form, $form_state, $form['#user']); } } - if (!empty($form['#node_edit_form'])) { - if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) { - $form['language'] = array( - '#type' => 'select', - '#title' => t('Language'), - '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''), - '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'), - ); - } - // Node type without language selector: assign the default for new nodes - elseif (!isset($form['#node']->nid)) { - $default = language_default(); - $form['language'] = array( - '#type' => 'value', - '#value' => $default->language - ); - } - $form['#submit'][] = 'locale_field_node_form_submit'; +} + +/** + * Implements hook_form_BASE_FORM_ID_alter(). + */ +function locale_form_node_form_alter(&$form, &$form_state) { + if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) { + $form['language'] = array( + '#type' => 'select', + '#title' => t('Language'), + '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''), + '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'), + ); + } + // Node type without language selector: assign the default for new nodes + elseif (!isset($form['#node']->nid)) { + $default = language_default(); + $form['language'] = array( + '#type' => 'value', + '#value' => $default->language + ); } + $form['#submit'][] = 'locale_field_node_form_submit'; } /** @@ -414,6 +418,10 @@ function locale_form_alter(&$form, &$form_state, $form_id) { * * Checks if Locale is registered as a translation handler and handle possible * node language changes. + * + * This submit handler needs to run before entity_form_submit_build_entity() + * is invoked by node_form_submit_build_node(), because it alters the values of + * attached fields. Therefore, it cannot be a hook_node_submit() implementation. */ function locale_field_node_form_submit($form, &$form_state) { if (field_has_translation_handler('node', 'locale')) { @@ -480,7 +488,7 @@ function locale_field_language_alter(&$display_language, $context) { * @param $entity * The entity to be displayed. * @param $langcode - * The language code $entity has to be displayed in. + * The language code $entity has to be displayed in. */ function locale_field_language_fallback(&$display_language, $entity, $langcode) { // Lazily init fallback candidates to avoid unnecessary calls. @@ -650,7 +658,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { // Refresh database stored cache of translations for given language. // We only store short strings used in current version, to improve // performance and consume less memory. - $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < 75", array(':language' => $langcode, ':version' => VERSION)); + $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < :length", array(':language' => $langcode, ':version' => VERSION, ':length' => variable_get('locale_cache_length', 75))); foreach ($result as $data) { $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation); } @@ -961,7 +969,7 @@ function locale_block_view($type) { */ function locale_url_outbound_alter(&$path, &$options, $original_path) { // Only modify internal URLs. - if (!$options['external']) { + if (!$options['external'] && drupal_multilingual()) { static $callbacks; if (!isset($callbacks)) { @@ -993,14 +1001,14 @@ function locale_url_outbound_alter(&$path, &$options, $original_path) { } } -/* +/** * Implements hook_form_FORM_ID_alter(). */ function locale_form_comment_form_alter(&$form, &$form_state, $form_id) { // If a content type has multilingual support we set the content language as // comment language. - if (empty($form['language']['#value']) && locale_multilingual_node_type($form['#node']->type)) { - global $language; - $form['language']['#value'] = $language->language; + if ($form['language']['#value'] == LANGUAGE_NONE && locale_multilingual_node_type($form['#node']->type)) { + global $language_content; + $form['language']['#value'] = $language_content->language; } } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 270eecc5d79f9149fa2918f76149c4aa1b72f091..d016648b1adea704ff250f07bca092c6ebed6dd6 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1,5 +1,5 @@ <?php -// $Id: locale.test,v 1.73 2010/06/26 19:55:47 dries Exp $ +// $Id: locale.test,v 1.79 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -758,11 +758,11 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { * Additional options to pass to the translation import form. */ function importPoFile($contents, array $options = array()) { - $name = tempnam(file_directory_path('temporary'), "po_") . '.po'; + $name = tempnam('temporary://', "po_") . '.po'; file_put_contents($name, $contents); $options['files[file]'] = $name; $this->drupalPost('admin/config/regional/translate/import', $options, t('Import')); - unlink($name); + drupal_unlink($name); } /** @@ -905,13 +905,13 @@ class LocaleExportFunctionalTest extends DrupalWebTestCase { function testExportTranslation() { // First import some known translations. // This will also automatically enable the 'fr' language. - $name = tempnam(file_directory_path('temporary'), "po_") . '.po'; + $name = tempnam('temporary://', "po_") . '.po'; file_put_contents($name, $this->getPoFile()); $this->drupalPost('admin/config/regional/translate/import', array( 'langcode' => 'fr', 'files[file]' => $name, ), t('Import')); - unlink($name); + drupal_unlink($name); // Get the French translations. $this->drupalPost('admin/config/regional/translate/export', array( @@ -1160,7 +1160,7 @@ class LocaleLanguageSwitchingFunctionalTest extends DrupalWebTestCase { // Enable the language switching block. $language_type = LANGUAGE_TYPE_INTERFACE; $edit = array( - "locale_{$language_type}[region]" => 'sidebar_first', + "blocks[locale_{$language_type}][region]" => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); @@ -1452,9 +1452,9 @@ class LocalePathFunctionalTest extends DrupalWebTestCase { ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); - // Set language negotiation. - drupal_load('module', 'locale'); - variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); + // Enable URL language detection and selection. + $edit = array('language[enabled][locale-url]' => 1); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); // Create a node. $node = $this->drupalCreateNode(array('type' => 'page')); diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info index b13725ecfe39a068c549c5f46319af900b468a96..4ae0f4a1a11f92acdf401245ff3edf10e5368c00 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc index a526586b6490b491455e32ac516dd28ba5430ee3..48d3022fea21d95486d9391bd1b4604df2fc4849 100644 --- a/modules/menu/menu.admin.inc +++ b/modules/menu/menu.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: menu.admin.inc,v 1.82 2010/06/26 21:32:20 dries Exp $ +// $Id: menu.admin.inc,v 1.84 2010/09/07 22:12:05 dries Exp $ /** * @file @@ -308,7 +308,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) { '#title' => t('Description'), '#default_value' => isset($item['options']['attributes']['title']) ? $item['options']['attributes']['title'] : '', '#rows' => 1, - '#description' => t('The description displayed when hovering over a menu link.'), + '#description' => t('Shown when hovering over the menu link.'), ); $form['enabled'] = array( '#type' => 'checkbox', @@ -674,7 +674,7 @@ function menu_reset_item_confirm_submit($form, &$form_state) { function menu_configure() { $form['intro'] = array( '#type' => 'item', - '#markup' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. The following option sets the default menu in which a new link will be added.'), + '#markup' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. To configure these settings for a particular content type, visit the <a href="@content-types">Content types</a> page, click the <em>edit</em> link for the content type, and go to the <em>Menu settings</em> section.', array('@content-types' => url('admin/structure/types'))), ); $menu_options = menu_get_menus(); diff --git a/modules/menu/menu.info b/modules/menu/menu.info index 7af94015dd27db1374cded1970f2d7dcafd9088c..5317a1441621b3dc84552c6492e92d9257e312e4 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/menu/menu.install b/modules/menu/menu.install index c4220e1accaaa94eba91ef947ec986b920095f96..7f6a1fa5e360162cd5f73d4b1bb32f3e59a0a8c9 100644 --- a/modules/menu/menu.install +++ b/modules/menu/menu.install @@ -1,5 +1,5 @@ <?php -// $Id: menu.install,v 1.25 2010/01/10 17:55:19 webchick Exp $ +// $Id: menu.install,v 1.26 2010/08/23 23:38:06 webchick Exp $ /** * @file @@ -52,7 +52,6 @@ function menu_install() { 'user-menu' => $t("The <em>User</em> menu contains links related to the user's account, as well as the 'Log out' link."), 'management' => $t('The <em>Management</em> menu contains links for administrative tasks.'), 'main-menu' => $t('The <em>Main</em> menu is used on many sites to show the major sections of the site, often in a top navigation bar.'), - 'secondary-menu' => $t('The <em>Secondary</em> menu is used on many sites for legal notices and contact pages.'), ); foreach ($system_menus as $menu_name => $title) { $menu = array( diff --git a/modules/menu/menu.module b/modules/menu/menu.module index 86f7543f627466b80d9b26c80ad16277dbaa3083..4fed193bbc773322b1a7084cdd6f257ec36f4519 100644 --- a/modules/menu/menu.module +++ b/modules/menu/menu.module @@ -1,5 +1,5 @@ <?php -// $Id: menu.module,v 1.230 2010/06/17 13:44:45 dries Exp $ +// $Id: menu.module,v 1.232 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -508,9 +508,13 @@ function menu_node_save($node) { elseif (trim($link['link_title'])) { $link['link_title'] = trim($link['link_title']); $link['link_path'] = "node/$node->nid"; - // If not already set, use the node's title as link title attribute. - if (empty($link['options']['attributes']['title']) && !$link['customized']) { - $link['options']['attributes']['title'] = trim($node->title); + if (trim($link['description'])) { + $link['options']['attributes']['title'] = trim($link['description']); + } + else { + // If the description field was left empty, remove the title attribute + // from the menu link. + unset($link['options']['attributes']['title']); } if (!menu_link_save($link)) { drupal_set_message(t('There was an error saving the menu link.'), 'error'); @@ -583,84 +587,96 @@ function _menu_parent_depth_limit($item) { } /** - * Implements hook_form_alter(). Adds menu item fields to the node form. + * Implements hook_form_BASE_FORM_ID_alter(). + * + * Adds menu item fields to the node form. + * + * @see menu_node_submit() */ -function menu_form_alter(&$form, $form_state, $form_id) { - if (!empty($form['#node_edit_form'])) { - // Generate a list of possible parents. - // @todo This must be handled in a #process handler. - $type = $form['#node']->type; - $options = menu_parent_options(menu_get_menus(), $type); - // If no possible parent menu items were found, there is nothing to display. - if (empty($options)) { - return; - } - $link = $form['#node']->menu; - - $form['menu'] = array( - '#type' => 'fieldset', - '#title' => t('Menu settings'), - '#access' => user_access('administer menu'), - '#collapsible' => TRUE, - '#collapsed' => !$link['link_title'], - '#group' => 'additional_settings', - '#attached' => array( - 'js' => array(drupal_get_path('module', 'menu') . '/menu.js'), - ), - '#tree' => TRUE, - '#weight' => -2, - '#attributes' => array('class' => array('menu-link-form')), - ); - $form['menu']['enabled'] = array( - '#type' => 'checkbox', - '#title' => t('Provide a menu link'), - '#default_value' => (int) (bool) $link['mlid'], - ); - $form['menu']['link'] = array( - '#type' => 'container', - '#parents' => array('menu'), - '#states' => array( - 'invisible' => array( - 'input[name="menu[enabled]"]' => array('checked' => FALSE), - ), +function menu_form_node_form_alter(&$form, $form_state) { + // Generate a list of possible parents. + // @todo This must be handled in a #process handler. + $type = $form['#node']->type; + $options = menu_parent_options(menu_get_menus(), $type); + // If no possible parent menu items were found, there is nothing to display. + if (empty($options)) { + return; + } + $link = $form['#node']->menu; + + $form['menu'] = array( + '#type' => 'fieldset', + '#title' => t('Menu settings'), + '#access' => user_access('administer menu'), + '#collapsible' => TRUE, + '#collapsed' => !$link['link_title'], + '#group' => 'additional_settings', + '#attached' => array( + 'js' => array(drupal_get_path('module', 'menu') . '/menu.js'), + ), + '#tree' => TRUE, + '#weight' => -2, + '#attributes' => array('class' => array('menu-link-form')), + ); + $form['menu']['enabled'] = array( + '#type' => 'checkbox', + '#title' => t('Provide a menu link'), + '#default_value' => (int) (bool) $link['mlid'], + ); + $form['menu']['link'] = array( + '#type' => 'container', + '#parents' => array('menu'), + '#states' => array( + 'invisible' => array( + 'input[name="menu[enabled]"]' => array('checked' => FALSE), ), - ); + ), + ); - // Populate the element with the link data. - foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) { - $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]); - } + // Populate the element with the link data. + foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) { + $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]); + } - $form['menu']['link']['link_title'] = array( - '#type' => 'textfield', - '#title' => t('Menu link title'), - '#default_value' => $link['link_title'], - ); + $form['menu']['link']['link_title'] = array( + '#type' => 'textfield', + '#title' => t('Menu link title'), + '#default_value' => $link['link_title'], + ); - $default = ($link['mlid'] ? $link['menu_name'] . ':' . $link['plid'] : variable_get('menu_parent_' . $type, 'main-menu:0')); - // @todo This will fail with the new selective menus per content type. - if (!isset($options[$default])) { - $default = 'navigation:0'; - } - $form['menu']['link']['parent'] = array( - '#type' => 'select', - '#title' => t('Parent item'), - '#default_value' => $default, - '#options' => $options, - '#attributes' => array('class' => array('menu-parent-select')), - ); - $form['menu']['link']['weight'] = array( - '#type' => 'weight', - '#title' => t('Weight'), - '#delta' => 50, - '#default_value' => $link['weight'], - '#description' => t('Menu links with smaller weights are displayed before links with larger weights.'), - ); + $form['menu']['link']['description'] = array( + '#type' => 'textarea', + '#title' => t('Description'), + '#default_value' => isset($link['options']['attributes']['title']) ? $link['options']['attributes']['title'] : '', + '#rows' => 1, + '#description' => t('Shown when hovering over the menu link.'), + ); + + $default = ($link['mlid'] ? $link['menu_name'] . ':' . $link['plid'] : variable_get('menu_parent_' . $type, 'main-menu:0')); + // @todo This will fail with the new selective menus per content type. + if (!isset($options[$default])) { + $default = 'navigation:0'; } + $form['menu']['link']['parent'] = array( + '#type' => 'select', + '#title' => t('Parent item'), + '#default_value' => $default, + '#options' => $options, + '#attributes' => array('class' => array('menu-parent-select')), + ); + $form['menu']['link']['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#delta' => 50, + '#default_value' => $link['weight'], + '#description' => t('Menu links with smaller weights are displayed before links with larger weights.'), + ); } /** * Implements hook_node_submit(). + * + * @see menu_form_node_form_alter() */ function menu_node_submit($node, $form, $form_state) { // Decompose the selected menu parent option into 'menu_name' and 'plid', if @@ -671,7 +687,8 @@ function menu_node_submit($node, $form, $form_state) { } /** - * Implements hook_form_FORM_ID_alter() for the node type form. + * Implements hook_form_FORM_ID_alter(). + * * Adds menu options to the node type form. */ function menu_form_node_type_form_alter(&$form, $form_state) { diff --git a/modules/menu/menu.test b/modules/menu/menu.test index 36ba1ab085160de65e5382b07b719bbe79a62bb7..e2cfe7ec8da73849a96ee5e4958b7a08c53b01f2 100644 --- a/modules/menu/menu.test +++ b/modules/menu/menu.test @@ -1,5 +1,5 @@ <?php -// $Id: menu.test,v 1.36 2010/05/06 05:59:31 webchick Exp $ +// $Id: menu.test,v 1.40 2010/08/30 00:22:03 webchick Exp $ /** * @file @@ -157,7 +157,7 @@ class MenuTestCase extends DrupalWebTestCase { // Enable the custom menu block. $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'. $edit = array(); - $edit['menu_' . $menu_name . '[region]'] = 'sidebar_first'; + $edit['blocks[menu_' . $menu_name . '][region]'] = 'sidebar_first'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('Custom menu block was enabled')); @@ -192,14 +192,28 @@ class MenuTestCase extends DrupalWebTestCase { // Add nodes to use as links for menu links. $node1 = $this->drupalCreateNode(array('type' => 'article')); $node2 = $this->drupalCreateNode(array('type' => 'article')); + $node3 = $this->drupalCreateNode(array('type' => 'article')); + $node4 = $this->drupalCreateNode(array('type' => 'article')); + $node5 = $this->drupalCreateNode(array('type' => 'article')); // Add menu links. $item1 = $this->addMenuLink(0, 'node/' . $node1->nid, $menu_name); $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name); + $item3 = $this->addMenuLink($item2['mlid'], 'node/' . $node3->nid, $menu_name); + $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => 0)); + $this->assertMenuLink($item2['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => 0)); + $this->assertMenuLink($item3['mlid'], array('depth' => 3, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => $item3['mlid'], 'p4' => 0)); // Verify menu links. $this->verifyMenuLink($item1, $node1); $this->verifyMenuLink($item2, $node2, $item1, $node1); + $this->verifyMenuLink($item3, $node3, $item2, $node2); + + // Add more menu links. + $item4 = $this->addMenuLink(0, 'node/' . $node4->nid, $menu_name); + $item5 = $this->addMenuLink($item4['mlid'], 'node/' . $node5->nid, $menu_name); + $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0)); + $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0)); // Modify menu links. $this->modifyMenuLink($item1); @@ -209,6 +223,14 @@ class MenuTestCase extends DrupalWebTestCase { $this->toggleMenuLink($item1); $this->toggleMenuLink($item2); + // Move link and verify that descendants are updated. + $this->moveMenuLink($item2, $item5['mlid'], $menu_name); + $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => 0)); + $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0)); + $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0)); + $this->assertMenuLink($item2['mlid'], array('depth' => 3, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => 0)); + $this->assertMenuLink($item3['mlid'], array('depth' => 4, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => $item3['mlid'], 'p5' => 0)); + // Enable a link via the overview form. $this->disableMenuLink($item1); $edit = array(); @@ -219,8 +241,7 @@ class MenuTestCase extends DrupalWebTestCase { $this->drupalPost('admin/structure/menu/manage/' . $item1['menu_name'], $edit, t('Save configuration')); // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item1['mlid']))->fetchField(); - $this->assertEqual($hidden, 0, t('Link is not hidden in the database table when enabled via the overview form')); + $this->assertMenuLink($item1['mlid'], array('hidden' => 0)); // Save menu links for later tests. $this->items[] = $item1; @@ -277,32 +298,9 @@ class MenuTestCase extends DrupalWebTestCase { // Unlike most other modules, there is no confirmation message displayed. $this->assertText($title, 'Menu link was added'); - // Retrieve menu link. - $item = db_query("SELECT * FROM {menu_links} WHERE link_title = :title", array(':title' => $title))->fetchAssoc(); - - // Check the structure in the DB of the two menu links. - // In general, if $n = $item['depth'] then $item['p'. $n] == $item['mlid'] and $item['p' . ($n - 1)] == $item['plid'] (unless depth == 0). - // All $item['p' . $n] for $n > depth must be 0. - // We know link1 is at the top level, so $item1['deptj'] == 1 and $item1['plid'] == 0. - // We know that the parent of link2 is link1, so $item2['plid'] == $item1['mlid']. - // Both menu links were created in the navigation menu. - $this->assertEqual($item['menu_name'], $menu_name); - $this->assertEqual($item['plid'], $plid); - $options = unserialize($item['options']); - if (!empty($options['query'])) { - $item['link_path'] .= '?' . drupal_http_build_query($options['query']); - } - if (!empty($options['fragment'])) { - $item['link_path'] .= '#' . $options['fragment']; - } - $this->assertEqual($item['link_path'], $link); - $this->assertEqual($item['link_title'], $title); - if ($plid == 0) { - $this->assertTrue($item['depth'] == 1 && !$item['has_children'] && $item['p1'] == $item['mlid'] && $item['p2'] == 0, 'Menu link has correct data'); - } - else { - $this->assertTrue($item['depth'] == 2 && !$item['has_children'] && $item['p1'] == $plid && $item['p2'] == $item['mlid'], 'Menu link has correct data'); - } + $item = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => $title))->fetchAssoc(); + $this->assertTrue(t('Menu link was found in database.')); + $this->assertMenuLink($item['mlid'], array('menu_name' => $menu_name, 'link_path' => $link, 'has_children' => 0, 'plid' => $plid)); return $item; } @@ -358,6 +356,19 @@ class MenuTestCase extends DrupalWebTestCase { $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Menu link link target was correct')); } + /** + * Change the parent of a menu link using the menu module UI. + */ + function moveMenuLink($item, $plid, $menu_name) { + $mlid = $item['mlid']; + + $edit = array( + 'parent' => $menu_name . ':' . $plid, + ); + $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save')); + $this->assertResponse(200); + } + /** * Modify a menu link using the menu module UI. * @@ -453,8 +464,7 @@ class MenuTestCase extends DrupalWebTestCase { // Unlike most other modules, there is no confirmation message displayed. // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField(); - $this->assertEqual($hidden, 1, t('Link is hidden in the database table')); + $this->assertMenuLink($mlid, array('hidden' => 1)); } /** @@ -469,8 +479,31 @@ class MenuTestCase extends DrupalWebTestCase { $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save')); // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField(); - $this->assertEqual($hidden, 0, t('Link is not hidden in the database table')); + $this->assertMenuLink($mlid, array('hidden' => 0)); + } + + /** + * Fetch the menu item from the database and compare it to the specified + * array. + * + * @param $mlid + * Menu item id. + * @param $item + * Array containing properties to verify. + */ + function assertMenuLink($mlid, array $expected_item) { + // Retrieve menu link. + $item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $mlid))->fetchAssoc(); + $options = unserialize($item['options']); + if (!empty($options['query'])) { + $item['link_path'] .= '?' . drupal_http_build_query($options['query']); + } + if (!empty($options['fragment'])) { + $item['link_path'] .= '#' . $options['fragment']; + } + foreach ($expected_item as $key => $value) { + $this->assertEqual($item[$key], $value, t('Parameter %key had expected value.', array('%key' => $key))); + } } /** diff --git a/modules/node/content_types.inc b/modules/node/content_types.inc index 9d036585cc9fb295bb1c689d721810de67dfa85c..ce7442701dc8c33ecc6878f61d24539fb6324d2a 100644 --- a/modules/node/content_types.inc +++ b/modules/node/content_types.inc @@ -1,5 +1,5 @@ <?php -// $Id: content_types.inc,v 1.115 2010/06/24 22:23:06 webchick Exp $ +// $Id: content_types.inc,v 1.117 2010/09/03 17:47:20 dries Exp $ /** * @file @@ -139,6 +139,9 @@ function node_type_form($form, &$form_state, $type = NULL) { $form['additional_settings'] = array( '#type' => 'vertical_tabs', + '#attached' => array( + 'js' => array(drupal_get_path('module', 'node') . '/content_types.js'), + ), ); $form['submission'] = array( @@ -207,13 +210,6 @@ function node_type_form($form, &$form_state, $type = NULL) { '#default_value' => variable_get('node_submitted_' . $type->type, TRUE), '#description' => t('Author username and publish date will be displayed.'), ); - $form['display']['teaser_length'] = array( - '#type' => 'select', - '#title' => t('Length of trimmed content'), - '#default_value' => variable_get('teaser_length_' . $type->type, 600), - '#options' => drupal_map_assoc(array(0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000), '_node_characters'), - '#description' => t("The maximum number of characters used in the trimmed version of content.") - ); $form['old_type'] = array( '#type' => 'value', '#value' => $type->type, @@ -451,7 +447,6 @@ function node_type_delete_confirm($form, &$form_state, $type) { function node_type_delete_confirm_submit($form, &$form_state) { node_type_delete($form_state['values']['type']); - variable_del('teaser_length_' . $form_state['values']['type']); variable_del('node_preview_' . $form_state['values']['type']); $t_args = array('%name' => $form_state['values']['name']); drupal_set_message(t('The content type %name has been deleted.', $t_args)); diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 6cb7874c289d26c97a8a868e267cdec160ea5159..de8ecbe04931f0943393191ed114f78d2e23c28b 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: node.admin.inc,v 1.97 2010/07/07 17:56:42 webchick Exp $ +// $Id: node.admin.inc,v 1.98 2010/08/30 06:26:45 webchick Exp $ /** * @file @@ -461,7 +461,7 @@ function node_admin_nodes() { $destination = drupal_get_destination(); $options = array(); foreach ($nodes as $node) { - $l_options = $node->language != LANGUAGE_NONE ? array('language' => $languages[$node->language]) : array(); + $l_options = $node->language != LANGUAGE_NONE && isset($languages[$node->language]) ? array('language' => $languages[$node->language]) : array(); $options[$node->nid] = array( 'title' => array( 'data' => array( @@ -478,7 +478,12 @@ function node_admin_nodes() { 'changed' => format_date($node->changed, 'short'), ); if ($multilanguage) { - $options[$node->nid]['language'] = $node->language == LANGUAGE_NONE ? t('Language neutral') : t($languages[$node->language]->name); + if ($node->language == LANGUAGE_NONE || isset($languages[$node->language])) { + $options[$node->nid]['language'] = $node->language == LANGUAGE_NONE ? t('Language neutral') : t($languages[$node->language]->name); + } + else { + $options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $node->language)); + } } // Build a list of all the accessible operations for the current node. $operations = array(); diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 490205e7487a3984cd41a1f6e4db5f3108d4cef8..b99d72bd7287c1af1be0e40b950ac5202b263bf7 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -1,5 +1,5 @@ <?php -// $Id: node.api.php,v 1.70 2010/06/17 13:44:45 dries Exp $ +// $Id: node.api.php,v 1.73 2010/09/03 19:56:51 dries Exp $ /** * @file @@ -406,13 +406,9 @@ function hook_node_delete($node) { * @ingroup node_api_hooks */ function hook_node_revision_delete($node) { - db_delete('upload')->condition('vid', $node->vid)->execute(); - if (!is_array($node->files)) { - return; - } - foreach ($node->files as $file) { - file_delete($file); - } + db_delete('mytable') + ->condition('vid', $node->vid) + ->execute(); } /** @@ -653,10 +649,12 @@ function hook_node_update_index($node) { * The node being validated. * @param $form * The form being used to edit the node. + * @param $form_state + * The form state array. * * @ingroup node_api_hooks */ -function hook_node_validate($node, $form) { +function hook_node_validate($node, $form, &$form_state) { if (isset($node->end) && isset($node->start)) { if ($node->start > $node->end) { form_set_error('time', t('An event may not end before it starts.')); @@ -977,7 +975,8 @@ function hook_prepare($node) { * @param $node * The node being added or edited. * @param $form_state - * The form state array. Changes made to this variable will have no effect. + * The form state array. + * * @return * An array containing the form elements to be displayed in the node * edit form. @@ -991,7 +990,7 @@ function hook_prepare($node) { * * @ingroup node_api_hooks */ -function hook_form($node, $form_state) { +function hook_form($node, &$form_state) { $type = node_type_get_type($node); $form['field1'] = array( @@ -1116,10 +1115,12 @@ function hook_update($node) { * The node being validated. * @param $form * The form being used to edit the node. + * @param $form_state + * The form state array. * * @ingroup node_api_hooks */ -function hook_validate($node, &$form) { +function hook_validate($node, $form, &$form_state) { if (isset($node->end) && isset($node->start)) { if ($node->start > $node->end) { form_set_error('time', t('An event may not end before it starts.')); @@ -1155,7 +1156,7 @@ function hook_validate($node, &$form) { * @ingroup node_api_hooks */ function hook_view($node, $view_mode) { - if (node_is_page($node)) { + if ($view_mode == 'full' && node_is_page($node)) { $breadcrumb = array(); $breadcrumb[] = l(t('Home'), NULL); $breadcrumb[] = l(t('Example'), 'example'); diff --git a/modules/node/node.info b/modules/node/node.info index 8165e06eeb605f0bc055960e93887856cb1f0718..1f52b5640c7ee0bc5142ee340f9b798b706bf767 100644 --- a/modules/node/node.info +++ b/modules/node/node.info @@ -1,4 +1,4 @@ -; $Id: node.info,v 1.13 2009/11/17 21:24:18 dries Exp $ +; $Id: node.info,v 1.14 2010/09/05 02:21:38 dries Exp $ name = Node description = Allows content to be submitted to the site and displayed on pages. package = Core @@ -13,9 +13,10 @@ files[] = node.test files[] = node.tokens.inc required = TRUE configure = admin/structure/types +stylesheets[all][] = node.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/node/node.install b/modules/node/node.install index c6085f84c5f0b76cb30fb1f3b19e738d05b9fa6e..108ef5a3dc9581a84d787f84511a1d4fc964fbb2 100644 --- a/modules/node/node.install +++ b/modules/node/node.install @@ -1,5 +1,5 @@ <?php -// $Id: node.install,v 1.51 2010/05/14 04:37:53 webchick Exp $ +// $Id: node.install,v 1.55 2010/09/13 05:50:09 webchick Exp $ /** * @file @@ -118,8 +118,14 @@ function node_schema() { 'vid' => array('vid'), ), 'foreign keys' => array( - 'vid' => array('node_revision' => 'vid'), - 'uid' => array('users' => 'uid'), + 'node_revision' => array( + 'table' => 'node_revision', + 'columns' => array('vid' => 'vid'), + ), + 'node_author' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), 'primary key' => array('nid'), ); @@ -174,7 +180,12 @@ function node_schema() { ), ), 'primary key' => array('nid', 'gid', 'realm'), - 'foreign keys' => array('node' => 'nid'), + 'foreign keys' => array( + 'affected_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + ), ); $schema['node_revision'] = array( @@ -249,8 +260,14 @@ function node_schema() { ), 'primary key' => array('vid'), 'foreign keys' => array( - 'node' => 'nid', - 'users' => 'uid' + 'versioned_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'version_author' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); @@ -401,6 +418,17 @@ function node_update_dependencies() { return $dependencies; } +/** + * Utility function: fetch the node types directly from the database. + * + * This function is valid for a database schema version 7000. + * + * @ingroup update-api-6.x-to-7.x + */ +function _update_7000_node_get_types() { + return db_query('SELECT * FROM {node_type}')->fetchAllAssoc('type', PDO::FETCH_OBJ); +} + /** * @defgroup updates-6.x-to-7.x System updates from 6.x to 7.x * @{ @@ -495,12 +523,44 @@ function node_update_7006(&$sandbox) { $result = db_select('node_type', 'node_type') ->fields('node_type') ->execute(); + foreach ($result as $type_object) { + $node_types[$type_object->type] = $type_object; + } + $extra_types = db_query('SELECT DISTINCT type FROM {node} WHERE type NOT IN (:types)', array(':types' => array_keys($node_types)))->fetchCol(); + foreach ($extra_types as $type) { + $type_object = new stdClass; + $type_object->type = $type; + // Always create a body. Querying node_revisions for a non-empty body + // would skip creating body fields for types that have a body but + // the nodes of that type so far had empty bodies. + $type_object->has_body = 1; + $type_object->body_label = 'Body'; + $node_types[$type_object->type] = $type_object; + } + $default_trim_length = variable_get('teaser_length', 600); // Add body field instances for existing node types. - foreach ($result as $node_type) { + foreach ($node_types as $node_type) { if ($node_type->has_body) { - node_add_body_field($node_type, $node_type->body_label); + $instance = node_add_body_field($node_type, $node_type->body_label); + // Update newly created instance to convert teaser_length variable + // into formatter settings. + $trim_length = variable_get('teaser_length_' . $node_type->type, $default_trim_length); + $instance_changed = FALSE; + foreach ($instance['display'] as $view_mode => $view_mode_info) { + if ($view_mode_info['type'] == 'text_trimmed' || $view_mode_info['type'] == 'text_summary_or_trimmed') { + if (!isset($view_mode_info['settings']['trim_length'])) { + $instance['display'][$view_mode]['settings']['trim_length'] = $trim_length; + $instance_changed = TRUE; + } + } + } + if ($instance_changed) { + field_update_instance($instance); + } + variable_del('teaser_length_' . $node_type->type); } + // Leave 'teaser_length' variable for aggregator module upgrade. $sandbox['node_types_info'][$node_type->type] = array( 'has_body' => $node_type->has_body, diff --git a/modules/node/node.module b/modules/node/node.module index f9009618a16eff344d89f46d54f787a55f06fa21..ffd6ed84c90b2cb740f97cd9bc2862a2450398ee 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -1,5 +1,5 @@ <?php -// $Id: node.module,v 1.1279 2010/06/25 19:33:47 dries Exp $ +// $Id: node.module,v 1.1298 2010/09/11 06:03:11 webchick Exp $ /** * @file @@ -181,6 +181,7 @@ function node_entity_info() { 'id' => 'nid', 'revision' => 'vid', 'bundle' => 'type', + 'label' => 'title', ), 'bundle keys' => array( 'bundle' => 'type', @@ -555,6 +556,9 @@ function node_type_save($info) { * A node type object. * @param $label * The label for the body instance. + * + * @return + * Body field instance. */ function node_add_body_field($type, $label = 'Body') { // Add or remove the body field, as needed. @@ -588,8 +592,9 @@ function node_add_body_field($type, $label = 'Body') { ), ), ); - field_create_instance($instance); + $instance = field_create_instance($instance); } + return $instance; } /** @@ -922,7 +927,7 @@ function node_object_prepare($node) { /** * Perform validation checks on the given node. */ -function node_validate($node, $form = array()) { +function node_validate($node, $form, &$form_state) { $type = node_type_get_type($node); if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) { @@ -942,9 +947,18 @@ function node_validate($node, $form = array()) { form_set_error('date', t('You have to specify a valid date.')); } - // Do node-type-specific validation checks. - node_invoke($node, 'validate', $form); - module_invoke_all('node_validate', $node, $form); + // Invoke hook_validate() for node type specific validation and + // hook_node_validate() for miscellaneous validation needed by modules. Can't + // use node_invoke() or module_invoke_all(), because $form_state must be + // receivable by reference. + $function = node_type_get_base($node) . '_validate'; + if (function_exists($function)) { + $function($node, $form, $form_state); + } + foreach (module_implements('node_validate') as $module) { + $function = $module . '_node_validate'; + $function($node, $form, $form_state); + } } /** @@ -1134,16 +1148,6 @@ function node_delete_multiple($nids) { if (!empty($nids)) { $nodes = node_load_multiple($nids, array()); - db_delete('node') - ->condition('nid', $nids, 'IN') - ->execute(); - db_delete('node_revision') - ->condition('nid', $nids, 'IN') - ->execute(); - db_delete('history') - ->condition('nid', $nids, 'IN') - ->execute(); - foreach ($nodes as $nid => $node) { // Call the node-specific callback (if any): node_invoke($node, 'delete'); @@ -1159,6 +1163,17 @@ function node_delete_multiple($nids) { } } + // Delete after calling hooks so that they can query node tables as needed. + db_delete('node') + ->condition('nid', $nids, 'IN') + ->execute(); + db_delete('node_revision') + ->condition('nid', $nids, 'IN') + ->execute(); + db_delete('history') + ->condition('nid', $nids, 'IN') + ->execute(); + // Clear the page and block and node_load_multiple caches. cache_clear_all(); entity_get_controller('node')->resetCache(); @@ -1218,7 +1233,7 @@ function node_view($node, $view_mode = 'full') { // displayed on its own page. Modules may alter this behavior (for example, // to restrict contextual links to certain view modes) by implementing // hook_node_view_alter(). - if (!node_is_page($node)) { + if (!empty($node->nid) && !($view_mode == 'full' && node_is_page($node))) { $build['#contextual_links']['node'] = array('node', array($node->nid)); } @@ -1281,7 +1296,7 @@ function node_build_content($node, $view_mode = 'full') { 'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)) ); } - $node->content['links']['node'] = array( + $node->content['links'] = array( '#theme' => 'links__node', '#links' => $links, '#attributes' => array('class' => array('links', 'inline')), @@ -1351,7 +1366,7 @@ function template_preprocess_node(&$variables) { $uri = entity_uri('node', $node); $variables['node_url'] = url($uri['path'], $uri['options']); $variables['title'] = check_plain($node->title); - $variables['page'] = node_is_page($node); + $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node); if (!empty($node->in_preview)) { unset($node->content['links']); @@ -1535,7 +1550,7 @@ function node_search_admin() { /** * Implements hook_search_execute(). */ -function node_search_execute($keys = NULL) { +function node_search_execute($keys = NULL, $conditions = NULL) { // Build matching conditions $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault'); $query->join('node', 'n', 'n.nid = i.sid'); @@ -1705,7 +1720,7 @@ function theme_node_search_admin($variables) { foreach (element_children($form['factors']) as $key) { $row = array(); $row[] = $form['factors'][$key]['#title']; - unset($form['factors'][$key]['#title']); + $form['factors'][$key]['#title_display'] = 'invisible'; $row[] = drupal_render($form['factors'][$key]); $rows[] = $row; } @@ -1876,6 +1891,10 @@ function node_menu() { ); } $items['node/%node'] = array( + 'title callback' => 'node_page_title', + 'title arguments' => array(1), + // The page callback also invokes drupal_set_title() in case + // the menu router's title is overridden by a menu link. 'page callback' => 'node_page_view', 'page arguments' => array(1), 'access callback' => 'node_access', @@ -1994,13 +2013,6 @@ function _node_custom_theme() { } } -/** - * Implements hook_init(). - */ -function node_init() { - drupal_add_css(drupal_get_path('module', 'node') . '/node.css'); -} - function node_last_changed($nid) { return db_query('SELECT changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetch()->changed; } @@ -2040,7 +2052,7 @@ function node_block_view($delta = '') { switch ($delta) { case 'syndicate': $block['subject'] = t('Syndicate'); - $block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate'))); + $block['content'] = theme('feed_icon', array('url' => 'rss.xml', 'title' => t('Syndicate'))); break; case 'recent': @@ -2159,7 +2171,7 @@ function theme_node_recent_block($variables) { if ($rows) { $output = theme('table', array('rows' => $rows)); if (user_access('access content overview')) { - $output .= theme('more_link', array('url' => url('admin/content'), 'title' => t('Show more content'))); + $output .= theme('more_link', array('url' => 'admin/content', 'title' => t('Show more content'))); } } @@ -2354,7 +2366,7 @@ function node_block_list_alter(&$blocks) { * The link should be an absolute URL. */ function node_feed($nids = FALSE, $channel = array()) { - global $base_url, $language; + global $base_url, $language_content; if ($nids === FALSE) { $nids = db_select('node', 'n') @@ -2409,7 +2421,7 @@ function node_feed($nids = FALSE, $channel = array()) { 'title' => variable_get('site_name', 'Drupal'), 'link' => $base_url, 'description' => variable_get('feed_description', ''), - 'language' => $language->language + 'language' => $language_content->language ); $channel_extras = array_diff_key($channel, $channel_defaults); $channel = array_merge($channel_defaults, $channel); @@ -2453,7 +2465,7 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0) { */ function node_page_default() { $select = db_select('node', 'n') - ->fields('n', array('nid')) + ->fields('n', array('nid', 'sticky', 'created')) ->condition('promote', 1) ->condition('status', 1) ->orderBy('sticky', 'DESC') @@ -2502,6 +2514,9 @@ function node_page_default() { * Menu callback; view a single node. */ function node_page_view($node) { + // If there is a menu link to this node, the link becomes the last part + // of the active trail, and the link name becomes the page title. + // Thus, we must explicitly set the page title to be the node title. drupal_set_title($node->title); $uri = entity_uri('node', $node); // Set the node path as the canonical URL to prevent duplicate content. @@ -2558,7 +2573,7 @@ function _node_index_node($node) { * Implements hook_form_FORM_ID_alter(). */ function node_form_search_form_alter(&$form, $form_state) { - if ($form['module']['#value'] == 'node' && user_access('use advanced search')) { + if (isset($form['module']) && $form['module']['#value'] == 'node' && user_access('use advanced search')) { // Keyword boxes: $form['advanced'] = array( '#type' => 'fieldset', @@ -2756,7 +2771,6 @@ function node_access($op, $node, $account = NULL) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } - if (!user_access('access content', $account)) { $rights[$account->uid][$cid][$op] = FALSE; return FALSE; @@ -2998,6 +3012,33 @@ function node_access_view_all_nodes() { * 'update' and 'delete'). */ function node_query_node_access_alter(QueryAlterableInterface $query) { + _node_query_node_access_alter($query, 'node', 'node'); +} + +/** + * Implements hook_query_TAG_alter(). + * + * This function implements the same functionality as + * node_query_node_access_alter() for the SQL field storage engine. Node access + * conditions are added for field values belonging to nodes only. + */ +function node_query_entity_field_access_alter(QueryAlterableInterface $query) { + _node_query_node_access_alter($query, $query->getMetaData('base_table'), 'entity'); +} + +/** + * Helper for node access functions. + * + * @param $query + * The query to add conditions to. + * @param $base_table + * The table holding node ids. + * @param $type + * Either 'node' or 'entity' depending on what sort of query it is. See + * node_query_node_access_alter() and node_query_entity_field_access_alter() + * for more. + */ +function _node_query_node_access_alter($query, $base_table, $type) { global $user; // Read meta-data from query, if provided. @@ -3026,31 +3067,82 @@ function node_query_node_access_alter(QueryAlterableInterface $query) { $tables = $query->getTables(); $grants = node_access_grants($op, $account); + if ($type == 'entity') { + // The original query looked something like: + // @code + // SELECT nid FROM sometable s + // INNER JOIN node_access na ON na.nid = s.nid + // WHERE ($node_access_conditions) + // @endcode + // + // Our query will look like: + // @code + // SELECT entity_type, entity_id + // FROM field_data_something s + // LEFT JOIN node_access na ON s.entity_id = na.nid + // WHERE (entity_type = 'node' AND $node_access_conditions) OR (entity_type <> 'node') + // @endcode + // + // So instead of directly adding to the query object, we need to collect + // in a separate db_and() object and then at the end add it to the query. + $entity_conditions = db_and(); + } foreach ($tables as $nalias => $tableinfo) { $table = $tableinfo['table']; - if (!($table instanceof SelectQueryInterface) && $table == 'node') { + if (!($table instanceof SelectQueryInterface) && $table == $base_table) { + + // The node_access table has the access grants for any given node so JOIN + // it to the table containing the nid which can be either the node + // table or a field value table. + if ($type == 'node') { + $access_alias = $query->join('node_access', 'na', '%alias.nid = ' . $nalias . '.nid'); + } + else { + $access_alias = $query->leftJoin('node_access', 'na', '%alias.nid = ' . $nalias . '.entity_id'); + $base_alias = $nalias; + } - // The node_access table has the access grants for any given node. - $access_alias = $query->join('node_access', 'na', '%alias.nid = ' . $nalias . '.nid'); - $or = db_or(); + $grant_conditions = db_or(); // If any grant exists for the specified user, then user has access // to the node for the specified operation. foreach ($grants as $realm => $gids) { foreach ($gids as $gid) { - $or->condition(db_and() + $grant_conditions->condition(db_and() ->condition($access_alias . '.gid', $gid) ->condition($access_alias . '.realm', $realm) ); } } - if (count($or->conditions())) { - $query->condition($or); + $count = count($grant_conditions->conditions()); + if ($type == 'node') { + if ($count) { + $query->condition($grant_conditions); + } + $query->condition($access_alias . '.grant_' . $op, 1, '>='); + } + else { + if ($count) { + $entity_conditions->condition($grant_conditions); + } + $entity_conditions->condition($access_alias . '.grant_' . $op, 1, '>='); } - - $query->condition($access_alias . '.grant_' . $op, 1, '>='); } } + + if ($type == 'entity' && count($entity_conditions->conditions())) { + // All the node access conditions are only for field values belonging to + // nodes. + $etid = variable_get('field_sql_storage_node_etid'); + $entity_conditions->condition("$base_alias.etid", $etid); + $or = db_or(); + $or->condition($entity_conditions); + // If the field value belongs to a non-node entity type then this function + // does not do anything with it. + $or->condition("$base_alias.etid", $etid, '<>'); + // Add the compiled set of rules to the query. + $query->condition($or); + } } /** @@ -3675,3 +3767,12 @@ class NodeController extends DrupalDefaultEntityController { return $query; } } + +/** + * Implements hook_file_download_access(). + */ +function node_file_download_access($field, $entity_type, $entity) { + if ($entity_type == 'node') { + return node_access('view', $entity); + } +} diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index d7244a695c9c2e75808a004db12bb5471db3608a..7d98fdf574b00e1e9fc61ce3b8a34282d8760f48 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: node.pages.inc,v 1.127 2010/06/17 13:44:45 dries Exp $ +// $Id: node.pages.inc,v 1.132 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -80,10 +80,7 @@ function node_form_validate($form, &$form_state) { // not update it with form values that have not yet been validated, so we // create a pseudo-entity to use during validation. $node = (object) $form_state['values']; - node_validate($node, $form); - - // Field validation. Requires access to $form_state, so this cannot be - // done in node_validate() as it currently exists. + node_validate($node, $form, $form_state); entity_form_field_validate('node', $form, $form_state); } @@ -117,7 +114,9 @@ function node_form($form, &$form_state, $node) { } // Identify this as a node edit form. + // @todo D8: Remove. Modules can implement hook_form_BASE_FORM_ID_alter() now. $form['#node_edit_form'] = TRUE; + $form['#attributes']['class'][] = 'node-form'; if (!empty($node->type)) { $form['#attributes']['class'][] = 'node-' . $node->type . '-form'; @@ -137,16 +136,18 @@ function node_form($form, &$form_state, $node) { '#type' => 'hidden', '#default_value' => isset($node->changed) ? $node->changed : NULL, ); - // Get the node-specific bits. - if ($extra = node_invoke($node, 'form', $form_state)) { + // Invoke hook_form() to get the node-specific bits. Can't use node_invoke(), + // because hook_form() needs to be able to receive $form_state by reference. + // @todo hook_form() implementations are unable to add #validate or #submit + // handlers to the form buttons below. Remove hook_form() entirely. + $function = node_type_get_base($node) . '_form'; + if (function_exists($function) && ($extra = $function($node, $form_state))) { $form = array_merge_recursive($form, $extra); } if (!isset($form['title']['#weight'])) { $form['title']['#weight'] = -5; } - // @todo Legacy support. Modules adding form building and processing functions - // to the node form are encouraged to access the node using - // $form_state['node']. Remove in Drupal 8. + // @todo D8: Remove. Modules should access the node using $form_state['node']. $form['#node'] = $node; $form['additional_settings'] = array( @@ -225,7 +226,7 @@ function node_form($form, &$form_state, $node) { '#type' => 'textfield', '#title' => t('Authored on'), '#maxlength' => 25, - '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the timezone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? $node->date : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? $node->date : format_date($node->created, 'custom', 'O'))), + '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O'))), '#default_value' => !empty($node->date) ? $node->date : '', ); @@ -282,7 +283,17 @@ function node_form($form, &$form_state, $node) { '#submit' => array('node_form_delete_submit'), ); } + // This form uses a button-level #submit handler for the form's main submit + // action. node_form_submit() manually invokes all form-level #submit handlers + // of the form. Without explicitly setting #submit, Form API would auto-detect + // node_form_submit() as submit handler, but that is the button-level #submit + // handler for the 'Save' action. To maintain backwards compatibility, a + // #submit handler is auto-suggested for custom node type modules. $form['#validate'][] = 'node_form_validate'; + if (!isset($form['#submit']) && function_exists($node->type . '_node_form_submit')) { + $form['#submit'][] = $node->type . '_node_form_submit'; + } + $form += array('#submit' => array()); $form['#builder_function'] = 'node_form_submit_build_node'; field_attach_form('node', $node, $form, $form_state, $node->language); @@ -339,11 +350,10 @@ function node_preview($node) { field_attach_prepare_view('node', $nodes, 'full'); // Display a preview of the node. - // Previewing alters $node so it needs to be cloned. if (!form_get_errors()) { - $cloned_node = clone $node; - $cloned_node->in_preview = TRUE; - $output = theme('node_preview', array('node' => $cloned_node)); + $node->in_preview = TRUE; + $output = theme('node_preview', array('node' => $node)); + unset($node->in_preview); } drupal_set_title(t('Preview'), PASS_THROUGH); @@ -509,7 +519,7 @@ function node_revision_overview($node) { $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision)))) . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''), 'class' => array('revision-current')); - $operations[] = array('data' => drupal_placeholder(array('text' => t('current revision'))), 'class' => array('revision-current'), 'colspan' => 2); + $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); } else { $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid/revisions/$revision->vid/view"), '!username' => theme('username', array('account' => $revision)))) diff --git a/modules/node/node.test b/modules/node/node.test index 1e5d539f0317a96fab6d735db04edcd3c56492a6..d6179b5bf486016df857c97371cbd0c243bf12c9 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -1,5 +1,5 @@ <?php -// $Id: node.test,v 1.89 2010/07/08 03:41:27 webchick Exp $ +// $Id: node.test,v 1.94 2010/08/30 00:22:03 webchick Exp $ /** * Test the node_load_multiple() function. @@ -565,11 +565,11 @@ class SummaryLengthTestCase extends DrupalWebTestCase { $expected = 'What is a Drupalism?'; $this->assertRaw($expected, t('Check that the summary is 600 characters in length'), 'Node'); - // Edit the teaser length for "Basic page" content type - $edit = array ( - 'teaser_length' => 200, - ); - $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type')); + // Change the teaser length for "Basic page" content type. + $instance = field_info_instance('node', 'body', $node->type); + $instance['display']['teaser']['settings']['trim_length'] = 200; + field_update_instance($instance); + // Attempt to access the front page again and check if the summary is now only 200 characters in length. $this->drupalGet("node"); $this->assertNoRaw($expected, t('Check that the summary is not longer than 200 characters'), 'Node'); @@ -634,7 +634,7 @@ class NodeBlockTestCase extends DrupalWebTestCase { // Set the block to a region to confirm block is available. $edit = array(); - $edit['node_syndicate[region]'] = 'footer'; + $edit['blocks[node_syndicate][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.')); } @@ -1072,7 +1072,7 @@ class NodeTypeTestCase extends DrupalWebTestCase { $this->drupalGet('node/add/' . str_replace('_', '-', $type->name)); $this->assertResponse(200, 'The new content type can be accessed at node/add.'); - + // Create a content type via the user interface. $web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types')); $this->drupalLogin($web_user); @@ -1398,7 +1398,7 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase { // Set the block to a region to confirm block is available. $edit = array( - 'node_recent[region]' => 'sidebar_first', + 'blocks[node_recent][region]' => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block saved to first sidebar region.')); @@ -1596,7 +1596,8 @@ class NodeQueryAlter extends DrupalWebTestCase { $this->drupalCreateNode(); $this->drupalCreateNode(); - // Create user with simple node access permission. + // Create user with simple node access permission. The 'node test view' + // permission is implemented and granted by the node_access_test module. $this->accessUser = $this->drupalCreateUser(array('access content', 'node test view')); $this->noAccessUser = $this->drupalCreateUser(array('access content')); } @@ -1606,14 +1607,12 @@ class NodeQueryAlter extends DrupalWebTestCase { */ function testNodeQueryAlterWithUI() { // Verify that a user with access permission can see at least one node. - $this->drupalLogin($this->accessUser); $this->drupalGet('node_access_test_page'); $this->assertText('Yes, 4 nodes', "4 nodes were found for access user"); $this->assertNoText('Exception', "No database exception"); // Verify that a user with no access permission cannot see nodes. - $this->drupalLogin($this->noAccessUser); $this->drupalGet('node_access_test_page'); $this->assertText('No nodes', "No nodes were found for no access user"); @@ -1692,6 +1691,71 @@ class NodeQueryAlter extends DrupalWebTestCase { } } + +/** + * Tests node_query_entity_field_access_alter(). + */ +class NodeEntityFieldQueryAlter extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Node entity query alter', + 'description' => 'Test that node access entity queries are properly altered by the node module.', + 'group' => 'Node', + ); + } + + /** + * User with permission to view content. + */ + protected $accessUser; + + /** + * User without permission to view content. + */ + protected $noAccessUser; + + function setUp() { + parent::setUp('node_access_test'); + node_access_rebuild(); + + // Creating 4 nodes with an entity field so we can test that sort of query + // alter. All field values starts with 'A' so we can identify and fetch them + // in the node_access_test module. + $settings = array('language' => LANGUAGE_NONE); + for ($i = 0; $i < 4; $i++) { + $body = array( + 'value' => 'A' . $this->randomName(32), + 'format' => filter_default_format(), + ); + $settings['body'][LANGUAGE_NONE][0] = $body; + $this->drupalCreateNode($settings); + } + + // Create user with simple node access permission. The 'node test view' + // permission is implemented and granted by the node_access_test module. + $this->accessUser = $this->drupalCreateUser(array('access content', 'node test view')); + $this->noAccessUser = $this->drupalCreateUser(array('access content')); + } + + /** + * Tests that node access permissions are followed. + */ + function testNodeQueryAlterWithUI() { + // Verify that a user with access permission can see at least one node. + $this->drupalLogin($this->accessUser); + $this->drupalGet('node_access_entity_test_page'); + $this->assertText('Yes, 4 nodes', "4 nodes were found for access user"); + $this->assertNoText('Exception', "No database exception"); + + // Verify that a user with no access permission cannot see nodes. + $this->drupalLogin($this->noAccessUser); + $this->drupalGet('node_access_entity_test_page'); + $this->assertText('No nodes', "No nodes were found for no access user"); + $this->assertNoText('Exception', "No database exception"); + } +} + /** * Test node token replacement in strings. */ diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info index a7ae296f68289af66c83f671b6fbe1665a04a0d0..8e5fee3d44a2003826af9e42c04d802452b47a6a 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/node/tests/node_access_test.module b/modules/node/tests/node_access_test.module index f595adb89192fdee32dcdff6a689acd748a99457..d134694df9a6347771d2f79b4be5530335868bec 100644 --- a/modules/node/tests/node_access_test.module +++ b/modules/node/tests/node_access_test.module @@ -1,5 +1,5 @@ <?php -// $Id: node_access_test.module,v 1.1 2010/02/15 19:00:30 webchick Exp $ +// $Id: node_access_test.module,v 1.2 2010/08/22 16:11:12 dries Exp $ /** * @file @@ -58,6 +58,12 @@ function node_access_test_menu() { 'access arguments' => array('access content'), 'type' => MENU_SUGGESTED_ITEM, ); + $items['node_access_entity_test_page'] = array( + 'title' => 'Node access test', + 'page callback' => 'node_access_entity_test_page', + 'access arguments' => array('access content'), + 'type' => MENU_SUGGESTED_ITEM, + ); return $items; } @@ -100,3 +106,37 @@ function node_access_test_page() { return $output; } + +/** + * Page callback for node access entity test page. + * + * Page should say "No nodes" if there are no nodes, and "Yes, # nodes" (with + * the number filled in) if there were nodes the user could access. Also, the + * database query is shown, and a list of the node IDs, for debugging purposes. + * And if there is a query exception, the page says "Exception" and gives the + * error. + */ +function node_access_entity_test_page() { + $output = ''; + try { + $query = new EntityFieldQuery; + $result = $query->fieldCondition('body', 'value', 'A', 'STARTS_WITH')->execute(); + if (!empty($result['node'])) { + $output .= '<p>Yes, ' . count($result['node']) . ' nodes</p>'; + $output .= '<ul>'; + foreach ($result['node'] as $nid => $v) { + $output .= '<li>' . $nid . '</li>'; + } + $output .= '</ul>'; + } + else { + $output .= '<p>No nodes</p>'; + } + } + catch (Exception $e) { + $output = '<p>Exception</p>'; + $output .= '<p>' . $e->getMessage() . '</p>'; + } + + return $output; +} diff --git a/modules/node/tests/node_presave_test.info b/modules/node/tests/node_presave_test.info index 41ac220469ac40c0f967e00580fa334926e34ecd..48ab18563efb6c09b7fe7b686eefbbaf9f39b7bd 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info index 5dcc63212252cc5c3db0215fbe38142a47aea84c..2cc4105b1b26a210e896f384c7811c275b5efe3a 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info index 70c5455ca53e2f5659d9c872f94310565b6b911a..b7bf6816a01932e4fa4154175472e73a33359b60 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc index a277902718694b52ea178d536fa5cce993be0b4a..4c0d33a77502a1e90682d611d750f52a3c8c1df4 100644 --- a/modules/openid/openid.inc +++ b/modules/openid/openid.inc @@ -1,5 +1,5 @@ <?php -// $Id: openid.inc,v 1.35 2010/07/01 19:06:56 dries Exp $ +// $Id: openid.inc,v 1.36 2010/08/22 22:00:16 dries Exp $ /** * @file @@ -359,8 +359,8 @@ function _openid_parse_message($message) { * Return a nonce value - formatted per OpenID spec. */ function _openid_nonce() { - // YYYY-MM-DDThh:mm:ssTZD UTC, plus some optional extra unique chars - return gmstrftime('%Y-%m-%dT%H:%M:%S%Z') . + // YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters. + return gmdate('Y-m-d\TH:i:s\Z') . chr(mt_rand(0, 25) + 65) . chr(mt_rand(0, 25) + 65) . chr(mt_rand(0, 25) + 65) . diff --git a/modules/openid/openid.info b/modules/openid/openid.info index 2bfc7f68931eaf13bb28f00a5979559e35077979..f616ce7ba34c92bd64006f8b8233f31ea6a0544e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/openid/openid.install b/modules/openid/openid.install index 70c423866010e6d5d2568b0e1240827089a6b7c8..105725bd2a1baa3916f9ab36fff316c984c38d68 100644 --- a/modules/openid/openid.install +++ b/modules/openid/openid.install @@ -1,5 +1,5 @@ <?php -// $Id: openid.install,v 1.11 2010/02/11 15:48:38 webchick Exp $ +// $Id: openid.install,v 1.12 2010/08/22 22:00:16 dries Exp $ /** * @file @@ -55,6 +55,32 @@ function openid_schema() { 'primary key' => array('assoc_handle'), ); + $schema['openid_nonce'] = array( + 'description' => 'Stores received openid.response_nonce per OpenID endpoint URL to prevent replay attacks.', + 'fields' => array( + 'idp_endpoint_uri' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'URI of the OpenID Provider endpoint.', + ), + 'nonce' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'The value of openid.response_nonce.', + ), + 'expires' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A Unix timestamp indicating when the entry should expire.', + ), + ), + 'indexes' => array( + 'nonce' => array('nonce'), + 'expires' => array('expires'), + ), + ); + return $schema; } @@ -84,3 +110,46 @@ function openid_requirements($phase) { return $requirements; } + +/** + * @defgroup updates-6.x-extra Extra openid updates for 6.x + * @{ + */ + +/** + * Add a table to store nonces. + */ +function openid_update_6000() { + $schema['openid_nonce'] = array( + 'description' => 'Stores received openid.response_nonce per OpenID endpoint URL to prevent replay attacks.', + 'fields' => array( + 'idp_endpoint_uri' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'URI of the OpenID Provider endpoint.', + ), + 'nonce' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'The value of openid.response_nonce' + ), + 'expires' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A Unix timestamp indicating when the entry should expire.', + ), + ), + 'indexes' => array( + 'nonce' => array('nonce'), + 'expires' => array('expires'), + ), + ); + + db_create_table('openid_nonce', $schema['openid_nonce']); +} + +/** + * @} End of "defgroup updates-6.x-extra" + * The next series of updates should start at 7000. + */ diff --git a/modules/openid/openid.module b/modules/openid/openid.module index 8932847b493c43797e67cef50b34ea69b9a0cdb1..c5fb70c8c071bc0fc72b2877c241f1c9ff13c50a 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -1,5 +1,5 @@ <?php -// $Id: openid.module,v 1.93 2010/07/07 08:05:01 webchick Exp $ +// $Id: openid.module,v 1.95 2010/08/22 22:00:16 dries Exp $ /** * @file @@ -48,6 +48,17 @@ function openid_menu_site_status_alter(&$menu_site_status, $path) { } } +/** + * Implements hook_admin_paths(). + */ +function openid_admin_paths() { + $paths = array( + 'user/*/openid' => TRUE, + 'user/*/openid/delete' => TRUE, + ); + return $paths; +} + /** * Implements hook_help(). */ @@ -321,7 +332,7 @@ function openid_complete($response = array()) { $response['status'] = 'cancel'; } else { - if (openid_verify_assertion($service['uri'], $response)) { + if (openid_verify_assertion($service, $response)) { // OpenID Authentication, section 7.3.2.3 and Appendix A.5: // The CanonicalID specified in the XRDS document must be used as the // account key. We rely on the XRI proxy resolver to verify that the @@ -715,15 +726,31 @@ function openid_authentication_request($claimed_id, $identity, $return_to = '', /** * Attempt to verify the response received from the OpenID Provider. * - * @param $op_endpoint The OpenID Provider URL. - * @param $response Array of response values from the provider. + * @param $service + * Array describing the OpenID provider. + * @param $response + * Array of response values from the provider. * * @return boolean * @see http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 */ -function openid_verify_assertion($op_endpoint, $response) { +function openid_verify_assertion($service, $response) { module_load_include('inc', 'openid'); + // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.3 + // Check the Nonce to protect against replay attacks. + if (!openid_verify_assertion_nonce($service, $response)) { + return FALSE; + } + + // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.1 + // Verifying the return URL. + if (!openid_verify_assertion_return_url($service, $response)) { + return FALSE; + } + + // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 + // Verify the signatures. $valid = FALSE; $association = FALSE; @@ -735,17 +762,13 @@ function openid_verify_assertion($op_endpoint, $response) { } if ($association && isset($association->session_type)) { - $keys_to_sign = explode(',', $response['openid.signed']); - $self_sig = _openid_signature($association, $response, $keys_to_sign); - if ($self_sig == $response['openid.sig']) { - $valid = TRUE; - } - else { - $valid = FALSE; - } + // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2 + // Verification using an association. + $valid = openid_verify_assertion_signature($service, $association, $response); } else { - // See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2.1 + // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2 + // Direct verification. // The verification requests contain all the fields from the response, // except openid.mode. $request = $response; @@ -756,7 +779,7 @@ function openid_verify_assertion($op_endpoint, $response) { 'method' => 'POST', 'data' => _openid_encode_message($message), ); - $result = drupal_http_request($op_endpoint, $options); + $result = drupal_http_request($service['uri'], $options); if (!isset($result->error)) { $response = _openid_parse_message($result->data); @@ -778,3 +801,152 @@ function openid_verify_assertion($op_endpoint, $response) { } return $valid; } + + +/** + * Verify the signature of the response received from the OpenID provider. + * + * @param $service + * Array describing the OpenID provider. + * @param $association + * Information on the association with the OpenID provider. + * @param $response + * Array of response values from the provider. + * + * @return + * TRUE if the signature is valid and covers all fields required to be signed. + * @see http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 + */ +function openid_verify_assertion_signature($service, $association, $response) { + if ($service['version'] == 2) { + // OpenID Authentication 2.0, section 10.1: + // These keys must always be signed. + $mandatory_keys = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle'); + if (isset($response['openid.claimed_id'])) { + // If present, these two keys must also be signed. According to the spec, + // they are either both present or both absent. + $mandatory_keys[] = 'claimed_id'; + $mandatory_keys[] = 'identity'; + } + } + else { + // OpenID Authentication 1.1. section 4.3.3. + $mandatory_keys = array('identity', 'return_to'); + } + + $keys_to_sign = explode(',', $response['openid.signed']); + + if (count(array_diff($mandatory_keys, $keys_to_sign)) > 0) { + return FALSE; + } + + return _openid_signature($association, $response, $keys_to_sign) === $response['openid.sig']; +} + +/** + * Verify that the nonce has not been used in earlier assertions from the same OpenID provider. + * + * @param $service + * Array describing the OpenID provider. + * @param $response + * Array of response values from the provider. + * + * @return + * TRUE if the nonce has not expired and has not been used earlier. + */ +function openid_verify_assertion_nonce($service, $response) { + if ($service['version'] != 2) { + return TRUE; + } + + if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/', $response['openid.response_nonce'], $matches)) { + list(, $year, $month, $day, $hour, $minutes, $seconds) = $matches; + $nonce_timestamp = gmmktime($hour, $minutes, $seconds, $month, $day, $year); + } + else { + watchdog('openid', 'Nonce from @endpoint rejected because it is not correctly formatted, nonce: @nonce.', array('@endpoint' => $service['uri'], '@nonce' => $response['openid.response_nonce']), WATCHDOG_WARNING); + return FALSE; + } + + // A nonce with a timestamp to far in the past or future will already have + // been removed and cannot be checked for single use anymore. + $time = time(); + $expiry = 900; + if ($nonce_timestamp <= $time - $expiry || $nonce_timestamp >= $time + $expiry) { + watchdog('openid', 'Nonce received from @endpoint is out of range (time difference: @intervals). Check possible clock skew.', array('@endpoint' => $service['uri'], '@interval' => $time - $nonce_timestamp), WATCHDOG_WARNING); + return FALSE; + } + + // Record that this nonce was used. + db_insert('openid_nonce') + ->fields(array( + 'idp_endpoint_uri' => $service['uri'], + 'nonce' => $response['openid.response_nonce'], + 'expires' => $nonce_timestamp + $expiry, + )) + ->execute(); + + // Count the number of times this nonce was used. + $count_used = db_query("SELECT COUNT(*) FROM {openid_nonce} WHERE nonce = :nonce AND idp_endpoint_uri = :idp_endpoint_uri", array( + ':nonce' => $response['openid.response_nonce'], + ':idp_endpoint_uri' => $service['uri'], + ))->fetchField(); + + if ($count_used == 1) { + return TRUE; + } + else { + watchdog('openid', 'Nonce replay attempt blocked from @ip, nonce: @nonce.', array('@ip' => ip_address(), '@nonce' => $response['openid.response_nonce']), WATCHDOG_CRITICAL); + return FALSE; + } +} + + +/** + * Verify that openid.return_to matches the current URL. + * + * See OpenID Authentication 2.0, section 11.1. While OpenID Authentication + * 1.1, section 4.3 does not mandate return_to verification, the received + * return_to should still match these constraints. + * + * @param $service + * Array describing the OpenID provider. + * @param $response + * Array of response values from the provider. + * + * @return + * TRUE if return_to is valid, FALSE otherwise. + */ +function openid_verify_assertion_return_url($service, $response) { + global $base_url; + + $return_to_parts = parse_url($response['openid.return_to']); + + $base_url_parts = parse_url($base_url); + $current_parts = parse_url($base_url_parts['scheme'] .'://'. $base_url_parts['host'] . request_uri()); + + if ($return_to_parts['scheme'] != $current_parts['scheme'] || $return_to_parts['host'] != $current_parts['host'] || $return_to_parts['path'] != $current_parts['path']) { + return FALSE; + } + // Verify that all query parameters in the openid.return_to URL have + // the same value in the current URL. In addition, the current URL + // contains a number of other parameters added by the OpenID Provider. + parse_str(isset($return_to_parts['query']) ? $return_to_parts['query'] : '', $return_to_query_parameters); + foreach ($return_to_query_parameters as $name => $value) { + if (!array_key_exists($name, $_GET) || $_GET[$name] != $value) { + return FALSE; + } + } + return TRUE; +} + +/** + * Remove expired nonces from the database. + * + * Implementation of hook_cron(). + */ +function openid_cron() { + db_delete('openid_nonce') + ->condition('expires', REQUEST_TIME, '<') + ->execute(); +} diff --git a/modules/openid/openid.test b/modules/openid/openid.test index 5adc06e6673a38fe73e06138df67e64e67742853..c695ce62c4fcffeb18665df5c2d195eeb60da224 100644 --- a/modules/openid/openid.test +++ b/modules/openid/openid.test @@ -1,5 +1,5 @@ <?php -// $Id: openid.test,v 1.28 2010/07/07 08:05:01 webchick Exp $ +// $Id: openid.test,v 1.31 2010/08/22 22:00:16 dries Exp $ /** * Base class for OpenID tests. @@ -264,6 +264,25 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { } $this->assertRaw(t('Successfully added %identity', array('%identity' => $claimed_id)), t('Identity %identity was added.', array('%identity' => $identity))); } + + /** + * Tests that openid.signed is verified. + */ + function testSignatureValidation() { + // Use a User-supplied Identity that is the URL of an XRDS document. + $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE)); + + // Do not sign all mandatory fields (e.g. assoc_handle). + variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce')); + $this->submitLoginForm($identity); + $this->assertRaw('OpenID login failed.'); + + // Sign all mandatory fields and some custom fields. + variable_set('openid_test_response', array('openid.foo' => 'bar', 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,foo')); + $this->submitLoginForm($identity); + $this->assertNoRaw('OpenID login failed.'); + } + } /** diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info index 93737556cf5a85101efe1989617822c02e98c409..d5c84495ca9d66e4189025d0f3921a9bb8204262 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/overlay/overlay-child.css b/modules/overlay/overlay-child.css index ab838411aadd3121db52534799c07cb27143f93b..a50bb496f90219ec25f5db08a33c3d8262055fc8 100644 --- a/modules/overlay/overlay-child.css +++ b/modules/overlay/overlay-child.css @@ -1,4 +1,4 @@ -/* $Id: overlay-child.css,v 1.3 2010/06/13 06:03:47 dries Exp $ */ +/* $Id: overlay-child.css,v 1.6 2010/07/28 01:26:00 dries Exp $ */ html.js { background: transparent !important; @@ -39,7 +39,6 @@ html.js body { #overlay-title { color: #fff; float: left; - font-family: Verdana,sans-serif; font-size: 20px; margin: 0; padding: 0.3em 0; @@ -72,16 +71,13 @@ html.js body { #overlay-close:focus { padding: 0; } -#overlay-close span { - display: none; -} /** * Tabs on the overlay. */ #overlay-tabs { line-height: 27px; - margin: -27px 0 0 0; + margin: -28px 0 0 0; position: absolute; right: 20px; text-transform: uppercase; diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js index f0a7444a272fa173275f03c3279b5b13bb1bd12d..607b8b40bc6b1bdef3cb0c7cd1107cecdd5c4c2a 100644 --- a/modules/overlay/overlay-parent.js +++ b/modules/overlay/overlay-parent.js @@ -1,4 +1,4 @@ -// $Id: overlay-parent.js,v 1.49 2010/07/08 12:20:23 dries Exp $ +// $Id: overlay-parent.js,v 1.54 2010/08/18 02:25:50 dries Exp $ (function ($) { @@ -70,6 +70,8 @@ Drupal.overlay.open = function (url) { return this.load(url); } this.isOpening = true; + // Store the original document title. + this.originalTitle = document.title; // Create the dialog and related DOM elements. this.create(); @@ -184,6 +186,8 @@ Drupal.overlay.close = function () { this.isClosing = true; this.isOpen = false; $(document.documentElement).removeClass('overlay-open'); + // Restore the original document title. + document.title = this.originalTitle; // Allow other scripts to respond to this event. $(document).trigger('drupalOverlayClose'); @@ -273,6 +277,9 @@ Drupal.overlay.loadChild = function (event) { if (this.isOpen && !this.isClosing) { // And child document is an actual overlayChild. if (iframeWindow.Drupal && iframeWindow.Drupal.overlayChild) { + // Replace the document title with title of iframe. + document.title = iframeWindow.document.title; + this.activeFrame = $(iframe) .addClass('overlay-active') // Add a title attribute to the iframe for accessibility. @@ -474,7 +481,12 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) { } // Open admin links in the overlay. else if (this.isAdminLink(href)) { - href = this.fragmentizeLink($target.get(0)); + // If the link contains the overlay-restore class and the overlay-context + // state is set, also update the parent window's location. + var parentLocation = ($target.hasClass('overlay-restore') && typeof $.bbq.getState('overlay-context') == 'string') + ? Drupal.settings.basePath + $.bbq.getState('overlay-context') + : null; + href = this.fragmentizeLink($target.get(0), parentLocation); // Only override default behavior when left-clicking and user is not // pressing the ALT, CTRL, META (Command key on the Macintosh keyboard) // or SHIFT key. @@ -493,29 +505,34 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) { .attr('href', href); } } - // Open external links in a new window. - else if (target.hostname != window.location.hostname) { - // Add a target attribute to the clicked link. This is being picked up by - // the default action handler. - if (!$target.attr('target')) { - $target.attr('target', '_new'); - } - } - // Non-admin links should close the overlay and open in the main window. - // Only handle them if the overlay is open and the clicked link is inside - // the overlay iframe, else default action will do fine. + // Non-admin links should close the overlay and open in the main window, + // which is the default action for a link. We only need to handle them + // if the overlay is open and the clicked link is inside the overlay iframe. else if (this.isOpen && target.ownerDocument === this.iframeWindow.document) { - // When the link has a destination query parameter and that destination - // is an admin link we need to fragmentize it. This will make it reopen - // in the overlay. - var params = $.deparam.querystring(href); - if (params.destination && this.isAdminLink(params.destination)) { - var fragmentizedDestination = $.param.fragment(this.getPath(window.location), { overlay: params.destination }); - $target.attr('href', $.param.querystring(href, { destination: fragmentizedDestination })); + // Open external links in the immediate parent of the frame, unless the + // link already has a different target. + if (target.hostname != window.location.hostname) { + if (!$target.attr('target')) { + $target.attr('target', '_parent'); + } + } + else { + // Add the overlay-context state to the link, so "overlay-restore" links + // can restore the context. + $target.attr('href', $.param.fragment(href, { 'overlay-context': this.getPath(window.location) + window.location.search })); + + // When the link has a destination query parameter and that destination + // is an admin link we need to fragmentize it. This will make it reopen + // in the overlay. + var params = $.deparam.querystring(href); + if (params.destination && this.isAdminLink(params.destination)) { + var fragmentizedDestination = $.param.fragment(this.getPath(window.location), { overlay: params.destination }); + $target.attr('href', $.param.querystring(href, { destination: fragmentizedDestination })); + } + + // Make the link open in the immediate parent of the frame. + $target.attr('target', '_parent'); } - - // Make the link to be opening in the immediate parent of the frame. - $target.attr('target', '_parent'); } } }; @@ -657,12 +674,14 @@ Drupal.overlay.eventhandlerDispatchEvent = function (event) { * * @param link * A Javascript Link object (i.e. an <a> element). + * @param parentLocation + * (optional) URL to override the parent window's location with. * * @return * A URL that will trigger the overlay (in the form * /node/1#overlay=admin/config). */ -Drupal.overlay.fragmentizeLink = function (link) { +Drupal.overlay.fragmentizeLink = function (link, parentLocation) { // Don't operate on links that are already overlay-ready. var params = $.deparam.fragment(link.href); if (params.overlay) { @@ -678,7 +697,7 @@ Drupal.overlay.fragmentizeLink = function (link) { var destination = path + link.search.replace(/&?render=overlay/, '').replace(/\?$/, '') + link.hash; // Assemble and return the overlay-ready link. - return $.param.fragment(window.location.href, { overlay: destination }); + return $.param.fragment(parentLocation || window.location.href, { overlay: destination }); }; /** @@ -726,7 +745,9 @@ Drupal.overlay.resetActiveClass = function(activePath) { var linkDomain = this.protocol + this.hostname; var linkPath = self.getPath(this); - if (linkDomain == windowDomain && activePath.indexOf(linkPath) === 0) { + // A link matches if it is part of the active trail of activePath, except + // for frontpage links. + if (linkDomain == windowDomain && (activePath + '/').indexOf(linkPath + '/') === 0 && (linkPath !== '' || activePath === '')) { $(this).addClass('active'); } }); diff --git a/modules/overlay/overlay.api.php b/modules/overlay/overlay.api.php index 349bbf2ca0e2807337df62212b5d7cdf9a029f8f..c561751d8b23c8e63aabb403d07e4169f3dfa25e 100644 --- a/modules/overlay/overlay.api.php +++ b/modules/overlay/overlay.api.php @@ -1,5 +1,5 @@ <?php -// $Id: overlay.api.php,v 1.1 2009/12/02 07:28:22 webchick Exp $ +// $Id: overlay.api.php,v 1.2 2010/07/17 02:12:36 dries Exp $ /** * @file @@ -32,13 +32,8 @@ function hook_overlay_parent_initialize() { * within the confines of the overlay. */ function hook_overlay_child_initialize() { - // Use a different theme for content administration pages. - if (arg(0) == 'admin' && arg(1) == 'content') { - if ($theme = variable_get('content_administration_pages_theme', FALSE)) { - global $custom_theme; - $custom_theme = $theme; - } - } + // Add our custom JavaScript. + drupal_add_js(drupal_get_path('module', 'hook') . '/hook-overlay-child.js'); } /** diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info index 362404f740e98783726700a7f22db27ea43ffac5..4d3fb25bd3db3751e0be56cdddc160b0d1ee7d59 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/overlay/overlay.module b/modules/overlay/overlay.module index 11f36905c25d6dd79285283b53542fdc15f5b99e..6c71fee022967e7e18e7b2cee96b8f28e33e34a3 100644 --- a/modules/overlay/overlay.module +++ b/modules/overlay/overlay.module @@ -1,5 +1,5 @@ <?php -// $Id: overlay.module,v 1.23 2010/07/08 12:20:23 dries Exp $ +// $Id: overlay.module,v 1.31 2010/08/31 15:04:09 webchick Exp $ /** * @file @@ -57,6 +57,39 @@ function overlay_theme() { ); } +/** + * Implements hook_form_FORM_ID_alter(). + */ +function overlay_form_user_profile_form_alter(&$form, &$form_state) { + if ($form['#user_category'] == 'account') { + $account = $form['#user']; + if (user_access('access overlay', $account)) { + $form['overlay_control'] = array( + '#type' => 'fieldset', + '#title' => t('Administrative overlay'), + '#weight' => 4, + '#collapsible' => TRUE, + ); + + $form['overlay_control']['overlay'] = array( + '#type' => 'checkbox', + '#title' => t('Use the overlay for administrative pages.'), + '#description' => t('Show administrative pages on top of the page you started from.'), + '#default_value' => isset($account->data['overlay']) ? $account->data['overlay'] : 1, + ); + } + } +} + +/** + * Implements hook_user_presave(). + */ +function overlay_user_presave(&$edit, $account, $category) { + if (isset($edit['overlay'])) { + $edit['data']['overlay'] = $edit['overlay']; + } +} + /** * Implements hook_init(). * @@ -66,14 +99,14 @@ function overlay_theme() { * @see overlay_set_mode() */ function overlay_init() { - // @todo: custom_theme does not exist anymore. - global $custom_theme; + global $user; $mode = overlay_get_mode(); // Only act if the user has access to the overlay and a mode was not already // set. Other modules can also enable the overlay directly for other uses. - if (empty($mode) && user_access('access overlay')) { + $use_overlay = !isset($user->data['overlay']) || $user->data['overlay']; + if (empty($mode) && user_access('access overlay') && $use_overlay) { $current_path = current_path(); // After overlay is enabled on the modules page, redirect to // <front>#overlay=admin/modules to actually enable the overlay. @@ -87,13 +120,7 @@ function overlay_init() { if (!path_is_admin($current_path)) { overlay_close_dialog($current_path); } - // If system module did not switch the theme yet (i.e. this is not an - // admin page, per se), we should switch the theme here. - $admin_theme = variable_get('admin_theme', 0); - if ($custom_theme != $admin_theme) { - $custom_theme = $admin_theme; - drupal_add_css(drupal_get_path('module', 'system') . '/admin.css'); - } + // Indicate that we are viewing an overlay child page. overlay_set_mode('child'); @@ -101,7 +128,7 @@ function overlay_init() { unset($_GET['render']); } // Do not enable the overlay if we already are on an admin page. - else if (!path_is_admin($current_path)) { + elseif (!path_is_admin($current_path)) { // Otherwise add overlay parent code and our behavior. overlay_set_mode('parent'); } @@ -138,15 +165,6 @@ function overlay_exit() { } } -/** - * Implements hook_element_info_alter(). - */ -function overlay_element_info_alter(&$types) { - foreach (array('submit', 'button', 'image_button', 'form') as $type) { - $types[$type]['#after_build'][] = 'overlay_form_after_build'; - } -} - /** * Implements hook_library(). */ @@ -326,82 +344,13 @@ function template_process_overlay(&$variables) { /** * Implements hook_preprocess_page(). * - * Display breadcrumbs correctly inside the overlay. + * Hide tabs inside the overlay. * * @see overlay_get_mode() */ function overlay_preprocess_page(&$variables) { if (overlay_get_mode() == 'child') { - // Remove 'Home' from the breadcrumbs. - $overlay_breadcrumb = drupal_get_breadcrumb(); - array_shift($overlay_breadcrumb); - $variables['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => $overlay_breadcrumb)); - - $variables['tabs'] = ''; - $variables['primary_local_tasks'] = ''; - } -} - -/** - * Form after_build callback. - * - * After all hook_form_alter() implementations have been processed, we look at - * the list of submit handlers and add our own at the end. The added handler - * determines whether or not the user is redirected done at the end of form - * processing, so that it's possible to close the overlay after submitting - * a form. - * - * @see form_execute_handlers() - * @see form_builder() - * @see overlay_form_submit() - * - * @ingroup forms - */ -function overlay_form_after_build($form, &$form_state) { - if (overlay_get_mode() == 'child') { - // If this element has submit handlers, then append our own. - if (isset($form['#submit'])) { - $form['#submit'][] = 'overlay_form_submit'; - } - } - return $form; -} - -/** - * Generic form submit handler. - * - * When we are requested to close an overlay, we don't want Form API to - * perform any redirection once the submitted form has been processed. Instead, - * we set $form_state['redirect'] to FALSE so that Form API will simply - * re-render the current page, and pass the redirect information on to the - * overlay JavaScript so that the redirection can be performed there. - * - * @see overlay_get_mode() - * @ingroup forms - */ -function overlay_form_submit($form, &$form_state) { - if (isset($form_state['redirect'])) { - // A destination set in the URL trumps $form_state['redirect']. - if (isset($_GET['destination'])) { - $url = $_GET['destination']; - $url_settings = array(); - } - elseif (is_array($form_state['redirect'])) { - $url = $form_state['redirect'][0]; - $url_settings = $form_state['redirect'][1]; - } - else { - $url = $form_state['redirect']; - $url_settings = array(); - } - // Close the overlay if we are redirecting to a non-admin page or if the - // overlay module has just been disabled. - if (!path_is_admin($url) || !module_exists('overlay')) { - overlay_close_dialog($url, $url_settings); - // Tell FAPI to stay on the same page after all submit callbacks have - // been processed. - $form_state['redirect'] = FALSE; - } + unset($variables['tabs'][0]); } } @@ -451,8 +400,6 @@ function overlay_page_delivery_callback_alter(&$callback) { * * This function is used to print out a bare minimum empty page which still has * the scripts and styles necessary in order to trigger the overlay to close. - * - * @see overlay_form_submit() */ function overlay_deliver_empty_page() { $empty_page = '<html><head><title></title>' . drupal_get_css() . drupal_get_js() . '</head><body class="overlay"></body></html>'; @@ -515,7 +462,6 @@ function overlay_set_mode($mode = NULL) { switch ($overlay_mode) { case 'parent': drupal_add_library('overlay', 'parent'); - drupal_add_library('overlay', 'jquery-bbq'); // Allow modules to act upon overlay events. module_invoke_all('overlay_parent_initialize'); @@ -716,7 +662,9 @@ function overlay_set_regions_to_render($regions = NULL) { * supplemental overlay regions (e.g., a region containing a toolbar). Passing * in a region that is intended to display the main page content is not * supported; the region will be rendered by this function, but the main page - * content will not appear in it. + * content will not appear in it. In addition, although this function returns + * the rendered HTML for the provided region, it does not place it on the final + * page, nor add any of its associated JavaScript or CSS to the page. * * @param $region * The name of the page region that should be rendered. @@ -740,7 +688,17 @@ function overlay_render_region($region) { '#theme' => NULL, '#theme_wrappers' => array(), ); + // Render the region, but do not cache any JavaScript or CSS associated with + // it. This region might not be included the next time drupal_render_page() + // is called, and we do not want its JavaScript or CSS to erroneously appear + // on the final rendered page. + $original_js = drupal_add_js(); + $original_css = drupal_add_css(); + $js = &drupal_static('drupal_add_js'); + $css = &drupal_static('drupal_add_css'); $markup = drupal_render_page($page); + $js = $original_js; + $css = $original_css; // Indicate that the main page content has not, in fact, been displayed, so // that future calls to drupal_render_page() will be able to render it // correctly. diff --git a/modules/overlay/overlay.tpl.php b/modules/overlay/overlay.tpl.php index b48de312fa511a68bf7280ca60c554e930ac4af4..e8aba59da70c3302978f8417439c4fc0488457d4 100644 --- a/modules/overlay/overlay.tpl.php +++ b/modules/overlay/overlay.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: overlay.tpl.php,v 1.2 2010/06/11 14:07:32 dries Exp $ +// $Id: overlay.tpl.php,v 1.3 2010/07/11 22:03:45 dries Exp $ /** * @file @@ -27,7 +27,7 @@ <h1 id="overlay-title"<?php print $title_attributes; ?>><?php print $title; ?></h1> </div> <div id="overlay-close-wrapper"> - <a id="overlay-close" href="#" class="overlay-close"><span><?php t('Close overlay'); ?></span></a> + <a id="overlay-close" href="#" class="overlay-close"><span class="element-invisible"><?php print t('Close overlay'); ?></span></a> </div> <?php if ($tabs): ?><ul id="overlay-tabs"><?php print render($tabs); ?></ul><?php endif; ?> </div> diff --git a/modules/path/path.admin.inc b/modules/path/path.admin.inc index d6fd4c0b4607be33382d9634342e6fe3d6dfab8e..7e792934a282eeeff250a19f37a751d6232ea827 100644 --- a/modules/path/path.admin.inc +++ b/modules/path/path.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: path.admin.inc,v 1.44 2010/05/14 04:57:59 webchick Exp $ +// $Id: path.admin.inc,v 1.45 2010/09/11 03:04:43 dries Exp $ /** * @file @@ -248,7 +248,8 @@ function path_admin_filter_form($form, &$form_state, $keys = '') { ); $form['basic']['filter'] = array( '#type' => 'textfield', - '#title' => '', + '#title' => 'Path alias', + '#title_display' => 'invisible', '#default_value' => $keys, '#maxlength' => 128, '#size' => 25, diff --git a/modules/path/path.info b/modules/path/path.info index 0f0c3dfc0f5c25815bcf333a9f0aa9108e14a9a2..812450e9d7bfa91f97ce0c5c0802d0699d851e40 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/path/path.module b/modules/path/path.module index beda47d011cb1f92a6cae09a9df173847aea49d6..bc4f48026c08e66a79422145b2212265ecf2dba8 100644 --- a/modules/path/path.module +++ b/modules/path/path.module @@ -1,5 +1,5 @@ <?php -// $Id: path.module,v 1.183 2010/02/13 21:41:58 dries Exp $ +// $Id: path.module,v 1.184 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -94,55 +94,53 @@ function path_menu() { } /** - * Implements hook_form_alter(). + * Implements hook_form_BASE_FORM_ID_alter(). */ -function path_form_alter(&$form, $form_state, $form_id) { - if (!empty($form['#node_edit_form'])) { - $path = array(); - if (!empty($form['#node']->nid)) { - $conditions = array('source' => 'node/' . $form['#node']->nid); - if ($form['#node']->language != LANGUAGE_NONE) { - $conditions['language'] = $form['#node']->language; - } - $path = path_load($conditions); - if ($path === FALSE) { - $path = array(); - } +function path_form_node_form_alter(&$form, $form_state) { + $path = array(); + if (!empty($form['#node']->nid)) { + $conditions = array('source' => 'node/' . $form['#node']->nid); + if ($form['#node']->language != LANGUAGE_NONE) { + $conditions['language'] = $form['#node']->language; + } + $path = path_load($conditions); + if ($path === FALSE) { + $path = array(); } - $path += array( - 'pid' => NULL, - 'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL, - 'alias' => '', - 'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE, - ); - - $form['path'] = array( - '#type' => 'fieldset', - '#title' => t('URL path settings'), - '#collapsible' => TRUE, - '#collapsed' => empty($path['alias']), - '#group' => 'additional_settings', - '#attached' => array( - 'js' => array(drupal_get_path('module', 'path') . '/path.js'), - ), - '#access' => user_access('create url aliases') || user_access('administer url aliases'), - '#weight' => 30, - '#tree' => TRUE, - '#element_validate' => array('path_form_element_validate'), - ); - $form['path']['alias'] = array( - '#type' => 'textfield', - '#title' => t('URL alias'), - '#default_value' => $path['alias'], - '#maxlength' => 255, - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'), - ); - $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']); - $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']); - $form['path']['language'] = array('#type' => 'value', '#value' => $path['language']); } + $path += array( + 'pid' => NULL, + 'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL, + 'alias' => '', + 'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE, + ); + + $form['path'] = array( + '#type' => 'fieldset', + '#title' => t('URL path settings'), + '#collapsible' => TRUE, + '#collapsed' => empty($path['alias']), + '#group' => 'additional_settings', + '#attached' => array( + 'js' => array(drupal_get_path('module', 'path') . '/path.js'), + ), + '#access' => user_access('create url aliases') || user_access('administer url aliases'), + '#weight' => 30, + '#tree' => TRUE, + '#element_validate' => array('path_form_element_validate'), + ); + $form['path']['alias'] = array( + '#type' => 'textfield', + '#title' => t('URL alias'), + '#default_value' => $path['alias'], + '#maxlength' => 255, + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'), + ); + $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']); + $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']); + $form['path']['language'] = array('#type' => 'value', '#value' => $path['language']); } /** diff --git a/modules/path/path.test b/modules/path/path.test index 92525e8d814b60bc7c82f1c3838697013d8079ef..4b524d5248bbd83a05eea1078ae172474e34d90c 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -1,5 +1,5 @@ <?php -// $Id: path.test,v 1.35 2010/03/26 12:37:30 dries Exp $ +// $Id: path.test,v 1.40 2010/08/05 23:53:38 webchick Exp $ /** * @file @@ -242,8 +242,8 @@ class PathLanguageTestCase extends DrupalWebTestCase { parent::setUp('path', 'locale', 'translation'); // Create and login user. - $web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages')); - $this->drupalLogin($web_user); + $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages')); + $this->drupalLogin($this->web_user); // Enable French language. $edit = array(); @@ -251,13 +251,9 @@ class PathLanguageTestCase extends DrupalWebTestCase { $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - // Set language negotiation to "Path prefix with fallback". - include_once DRUPAL_ROOT . '/includes/locale.inc'; - variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); - variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); - - // Force inclusion of language.inc. - drupal_language_initialize(); + // Enable URL language detection and selection. + $edit = array('language[enabled][locale-url]' => 1); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); } /** @@ -268,26 +264,26 @@ class PathLanguageTestCase extends DrupalWebTestCase { variable_set('language_content_type_page', 2); $english_node = $this->drupalCreateNode(array('type' => 'page')); + $english_alias = $this->randomName(); // Edit the node to set language and path. $edit = array(); $edit['language'] = 'en'; - $edit['path[alias]'] = $this->randomName(); + $edit['path[alias]'] = $english_alias; $this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. - $this->drupalGet($edit['path[alias]']); + $this->drupalGet($english_alias); $this->assertText($english_node->title, 'Alias works.'); // Translate the node into French. $this->drupalGet('node/' . $english_node->nid . '/translate'); $this->clickLink(t('add translation')); $edit = array(); - $langcode = 'fr'; - $edit["body[$langcode][0][value]"] = $this->randomName(); - $langcode = LANGUAGE_NONE; $edit["title"] = $this->randomName(); - $edit['path[alias]'] = $this->randomName(); + $edit["body[fr][0][value]"] = $this->randomName(); + $french_alias = $this->randomName(); + $edit['path[alias]'] = $french_alias; $this->drupalPost(NULL, $edit, t('Save')); // Clear the path lookup cache. @@ -306,6 +302,66 @@ class PathLanguageTestCase extends DrupalWebTestCase { $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.')); + + // Confirm that the alias works even when changing language negotiation + // options. Enable User language detection and selection over URL one. + $edit = array( + 'language[enabled][locale-user]' => 1, + 'language[weight][locale-user]' => -9, + 'language[enabled][locale-url]' => 1, + 'language[weight][locale-url]' => -8, + ); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + + // Change user language preference. + $edit = array('language' => 'fr'); + $this->drupalPost("user/{$this->web_user->uid}/edit", $edit, t('Save')); + + // Check that the English alias works. In this situation French is the + // current UI and content language, while URL language is English (since we + // do not have a path prefix we fall back to the site's default language). + // We need to ensure that the user language preference is not taken into + // account while determining the path alias language, because if this + // happens we have no way to check that the path alias is valid: there is no + // path alias for French matching the english alias. So drupal_lookup_path() + // needs to use the URL language to check whether the alias is valid. + $this->drupalGet($english_alias); + $this->assertText($english_node->title, 'Alias for English translation works.'); + + // Check that the French alias works. + $this->drupalGet("fr/$french_alias"); + $this->assertText($french_node->title, 'Alias for French translation works.'); + + // Disable URL language negotiation. + $edit = array('language[enabled][locale-url]' => FALSE); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + + // Check that the English alias still works. + $this->drupalGet($english_alias); + $this->assertText($english_node->title, 'Alias for English translation works.'); + + // Check that the French alias is not available. We check the unprefixed + // alias because we disabled URL language negotiation above. In this + // situation only aliases in the default language and language neutral ones + // should keep working. + $this->drupalGet($french_alias); + $this->assertResponse(404, t('Alias for French translation is unavailable when URL language negotiation is disabled.')); + + // drupal_lookup_path() has an internal static cache. Check to see that + // it has the appropriate contents at this point. + drupal_lookup_path('wipe'); + $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language); + $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path works.')); + // Second call should return the same path. + $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language); + $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path is the same.')); + + // Confirm that the alias works. + $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language); + $this->assertEqual($french_node_alias, $french_alias, t('Alias works.')); + // Second call should return the same alias. + $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language); + $this->assertEqual($french_node_alias, $french_alias, t('Alias is the same.')); } } @@ -334,13 +390,9 @@ class PathLanguageUITestCase extends DrupalWebTestCase { $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - // Set language negotiation to "Path prefix with fallback". - include_once DRUPAL_ROOT . '/includes/locale.inc'; - variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); - variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); - - // Force inclusion of language.inc. - drupal_language_initialize(); + // Enable URL language detection and selection. + $edit = array('language[enabled][locale-url]' => 1); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); } /** @@ -388,3 +440,65 @@ class PathLanguageUITestCase extends DrupalWebTestCase { } } + +/** + * Tests that paths are not prefixed on a monolingual site. + */ +class PathMonolingualTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Paths on non-English monolingual sites', + 'description' => 'Confirm that paths are not changed on monolingual non-English sites', + 'group' => 'Path', + ); + } + + function setUp() { + global $language; + parent::setUp('path', 'locale', 'translation'); + + // Create and login user. + $web_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); + $this->drupalLogin($web_user); + + // Enable French language. + $edit = array(); + $edit['langcode'] = 'fr'; + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + + // Make French the default language. + $edit = array('site_default' => 'fr'); + $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); + + // Disable English. + $edit = array('enabled[en]' => FALSE); + $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); + + // Verify that French is the only language. + $this->assertFalse(drupal_multilingual(), t('Site is mono-lingual')); + $this->assertEqual(language_default('language'), 'fr', t('French is the default language')); + + // Set language detection to URL. + $edit = array('language[enabled][locale-url]' => TRUE); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + + // Force languages to be initialized. + drupal_language_initialize(); + } + + /** + * Verifies that links do not have language prefixes in them. + */ + function testPageLinks() { + // Navigate to 'admin/config' path. + $this->drupalGet('admin/config'); + + // Verify that links in this page do not have a 'fr/' prefix. + $this->assertNoLinkByHref('/fr/', 'Links do not contain language prefix'); + + // Verify that links in this page can be followed and work. + $this->clickLink(t('Languages')); + $this->assertResponse(200, 'Clicked link results in a valid page'); + $this->assertText(t('Add language'), 'Page contains the add language text'); + } +} diff --git a/modules/php/php.info b/modules/php/php.info index 95ba116e8b76a3fe202dd7f81769fbdd8dae5386..cff8a01b4e9b492168b38243e18846d8e76b5c76 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/php/php.install b/modules/php/php.install index 4ec0feb07653ba476b938d6c748ff49d2e979ed0..bc5d7d66d28f36ee4d0eae4bdfcbd79e4b7ad535 100644 --- a/modules/php/php.install +++ b/modules/php/php.install @@ -1,5 +1,5 @@ <?php -// $Id: php.install,v 1.17 2010/01/09 23:03:21 webchick Exp $ +// $Id: php.install,v 1.18 2010/09/13 01:11:08 dries Exp $ /** * @file @@ -33,7 +33,7 @@ function php_enable() { $php_format = (object) $php_format; filter_format_save($php_format); - drupal_set_message(t('A !php-code text format has been created.', array('!php-code' => l('PHP code', 'admin/config/content/formats/' . $php_format->format)))); + drupal_set_message(t('A <a href="@php-code">PHP code</a> text format has been created.', array('@php-code' => url('admin/config/content/formats/' . $php_format->format)))); } } diff --git a/modules/php/php.test b/modules/php/php.test index caa2224f42052049df1c1653042b9e3355d914d0..877c2e442236df64e0c3de3fc5426c66cfcd9c9e 100644 --- a/modules/php/php.test +++ b/modules/php/php.test @@ -1,5 +1,5 @@ <?php -// $Id: php.test,v 1.24 2010/03/07 23:14:20 webchick Exp $ +// $Id: php.test,v 1.26 2010/08/05 23:53:38 webchick Exp $ /** * Base PHP test case class. diff --git a/modules/poll/poll.info b/modules/poll/poll.info index 2d2057ad4b29bf791104e077c8fb177c4454a839..bc4b3bc8538b2e24018c1a6f1f5fefbcbcf33f95 100644 --- a/modules/poll/poll.info +++ b/modules/poll/poll.info @@ -1,4 +1,4 @@ -; $Id: poll.info,v 1.10 2009/08/19 20:19:36 dries Exp $ +; $Id: poll.info,v 1.11 2010/09/05 02:21:38 dries Exp $ name = Poll description = Allows your site to capture votes on different topics in the form of multiple choice questions. package = Core @@ -9,9 +9,10 @@ files[] = poll.pages.inc files[] = poll.install files[] = poll.test files[] = poll.tokens.inc +stylesheets[all][] = poll.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/poll/poll.install b/modules/poll/poll.install index f9dce989cd4779b7c34da1d56ceef8bdda085ac8..4d9a814c26e7ba6b23af75ec9466c4ef7779db40 100644 --- a/modules/poll/poll.install +++ b/modules/poll/poll.install @@ -1,5 +1,5 @@ <?php -// $Id: poll.install,v 1.32 2010/07/01 15:09:11 webchick Exp $ +// $Id: poll.install,v 1.33 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -36,7 +36,10 @@ function poll_schema() { ), 'primary key' => array('nid'), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), + 'poll_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), ), ); @@ -82,7 +85,10 @@ function poll_schema() { ), 'primary key' => array('chid'), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), + 'choice_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), ), ); @@ -124,8 +130,14 @@ function poll_schema() { ), 'primary key' => array('nid', 'uid', 'hostname'), 'foreign keys' => array( - 'nid' => array('node' => 'nid'), - 'uid' => array('users' => 'uid'), + 'poll_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'voter' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), 'indexes' => array( 'chid' => array('chid'), diff --git a/modules/poll/poll.module b/modules/poll/poll.module index c9bbd2c733cf50ac877a9e5e8fbabfc9700fa819..afadf50ecdfc01b0941ba426c0d65165e18a75a3 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -1,5 +1,5 @@ <?php -// $Id: poll.module,v 1.352 2010/06/24 18:53:31 dries Exp $ +// $Id: poll.module,v 1.356 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -27,13 +27,6 @@ function poll_help($path, $arg) { } } -/** - * Implements hook_init(). - */ -function poll_init() { - drupal_add_css(drupal_get_path('module', 'poll') . '/poll.css'); -} - /** * Implements hook_theme(). */ @@ -243,10 +236,12 @@ function poll_field_extra_fields() { function poll_form($node, &$form_state) { global $user; - $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid); + $admin = user_access('bypass node access') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid); $type = node_type_get_type($node); + // The submit handlers to add more poll choices require that this form is + // cached, regardless of whether AJAX is used. $form_state['cache'] = TRUE; $form['title'] = array( @@ -366,7 +361,7 @@ function poll_form($node, &$form_state) { * return just the changed part of the form. */ function poll_more_choices_submit($form, &$form_state) { - // Make the changes we want to the form state. + // If this is a AJAX POST, add 1, otherwise add 5 more choices to the form. if ($form_state['values']['poll_more']) { $n = $_GET['q'] == 'system/ajax' ? 1 : 5; $form_state['choice_count'] = count($form_state['values']['choice']) + $n; @@ -431,10 +426,13 @@ function poll_choice_js($form, $form_state) { } /** - * Renumber fields and create a teaser when a poll node is submitted. + * Form submit handler for node_form(). + * + * Upon preview and final submission, we need to renumber poll choices and + * create a teaser output. */ function poll_node_form_submit(&$form, &$form_state) { - // Renumber fields + // Renumber choices. $form_state['values']['choice'] = array_values($form_state['values']['choice']); $form_state['values']['teaser'] = poll_teaser((object) $form_state['values']); } diff --git a/modules/poll/poll.test b/modules/poll/poll.test index 0ca7aa8170e4328111366a2918e02a21fb6f6053..2d14b8aa85b8617c37ce5384ed965a132e369aa9 100644 --- a/modules/poll/poll.test +++ b/modules/poll/poll.test @@ -1,5 +1,5 @@ <?php -// $Id: poll.test,v 1.35 2010/06/23 19:15:07 dries Exp $ +// $Id: poll.test,v 1.38 2010/08/30 00:22:03 webchick Exp $ /** * @file @@ -265,7 +265,7 @@ class PollBlockTestCase extends PollTestCase { // Set the block to a region to confirm block is available. $edit = array(); - $edit['poll_recent[region]'] = 'footer'; + $edit['blocks[poll_recent][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.')); diff --git a/modules/profile/profile.info b/modules/profile/profile.info index 6bda1049303ed4165424c2813ec7b0920cb73c3c..c5cac1bd18b7d154b6559d3012bff203611b5077 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/profile/profile.install b/modules/profile/profile.install index 697062116b59c0077115c4955e55480c1ed4ad50..dccbb322d129e5c93f87cda0e9df4d4fc78b3aaa 100644 --- a/modules/profile/profile.install +++ b/modules/profile/profile.install @@ -1,5 +1,5 @@ <?php -// $Id: profile.install,v 1.25 2010/02/03 18:16:23 webchick Exp $ +// $Id: profile.install,v 1.26 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -138,8 +138,14 @@ function profile_schema() { 'fid' => array('fid'), ), 'foreign keys' => array( - 'fid' => array('profile_field' => 'fid'), - 'uid' => array('users' => 'uid'), + 'profile_field' => array( + 'table' => 'profile_field', + 'columns' => array('fid' => 'fid'), + ), + 'profile_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); diff --git a/modules/profile/profile.module b/modules/profile/profile.module index 466f9caf9fb75203451708443909bc73ce8484d4..efeb768459f97064ecfcd9eb7b170ff34e3ceda0 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -1,5 +1,5 @@ <?php -// $Id: profile.module,v 1.291 2010/05/29 11:37:33 dries Exp $ +// $Id: profile.module,v 1.292 2010/07/10 01:44:28 dries Exp $ /** * @file @@ -177,7 +177,7 @@ function profile_block_save($delta = '', $edit = array()) { function profile_block_view($delta = '') { if (user_access('access user profiles')) { $output = ''; - if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) { + if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) { $node = node_load(arg(1)); $account = user_load($node->uid); diff --git a/modules/profile/profile.pages.inc b/modules/profile/profile.pages.inc index 9107699cf1242f584f2e2c0401041e85092104cd..79d024cda99cef65621e9a12f656f3fb973d3d8b 100644 --- a/modules/profile/profile.pages.inc +++ b/modules/profile/profile.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: profile.pages.inc,v 1.25 2010/03/08 15:46:58 webchick Exp $ +// $Id: profile.pages.inc,v 1.26 2010/08/17 13:50:52 dries Exp $ /** * @file @@ -77,7 +77,7 @@ function profile_browse() { $output .= theme('pager', array('tags' => NULL)); if ($field->type == 'selection' || $field->type == 'list' || $field->type == 'textfield') { - $title = strtr(check_plain($field->page), array('%value' => drupal_placeholder(array('text' => $value)))); + $title = strtr(check_plain($field->page), array('%value' => drupal_placeholder($value))); } else { $title = check_plain($field->page); diff --git a/modules/profile/profile.test b/modules/profile/profile.test index 559371ced483447807b6699c9b7d3bc0f053586a..9542623715deecdf2e63e48bb68136bfd28ef8db 100644 --- a/modules/profile/profile.test +++ b/modules/profile/profile.test @@ -1,5 +1,5 @@ <?php -// $Id: profile.test,v 1.26 2010/05/29 11:37:33 dries Exp $ +// $Id: profile.test,v 1.30 2010/08/30 00:22:03 webchick Exp $ /** * A class for common methods for testing profile fields. @@ -335,7 +335,7 @@ class ProfileTestAutocomplete extends ProfileTestCase { $this->setProfileField($field, $field['value']); // Set some html for what we want to see in the page output later. - $autocomplete_html = '<input class="autocomplete" type="hidden" id="' . drupal_html_id('edit-' . $field['form_name'] . '-autocomplete') . '" value="' . url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE)) . '" disabled="disabled" />'; + $autocomplete_html = '<input type="hidden" id="' . drupal_html_id('edit-' . $field['form_name'] . '-autocomplete') . '" value="' . url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE)) . '" disabled="disabled" class="autocomplete" />'; $field_html = '<input type="text" maxlength="255" name="' . $field['form_name'] . '" id="' . drupal_html_id('edit-' . $field['form_name']) . '" size="60" value="' . $field['value'] . '" class="form-text form-autocomplete required" />'; // Check that autocompletion html is found on the user's profile edit page. @@ -397,7 +397,7 @@ class ProfileBlockTestCase extends ProfileTestCase { function testAuthorInformationBlock() { // Set the block to a region to confirm the block is availble. $edit = array(); - $edit['profile_author-information[region]'] = 'footer'; + $edit['blocks[profile_author-information][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.')); diff --git a/modules/rdf/rdf.api.php b/modules/rdf/rdf.api.php index e33c5dc4f6ca1041e862b61e7b754e35df47fc1f..25a67f8167609971300c6b8af9ee3d71a08aacc4 100644 --- a/modules/rdf/rdf.api.php +++ b/modules/rdf/rdf.api.php @@ -1,5 +1,5 @@ <?php -// $Id: rdf.api.php,v 1.4 2010/03/06 06:40:35 dries Exp $ +// $Id: rdf.api.php,v 1.5 2010/09/09 20:22:00 dries Exp $ /** * @file @@ -90,18 +90,13 @@ function hook_rdf_mapping() { */ function hook_rdf_namespaces() { return array( - 'admin' => 'http://webns.net/mvcb/', 'content' => 'http://purl.org/rss/1.0/modules/content/', 'dc' => 'http://purl.org/dc/terms/', 'foaf' => 'http://xmlns.com/foaf/0.1/', - 'owl' => 'http://www.w3.org/2002/07/owl#', - 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'og' => 'http://ogp.me/ns#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', - 'rss' => 'http://purl.org/rss/1.0/', - 'tags' => 'http://www.holygoat.co.uk/owl/redwood/0.1/tags/', 'sioc' => 'http://rdfs.org/sioc/ns#', 'sioct' => 'http://rdfs.org/sioc/types#', - 'ctag' => 'http://commontag.org/ns#', 'skos' => 'http://www.w3.org/2004/02/skos/core#', 'xsd' => 'http://www.w3.org/2001/XMLSchema#', ); diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info index a856b73dc165f258f63a55d59f4e048aae03764f..bf42f4e1d394922af9cf15d992a80a503e5acf0e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module index 9f3945f3c2ae85c006b01a8ae350f5bb82240ee7..f47926ed68a0c085fccf5c375b92a1c9dffd9d4b 100644 --- a/modules/rdf/rdf.module +++ b/modules/rdf/rdf.module @@ -1,5 +1,5 @@ <?php -// $Id: rdf.module,v 1.41 2010/06/23 02:40:56 dries Exp $ +// $Id: rdf.module,v 1.45 2010/09/09 20:22:00 dries Exp $ /** * @file @@ -80,10 +80,8 @@ function rdf_rdf_namespaces() { 'content' => 'http://purl.org/rss/1.0/modules/content/', 'dc' => 'http://purl.org/dc/terms/', 'foaf' => 'http://xmlns.com/foaf/0.1/', - 'owl' => 'http://www.w3.org/2002/07/owl#', - 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'og' => 'http://ogp.me/ns#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', - 'rss' => 'http://purl.org/rss/1.0/', 'sioc' => 'http://rdfs.org/sioc/ns#', 'sioct' => 'http://rdfs.org/sioc/types#', 'skos' => 'http://www.w3.org/2004/02/skos/core#', @@ -502,7 +500,7 @@ function rdf_preprocess_node(&$variables) { // Adds RDFa markup annotating the number of comments a node has. if (isset($variables['node']->comment_count) && !empty($variables['node']->rdf_mapping['comment_count']['predicates'])) { // Annotates the 'x comments' link in teaser view. - if (isset($variables['content']['links']['comment']['#links']['comment-comments'])) { + if (isset($variables['content']['links']['#links']['comment-comments'])) { $comment_count_attributes['property'] = $variables['node']->rdf_mapping['comment_count']['predicates']; $comment_count_attributes['content'] = $variables['node']->comment_count; $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['datatype']; @@ -512,7 +510,7 @@ function rdf_preprocess_node(&$variables) { // we set an empty rel attribute which triggers rule number 5. See // http://www.w3.org/TR/rdfa-syntax/#sec_5.5. $comment_count_attributes['rel'] = ''; - $variables['content']['links']['comment']['#links']['comment-comments']['attributes'] += $comment_count_attributes; + $variables['content']['links']['#links']['comment-comments']['attributes'] += $comment_count_attributes; } // In full node view, the number of comments is not displayed by // node.tpl.php so it is expressed in RDFa in the <head> tag. @@ -727,12 +725,17 @@ function rdf_field_attach_view_alter(&$output, $context) { $element = &$output[$field_name]; if ($element['#field_type'] == 'taxonomy_term_reference' && $element['#formatter'] == 'taxonomy_term_reference_link') { foreach ($element['#items'] as $delta => $item) { - $term = $item['taxonomy_term']; - if (!empty($term->rdf_mapping['rdftype'])) { - $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; - } - if (!empty($term->rdf_mapping['name']['predicates'])) { - $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + // This function is invoked during entity preview when + // taxonomy term reference items might contain free-tagging + // terms that do not exist yet and thus have no $item['taxonomy_term']. + if (isset($item['taxonomy_term'])) { + $term = $item['taxonomy_term']; + if (!empty($term->rdf_mapping['rdftype'])) { + $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; + } + if (!empty($term->rdf_mapping['name']['predicates'])) { + $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + } } } } diff --git a/modules/rdf/rdf.test b/modules/rdf/rdf.test index 1e152a78188b119ed90e64ba851e38e97044d919..a856f93f30959f5f7fc6679deaa53771c7a66bbe 100644 --- a/modules/rdf/rdf.test +++ b/modules/rdf/rdf.test @@ -1,5 +1,5 @@ <?php -// $Id: rdf.test,v 1.24 2010/06/01 18:29:41 dries Exp $ +// $Id: rdf.test,v 1.27 2010/09/09 20:22:00 dries Exp $ /** * @file @@ -688,7 +688,7 @@ class RdfGetRdfNamespacesTestCase extends DrupalWebTestCase { // Get all RDF namespaces. $ns = rdf_get_namespaces(); - $this->assertEqual($ns['owl'], 'http://www.w3.org/2002/07/owl#', t('A prefix declared once is included.')); + $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', t('A prefix declared once is included.')); $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', t('The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.')); $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', t('Two prefixes can be assigned the same namespace.')); $this->assertTrue(!isset($ns['dc']), t('A prefix with conflicting namespaces is discarded.')); diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info index c2bcfb607e3a2570f8113a12e91f159dfb154bfe..f1caf96de5ede0c1616739c3dd222e215bdc4be8 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/search/search-result.tpl.php b/modules/search/search-result.tpl.php index e804545b44ab9d24874dbd95dfe62cea5e23647a..d6ebd5f4fa37939addd39b3d261bd0e824e09434 100644 --- a/modules/search/search-result.tpl.php +++ b/modules/search/search-result.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: search-result.tpl.php,v 1.6 2010/05/29 07:50:33 dries Exp $ +// $Id: search-result.tpl.php,v 1.7 2010/08/18 18:40:50 dries Exp $ /** * @file @@ -16,7 +16,8 @@ * - $info: String of all the meta information ready for print. Does not apply * to user searches. * - $info_split: Contains same data as $info, split into a keyed array. - * - $type: The type of search, e.g., "node" or "user". + * - $module: The machine-readable name of the module (tab) being searched, such + * as "node" or "user". * * Default keys within $info_split: * - $info_split['type']: Node type. diff --git a/modules/search/search-results.tpl.php b/modules/search/search-results.tpl.php index db218d74bfeec4e6923762f76569f8fb7f569cc3..d299952d047133002598434af1172f55f8cc758f 100644 --- a/modules/search/search-results.tpl.php +++ b/modules/search/search-results.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: search-results.tpl.php,v 1.6 2010/05/28 11:53:57 dries Exp $ +// $Id: search-results.tpl.php,v 1.7 2010/08/18 18:40:50 dries Exp $ /** * @file @@ -15,7 +15,8 @@ * Available variables: * - $search_results: All results as it is rendered through * search-result.tpl.php - * - $type: The type of search, e.g., "node" or "user". + * - $module: The machine-readable name of the module (tab) being searched, such + * as "node" or "user". * * * @see template_preprocess_search_results() @@ -23,7 +24,7 @@ ?> <?php if ($search_results) : ?> <h2><?php print t('Search results');?></h2> - <ol class="search-results <?php print $type; ?>-results"> + <ol class="search-results <?php print $module; ?>-results"> <?php print $search_results; ?> </ol> <?php print $pager; ?> diff --git a/modules/search/search.admin.inc b/modules/search/search.admin.inc index 71844a123a752b8ea771a2f598d92d2ba160f6de..2b05748edde60ce3072d3b049e0f9deaad1be32f 100644 --- a/modules/search/search.admin.inc +++ b/modules/search/search.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: search.admin.inc,v 1.15 2010/06/26 21:32:20 dries Exp $ +// $Id: search.admin.inc,v 1.16 2010/08/05 07:11:15 webchick Exp $ /** * @file @@ -31,7 +31,7 @@ function search_reindex_confirm_submit(&$form, &$form_state) { */ function _search_get_module_names() { - $search_info = search_get_info(); + $search_info = search_get_info(TRUE); $modules = db_select('system', 's') ->fields('s', array('name', 'info')) ->condition('s.status', 1) @@ -48,10 +48,11 @@ function _search_get_module_names() { } /** - * Menu callback; displays the search module settings page. + * Menu callback: displays the search module settings page. * * @ingroup forms - * @see system_settings_form() + * + * @see search_admin_settings_validate() * @see search_admin_settings_submit() * @see search_admin_reindex_submit() */ @@ -118,9 +119,16 @@ function search_admin_settings($form) { '#type' => 'checkboxes', '#default_value' => variable_get('search_active_modules', array('node', 'user')), '#options' => _search_get_module_names(), - '#description' => t('Determine which search modules are active from the available modules.') + '#description' => t('Choose which search modules are active from the available modules.') ); - + $form['active']['search_default_module'] = array( + '#title' => t('Default search module'), + '#type' => 'radios', + '#default_value' => 'node', + '#options' => _search_get_module_names(), + '#description' => t('Choose which search module is the default.') + ); + $form['#validate'][] = 'search_admin_settings_validate'; $form['#submit'][] = 'search_admin_settings_submit'; // Per module settings @@ -135,7 +143,21 @@ function search_admin_settings($form) { } /** - * Submit callback. + * Form validation handler for search_admin_settings(). + */ +function search_admin_settings_validate($form, &$form_state) { + // Check whether we selected a valid default. + if ($form_state['clicked_button']['#value'] != t('Reset to defaults')) { + $new_modules = array_filter($form_state['values']['search_active_modules']); + $default = $form_state['values']['search_default_module']; + if (!in_array($default, $new_modules, TRUE)) { + form_set_error('search_default_module', t('Your default search module is not selected as an active module.')); + } + } +} + +/** + * Form submission handler for search_admin_settings(). */ function search_admin_settings_submit($form, &$form_state) { // If these settings change, the index needs to be rebuilt. @@ -159,7 +181,7 @@ function search_admin_settings_submit($form, &$form_state) { } /** - * Submit callback. + * Form submission handler for reindex button on search_admin_settings_form(). */ function search_admin_reindex_submit($form, &$form_state) { // send the user to the confirmation page diff --git a/modules/search/search.api.php b/modules/search/search.api.php index 49b39545dbf3cd7c460b4b3e9a03faf60b52a46d..d003567654c65510f2635b2bcc06b4a1d84bf145 100644 --- a/modules/search/search.api.php +++ b/modules/search/search.api.php @@ -1,5 +1,5 @@ <?php -// $Id: search.api.php,v 1.27 2010/06/01 17:56:46 dries Exp $ +// $Id: search.api.php,v 1.29 2010/08/18 18:40:50 dries Exp $ /** * @file @@ -25,14 +25,23 @@ * hook_update_index(). If your search type has settings, you can implement * hook_search_admin() to add them to the search settings page. You can also * alter the display of your module's search results by implementing - * hook_search_page(). And you can use hook_form_FORM_ID_alter(), with - * FORM_ID set to 'search', to add fields to the search form. See - * node_form_search_form_alter() for an example. + * hook_search_page(). You can use hook_form_FORM_ID_alter(), with + * FORM_ID set to 'search', to add fields to the search form (see + * node_form_search_form_alter() for an example). You can use + * hook_search_access() to limit access to searching, and hook_search_page() to + * override how search results are displayed. * * @return - * Array with the optional keys 'title' for the tab title and 'path' for - * the path component after 'search/'. Both will default to the module - * name. + * Array with optional keys: + * - 'title': Title for the tab on the search page for this module. Defaults + * to the module name if not given. + * - 'path': Path component after 'search/' for searching with this module. + * Defaults to the module name if not given. + * - 'conditions_callback': Name of a callback function that is invoked by + * search_view() to get an array of additional search conditions to pass to + * search_data(). For example, a search module may get additional keywords, + * filters, or modifiers for the search from the query string. Sample + * callback function: sample_search_conditions_callback(). * * @ingroup search */ @@ -40,9 +49,35 @@ function hook_search_info() { return array( 'title' => 'Content', 'path' => 'node', + 'conditions_callback' => 'sample_search_conditions_callback', ); } +/** + * An example conditions callback function for search. + * + * This example pulls additional search keywords out of the $_REQUEST variable, + * (i.e. from the query string of the request). The conditions may also be + * generated internally - for example based on a module's settings. + * + * @see hook_search_info() + * @ingroup search + */ +function sample_search_conditions_callback($keys) { + $conditions = array(); + + if (!empty($_REQUEST['keys'])) { + $conditions['keys'] = $_REQUEST['keys']; + } + if (!empty($_REQUEST['sample_search_keys'])) { + $conditions['sample_search_keys'] = $_REQUEST['sample_search_keys']; + } + if ($force_keys = variable_get('sample_search_force_keywords', '')) { + $conditions['sample_search_force_keywords'] = $force_keys; + } + return $conditions; +} + /** * Define access to a custom search routine. * @@ -139,6 +174,8 @@ function hook_search_admin() { * * @param $keys * The search keywords as entered by the user. + * @param $conditions + * An optional array of additional conditions, such as filters. * * @return * An array of search results. To use the default search result @@ -154,7 +191,7 @@ function hook_search_admin() { * * @ingroup search */ -function hook_search_execute($keys = NULL) { +function hook_search_execute($keys = NULL, $conditions = NULL) { // Build matching conditions $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault'); $query->join('node', 'n', 'n.nid = i.sid'); @@ -213,7 +250,7 @@ function hook_search_execute($keys = NULL) { /** * Override the rendering of search results. * - * A module that implements hook_search() to define a type of search + * A module that implements hook_search_info() to define a type of search * may implement this hook in order to override the default theming of * its search results, which is otherwise themed using theme('search_results'). * @@ -227,17 +264,20 @@ function hook_search_execute($keys = NULL) { * An array of search results. * * @return - * An HTML string containing the formatted search results, with + * A renderable array, which will render the formatted search results with * a pager included. */ function hook_search_page($results) { - $output = '<ol class="search-results">'; + $output['prefix']['#markup'] = '<ol class="search-results">'; foreach ($results as $entry) { - $output .= theme('search_result', $entry, $type); + $output[] = array( + '#theme' => 'search_result', + '#result' => $entry, + '#module' => 'my_module_name', + ); } - $output .= '</ol>'; - $output .= theme('pager', NULL); + $output['suffix']['#markup'] = '</ol>' . theme('pager'); return $output; } diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc index ebab18211e993b5601f5aba8981f454c8c9e8ee3..907c6ece03a065f9dd16b0b37a44b6206fe76ac9 100644 --- a/modules/search/search.extender.inc +++ b/modules/search/search.extender.inc @@ -1,5 +1,5 @@ <?php -// $Id: search.extender.inc,v 1.5 2010/05/12 15:53:43 dries Exp $ +// $Id: search.extender.inc,v 1.7 2010/09/01 01:43:50 webchick Exp $ /** * @file diff --git a/modules/search/search.info b/modules/search/search.info index 2e781b9bf6a1382c9e8709ad5d4afb1bba3ab47c..681da5d83de1060bab2157e32857bb0e5c82513c 100644 --- a/modules/search/search.info +++ b/modules/search/search.info @@ -1,4 +1,4 @@ -; $Id: search.info,v 1.11 2009/11/17 21:24:18 dries Exp $ +; $Id: search.info,v 1.12 2010/09/05 02:21:38 dries Exp $ name = Search description = Enables site-wide keyword searching. package = Core @@ -11,9 +11,10 @@ files[] = search.install files[] = search.test files[] = search.extender.inc configure = admin/config/search/settings +stylesheets[all][] = search.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/search/search.install b/modules/search/search.install index 7d8ed588a34e8d93fb94091d39b86c78affe2c4d..62cc7d394e1df525c0f93ab29a806f3356287822 100644 --- a/modules/search/search.install +++ b/modules/search/search.install @@ -1,5 +1,5 @@ <?php -// $Id: search.install,v 1.27 2009/12/04 16:49:47 dries Exp $ +// $Id: search.install,v 1.29 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -32,7 +32,7 @@ function search_schema() { 'type' => array( 'type' => 'varchar', 'length' => 16, - 'not null' => FALSE, + 'not null' => TRUE, 'description' => 'Type of item, e.g. node.', ), 'data' => array( @@ -72,7 +72,7 @@ function search_schema() { 'type' => array( 'type' => 'varchar', 'length' => 16, - 'not null' => FALSE, + 'not null' => TRUE, 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', ), 'score' => array( @@ -85,8 +85,13 @@ function search_schema() { 'sid_type' => array('sid', 'type'), ), 'foreign keys' => array( - 'sid' => array('search_dataset' => 'sid'), - 'type' => array('search_dataset' => 'type'), + 'search_dataset' => array( + 'table' => 'search_dataset', + 'columns' => array( + 'sid' => 'sid', + 'type' => 'type', + ), + ), ), 'primary key' => array('word', 'sid', 'type'), ); @@ -155,9 +160,24 @@ function search_schema() { */ function search_update_7000() { db_drop_unique_key('search_dataset', 'sid_type'); + $dataset_type_spec = array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'Type of item, e.g. node.', + ); + db_change_field('search_dataset', 'type', 'type', $dataset_type_spec); db_add_primary_key('search_dataset', array('sid', 'type')); db_drop_index('search_index', 'word'); db_drop_unique_key('search_index', 'word_sid_type'); + $index_type_spec = array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', + ); + db_change_field('search_index', 'type', 'type', $index_type_spec); db_add_primary_key('search_index', array('word', 'sid', 'type')); } + diff --git a/modules/search/search.module b/modules/search/search.module index 7f1bfa359a19cc1bfef251106a1450101d3286d2..2772c8e58f228258d334085ec16304997a7817fc 100644 --- a/modules/search/search.module +++ b/modules/search/search.module @@ -1,5 +1,5 @@ <?php -// $Id: search.module,v 1.351 2010/06/30 15:47:50 dries Exp $ +// $Id: search.module,v 1.359 2010/09/05 02:21:38 dries Exp $ /** * @file @@ -107,12 +107,12 @@ function search_theme() { 'template' => 'search-block-form', ), 'search_result' => array( - 'variables' => array('result' => NULL, 'type' => NULL), + 'variables' => array('result' => NULL, 'module' => NULL), 'file' => 'search.pages.inc', 'template' => 'search-result', ), 'search_results' => array( - 'variables' => array('results' => NULL, 'type' => NULL), + 'variables' => array('results' => NULL, 'module' => NULL), 'file' => 'search.pages.inc', 'template' => 'search-results', ), @@ -163,7 +163,7 @@ function search_menu() { $items['search'] = array( 'title' => 'Search', 'page callback' => 'search_view', - 'access arguments' => array('search content'), + 'access callback' => 'search_is_active', 'type' => MENU_SUGGESTED_ITEM, 'file' => 'search.pages.inc', ); @@ -194,23 +194,24 @@ function search_menu() { // http://drupal.org/node/245103 for details. drupal_static_reset('search_get_info'); - if ($active = variable_get('search_active_modules', array('node', 'user'))) { - foreach (array_intersect_key(search_get_info(), array_flip($active)) as $module => $search_info) { + $default_info = search_get_default_module_info(); + if ($default_info) { + foreach (search_get_info() as $module => $search_info) { $path = 'search/' . $search_info['path']; $items[$path] = array( 'title' => $search_info['title'], 'page callback' => 'search_view', - 'page arguments' => array($module), + 'page arguments' => array($module, ''), 'access callback' => '_search_menu_access', 'access arguments' => array($module), - 'type' => $module == 'node' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, + 'type' => MENU_LOCAL_TASK, 'file' => 'search.pages.inc', - 'weight' => $module == 'node' ? -10 : 0, + 'weight' => $module == $default_info['module'] ? -10 : 0, ); $items["$path/%menu_tail"] = array( 'title' => $search_info['title'], 'page callback' => 'search_view', - 'page arguments' => array($module), + 'page arguments' => array($module, 2), 'access callback' => '_search_menu_access', 'access arguments' => array($module), // The default local task points to its parent, but this item points to @@ -219,9 +220,9 @@ function search_menu() { 'file' => 'search.pages.inc', 'weight' => 0, // These tabs are not subtabs. - 'tab_root' => 'search/node/%', + 'tab_root' => 'search/' . $default_info['path'] . '/%', // These tabs need to display at the same level. - 'tab_parent' => 'search', + 'tab_parent' => 'search/' . $default_info['path'], ); } } @@ -229,19 +230,63 @@ function search_menu() { } /** - * Get information about all available search hooks. + * Determines access for the ?q=search path. + */ +function search_is_active() { + // This path cannot be accessed if there are no active modules. + return user_access('search content') && search_get_info(); +} + +/** + * Returns information about available search modules. + * + * @param $all + * If TRUE, information about all enabled modules implementing + * hook_search_info() will be returned. If FALSE (default), only modules that + * have been set to active on the search settings page will be returned. + * + * @return + * Array of hook_search_info() return values, keyed by module name. The + * 'title' and 'path' array elements will be set to defaults for each module + * if not supplied by hook_search_info(), and an additional array element of + * 'module' will be added (set to the module name). */ -function search_get_info() { +function search_get_info($all = FALSE) { $search_hooks = &drupal_static(__FUNCTION__); if (!isset($search_hooks)) { foreach (module_implements('search_info') as $module) { $search_hooks[$module] = call_user_func($module . '_search_info'); - // Use module name as the default. + // Use module name as the default value. $search_hooks[$module] += array('title' => $module, 'path' => $module); + // Include the module name itself in the array. + $search_hooks[$module]['module'] = $module; } } - return $search_hooks; + + if ($all) { + return $search_hooks; + } + + $active = variable_get('search_active_modules', array('node', 'user')); + return array_intersect_key($search_hooks, array_flip($active)); +} + +/** + * Returns information about the default search module. + * + * @return + * The search_get_info() array element for the default search module, if any. + */ +function search_get_default_module_info() { + $info = search_get_info(); + $default = variable_get('search_default_module', 'node'); + if (isset($info[$default])) { + return $info[$default]; + } + // The variable setting does not match any active module, so just return + // the info for the first active module (if any). + return reset($info); } /** @@ -372,10 +417,13 @@ function search_simplify($text) { // Readable regexp: ([number]+)[punctuation]+(?=[number]) $text = preg_replace('/([' . PREG_CLASS_NUMBERS . ']+)[' . PREG_CLASS_PUNCTUATION . ']+(?=[' . PREG_CLASS_NUMBERS . '])/u', '\1', $text); + // Multiple dot and dash groups are word boundaries and replaced with space. + // No need to use the unicode modifer here because 0-127 ASCII characters + // can't match higher UTF-8 characters as the leftmost bit of those are 1. + $text = preg_replace('/[.-]{2,}/', ' ', $text); + // The dot, underscore and dash are simply removed. This allows meaningful - // search behavior with acronyms and URLs. No need to use the unicode modifer - // here because 0-127 ASCII characters can't match higher UTF-8 characters as - // the leftmost bit of those are 1. + // search behavior with acronyms and URLs. See unicode note directly above. $text = preg_replace('/[._-]+/', '', $text); // With the exception of the rules above, we consider all punctuation, @@ -791,41 +839,59 @@ function search_comment_unpublish($comment) { } /** - * Extract a module-specific search option from a search query. e.g. 'type:book' + * Extracts a module-specific search option from a search expression. + * + * Search options are added using search_expression_insert(), and retrieved + * using search_expression_extract(). They take the form option:value, and + * are added to the ordinary keywords in the search expression. + * + * @param $expression + * The search expression to extract from. + * @param $option + * The name of the option to retrieve from the search expression. + * + * @return + * The value previously stored in the search expression for option $option, + * if any. Trailing spaces in values will not be included. */ -function search_expression_extract($keys, $option) { - if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $keys, $matches)) { +function search_expression_extract($expression, $option) { + if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $expression, $matches)) { return $matches[2]; } } /** - * Return a query with the given module-specific search option inserted in. - * e.g. 'type:book'. + * Adds a module-specific search option to a search expression. + * + * Search options are added using search_expression_insert(), and retrieved + * using search_expression_extract(). They take the form option:value, and + * are added to the ordinary keywords in the search expression. + * + * @param $expression + * The search expression to add to. + * @param $option + * The name of the option to add to the search expression. + * @param $value + * The value to add for the option. If present, it will replace any previous + * value added for the option. Cannot contain any spaces or | characters, as + * these are used as delimiters. If you want to add a blank value $option: to + * the search expression, pass in an empty string or a string that is composed + * of only spaces. To clear a previously-stored option without adding a + * replacement, pass in NULL for $value or omit. + * + * @return + * $expression, with any previous value for this option removed, and a new + * $option:$value pair added if $value was provided. */ -function search_expression_insert($keys, $option, $value = '') { - if (search_expression_extract($keys, $option)) { - $keys = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $keys)); - } - if ($value != '') { - $keys .= ' ' . $option . ':' . $value; - } - return $keys; -} +function search_expression_insert($expression, $option, $value = NULL) { + // Remove any previous values stored with $option. + $expression = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $expression)); -/** - * Helper function for grabbing search keys. - */ -function search_get_keys() { - $return = &drupal_static(__FUNCTION__); - if (!isset($return)) { - // Extract keys as remainder of path - // Note: support old GET format of searches for existing links. - $path = explode('/', $_GET['q'], 3); - $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys']; - $return = count($path) == 3 ? $path[2] : $keys; + // Set new value, if provided. + if (isset($value)) { + $expression .= ' ' . $option . ':' . trim($value); } - return $return; + return $expression; } /** @@ -857,26 +923,42 @@ function search_get_keys() { */ /** - * Render a search form. + * Builds a search form. * * @param $action - * Form action. Defaults to "search/$type". This will be run through url(). + * Form action. Defaults to "search/$path", where $path is the search path + * associated with the $type module in its hook_search_info(). This will be + * run through url(). * @param $keys * The search string entered by the user, containing keywords for the search. * @param $type - * The type of search to render the node for. Must be the name of module - * which implements hook_search(). Defaults to 'node'. + * The search module to render the form for: a module that implements + * hook_search_info(). If not supplied, the default search module is used. * @param $prompt - * A piece of text to put before the form (e.g. "Enter your keywords") + * Label for the keywords field. Defaults to t('Enter your keywords') if NULL. + * Supply '' to omit. + * * @return - * An HTML string containing the search form. + * A Form API array for the search form. */ function search_form($form, &$form_state, $action = '', $keys = '', $type = NULL, $prompt = NULL) { - // Add CSS - drupal_add_css(drupal_get_path('module', 'search') . '/search.css', array('preprocess' => FALSE)); + $module_info = FALSE; + if (!$type) { + $module_info = search_get_default_module_info(); + } + else { + $info = search_get_info(); + $module_info = isset($info[$type]) ? $info[$type] : FALSE; + } + + // Sanity check. + if (!$module_info) { + form_set_error(NULL, t('Search is currently disabled.'), 'error'); + return $form; + } if (!$action) { - $action = 'search/' . $type; + $action = 'search/' . $module_info['path']; } if (is_null($prompt)) { $prompt = t('Enter your keywords'); @@ -938,7 +1020,13 @@ function search_box_form_submit($form, &$form_state) { } $form_id = $form['form_id']['#value']; - $form_state['redirect'] = 'search/node/' . trim($form_state['values'][$form_id]); + $info = search_get_default_module_info(); + if ($info) { + $form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]); + } + else { + form_set_error(NULL, t('Search is currently disabled.'), 'error'); + } } /** @@ -969,19 +1057,31 @@ function template_preprocess_search_block_form(&$variables) { } /** - * Perform a standard search on the given keys, and return the formatted results. + * Performs a search by calling hook_search_execute(). + * + * @param $keys + * Keyword query to search on. + * @param $module + * Search module to search. + * @param $conditions + * Optional array of additional search conditions. + * + * @return + * Renderable array of search results. No return value if $keys are not + * supplied or if the given search module is not active. */ -function search_data($keys = NULL, $type = 'node') { - - if (isset($keys)) { - if (module_hook($type, 'search_execute')) { - $results = module_invoke($type, 'search_execute', $keys); - if (module_hook($type, 'search_page')) { - return module_invoke($type, 'search_page', $results); - } - else { - return theme('search_results', array('results' => $results, 'type' => $type)); - } +function search_data($keys, $module, $conditions = NULL) { + if (module_hook($module, 'search_execute')) { + $results = module_invoke($module, 'search_execute', $keys, $conditions); + if (module_hook($module, 'search_page')) { + return module_invoke($module, 'search_page', $results); + } + else { + return array( + '#theme' => 'search_results', + '#results' => $results, + '#module' => $module, + ); } } } @@ -1007,8 +1107,11 @@ function search_excerpt($keys, $text) { preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches); $keys = array_merge($matches[2], $matches[3]); - // Prepare text - $text = ' ' . strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text)) . ' '; + // Prepare text by stripping HTML tags and decoding HTML entities. + $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text)); + $text = decode_entities($text); + + // Slash-escape quotes in the search keyword string. array_walk($keys, '_search_excerpt_replace'); $workkeys = $keys; @@ -1038,9 +1141,12 @@ function search_excerpt($keys, $text) { // $q) and behind it (position $s) if (preg_match('/' . $boundary . $key . $boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) { $p = $match[0][1]; - if (($q = strpos($text, ' ', max(0, $p - 60))) !== FALSE) { - $end = substr($text, $p, 80); + if (($q = strpos(' ' . $text, ' ', max(0, $p - 61))) !== FALSE) { + $end = substr($text . ' ', $p, 80); if (($s = strrpos($end, ' ')) !== FALSE) { + // Account for the added spaces. + $q = max($q - 1, 0); + $s = min($s, drupal_strlen($end) - 1); $ranges[$q] = $p + $s; $length += $p + $s - $q; $included[$key] = $p + 1; @@ -1059,9 +1165,11 @@ function search_excerpt($keys, $text) { } } - // If we didn't find anything, return the beginning. if (count($ranges) == 0) { - return truncate_utf8($text, 256, TRUE, TRUE); + // We didn't find any keyword matches, so just return the first part of the + // text. We also need to re-encode any HTML special characters that we + // entity-decoded above. + return check_plain(truncate_utf8($text, 256, TRUE, TRUE)); } // Sort the text ranges by starting position. @@ -1091,7 +1199,12 @@ function search_excerpt($keys, $text) { foreach ($newranges as $from => $to) { $out[] = substr($text, $from, $to - $from); } - $text = (isset($newranges[0]) ? '' : '... ') . implode(' ... ', $out) . ' ...'; + + // Let translators have the ... separator text as one chunk. + $dots = explode('!excerpt', t('... !excerpt ... !excerpt ...')); + + $text = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2]; + $text = check_plain($text); // Highlight keywords. Must be done at once to prevent conflicts ('strong' and '<strong>'). $text = preg_replace('/' . $boundary . '(' . implode('|', $keys) . ')' . $boundary . '/iu', '<strong>\0</strong>', $text); @@ -1103,12 +1216,15 @@ function search_excerpt($keys, $text) { */ /** - * Helper function for array_walk in search_except. + * Helper function for array_walk() in search_excerpt(). */ function _search_excerpt_replace(&$text) { $text = preg_quote($text, '/'); } +/** + * Implements hook_forms(). + */ function search_forms() { $forms['search_block_form']= array( 'callback' => 'search_box', @@ -1116,3 +1232,4 @@ function search_forms() { ); return $forms; } + diff --git a/modules/search/search.pages.inc b/modules/search/search.pages.inc index ea776854d9809f2333fbb7930d8805699ccde610..8356c9996008d57147032089645e07fea440d931 100644 --- a/modules/search/search.pages.inc +++ b/modules/search/search.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: search.pages.inc,v 1.20 2010/06/30 15:47:50 dries Exp $ +// $Id: search.pages.inc,v 1.23 2010/08/18 18:40:50 dries Exp $ /** * @file @@ -8,39 +8,69 @@ /** * Menu callback; presents the search form and/or search results. + * + * @param $module + * Search module to use for the search. + * @param $keys + * Keywords to use for the search. */ -function search_view($type = 'node') { - // Search form submits with POST but redirects to GET. This way we can keep - // the search query URL clean as a whistle: - // search/type/keyword+keyword - if (!isset($_POST['form_id'])) { - $keys = search_get_keys(); - if ($_GET['q'] != 'search' && $type == 'node' && !$keys) { - // Due to how search_menu() sets up the tabs, path search/node would - // display two sets of tabs. So instead, if there are no keywords and - // we're on the node tab, just redirect to the bare 'search' path. - drupal_goto('search'); +function search_view($module = NULL, $keys = '') { + $info = FALSE; + $redirect = FALSE; + $keys = trim($keys); + // Also try to pull search keywords out of the $_REQUEST variable to + // support old GET format of searches for existing links. + if (!$keys && !empty($_REQUEST['keys'])) { + $keys = trim($_REQUEST['keys']); + } + + if (!empty($module)) { + $active_module_info = search_get_info(); + if (isset($active_module_info[$module])) { + $info = $active_module_info[$module]; } + } - // Only perform search if there is non-whitespace search term: - $results = ''; - if (trim($keys)) { + if (empty($info)) { + // No path or invalid path: find the default module. Note that if there + // are no enabled search modules, this function should never be called, + // since hook_menu() would not have defined any search paths. + $info = search_get_default_module_info(); + // Redirect from bare /search or an invalid path to the default search path. + $path = 'search/' . $info['path']; + if ($keys) { + $path .= '/' . $keys; + } + drupal_goto($path); + } + + // Default results output is an empty string. + $results = array('#markup' => ''); + // Process the search form. Note that if there is $_POST data, + // search_form_submit() will cause a redirect to search/[module path]/[keys], + // which will get us back to this page callback. In other words, the search + // form submits with POST but redirects to GET. This way we can keep + // the search query URL clean as a whistle. + if (empty($_POST['form_id']) || $_POST['form_id'] != 'search_form') { + $conditions = NULL; + if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) { + // Build an optional array of more search conditions. + $conditions = call_user_func($info['conditions_callback'], $keys); + } + // Only search if there are keywords or non-empty conditions. + if ($keys || !empty($conditions)) { // Log the search keys. - $info = search_get_info(); - watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info[$type]['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $type . '/' . $keys)); + watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys)); // Collect the search results. - $results = search_data($keys, $type); - - // Construct the search form. - $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $type); - $build['search_results'] = array('#markup' => $results); - - return $build; + $results = search_data($keys, $info['module'], $conditions); } } + // The form may be altered based on whether the search was run. + $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']); + $build['search_results'] = $results; - return drupal_get_form('search_form', NULL, empty($keys) ? '' : $keys, $type); + return $build; } /** @@ -48,17 +78,20 @@ function search_view($type = 'node') { * * The $variables array contains the following arguments: * - $results - * - $type + * - $module * * @see search-results.tpl.php */ function template_preprocess_search_results(&$variables) { $variables['search_results'] = ''; + if (!empty($variables['module'])) { + $variables['module'] = check_plain($variables['module']); + } foreach ($variables['results'] as $result) { - $variables['search_results'] .= theme('search_result', array('result' => $result, 'type' => $variables['type'])); + $variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module'])); } $variables['pager'] = theme('pager', array('tags' => NULL)); - $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['type']; + $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module']; } /** @@ -66,7 +99,7 @@ function template_preprocess_search_results(&$variables) { * * The $variables array contains the following arguments: * - $result - * - $type + * - $module * * @see search-result.tpl.php */ @@ -76,8 +109,8 @@ function template_preprocess_search_result(&$variables) { $variables['title'] = check_plain($result['title']); $info = array(); - if (!empty($result['type'])) { - $info['type'] = check_plain($result['type']); + if (!empty($result['module'])) { + $info['module'] = check_plain($result['module']); } if (!empty($result['user'])) { $info['user'] = $result['user']; @@ -93,7 +126,7 @@ function template_preprocess_search_result(&$variables) { // Provide separated and grouped meta information.. $variables['info_split'] = $info; $variables['info'] = implode(' - ', $info); - $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['type']; + $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module']; } /** @@ -113,10 +146,9 @@ function search_form_submit($form, &$form_state) { $keys = $form_state['values']['processed_keys']; if ($keys == '') { form_set_error('keys', t('Please enter some keywords.')); - // Fall through to the drupal_goto() call. + // Fall through to the form redirect. } - $type = $form_state['values']['module'] ? $form_state['values']['module'] : 'node'; $form_state['redirect'] = $form_state['action'] . '/' . $keys; return; } diff --git a/modules/search/search.test b/modules/search/search.test index 120d11fca9fc38029e8e790267c345a2330058a2..3dbbd1b562dd3b42da6957abc1d1176744edd5fe 100644 --- a/modules/search/search.test +++ b/modules/search/search.test @@ -1,5 +1,5 @@ <?php -// $Id: search.test,v 1.64 2010/06/08 06:34:36 webchick Exp $ +// $Id: search.test,v 1.74 2010/09/04 13:33:53 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. @@ -559,7 +559,7 @@ class SearchBlockTestCase extends DrupalWebTestCase { // Set the block to a region to confirm block is availble. $edit = array(); - $edit['search_form[region]'] = 'footer'; + $edit['blocks[search_form][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.')); } @@ -570,7 +570,7 @@ class SearchBlockTestCase extends DrupalWebTestCase { function testBlock() { // Enable the block, and place it in the 'content' region so that it isn't // hidden on 404 pages. - $edit = array('search_form[region]' => 'content'); + $edit = array('blocks[search_form][region]' => 'content'); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Test a normal search via the block form, from the front page. @@ -846,6 +846,67 @@ class SearchCommentTestCase extends DrupalWebTestCase { } +/** + * Tests search_expression_insert() and search_expression_extract(). + * + * @see http://drupal.org/node/419388 (issue) + */ +class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'Search expression insert/extract', + 'description' => 'Tests the functions search_expression_insert() and search_expression_extract()', + 'group' => 'Search', + ); + } + + function setUp() { + drupal_load('module', 'search'); + parent::setUp(); + } + + /** + * Tests search_expression_insert() and search_expression_extract(). + */ + function testInsertExtract() { + $base_expression = "mykeyword"; + // Build an array of option, value, what should be in the expression, what + // should be retrieved from expression. + $cases = array( + array('foo', 'bar', 'foo:bar', 'bar'), // Normal case. + array('foo', NULL, '', NULL), // Empty value: shouldn't insert. + array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string. + array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string. + array('foo', '0', 'foo:0', '0'), // String zero as value: should insert. + array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert. + ); + + foreach ($cases as $index => $case) { + $after_insert = search_expression_insert($base_expression, $case[0], $case[1]); + if (empty($case[2])) { + $this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index"); + } + else { + $this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index"); + } + + $retrieved = search_expression_extract($after_insert, $case[0]); + if (!isset($case[3])) { + $this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index"); + } + else { + $this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index"); + } + + $after_clear = search_expression_insert($after_insert, $case[0]); + $this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index"); + + $cleared = search_expression_extract($after_clear, $case[0]); + $this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index"); + } + } +} + /** * Tests that comment count display toggles properly on comment status of node * @@ -935,22 +996,22 @@ class SearchCommentCountToggleTestCase extends DrupalWebTestCase { $this->drupalPost('', $edit, t('Search')); $this->assertNoText(t('0 comments'), t('Empty comment count does not display for nodes with comment status set to Hidden')); $this->assertNoText(t('1 comment'), t('Non-empty comment count does not display for nodes with comment status set to Hidden')); - } + } } /** - * Test search_simplify() on every Unicode character. + * Test search_simplify() on every Unicode character, and some other cases. */ class SearchSimplifyTestCase extends DrupalWebTestCase { public static function getInfo() { return array( 'name' => 'Search simplify', - 'description' => 'Check that simplification works as intended.', + 'description' => 'Check that the search_simply() function works as intended.', 'group' => 'Search', ); } - function testSearchSimplify() { + function testSearchSimplifyUnicode() { $input = file_get_contents(DRUPAL_ROOT . '/modules/search/tests/UnicodeTest.txt'); $strings = explode(chr(10), $input); foreach ($strings as $key => $string) { @@ -969,12 +1030,84 @@ class SearchSimplifyTestCase extends DrupalWebTestCase { // Diff really does not like files starting with \0 so test it separately. $this->assertIdentical(' ', search_simplify($string), t('Search simplify works for ASCII control characters.')); } + + /** + * Tests that search_simplify() does the right thing with punctuation. + */ + function testSearchSimplifyPunctuation() { + $cases = array( + array('20.03/94-28,876', '20039428876', 'Punctuation removed from numbers'), + array('great...drupal--module', 'great drupal module', 'Multiple dot and dashes are word boundaries'), + array('very_great-drupal.module', 'verygreatdrupalmodule', 'Single dot, dash, underscore are removed'), + array('regular,punctuation;word', 'regular punctuation word', 'Punctuation is a word boundary'), + ); + + foreach ($cases as $case) { + $out = trim(search_simplify($case[0])); + $this->assertEqual($out, $case[1], $case[2]); + } + } +} + + +/** + * Tests keywords and conditions. + */ +class SearchKeywordsConditions extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Keywords and conditions', + 'description' => 'Verify the search pulls in keywords and extra conditions.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_extra_type'); + // Create searching user. + $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'post comments without approval')); + // Login with sufficient privileges. + $this->drupalLogin($this->searching_user); + // Test with all search modules enabled. + variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); + menu_rebuild(); + } + + /** + * Verify the kewords are captured and conditions respected. + */ + function testSearchKeyswordsConditions() { + // No keys, not conditions - no results. + $this->drupalGet('search/dummy_path'); + $this->assertNoText('Dummy search snippet to display'); + // With keys - get results. + $keys = 'bike shed ' . $this->randomName(); + $this->drupalGet("search/dummy_path/{$keys}"); + $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); + $keys = 'blue drop ' . $this->randomName(); + $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys))); + $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); + // Add some conditions and keys. + $keys = 'moving drop ' . $this->randomName(); + $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys))); + $this->assertText("Dummy search snippet to display."); + $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); + // Add some conditions and no keys. + $keys = 'drop kick ' . $this->randomName(); + $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys))); + $this->assertText("Dummy search snippet to display."); + $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); + } } /** * Test config page. */ class SearchConfigSettingsForm extends DrupalWebTestCase { + public $search_user; + public $search_node; + public static function getInfo() { return array( 'name' => 'Config settings form', @@ -984,27 +1117,36 @@ class SearchConfigSettingsForm extends DrupalWebTestCase { } function setUp() { - parent::setUp('search'); + parent::setUp('search', 'search_extra_type'); // Login as a user that can create and search content. - $this->drupalLogin($this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access'))); - } + $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks')); + $this->drupalLogin($this->search_user); - /** - * Verify the search settings form. - */ - function testSearchSettingsPage() { // Add a single piece of content and index it. $node = $this->drupalCreateNode(); - // Link the node to itself to test that it's only indexed once. + $this->search_node = $node; + // Link the node to itself to test that it's only indexed once. The content + // also needs the word "pizza" so we can use it as the search keyword. $langcode = LANGUAGE_NONE; $body_key = "body[$langcode][0][value]"; - $edit[$body_key] = l($node->title, 'node/' . $node->nid); + $edit[$body_key] = l($node->title, 'node/' . $node->nid) . ' pizza sandwich'; $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); node_update_index(); search_update_totals(); + // Enable the search block. + $edit = array(); + $edit['blocks[search_form][region]'] = 'content'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + } + + /** + * Verify the search settings form. + */ + function testSearchSettingsPage() { + // Test that the settings form displays the correct count of items left to index. $this->drupalGet('admin/config/search/settings'); $this->assertText(t('There are @count items left to index.', array('@count' => 0))); @@ -1017,6 +1159,144 @@ class SearchConfigSettingsForm extends DrupalWebTestCase { $this->drupalGet('admin/config/search/settings'); $this->assertText(t('There is 1 item left to index.')); } + + /** + * Verify that you can disable individual search modules. + */ + function testSearchModuleDisabling() { + // Array of search types to test: 'path' is the search path, 'title' is + // the tab title, 'keys' are the keywords to search for, and 'text' is + // the text to assert is on the results page. + $module_info = array( + 'node' => array( + 'path' => 'node', + 'title' => 'Content', + 'keys' => 'pizza', + 'text' => $this->search_node->title, + ), + 'user' => array( + 'path' => 'user', + 'title' => 'User', + 'keys' => $this->search_user->name, + 'text' => $this->search_user->mail, + ), + 'search_extra_type' => array( + 'path' => 'dummy_path', + 'title' => 'Dummy search type', + 'keys' => 'foo', + 'text' => 'Dummy search snippet to display', + ), + ); + $modules = array_keys($module_info); + + // Test each module if it's enabled as the only search module. + foreach ($modules as $module) { + // Enable the one module and disable other ones. + $info = $module_info[$module]; + $edit = array(); + foreach ($modules as $other) { + $edit['search_active_modules[' . $other . ']'] = (($other == $module) ? $module : FALSE); + } + $edit['search_default_module'] = $module; + $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); + + // Run a search from the correct search URL. + $this->drupalGet('search/' . $info['path'] . '/' . $info['keys']); + $this->assertNoText('no results', $info['title'] . ' search found results'); + $this->assertText($info['text'], 'Correct search text found'); + + // Verify that other module search tab titles are not visible. + foreach ($modules as $other) { + if ($other != $module) { + $title = $module_info[$other]['title']; + $this->assertNoText($title, $title . ' search tab is not shown'); + } + } + + // Run a search from the search block on the node page. Verify you get + // to this module's search results page. + $terms = array('search_block_form' => $info['keys']); + $this->drupalPost('node', $terms, t('Search')); + $this->assertEqual( + $this->getURL(), + url('search/' . $info['path'] . '/' . $info['keys'], array('absolute' => TRUE)), + 'Block redirected to right search page'); + + // Try an invalid search path. Should redirect to our active module. + $this->drupalGet('search/not_a_module_path'); + $this->assertEqual( + $this->getURL(), + url('search/' . $info['path'], array('absolute' => TRUE)), + 'Invalid search path redirected to default search page'); + } + + // Test with all search modules enabled. When you go to the search + // page or run search, all modules should be shown. + $edit = array(); + foreach ($modules as $module) { + $edit['search_active_modules[' . $module . ']'] = $module; + } + $edit['search_default_module'] = 'node'; + + $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); + + foreach (array('search/node/pizza', 'search/node') as $path) { + $this->drupalGet($path); + foreach ($modules as $module) { + $title = $module_info[$module]['title']; + $this->assertText($title, $title . ' search tab is shown'); + } + } + } +} + +/** + * Tests the search_excerpt() function. + */ +class SearchExcerptTestCase extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'Search excerpt extraction', + 'description' => 'Tests that the search_excerpt() function works.', + 'group' => 'Search', + ); + } + + function setUp() { + drupal_load('module', 'search'); + parent::setUp(); + } + + /** + * Tests search_excerpt() with several simulated search keywords. + * + * Passes keywords and a sample marked up string, "The quick + * brown fox jumps over the lazy dog", and compares it to the + * correctly marked up string. The correctly marked up string + * contains either highlighted keywords or the original marked + * up string if no keywords matched the string. + */ + function testSearchExcerpt() { + // Make some text with entities and tags. + $text = 'The <strong>quick</strong> <a href="#">brown</a> fox & jumps <h2>over</h2> the lazy dog'; + // Note: The search_excerpt() function adds some extra spaces -- not + // important for HTML formatting. Remove these for comparison. + $expected = 'The quick brown fox & jumps over the lazy dog'; + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text)); + $this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string is returned when keyword is not found in short string'); + + $result = preg_replace('| +|', ' ', search_excerpt('fox', $text)); + $this->assertEqual($result, 'The quick brown <strong>fox</strong> & jumps over the lazy dog ...', 'Found keyword is highlighted'); + + $longtext = str_repeat($text . ' ', 10); + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text)); + $this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected'); + + $entities = str_repeat('készítése ', 20); + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities)); + $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt'); + $this->assertTrue(strpos($result, 'Ã') > 0, 'Entities are converted in excerpt'); + } } /** @@ -1169,3 +1449,113 @@ class SearchTokenizerTestCase extends DrupalWebTestCase { return ''; } } + +/** + * Tests that we can embed a form in search results and submit it. + */ +class SearchEmbedForm extends DrupalWebTestCase { + /** + * Node used for testing. + */ + public $node; + + /** + * Count of how many times the form has been submitted. + */ + public $submit_count = 0; + + public static function getInfo() { + return array( + 'name' => 'Embedded forms', + 'description' => 'Verifies that a form embedded in search results works', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_embedded_form'); + + // Create a user and a node, and update the search index. + $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes')); + $this->drupalLogin($test_user); + + $this->node = $this->drupalCreateNode(); + + node_update_index(); + search_update_totals(); + + // Set up a dummy initial count of times the form has been submitted. + $this->submit_count = 12; + variable_set('search_embedded_form_submitted', $this->submit_count); + $this->refreshVariables(); + } + + /** + * Tests that the embedded form appears and can be submitted. + */ + function testEmbeddedForm() { + // First verify we can submit the form from the module's page. + $this->drupalPost('search_embedded_form', + array('name' => 'John'), + t('Send away')); + $this->assertText(t('Test form was submitted'), 'Form message appears'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); + $this->submit_count = $count; + + // Now verify that we can see and submit the form from the search results. + $this->drupalGet('search/node/' . $this->node->title); + $this->assertText(t('Your name'), 'Form is visible'); + $this->drupalPost('search/node/' . $this->node->title, + array('name' => 'John'), + t('Send away')); + $this->assertText(t('Test form was submitted'), 'Form message appears'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); + $this->submit_count = $count; + + // Now verify that if we submit the search form, it doesn't count as + // our form being submitted. + $this->drupalPost('search', + array('keys' => 'foo'), + t('Search')); + $this->assertNoText(t('Test form was submitted'), 'Form message does not appear'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count, $count, 'Form submission count is correct'); + $this->submit_count = $count; + } +} + +/** + * Tests that hook_search_page runs. + */ +class SearchPageOverride extends DrupalWebTestCase { + public $search_user; + + public static function getInfo() { + return array( + 'name' => 'Search page override', + 'description' => 'Verify that hook_search_page can override search page display.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_extra_type'); + + // Login as a user that can create and search content. + $this->search_user = $this->drupalCreateUser(array('search content', 'administer search')); + $this->drupalLogin($this->search_user); + + // Enable the extra type module for searching. + variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); + menu_rebuild(); + } + + function testSearchPageHook() { + $keys = 'bike shed ' . $this->randomName(); + $this->drupalGet("search/dummy_path/{$keys}"); + $this->assertText('Dummy search snippet', 'Dummy search snippet is shown'); + $this->assertText('Test page text is here', 'Page override is working'); + } +} diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info new file mode 100644 index 0000000000000000000000000000000000000000..50de79e8aa2155dd95d1f4254eb6849cdc36376f --- /dev/null +++ b/modules/search/tests/search_embedded_form.info @@ -0,0 +1,14 @@ +; $Id: search_embedded_form.info,v 1.1 2010/08/11 14:21:39 dries Exp $ +name = "Search embedded form" +description = "Support module for search module testing of embedded forms." +package = Testing +version = VERSION +core = 7.x +files[] = search_embedded_form.module +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" +project = "drupal" +datestamp = "1284599761" + diff --git a/modules/search/tests/search_embedded_form.module b/modules/search/tests/search_embedded_form.module new file mode 100644 index 0000000000000000000000000000000000000000..2251a9b3fcb890dd14dc4c8bb31e619077e8f5a1 --- /dev/null +++ b/modules/search/tests/search_embedded_form.module @@ -0,0 +1,70 @@ +<?php +// $Id: search_embedded_form.module,v 1.1 2010/08/11 14:21:39 dries Exp $ + +/** + * @file + * Test module implementing a form that can be embedded in search results. + * + * Embedded form are important, for example, for ecommerce sites where each + * search result may included an embedded form with buttons like "Add to cart" + * for each individual product (node) listed in the search results. + */ + +/** + * Implements hook_menu(). + */ +function search_embedded_form_menu() { + $items['search_embedded_form'] = array( + 'title' => 'Search_Embed_Form', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('search_embedded_form_form'), + 'access arguments' => array('search content'), + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Builds a form for embedding in search results for testing. + * + * @see search_embedded_form_form_submit(). + */ +function search_embedded_form_form($form, &$form_state) { + $count = variable_get('search_embedded_form_submitted', 0); + + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Your name'), + '#maxlength' => 255, + '#default_value' => '', + '#required' => TRUE, + '#description' => t('Times form has been submitted: %count', array('%count' => $count)), + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Send away'), + ); + + $form['#submit'][] = 'search_embedded_form_form_submit'; + + return $form; +} + +/** + * Submit handler for search_embedded_form_form(). + */ +function search_embedded_form_form_submit($form, &$form_state) { + $count = variable_get('search_embedded_form_submitted', 0) + 1; + variable_set('search_embedded_form_submitted', $count); + drupal_set_message(t('Test form was submitted')); +} + +/** + * Adds the test form to search results. + */ +function search_embedded_form_preprocess_search_result(&$variables) { + $variables['snippet'] .= drupal_render(drupal_get_form('search_embedded_form_form')); +} diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info new file mode 100644 index 0000000000000000000000000000000000000000..cbf66ec9e72603fcf10f25e8d53e46feab221805 --- /dev/null +++ b/modules/search/tests/search_extra_type.info @@ -0,0 +1,14 @@ +; $Id: search_extra_type.info,v 1.1 2010/08/05 07:11:15 webchick Exp $ +name = "Test search type" +description = "Support module for search module testing." +package = Testing +version = VERSION +core = 7.x +files[] = search_extra_type.module +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" +project = "drupal" +datestamp = "1284599761" + diff --git a/modules/search/tests/search_extra_type.module b/modules/search/tests/search_extra_type.module new file mode 100644 index 0000000000000000000000000000000000000000..8892a84bea8aa793fd077346708af09f3ea30707 --- /dev/null +++ b/modules/search/tests/search_extra_type.module @@ -0,0 +1,70 @@ +<?php +// $Id: search_extra_type.module,v 1.3 2010/08/18 18:40:50 dries Exp $ + +/** + * @file + * Dummy module implementing a search type for search module testing. + */ + +/** + * Implements hook_search_info(). + */ +function search_extra_type_search_info() { + return array( + 'title' => 'Dummy search type', + 'path' => 'dummy_path', + 'conditions_callback' => 'search_extra_type_conditions', + ); +} + +/** + * Test conditions callback for hook_search_info(). + */ +function search_extra_type_conditions() { + $conditions = array(); + + if (!empty($_REQUEST['search_conditions'])) { + $conditions['search_conditions'] = $_REQUEST['search_conditions']; + } + return $conditions; +} + +/** + * Implements hook_search_execute(). + * + * This is a dummy search, so when search "executes", we just return a dummy + * result containing the keywords and a list of conditions. + */ +function search_extra_type_search_execute($keys = NULL, $conditions = NULL) { + if (!$keys) { + $keys = ''; + } + return array( + array( + 'link' => url('node'), + 'type' => 'Dummy result type', + 'title' => 'Dummy title', + 'snippet' => "Dummy search snippet to display. Keywords: {$keys}\n\nConditions: " . print_r($conditions, TRUE), + ), + ); +} + +/** + * Implements hook_search_page(). + * + * Adds some text to the search page so we can verify that it runs. + */ +function search_extra_type_search_page($results) { + $output['prefix']['#markup'] = '<h2>Test page text is here</h2> <ol class="search-results">'; + + foreach ($results as $entry) { + $output[] = array( + '#theme' => 'search_result', + '#result' => $entry, + '#module' => 'search_extra_type', + ); + } + $output['suffix']['#markup'] = '</ol>' . theme('pager'); + + return $output; +} diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info index 6db5b7e5c522c3ceb75a87727f15465015f9e93f..7cfa243e1b50aac142b2f0528436faf49a822be6 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/shortcut/shortcut.install b/modules/shortcut/shortcut.install index b2e19d3e0afd9fa7a07f47e8794fa65a1e0f0e9f..127500ce009dca61af40f3ef83f5ac5aa5fb5dc7 100644 --- a/modules/shortcut/shortcut.install +++ b/modules/shortcut/shortcut.install @@ -1,5 +1,5 @@ <?php -// $Id: shortcut.install,v 1.5 2010/06/17 13:16:57 dries Exp $ +// $Id: shortcut.install,v 1.6 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -63,7 +63,10 @@ function shortcut_schema() { ), 'primary key' => array('set_name'), 'foreign keys' => array( - 'set_name' => array('menu_links' => 'menu_name'), + 'menu_name' => array( + 'table' => 'menu_links', + 'columns' => array('set_name' => 'menu_name'), + ), ), ); @@ -90,8 +93,14 @@ function shortcut_schema() { 'set_name' => array('set_name'), ), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), - 'set_name' => array('shortcut_set' => 'set_name'), + 'set_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + 'set_name' => array( + 'table' => 'shortcut_set', + 'columns' => array('set_name' => 'set_name'), + ), ), ); diff --git a/modules/shortcut/shortcut.module b/modules/shortcut/shortcut.module index 464312dfdc93e8f1d60c400f57677dcc21fe5d2a..455961f1c08efad7331e079c9df1b97995db3350 100644 --- a/modules/shortcut/shortcut.module +++ b/modules/shortcut/shortcut.module @@ -1,5 +1,5 @@ <?php -// $Id: shortcut.module,v 1.25 2010/06/30 15:12:59 dries Exp $ +// $Id: shortcut.module,v 1.26 2010/08/03 23:07:01 dries Exp $ /** * @file @@ -166,6 +166,16 @@ function shortcut_menu() { return $items; } +/** + * Implements hook_admin_paths(). + */ +function shortcut_admin_paths() { + $paths = array( + 'user/*/shortcuts' => TRUE, + ); + return $paths; +} + /** * Implements hook_theme(). */ diff --git a/modules/shortcut/shortcut.test b/modules/shortcut/shortcut.test index f9c93ef3286f1acc65b775bf9cb3eb0a9d47bba2..a3fd392497a8026bb58b72abb8c110b5142d2bb5 100644 --- a/modules/shortcut/shortcut.test +++ b/modules/shortcut/shortcut.test @@ -1,5 +1,5 @@ <?php -// $Id: shortcut.test,v 1.3 2010/06/30 15:12:59 dries Exp $ +// $Id: shortcut.test,v 1.5 2010/08/05 23:53:38 webchick Exp $ /** * @file diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 6e6785d29e9c8fd4357bbdb863c0b884bf7262a9..088b39c47278edad07e2ae311edfe38e2bd9a531 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.224 2010/07/08 12:22:59 dries Exp $ +// $Id: drupal_web_test_case.php,v 1.232 2010/09/11 21:14:31 webchick Exp $ /** * Global variable that holds information about the tests being run. @@ -411,12 +411,30 @@ abstract class DrupalTestCase { return $this->assert('exception', $message, $group, $caller); } + /** + * Logs verbose message in a text file. + * + * The a link to the vebose message will be placed in the test results via + * as a passing assertion with the text '[verbose message]'. + * + * @param $message + * The verbose message to be stored. + * + * @see simpletest_verbose() + */ + protected function verbose($message) { + if ($id = simpletest_verbose($message)) { + $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html'); + $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice'); + } + } + /** * Run all tests in this class. */ public function run() { // Initialize verbose debugging. - simpletest_verbose(NULL, file_directory_path(), get_class($this)); + simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), get_class($this)); // HTTP auth settings (<username>:<password>) for the simpletest browser // when sending requests to the test site. @@ -564,11 +582,19 @@ class DrupalUnitTestCase extends DrupalTestCase { $this->skipClasses[__CLASS__] = TRUE; } + /** + * Sets up unit test environment. + * + * Unlike DrupalWebTestCase::setUp(), DrupalUnitTestCase::setUp() does not + * install modules because tests are performed without accessing the database. + * Any required files must be explicitly included by the child class setUp() + * method. + */ protected function setUp() { global $conf; // Store necessary current values before switching to the test environment. - $this->originalFileDirectory = file_directory_path(); + $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); spl_autoload_register('db_autoload'); @@ -621,6 +647,13 @@ class DrupalUnitTestCase extends DrupalTestCase { * Test case for typical Drupal tests. */ class DrupalWebTestCase extends DrupalTestCase { + /** + * The profile to install as a basis for testing. + * + * @var string + */ + protected $profile = 'standard'; + /** * The URL currently loaded in the internal browser. * @@ -896,9 +929,8 @@ class DrupalWebTestCase extends DrupalTestCase { // Copy other test files from simpletest. $original = drupal_get_path('module', 'simpletest') . '/files'; $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); - $destination_path = file_directory_path('public'); foreach ($files as $file) { - file_unmanaged_copy($file->uri, $destination_path); + file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files')); } $this->generatedTestFiles = TRUE; @@ -907,7 +939,7 @@ class DrupalWebTestCase extends DrupalTestCase { $files = array(); // Make sure type is valid. if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) { - $files = file_scan_directory(file_directory_path('public'), '/' . $type . '\-.*/'); + $files = file_scan_directory('public://', '/' . $type . '\-.*/'); // If size is set then remove any files that are not of that size. if ($size !== NULL) { @@ -1145,7 +1177,7 @@ class DrupalWebTestCase extends DrupalTestCase { // Store necessary current values before switching to prefixed database. $this->originalLanguage = $language; $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = file_directory_path(); + $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); $this->originalProfile = drupal_get_profile(); $clean_url_original = variable_get('clean_url', 0); @@ -1193,11 +1225,11 @@ class DrupalWebTestCase extends DrupalTestCase { variable_set('file_private_path', $private_files_directory); variable_set('file_temporary_path', $temp_files_directory); - // Include the default profile. - variable_set('install_profile', 'standard'); - $profile_details = install_profile_info('standard', 'en'); + // Include the testing profile. + variable_set('install_profile', $this->profile); + $profile_details = install_profile_info($this->profile, 'en'); - // Install the modules specified by the default profile. + // Install the modules specified by the testing profile. module_enable($profile_details['dependencies'], FALSE); // Install modules needed for this test. This could have been passed in as @@ -1212,22 +1244,16 @@ class DrupalWebTestCase extends DrupalTestCase { module_enable($modules, TRUE); } - // Run default profile tasks. - module_enable(array('standard'), FALSE); - - // Rebuild caches. - drupal_static_reset(); - drupal_flush_all_caches(); - - // Register actions declared by any modules. - actions_synchronize(); - - // Reload global $conf array and permissions. - $this->refreshVariables(); - $this->checkPermissions(array(), TRUE); + // Run the profile tasks. + $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( + ':name' => $this->profile, + ))->fetchField(); + if ($install_profile_module_exists) { + module_enable(array($this->profile), FALSE); + } - // Reset statically cached schema for new database prefix. - drupal_get_schema(NULL, TRUE); + // Reset/rebuild all data structures after enabling the modules. + $this->resetAll(); // Run cron once in that environment, as install.php does at the end of // the installation process. @@ -1254,9 +1280,11 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * This method is called by DrupalWebTestCase::setUp, and preloads the + * Preload the registry from the testing site. + * + * This method is called by DrupalWebTestCase::setUp(), and preloads the * registry from the testing site to cut down on the time it takes to - * setup a clean environment for the current test run. + * set up a clean environment for the current test run. */ protected function preloadRegistry() { $original_connection = Database::getConnection('default', 'simpletest_original_default'); @@ -1264,6 +1292,29 @@ class DrupalWebTestCase extends DrupalTestCase { db_query('INSERT INTO {registry_file} SELECT * FROM ' . $original_connection->prefixTables('{registry_file}')); } + /** + * Reset all data structures after having enabled new modules. + * + * This method is called by DrupalWebTestCase::setUp() after enabling + * the requested modules. It must be called again when additional modules + * are enabled later. + */ + protected function resetAll() { + // Rebuild caches. + drupal_static_reset(); + drupal_flush_all_caches(); + + // Register actions declared by any modules. + actions_synchronize(); + + // Reload global $conf array and permissions. + $this->refreshVariables(); + $this->checkPermissions(array(), TRUE); + + // Reset statically cached schema for new database prefix. + drupal_get_schema(NULL, TRUE); + } + /** * Refresh the in-memory set of variables. Useful after a page request is made * that changes a variable in a different thread. @@ -1360,7 +1411,7 @@ class DrupalWebTestCase extends DrupalTestCase { if (!isset($this->curlHandle)) { $this->curlHandle = curl_init(); - $curl_options = $this->additionalCurlOptions + array( + $curl_options = array( CURLOPT_COOKIEJAR => $this->cookieFile, CURLOPT_URL => $base_url, CURLOPT_FOLLOWLOCATION => FALSE, @@ -3046,25 +3097,6 @@ class DrupalWebTestCase extends DrupalTestCase { $this->verbose(t('Email:') . '<pre>' . print_r($mail, TRUE) . '</pre>'); } } - - /** - * Logs verbose message in a text file. - * - * The a link to the vebose message will be placed in the test results via - * as a passing assertion with the text '[verbose message]'. - * - * @param $message - * The verbose message to be stored. - * - * @see simpletest_verbose() - */ - protected function verbose($message) { - if ($id = simpletest_verbose($message)) { - $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html'); - $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice'); - } - } - } /** diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 17a5d26147c467ae408d6923bd8f67ca72e6d0ba..3c34a4ceb9feed84433cc53de4325dc01d0f6cda 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -1,4 +1,4 @@ -; $Id: simpletest.info,v 1.18 2010/06/28 02:05:47 webchick Exp $ +; $Id: simpletest.info,v 1.22 2010/09/13 05:50:09 webchick Exp $ name = Testing description = Provides a framework for unit and functional testing. package = Core @@ -39,9 +39,12 @@ files[] = tests/unicode.test files[] = tests/update.test files[] = tests/xmlrpc.test files[] = tests/upgrade/upgrade.test +files[] = tests/upgrade/upgrade.comment.test +files[] = tests/upgrade/upgrade.node.test +files[] = tests/upgrade/upgrade.taxonomy.test -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/simpletest.install b/modules/simpletest/simpletest.install index 2805a9d030c7ccf68c3851003bfb945fafef1246..135de030a55916e2679cc15f77d4b7397b3b0fb0 100644 --- a/modules/simpletest/simpletest.install +++ b/modules/simpletest/simpletest.install @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.install,v 1.35 2010/03/26 17:14:45 dries Exp $ +// $Id: simpletest.install,v 1.37 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -20,12 +20,12 @@ function simpletest_uninstall() { variable_del('simpletest_verbose'); // Remove generated files. - $path = file_directory_path() . '/simpletest'; + $path = 'public://simpletest'; $files = file_scan_directory($path, '/.*/'); foreach ($files as $file) { file_unmanaged_delete($file->uri); } - rmdir($path); + drupal_rmdir($path); } /** diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index 228a1dfb41d7c56b13ce39cf5ec2c96d795c9d64..10fc6fbb75f339d0baf0a055e3368fc777cc4717 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.module,v 1.91 2010/03/30 07:17:19 webchick Exp $ +// $Id: simpletest.module,v 1.93 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -90,19 +90,6 @@ function simpletest_theme() { ); } -/** - * Implements hook_stream_wrappers(). - */ -function simpletest_test_stream_wrappers() { - return array( - 'simpletest' => array( - 'name' => t('Simpletest files'), - 'class' => 'DrupalSimpleTestStreamWrapper', - 'description' => t('Stream Wrapper for Simpletest files.'), - ), - ); -} - /** * Implements hook_js_alter(). */ @@ -144,7 +131,7 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') { ->execute(); // Clear out the previous verbose files. - file_unmanaged_delete_recursive(file_directory_path() . '/simpletest/verbose'); + file_unmanaged_delete_recursive('public://simpletest/verbose'); // Get the info for the first test being run. $first_test = array_shift($test_list); @@ -419,7 +406,7 @@ function simpletest_generate_file($filename, $width, $lines, $type = 'binary-tex $text = wordwrap($text, $width - 1, "\n", TRUE) . "\n"; // Add \n for symetrical file. // Create filename. - file_put_contents(file_directory_path() . '/' . $filename . '.txt', $text); + file_put_contents('public://' . $filename . '.txt', $text); return $filename; } diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc index 2a0844a9e07f1e5f3f2a06df7a6865056a1196f1..6030f2c47a76e064d61838403c4332eb9998e001 100644 --- a/modules/simpletest/simpletest.pages.inc +++ b/modules/simpletest/simpletest.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.pages.inc,v 1.30 2010/05/19 19:22:24 dries Exp $ +// $Id: simpletest.pages.inc,v 1.31 2010/07/24 17:28:26 dries Exp $ /** * @file @@ -344,7 +344,7 @@ function simpletest_result_form_submit($form, &$form_state) { if ($form_state['values']['filter'] == 'all') { $classes = array_merge($pass, $fail); } - else if ($form_state['values']['filter'] == 'pass') { + elseif ($form_state['values']['filter'] == 'pass') { $classes = $pass; } else { diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test index 32c44ebf2b4fda68e100eda972b17bd7b1c86641..252da04e1a5f544bcb483e846282df3e8a710959 100644 --- a/modules/simpletest/simpletest.test +++ b/modules/simpletest/simpletest.test @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.test,v 1.43 2010/06/28 19:57:34 dries Exp $ +// $Id: simpletest.test,v 1.45 2010/09/01 20:08:17 dries Exp $ class SimpleTestFunctionalTest extends DrupalWebTestCase { /** @@ -154,7 +154,7 @@ class SimpleTestFunctionalTest extends DrupalWebTestCase { $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - // Check that a warning is catched by simpletest. + // Check that a warning is caught by simpletest. $this->assertAssertion('Division by zero', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); // Check that the backtracing code works for specific assert function. @@ -433,11 +433,13 @@ class SimpleTestFolderTestCase extends DrupalWebTestCase { ); } + function setUp() { + return parent::setUp('image'); + } + function testFolderSetup() { - if (module_exists('image')) { - $path = file_directory_path() . '/styles'; - $this->assertTrue(file_prepare_directory($path, FALSE), "Directory created."); - } + $directory = file_default_scheme() . '://styles'; + $this->assertTrue(file_prepare_directory($directory, FALSE), "Directory created."); } } diff --git a/modules/simpletest/tests/actions.test b/modules/simpletest/tests/actions.test index 8e8f176e2f7a70914c93fd9df75c5443f49ddf04..940b994127afea76aa1f5a9d8a941d3db2e9bd73 100644 --- a/modules/simpletest/tests/actions.test +++ b/modules/simpletest/tests/actions.test @@ -1,5 +1,5 @@ <?php -// $Id: actions.test,v 1.16 2010/05/26 11:26:49 dries Exp $ +// $Id: actions.test,v 1.18 2010/08/05 23:53:38 webchick Exp $ class ActionsConfigurationTestCase extends DrupalWebTestCase { public static function getInfo() { diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info index 65b5abb5066af6cbbe534bbf27da49581b98e4cd..94bb49e8712fd97443ec41e0828b1682ee054b57 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test index 2e10b80f18a53044271439bddaa853f434377ea5..232074f22e19ee3cb1ddefc420fc7ced029d0693 100644 --- a/modules/simpletest/tests/ajax.test +++ b/modules/simpletest/tests/ajax.test @@ -1,5 +1,5 @@ <?php -// $Id: ajax.test,v 1.11 2010/04/30 08:07:55 dries Exp $ +// $Id: ajax.test,v 1.14 2010/08/05 23:53:38 webchick Exp $ class AJAXTestCase extends DrupalWebTestCase { function setUp() { @@ -53,7 +53,7 @@ class AJAXFrameworkTestCase extends AJAXTestCase { * Test behavior of ajax_render_error(). */ function testAJAXRenderError() { - $result = $this->drupalGetAJAX('ajax-test/render-error'); + $result = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error')); // Verify default error message. $this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.')); $this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.')); @@ -61,7 +61,7 @@ class AJAXFrameworkTestCase extends AJAXTestCase { $edit = array( 'message' => 'Custom error message.', ); - $result = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit)); + $result = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit))); $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.')); } } diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info index ab46b57bdd9a2c4bb3f40bdee0467733b3868811..2da1410f14199c0e2a2482ed6a91f980b2e45655 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info index b4d74d1639715905c47f35c293861ab87b9fb7c9..d2691e8d8e63ea32352227f95515b9e4cbe31617 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/batch.test b/modules/simpletest/tests/batch.test index 5c11f7269c457174ae93414e167253b6ad50f7db..0e0ee2e5b54c847a67ad7433a860887318f4957c 100644 --- a/modules/simpletest/tests/batch.test +++ b/modules/simpletest/tests/batch.test @@ -1,5 +1,5 @@ <?php -// $Id: batch.test,v 1.9 2010/05/06 05:59:31 webchick Exp $ +// $Id: batch.test,v 1.14 2010/09/04 13:33:53 dries Exp $ /** * @file @@ -274,7 +274,7 @@ class BatchPageTestCase extends DrupalWebTestCase { function testBatchProgressPageTheme() { // Make sure that the page which starts the batch (an administrative page) // is using a different theme than would normally be used by the batch API. - variable_set('theme_default', 'garland'); + variable_set('theme_default', 'bartik'); variable_set('admin_theme', 'seven'); // Visit an administrative page that runs a test batch, and check that the // theme that was used during batch execution (which the batch callback @@ -340,7 +340,7 @@ class BatchPercentagesUnitTestCase extends DrupalUnitTestCase { // 19999/20000 should add yet another digit and go to 99.995%. '99.995' => array('total' => 20000, 'current' => 19999), ); - + require_once DRUPAL_ROOT . '/includes/batch.inc'; parent::setUp(); } @@ -348,7 +348,6 @@ class BatchPercentagesUnitTestCase extends DrupalUnitTestCase { * Test the _batch_api_percentage() function. */ function testBatchPercentages() { - require_once DRUPAL_ROOT . '/includes/batch.inc'; foreach ($this->testCases as $expected_result => $arguments) { // PHP sometimes casts numeric strings that are array keys to integers, // cast them back here. diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info index 1822040f79487a608bc657591a001dfbccab6835..cf5a8b3b83fe45d5279bb45b6254aa085383d84f 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test index 95d724482d519435289d6250e73a27355ac20268..6d4bfb966b19cabc6a85889aee3eaa1a21ee49f9 100644 --- a/modules/simpletest/tests/bootstrap.test +++ b/modules/simpletest/tests/bootstrap.test @@ -1,5 +1,5 @@ <?php -// $Id: bootstrap.test,v 1.31 2010/06/14 13:24:32 dries Exp $ +// $Id: bootstrap.test,v 1.33 2010/08/05 23:53:38 webchick Exp $ class BootstrapIPAddressTestCase extends DrupalWebTestCase { diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test index d5a9c5f5008dc53b8c8ebd2c2ed104b8a765d4a3..c533722af97b3e5f87f289704f3d42c2f4dc255e 100644 --- a/modules/simpletest/tests/cache.test +++ b/modules/simpletest/tests/cache.test @@ -1,5 +1,5 @@ <?php -// $Id: cache.test,v 1.10 2009/09/13 17:49:51 dries Exp $ +// $Id: cache.test,v 1.12 2010/08/05 23:53:38 webchick Exp $ class CacheTestCase extends DrupalWebTestCase { protected $default_bin = 'cache'; diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index e81a95039d84a9c866aaefd63474c2502cc51f16..93ec55a9bfa5c4e93581ed584f8fd08f585e25df 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -1,5 +1,5 @@ <?php -// $Id: common.test,v 1.116 2010/07/07 17:00:43 webchick Exp $ +// $Id: common.test,v 1.125 2010/09/09 20:22:00 dries Exp $ /** * @file @@ -23,10 +23,10 @@ class DrupalAlterTestCase extends DrupalWebTestCase { } function testDrupalAlter() { - // This test depends on Garland, so make sure that it is always the current + // This test depends on Bartik, so make sure that it is always the current // active theme. global $theme, $base_theme_info; - $theme = 'garland'; + $theme = 'bartik'; $base_theme_info = array(); $array = array('foo' => 'bar'); @@ -268,6 +268,14 @@ class CommonURLUnitTest extends DrupalWebTestCase { $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); $this->assertEqual($url, $result, "$url == $result"); + $url = $base . '?q=node/123&foo#0'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + $url = $base; $result = url('<front>', array('absolute' => $absolute)); $this->assertEqual($url, $result, "$url == $result"); @@ -357,6 +365,9 @@ class CommonXssUnitTest extends DrupalUnitTestCase { // Ignore PHP 5.3+ invalid multibyte sequence warning. $text = @check_plain("Foo\xC0barbaz"); $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"'); + // Ignore PHP 5.3+ invalid multibyte sequence warning. + $text = @check_plain("\xc2\""); + $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""'); $text = check_plain("Fooÿñ"); $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"'); $text = filter_xss("Foo\xC0barbaz"); @@ -371,6 +382,8 @@ class CommonXssUnitTest extends DrupalUnitTestCase { function testEscaping() { $text = check_plain("<script>"); $this->assertEqual($text, '<script>', 'check_plain() escapes <script>'); + $text = check_plain('<>&"\''); + $this->assertEqual($text, '<>&"'', 'check_plain() escapes reserved HTML characters.'); } /** @@ -567,6 +580,27 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { $this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.')); } + /** + * Test that stylesheets in module .info files are loaded. + */ + function testModuleInfo() { + $this->drupalGet(''); + + // Verify common_test.css in a STYLE media="all" tag. + $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array( + ':media' => 'all', + ':filename' => 'tests/common_test.css', + )); + $this->assertTrue(count($elements), "Stylesheet with media 'all' in module .info file found."); + + // Verify common_test.print.css in a STYLE media="print" tag. + $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array( + ':media' => 'print', + ':filename' => 'tests/common_test.print.css', + )); + $this->assertTrue(count($elements), "Stylesheet with media 'print' in module .info file found."); + } + /** * Tests adding a file stylesheet. */ @@ -621,7 +655,7 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { function testRenderInlinePreprocess() { $css = 'body { padding: 0px; }'; $css_preprocessed = '<style type="text/css" media="all">' . drupal_load_stylesheet_content($css, TRUE) . '</style>'; - drupal_add_css($css, 'inline'); + drupal_add_css($css, array('type' => 'inline', 'preprocess' => TRUE)); $styles = drupal_get_css(); $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.')); } @@ -631,7 +665,7 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { */ function testRenderInlineNoPreprocess() { $css = 'body { padding: 0px; }'; - drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE)); + drupal_add_css($css, array('type' => 'inline')); $styles = drupal_get_css(); $this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.')); } @@ -641,7 +675,7 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { */ function testRenderInlineFullPage() { $css = 'body { font-size: 254px; }'; - $expected = 'font-size:254px;'; + $expected = $css; // Create a node, using the PHP filter that tests drupal_add_css(). $php_format_id = db_query_range('SELECT format FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'PHP code'))->fetchField(); @@ -741,8 +775,9 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase { */ function testAddCssFileWithQueryString() { $this->drupalGet('common-test/query-string'); - $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1); - $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?arg1=value1&arg2=value2&' . $query_string, t('Query string was appended correctly to css.')); + $query_string = variable_get('css_js_query_string', '0'); + $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, t('Query string was appended correctly to css.')); + $this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2', t('Query string not escaped on a URI.')); } } @@ -1346,8 +1381,8 @@ class JavaScriptTestCase extends DrupalWebTestCase { */ function testAddJsFileWithQueryString() { $this->drupalGet('common-test/query-string'); - $query_string = substr(variable_get('css_js_query_string', '0'), 0, 1); - $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?arg1=value1&arg2=value2&' . $query_string, t('Query string was appended correctly to js.')); + $query_string = variable_get('css_js_query_string', '0'); + $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, t('Query string was appended correctly to js.')); } } @@ -1982,7 +2017,7 @@ class DrupalGetRdfNamespacesTestCase extends DrupalWebTestCase { $xml = new SimpleXMLElement($this->content); $ns = $xml->getDocNamespaces(); - $this->assertEqual($ns['owl'], 'http://www.w3.org/2002/07/owl#', t('A prefix declared once is displayed.')); + $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', t('A prefix declared once is displayed.')); $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', t('The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.')); $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', t('Two prefixes can be assigned the same namespace.')); $this->assertTrue(!isset($ns['dc']), t('A prefix with conflicting namespaces is discarded.')); diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info index 5b51ca75e94097af19961c7ce7aaf703efb99397..8996c0b833371c7bd86d055421e93cd6a30ab925 100644 --- a/modules/simpletest/tests/common_test.info +++ b/modules/simpletest/tests/common_test.info @@ -1,14 +1,16 @@ -; $Id: common_test.info,v 1.1 2009/07/02 04:27:23 webchick Exp $ +; $Id: common_test.info,v 1.2 2010/09/05 02:21:38 dries Exp $ name = "Common Test" description = "Support module for Common tests." package = Testing version = VERSION core = 7.x files[] = common_test.module +stylesheets[all][] = common_test.css +stylesheets[print][] = common_test.print.css hidden = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/common_test.module b/modules/simpletest/tests/common_test.module index 4dc29b1c84eb06e5712f5fec12a0a32bef1b10f3..1671f5e08138a78c9862f10e21f108e92a6f684e 100644 --- a/modules/simpletest/tests/common_test.module +++ b/modules/simpletest/tests/common_test.module @@ -1,5 +1,5 @@ <?php -// $Id: common_test.module,v 1.11 2010/05/18 06:59:46 dries Exp $ +// $Id: common_test.module,v 1.13 2010/08/17 21:31:13 dries Exp $ /** * @file @@ -133,12 +133,12 @@ function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { } /** - * Implements hook_TYPE_alter() on behalf of Garland theme. + * Implements hook_TYPE_alter() on behalf of Bartik theme. * * Same as common_test_drupal_alter_alter(), but here, we verify that themes * can also alter and come last. */ -function garland_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { +function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { // Alter first argument. if (is_array($data)) { $data['foo'] .= ' theme'; @@ -220,7 +220,9 @@ function common_test_library() { * Adds a JavaScript file and a CSS file with a query string appended. */ function common_test_js_and_css_querystring() { - drupal_add_js(drupal_get_path('module', 'node') . '/node.js?arg1=value1&arg2=value2'); - drupal_add_css(drupal_get_path('module', 'node') . '/node.css?arg1=value1&arg2=value2'); + drupal_add_js(drupal_get_path('module', 'node') . '/node.js'); + drupal_add_css(drupal_get_path('module', 'node') . '/node.css'); + // A relative URI may have a query string. + drupal_add_css('/' . drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2'); return ''; } diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info index 9768bc73bf91f0e9993f56abb19348f590dca6a5..81a4af64b3913160738fd0bf9bd39b09bba28a21 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/database_test.install b/modules/simpletest/tests/database_test.install index 378ecb9b311ae8791aab27ea8b68e7fd873458e4..08db2e3c6c1ed72d293a206e6a6ece9f15f29bb9 100644 --- a/modules/simpletest/tests/database_test.install +++ b/modules/simpletest/tests/database_test.install @@ -1,5 +1,5 @@ <?php -// $Id: database_test.install,v 1.12 2010/06/25 17:47:22 dries Exp $ +// $Id: database_test.install,v 1.13 2010/07/28 10:52:12 dries Exp $ /** * @file @@ -156,22 +156,6 @@ function database_test_schema() { 'primary key' => array('tid'), ); - $schema['test_date'] = array( - 'description' => 'A simple table including a datetime field for testing datetime behavior.', - 'fields' => array( - 'id' => array( - 'description' => 'Simple unique ID.', - 'type' => 'serial', - 'not null' => TRUE, - ), - 'dummy_date' => array( - 'description' => 'A dummy datetime field.', - 'type' => 'datetime', - ), - ), - 'primary key' => array('id'), - ); - $schema['test_null'] = array( 'description' => 'Basic test table for NULL value handling.', 'fields' => array( diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index e97435ba7e42092ec0776635aceaf48a6c263189..38c1fa9f600d6ca0250486bd36d3577a7762d356 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.93 2010/05/28 10:13:38 dries Exp $ +// $Id: database_test.test,v 1.101 2010/09/09 23:18:30 webchick Exp $ /** * Dummy class for fetching into a class. @@ -17,6 +17,7 @@ class FakeRecord { } * here. */ class DatabaseTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; function setUp() { parent::setUp('database_test'); @@ -1543,6 +1544,16 @@ class DatabaseSelectTestCase extends DatabaseTestCase { sort($sorted_ids_second_set); $this->assertEqual($sorted_ids_second_set, $sorted_ids, t('After sorting the second random list, the result matches the sorted version of the first random list.')); } + + /** + * Test that aliases are renamed when duplicates. + */ + function testSelectDuplicateAlias() { + $query = db_select('test', 't'); + $alias1 = $query->addField('t', 'name', 'the_alias'); + $alias2 = $query->addField('t', 'age', 'the_alias'); + $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.'); + } } /** @@ -1976,6 +1987,19 @@ class DatabaseSelectComplexTestCase extends DatabaseTestCase { $this->assertEqual($count, 6, t('Counted the correct number of records.')); } + /** + * Test that we can generate a count query from a query with GROUP BY. + */ + function testCountQueryGroupBy() { + $query = db_select('test_task'); + $pid_field = $query->addField('test_task', 'pid'); + $query->groupBy('pid'); + + $count = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($count, 3, t('Counted the correct number of records.')); + } + /** * Confirm that we can properly nest conditional clauses. */ @@ -2006,6 +2030,72 @@ class DatabaseSelectComplexTestCase extends DatabaseTestCase { $this->assertEqual($crowded_job->job, $crowded_job->otherjob, t('Correctly joined same table twice.')); $this->assertNotEqual($crowded_job->name, $crowded_job->othername, t('Correctly joined same table twice.')); } + +} + +/** + * Test more complex select statements, part 2. + */ +class DatabaseSelectComplexTestCase2 extends DatabaseTestCase { + + public static function getInfo() { + return array( + 'name' => 'Select tests, complex 2', + 'description' => 'Test the Select query builder with even more complex queries.', + 'group' => 'Database', + ); + } + + function setUp() { + DrupalWebTestCase::setUp('database_test', 'node_access_test'); + + $schema['test'] = drupal_get_schema('test'); + $schema['test_people'] = drupal_get_schema('test_people'); + $schema['test_one_blob'] = drupal_get_schema('test_one_blob'); + $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs'); + $schema['test_task'] = drupal_get_schema('test_task'); + + $this->installTables($schema); + + $this->addSampleData(); + } + + /** + * Test that we can join on a query. + */ + function testJoinSubquery() { + $acct = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($acct); + + $query = db_select('test_task', 'tt', array('target' => 'slave')); + $query->addExpression('tt.pid + 1', 'abc'); + $query->condition('priority', 1, '>'); + $query->condition('priority', 100, '<'); + + $subquery = db_select('test', 'tp'); + $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id'); + $subquery->join('node', 'n', 'tp.id = n.nid'); + $subquery->addTag('node_access'); + $subquery->addMetaData('account', $acct); + $subquery->addField('tp', 'id'); + $subquery->condition('age', 5, '>'); + $subquery->condition('age', 500, '<'); + + $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id'); + $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id'); + + // Construct the query string. + // This is the same sequence that SelectQuery::execute() goes through. + $query->preExecute(); + $query->getArguments(); + $str = (string) $query; + + // Verify that the string only has one copy of condition placeholder 0. + debug($str); + $pos = strpos($str, 'db_condition_placeholder_0', 0); + $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1); + $this->assertFalse($pos2, "Condition placeholder is not repeated"); + } } class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase { @@ -2126,6 +2216,43 @@ class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase { ->fetchCol(); $this->assertEqual($ages, array('George', 'Ringo'), t('Pager query with having expression returned the correct ages.')); } + + /** + * Confirm that every pager gets a valid non-overlaping element ID. + */ + function testElementNumbers() { + $_GET['page'] = '3, 2, 1, 0'; + + $name = db_select('test', 't')->extend('PagerDefault') + ->element(2) + ->fields('t', array('name')) + ->orderBy('age') + ->limit(1) + ->execute() + ->fetchField(); + $this->assertEqual($name, 'Paul', t('Pager query #1 with a specified element ID returned the correct results.')); + + // Setting an element smaller than the previous one + // should not overwrite the pager $maxElement with a smaller value. + $name = db_select('test', 't')->extend('PagerDefault') + ->element(1) + ->fields('t', array('name')) + ->orderBy('age') + ->limit(1) + ->execute() + ->fetchField(); + $this->assertEqual($name, 'George', t('Pager query #2 with a specified element ID returned the correct results.')); + + $name = db_select('test', 't')->extend('PagerDefault') + ->fields('t', array('name')) + ->orderBy('age') + ->limit(1) + ->execute() + ->fetchField(); + $this->assertEqual($name, 'John', t('Pager query #3 with a generated element ID returned the correct results.')); + + unset($_GET['page']); + } } @@ -3096,109 +3223,6 @@ class DatabaseTransactionTestCase extends DatabaseTestCase { } -/** - * Test proposed new data types for the schema API. - */ -class DatabaseExtraTypesTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Extra Types tests', - 'description' => 'Test the Extra Types.', - 'group' => 'Database', - ); - } - - - /** - * Test the date data type. - */ - function testDateField() { - try { - $date_table = array( - 'fields' => array( - 'date_field' => array( - 'description' => t('Test Date field'), - 'type' => 'date', - 'not null' => FALSE, - ), - ), - ); - db_create_table('date_table', $date_table); - $this->assertTrue(db_table_exists('date_table'), t('Created table with date field')); - - db_insert('date_table')->fields(array('date_field')) - ->values(array('date_field' => '2001-01-01')) - ->values(array('date_field' => '1856-12-31')) - ->values(array('date_field' => '2100-06-30')) - ->execute(); - - $num_records = (int) db_query('SELECT COUNT(*) FROM {date_table}')->fetchField(); - $this->assertEqual($num_records, 3, t('Inserted 3 records, and counted 3 records')); - $res = db_query('SELECT date_field from {date_table} ORDER BY date_field'); - - $date = $res->fetch()->date_field; - $this->assertEqual($date, '1856-12-31', t('Date retrieved in order @date', array('@date' => $date))); - $date = $res->fetch()->date_field; - $this->assertEqual($date, '2001-01-01', t('Date retrieved in order @date', array('@date' => $date))); - $date = $res->fetch()->date_field; - $this->assertEqual($date, '2100-06-30', t('Date retrieved in order @date', array('@date' => $date))); - - - db_drop_table('date_table'); - $this->assertFalse(db_table_exists('date_table'), t('Dropped table with date field')); - - } catch (Exception $e) { - $this->fail($e->getMessage()); - } - - } - - /** - * Test the time data type. - */ - function testTimeField() { - try { - $time_table = array( - 'fields' => array( - 'time_field' => array( - 'description' => t('Test Time field'), - 'type' => 'time', - 'not null' => FALSE, - ), - ), - ); - db_create_table('time_table', $time_table); - $this->assertTrue(db_table_exists('time_table'), t('Created table with time field')); - - db_insert('time_table')->fields(array('time_field')) - ->values(array('time_field' => '12:59:00')) - ->values(array('time_field' => '00:01:00')) - ->values(array('time_field' => '23:17:00')) - ->execute(); - - $num_records = (int) db_query('SELECT COUNT(*) FROM {time_table}')->fetchField(); - $this->assertEqual($num_records, 3, t('Inserted 3 records, and counted 3 records')); - $res = db_query('SELECT time_field from {time_table} ORDER BY time_field'); - - $time = $res->fetch()->time_field; - $this->assertEqual($time, '00:01:00', t('Time retrieved in order @time', array('@time' => $time))); - $time = $res->fetch()->time_field; - $this->assertEqual($time, '12:59:00', t('Time retrieved in order @time', array('@time' => $time))); - $time = $res->fetch()->time_field; - $this->assertEqual($time, '23:17:00', t('Time retrieved in order @time', array('@time' => $time))); - - db_drop_table('time_table'); - $this->assertFalse(db_table_exists('time_table'), t('Dropped table with time field')); - } catch (Exception $e) { - $this->fail($e->getMessage()); - } - - } - -} - - /** * Check the sequences API. */ diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/simpletest/tests/entity_cache_test.info index ea9c2be73c3b35f330eeb0dfed9bdf60a9b64c64..1cfdb47107dff74b8ee450e6ed587492bd1f1d95 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/simpletest/tests/entity_cache_test_dependency.info index 757efda01a9b82caccdd62260c95285e511d5a88..bab6ea2206aa1d2e21b8686eb1ff7e3f029d63f3 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/simpletest/tests/entity_cache_test_dependency.module index 387921e80d1d277ed6541c881f133cb26701585a..31b7d384b41e7e53f518b4964ddb7d7fcc5e5fa4 100644 --- a/modules/simpletest/tests/entity_cache_test_dependency.module +++ b/modules/simpletest/tests/entity_cache_test_dependency.module @@ -1,5 +1,5 @@ <?php -// $Id: entity_cache_test_dependency.module,v 1.1 2010/04/18 15:01:56 webchick Exp $ +// $Id: entity_cache_test_dependency.module,v 1.2 2010/09/15 04:34:27 webchick Exp $ /** * @file @@ -13,7 +13,6 @@ function entity_cache_test_dependency_entity_info() { return array( 'entity_cache_test' => array( 'label' => 'Entity Cache Test', - 'base table' => 'entity_cache_test', ), ); } diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test index 39a176f5998eb9ebe1e596c2da6b58c938a92ab1..638a3fb72b5dcb70f52153c3e925a36c177e0328 100644 --- a/modules/simpletest/tests/entity_query.test +++ b/modules/simpletest/tests/entity_query.test @@ -1,6 +1,6 @@ <?php -// $Id: entity_query.test,v 1.5 2010/06/26 02:16:23 dries Exp $ +// $Id: entity_query.test,v 1.11 2010/09/13 06:03:21 webchick Exp $ /** * @file @@ -23,9 +23,9 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { function setUp() { parent::setUp(array('field_test')); - field_attach_create_bundle('bundle1', 'test_entity_bundle_key'); - field_attach_create_bundle('bundle2', 'test_entity_bundle_key'); - field_attach_create_bundle('test_bundle', 'test_entity'); + field_attach_create_bundle('test_entity_bundle_key', 'bundle1'); + field_attach_create_bundle('test_entity_bundle_key', 'bundle2'); + field_attach_create_bundle('test_entity', 'test_bundles'); field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle'); $instances = array(); @@ -59,6 +59,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $instances[0]['bundle'] = 'bundle1'; $instances[0]['entity_type'] = 'test_entity_bundle_key'; field_create_instance($instances[0]); + $instances[0]['bundle'] = 'bundle2'; + field_create_instance($instances[0]); $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle'; field_create_instance($instances[0]); @@ -87,7 +89,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $instances[1] = $instance; - // Add an instance to that bundle. + // Add a field instance to the bundles. $instances[1]['bundle'] = 'bundle1'; $instances[1]['entity_type'] = 'test_entity_bundle_key'; field_create_instance($instances[1]); @@ -259,6 +261,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 1), array('test_entity', 2), array('test_entity', 3), @@ -291,6 +295,35 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 1), ), t('Test sort entity entity_id in descending order.'), TRUE); + + // Test entity sort by entity_id, with a field condition. + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->entityOrderBy('entity_id', 'ASC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 1), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), + ), t('Test sort entity entity_id in ascending order, with a field condition.'), TRUE); + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->propertyOrderBy('ftid', 'DESC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 1), + ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE); // Test property sort by entity id. $query = new EntityFieldQuery(); @@ -318,12 +351,71 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 1), ), t('Test sort entity entity_id property in descending order.'), TRUE); + + // Test property sort by entity id, with a field condition. + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->propertyOrderBy('ftid', 'ASC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 1), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), + ), t('Test sort entity entity_id property in ascending order, with a field condition.'), TRUE); + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->propertyOrderBy('ftid', 'DESC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 1), + ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE); // Test entity sort by bundle. $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') ->entityOrderBy('bundle', 'ASC') + ->propertyOrderBy('ftid', 'DESC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 1), + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 5), + ), t('Test sort entity bundle in ascending order.'), TRUE); + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->entityOrderBy('bundle', 'DESC') + ->propertyOrderBy('ftid', 'ASC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 1), + array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 3), + array('test_entity_bundle_key', 4), + ), t('Test sort entity bundle in descending order.'), TRUE); + + // Test entity sort by bundle, with a field condition. + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->entityOrderBy('bundle', 'ASC') ->propertyOrderBy('ftid', 'ASC'); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 1), @@ -332,11 +424,12 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 4), array('test_entity_bundle_key', 5), array('test_entity_bundle_key', 6), - ), t('Test sort entity bundle in ascending order.'), TRUE); + ), t('Test sort entity bundle in ascending order, with a field condition.'), TRUE); $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($this->fields[0], 'value', 0, '>') ->entityOrderBy('bundle', 'DESC') ->propertyOrderBy('ftid', 'DESC'); $this->assertEntityFieldQuery($query, array( @@ -346,7 +439,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 1), - ), t('Test sort entity bundle in descending order.'), TRUE); + ), t('Test sort entity bundle in descending order, with a field condition.'), TRUE); // Test entity sort by revision_id. $query = new EntityFieldQuery(); @@ -370,6 +463,31 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity', 2), array('test_entity', 1), ), t('Test sort entity revision_id in descending order.'), TRUE); + + // Test entity sort by revision_id, with a field condition. + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->entityOrderBy('revision_id', 'ASC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity', 1), + array('test_entity', 2), + array('test_entity', 3), + array('test_entity', 4), + ), t('Test sort entity revision_id in ascending order, with a field condition.'), TRUE); + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->entityOrderBy('revision_id', 'DESC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity', 4), + array('test_entity', 3), + array('test_entity', 2), + array('test_entity', 1), + ), t('Test sort entity revision_id in descending order, with a field condition.'), TRUE); // Test property sort by revision_id. $query = new EntityFieldQuery(); @@ -394,6 +512,31 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity', 1), ), t('Test sort entity revision_id property in descending order.'), TRUE); + // Test property sort by revision_id, with a field condition. + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->propertyOrderBy('ftvid', 'ASC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity', 1), + array('test_entity', 2), + array('test_entity', 3), + array('test_entity', 4), + ), t('Test sort entity revision_id property in ascending order, with a field condition.'), TRUE); + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity') + ->fieldCondition($this->fields[0], 'value', 0, '>') + ->propertyOrderBy('ftvid', 'DESC'); + $this->assertEntityFieldQuery($query, array( + array('test_entity', 4), + array('test_entity', 3), + array('test_entity', 2), + array('test_entity', 1), + ), t('Test sort entity revision_id property in descending order, with a field condition.'), TRUE); + $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') @@ -403,6 +546,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), ), t('Test sort field in ascending order without field condition.'), TRUE); $query = new EntityFieldQuery(); @@ -410,6 +555,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { ->entityCondition('entity_type', 'test_entity_bundle_key') ->fieldOrderBy($this->fields[0], 'value', 'DESC'); $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 5), array('test_entity_bundle_key', 4), array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 2), @@ -420,20 +567,24 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $query ->entityCondition('entity_type', 'test_entity_bundle_key') ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'asc'); + ->fieldOrderBy($this->fields[0], 'value', 'ASC'); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 1), array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), ), t('Test sort field in ascending order.'), TRUE); $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'desc'); + ->fieldOrderBy($this->fields[0], 'value', 'DESC'); $this->assertEntityFieldQuery($query, array( + array('test_entity_bundle_key', 6), + array('test_entity_bundle_key', 5), array('test_entity_bundle_key', 4), array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 2), @@ -546,6 +697,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { array('test_entity_bundle_key', 1), array('test_entity_bundle_key', 2), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 1), array('test_entity', 2), array('test_entity', 4), @@ -598,6 +751,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 3), array('test_entity', 4), ), t('Test the "greater than" operation on a field.')); @@ -617,6 +772,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 3), array('test_entity', 4), ), t('Test the "greater than or equal to" operation on a field.')); @@ -637,6 +794,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 1), array('test_entity_bundle_key', 2), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 1), array('test_entity', 2), ), t('Test the "not in" operation on a field.')); @@ -738,7 +897,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'asc') + ->propertyOrderBy('ftid', 'ASC') ->range(0, 2); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 1), @@ -749,7 +908,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $query ->entityCondition('entity_type', 'test_entity_bundle_key') ->fieldCondition($this->fields[0], 'value', 0, '>=') - ->fieldOrderBy($this->fields[0], 'value', 'asc') + ->fieldOrderBy($this->fields[0], 'value', 'ASC') ->range(0, 2); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 1), @@ -759,7 +918,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $query = new EntityFieldQuery(); $query ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'asc') + ->propertyOrderBy('ftid', 'ASC') ->range(4, 6); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 5), @@ -770,11 +929,13 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $query ->entityCondition('entity_type', 'test_entity_bundle_key') ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'asc') + ->fieldOrderBy($this->fields[0], 'value', 'ASC') ->range(2, 4); $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), ), t('Test offset on a field.'), TRUE); for ($i = 6; $i < 10; $i++) { @@ -790,6 +951,8 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->assertEntityFieldQuery($query, array( array('test_entity_bundle_key', 3), array('test_entity_bundle_key', 4), + array('test_entity_bundle_key', 5), + array('test_entity_bundle_key', 6), array('test_entity', 3), array('test_entity', 4), array('test_entity_bundle', 8), diff --git a/modules/simpletest/tests/error.test b/modules/simpletest/tests/error.test index b0d1991c4f25f67bfe754b869f50136e4432474e..2ebecb28d0c9bba2428af52052f16b39915e1fcc 100644 --- a/modules/simpletest/tests/error.test +++ b/modules/simpletest/tests/error.test @@ -1,5 +1,5 @@ <?php -// $Id: error.test,v 1.6 2009/08/17 19:14:41 webchick Exp $ +// $Id: error.test,v 1.8 2010/08/05 23:53:38 webchick Exp $ /** * Tests Drupal error and exception handlers. diff --git a/modules/simpletest/tests/error_test.info b/modules/simpletest/tests/error_test.info index fb225f3a877845f15863dae2dd1c6f5b9f4a7023..e079762e0c421ca433edee09c4d0044adb4a082a 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test index 192674f2222fd2365cfe5e11896c3727678a25f7..ea53dcd18f19ae8f521d5feb2d0168456433a60a 100644 --- a/modules/simpletest/tests/file.test +++ b/modules/simpletest/tests/file.test @@ -1,5 +1,5 @@ <?php -// $Id: file.test,v 1.58 2010/06/30 22:37:49 webchick Exp $ +// $Id: file.test,v 1.67 2010/09/11 21:14:31 webchick Exp $ /** * @file @@ -108,7 +108,20 @@ class FileTestCase extends DrupalWebTestCase { clearstatcache(); // Mask out all but the last three octets. - $actual_mode = fileperms($filepath) & 511; + $actual_mode = fileperms($filepath) & 0777; + + // PHP on Windows has limited support for file permissions. Usually each of + // "user", "group" and "other" use one octal digit (3 bits) to represent the + // read/write/execute bits. On Windows, chmod() ignores the "group" and + // "other" bits, and fileperms() returns the "user" bits in all three + // positions. $expected_mode is updated to reflect this. + if (substr(PHP_OS, 0, 3) == 'WIN') { + // Reset the "group" and "other" bits. + $expected_mode = $expected_mode & 0700; + // Shift the "user" bits to the "group" and "other" positions also. + $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; + } + if (!isset($message)) { $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); } @@ -130,7 +143,20 @@ class FileTestCase extends DrupalWebTestCase { clearstatcache(); // Mask out all but the last three octets. - $actual_mode = fileperms($directory) & 511; + $actual_mode = fileperms($directory) & 0777; + + // PHP on Windows has limited support for file permissions. Usually each of + // "user", "group" and "other" use one octal digit (3 bits) to represent the + // read/write/execute bits. On Windows, chmod() ignores the "group" and + // "other" bits, and fileperms() returns the "user" bits in all three + // positions. $expected_mode is updated to reflect this. + if (substr(PHP_OS, 0, 3) == 'WIN') { + // Reset the "group" and "other" bits. + $expected_mode = $expected_mode & 0700; + // Shift the "user" bits to the "group" and "other" positions also. + $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; + } + if (!isset($message)) { $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); } @@ -149,7 +175,7 @@ class FileTestCase extends DrupalWebTestCase { function createDirectory($path = NULL) { // A directory to operate on. if (is_null($path)) { - $path = file_directory_path() . '/' . $this->randomName(); + $path = file_default_scheme() . '://' . $this->randomName(); } $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.')); return $path; @@ -230,13 +256,13 @@ class FileHookTestCase extends FileTestCase { $this->assertTrue(FALSE, t('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)))); } else { - $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => implode(', ', $expected)))); + $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => empty($expected) ? t('(none)') : implode(', ', $expected)))); } // Determine if there were any unexpected calls. $unexpected = array_diff($actual, $expected); if (count($unexpected)) { - $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected)))); + $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => empty($unexpected) ? t('(none)') : implode(', ', $unexpected)))); } else { $this->assertTrue(TRUE, t('No unexpected hooks were called.')); @@ -297,44 +323,33 @@ class FileSpaceUsedTest extends FileTestCase { $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT); drupal_write_record('file_managed', $file); - // Now create some with other statuses. These values were chosen arbitrarily - // for the sole purpose of testing that bitwise operators were used - // correctly on the field. - $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 2 | 8); + // Now create some non-permanent files. + $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0); drupal_write_record('file_managed', $file); - $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 2 | 4); + $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0); drupal_write_record('file_managed', $file); } /** * Test different users with the default status. */ - function testUser() { - $this->assertEqual(file_space_used(2), 70, t("Found the size of the first user's files.")); - $this->assertEqual(file_space_used(3), 300, t("Found the size of the second user's files.")); - $this->assertEqual(file_space_used(), 370, t("Found the size of all user's files.")); - } + function testFileSpaceUsed() { + // Test different users with default status. + $this->assertEqual(file_space_used(2), 70); + $this->assertEqual(file_space_used(3), 300); + $this->assertEqual(file_space_used(), 370); - /** - * Test the status fields - */ - function testStatus() { - // Check selection with a single bit set. - $this->assertEqual(file_space_used(NULL, 2), 4, t("Found the size of all user's files with status 2.")); - $this->assertEqual(file_space_used(NULL, 4), 3, t("Found the size of all user's files with status 4.")); - // Check that the bitwise AND operator is used when selecting so that we - // only get files with the 2 AND 4 bits set. - $this->assertEqual(file_space_used(NULL, 2 | 4), 3, t("Found the size of all user's files with status 6.")); - } + // Test the status fields + $this->assertEqual(file_space_used(NULL, 0), 4); + $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370); - /** - * Test both the user and status. - */ - function testUserAndStatus() { - $this->assertEqual(file_space_used(1, 8), 0, t("Found the size of the admin user's files with status 8.")); - $this->assertEqual(file_space_used(2, 8), 1, t("Found the size of the first user's files with status 8.")); - $this->assertEqual(file_space_used(2, 2), 1, t("Found the size of the first user's files with status 2.")); - $this->assertEqual(file_space_used(3, 2), 3, t("Found the size of the second user's files with status 2.")); + // Test both the user and status. + $this->assertEqual(file_space_used(1, 0), 0); + $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0); + $this->assertEqual(file_space_used(2, 0), 1); + $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70); + $this->assertEqual(file_space_used(3, 0), 3); + $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300); } } @@ -413,9 +428,8 @@ class FileValidatorTest extends DrupalWebTestCase { // Maximum size. if (image_get_toolkit()) { // Copy the image so that the original doesn't get resized. - $temp_dir = file_directory_path('temporary'); - copy(drupal_realpath('misc/druplicon.png'), drupal_realpath($temp_dir) . '/druplicon.png'); - $this->image->uri = $temp_dir . '/druplicon.png'; + copy(drupal_realpath('misc/druplicon.png'), 'temporary://druplicon.png'); + $this->image->uri = 'temporary://druplicon.png'; $errors = file_validate_image_resolution($this->image, '10x5'); $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File'); @@ -424,7 +438,7 @@ class FileValidatorTest extends DrupalWebTestCase { $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File'); $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File'); - unlink(drupal_realpath($temp_dir . '/druplicon.png')); + drupal_unlink(drupal_realpath('temporary://druplicon.png')); } else { // TODO: should check that the error is returned if no toolkit is available. @@ -517,13 +531,12 @@ class FileUnmanagedSaveDataTest extends FileTestCase { // No filename. $filepath = file_unmanaged_save_data($contents); $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($filepath)), t("File was placed in Drupal's files directory.")); + $this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), t("File was placed in Drupal's files directory.")); $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.')); // Provide a filename. $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_uri_scheme($filepath)), t("File was placed in Drupal's files directory.")); $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.')); $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.')); $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664)); @@ -562,7 +575,9 @@ class FileSaveUploadTest extends FileHookTestCase { $account = $this->drupalCreateUser(array('access content')); $this->drupalLogin($account); - $this->image = current($this->drupalGetTestFiles('image')); + $image_files = $this->drupalGetTestFiles('image'); + $this->image = current($image_files); + list(, $this->image_extension) = explode('.', $this->image->filename); $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists.")); @@ -861,11 +876,11 @@ class FileDirectoryTest extends FileTestCase { } /** - * Test the file_directory_path() function. + * Test directory handling functions. */ - function testFileCheckDirectory() { + function testFileCheckDirectoryHandling() { // A directory to operate on. - $directory = file_directory_path() . '/' . $this->randomName() . '/' . $this->randomName(); + $directory = file_stream_wrapper_get_instance_by_scheme(file_default_scheme())->getDirectoryPath() . '/' . $this->randomName() . '/' . $this->randomName(); $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.')); // Non-existent directory. @@ -877,46 +892,34 @@ class FileDirectoryTest extends FileTestCase { // Make sure directory actually exists. $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File'); - // Make directory read only. - @chmod($directory, 0444); - $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File'); + if (substr(PHP_OS, 0, 3) != 'WIN') { + // PHP on Windows doesn't support any kind of useful read-only mode for + // directories. When executing a chmod() on a directory, PHP only sets the + // read-only flag, which doesn't prevent files to actually be written + // in the directory on any recent version of Windows. + + // Make directory read only. + debug($directory); + @drupal_chmod($directory, 0444); + $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File'); - // Test directory permission modification. - $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); + // Test directory permission modification. + $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); + } - // Test directory permission modification actually set correct permissions. + // Test that the directory has the correct permissions. $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775)); // Remove .htaccess file to then test that it gets re-created. - $directory = file_directory_path(); - @unlink($directory . '/.htaccess'); - $this->assertFalse(is_file($directory . '/.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File'); + @drupal_unlink(file_default_scheme() . '://.htaccess'); + $this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File'); file_ensure_htaccess(); - $this->assertTrue(is_file($directory . '/.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File'); + $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File'); // Verify contents of .htaccess file. - $file = file_get_contents(file_directory_path() . '/.htaccess'); + $file = file_get_contents(file_default_scheme() . '://.htaccess'); $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File'); } - /** - * Check file_directory_path('public'). - */ - function testFileDirectoryPath() { - // Directory path. - $path = variable_get('file_public_path', ''); - $this->assertEqual($path, file_directory_path('public'), t('Properly returns the stored file directory path.'), 'File'); - } - - /** - * Check file_directory_path() and file_directory_path('temporary'). - */ - function testFileDirectoryTemp() { - // Temporary directory handling. - variable_set('file_directory_temp', NULL); - $temp = file_directory_temp(); - $this->assertTrue(!is_null($temp), t('Properly set and retrieved temp directory %directory.', array('%directory' => $temp)), 'File'); - } - /** * This will take a directory and path, and find a valid filepath that is not * taken by another file. @@ -1132,7 +1135,7 @@ class FileUnmanagedDeleteTest extends FileTestCase { */ function testMissing() { // Try to delete a non-existing file - $this->assertTrue(file_unmanaged_delete(file_directory_path() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.')); + $this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.')); } /** @@ -1166,7 +1169,7 @@ class FileUnmanagedDeleteRecursiveTest extends FileTestCase { */ function testSingleFile() { // Create a file for testing - $filepath = file_directory_path() . '/' . $this->randomName(); + $filepath = file_default_scheme() . '://' . $this->randomName(); file_put_contents($filepath, ''); // Delete the file. @@ -1400,20 +1403,44 @@ class FileDeleteTest extends FileHookTestCase { } /** - * Try deleting a normal file (as opposed to a directory, symlink, etc). + * Tries deleting a normal file (as opposed to a directory, symlink, etc). */ - function testNormal() { + function testUnused() { $file = $this->createFile(); // Check that deletion removes the file and database record. - $this->assertTrue(is_file($file->uri), t("File exists.")); - $this->assertIdentical(file_delete($file), TRUE, t("Delete worked.")); - $this->assertFileHooksCalled(array('references', 'delete')); - $this->assertFalse(file_exists($file->uri), t("Test file has actually been deleted.")); + $this->assertTrue(is_file($file->uri), t('File exists.')); + $this->assertIdentical(file_delete($file), TRUE, t('Delete worked.')); + $this->assertFileHooksCalled(array('delete')); + $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); + } + + /** + * Tries deleting a file that is in use. + */ + function testInUse() { + $file = $this->createFile(); + file_usage_add($file, 'testing', 'test', 1); + file_usage_add($file, 'testing', 'test', 1); + + file_usage_delete($file, 'testing', 'test', 1); + file_delete($file); + $usage = file_usage_list($file); + $this->assertEqual($usage['testing']['test'], array('id' => 1, 'count' => 1), t('Test file is still in use.')); + $this->assertTrue(file_exists($file->uri), t('File still exists on the disk.')); + $this->assertTrue(file_load($file->fid), t('File still exists in the database.')); + + // Clear out the call to hook_file_load(). + file_test_reset(); - // TODO: implement hook_file_references() in file_test.module and report a - // file in use and test the $force parameter. + file_usage_delete($file, 'testing', 'test', 1); + file_delete($file); + $usage = file_usage_list($file); + $this->assertFileHooksCalled(array('delete')); + $this->assertTrue(empty($usage), t('File usage data was removed.')); + $this->assertFalse(file_exists($file->uri), t('File has been deleted after its last usage was removed.')); + $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); } } @@ -1515,7 +1542,7 @@ class FileMoveTest extends FileHookTestCase { $this->assertTrue($result, t('File moved sucessfully.')); // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('move', 'update', 'delete', 'references', 'load')); + $this->assertFileHooksCalled(array('move', 'update', 'delete', 'load')); // Reload the file from the database and check that the changes were // actually saved. @@ -1864,6 +1891,108 @@ class FileSaveTest extends FileHookTestCase { } } +/** + * Tests file usage functions. + */ +class FileUsageTest extends FileTestCase { + public static function getInfo() { + return array( + 'name' => 'File usage', + 'description' => 'Tests the file usage functions.', + 'group' => 'File', + ); + } + + /** + * Tests file_usage_list(). + */ + function testGetUsage() { + $file = $this->createFile(); + db_insert('file_usage') + ->fields(array( + 'fid' => $file->fid, + 'module' => 'testing', + 'type' => 'foo', + 'id' => 1, + 'count' => 1 + )) + ->execute(); + db_insert('file_usage') + ->fields(array( + 'fid' => $file->fid, + 'module' => 'testing', + 'type' => 'bar', + 'id' => 2, + 'count' => 2 + )) + ->execute(); + + $usage = file_usage_list($file); + + $this->assertEqual(count($usage['testing']), 2, t('Returned the correct number of items.')); + $this->assertEqual($usage['testing']['foo']['id'], 1, t('Returned the correct id.')); + $this->assertEqual($usage['testing']['bar']['id'], 2, t('Returned the correct id.')); + $this->assertEqual($usage['testing']['foo']['count'], 1, t('Returned the correct count.')); + $this->assertEqual($usage['testing']['bar']['count'], 2, t('Returned the correct count.')); + } + + /** + * Tests file_usage_add(). + */ + function testAddUsage() { + $file = $this->createFile(); + file_usage_add($file, 'testing', 'foo', 1); + // Add the file twice to ensure that the count is incremented rather than + // creating additional records. + file_usage_add($file, 'testing', 'bar', 2); + file_usage_add($file, 'testing', 'bar', 2); + + $usage = db_select('file_usage', 'f') + ->fields('f') + ->condition('f.fid', $file->fid) + ->execute() + ->fetchAllAssoc('id'); + $this->assertEqual(count($usage), 2, t('Created two records')); + $this->assertEqual($usage[1]->module, 'testing', t('Correct module')); + $this->assertEqual($usage[2]->module, 'testing', t('Correct module')); + $this->assertEqual($usage[1]->type, 'foo', t('Correct type')); + $this->assertEqual($usage[2]->type, 'bar', t('Correct type')); + $this->assertEqual($usage[1]->count, 1, t('Correct count')); + $this->assertEqual($usage[2]->count, 2, t('Correct count')); + } + + /** + * Tests file_usage_delete(). + */ + function testRemoveUsage() { + $file = $this->createFile(); + db_insert('file_usage') + ->fields(array( + 'fid' => $file->fid, + 'module' => 'testing', + 'type' => 'bar', + 'id' => 2, + 'count' => 2 + )) + ->execute(); + + file_usage_delete($file, 'testing', 'bar', 2); + $count = db_select('file_usage', 'f') + ->fields('f', array('count')) + ->condition('f.fid', $file->fid) + ->execute() + ->fetchField(); + $this->assertEqual(1, $count, t('The count was decremented correctly.')); + + file_usage_delete($file, 'testing', 'bar', 2); + $count = db_select('file_usage', 'f') + ->fields('f', array('count')) + ->condition('f.fid', $file->fid) + ->execute() + ->fetchField(); + $this->assertEqual(0, $count, t('The count was decremented correctly.')); + } +} /** * Tests the file_validate() function.. @@ -1925,7 +2054,7 @@ class FileSaveDataTest extends FileHookTestCase { $result = file_save_data($contents); $this->assertTrue($result, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual(file_default_scheme(), file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename.")); $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); @@ -1947,7 +2076,7 @@ class FileSaveDataTest extends FileHookTestCase { $result = file_save_data($contents, 'public://' . 'asdf.txt'); $this->assertTrue($result, t('Unnamed file saved correctly.')); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.')); $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); @@ -1971,7 +2100,7 @@ class FileSaveDataTest extends FileHookTestCase { $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME); $this->assertTrue($result, t("File saved sucessfully.")); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file.")); $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct.")); $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set.")); @@ -1999,7 +2128,7 @@ class FileSaveDataTest extends FileHookTestCase { $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE); $this->assertTrue($result, t('File saved sucessfully.')); - $this->assertEqual(file_directory_path('public'), file_directory_path(file_stream_wrapper_valid_scheme($result->uri)), t("File was placed in Drupal's files directory.")); + $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.')); $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); @@ -2060,7 +2189,7 @@ class FileDownloadTest extends FileTestCase { // Test generating an URL to a created file. $file = $this->createFile(); $url = file_create_url($file->uri); - $this->assertEqual($GLOBALS['base_url'] . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.')); + $this->assertEqual($GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.')); $this->drupalHead($url); $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.')); @@ -2117,7 +2246,7 @@ class FileDownloadTest extends FileTestCase { '%2523%2525%2526%252B%252F%253F' . '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E'; - $this->checkUrl('public', '', $basename, $base_url . '/' . file_directory_path() . '/' . $basename_encoded); + $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded); $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded); $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0'); } @@ -2230,19 +2359,20 @@ class FileURLRewritingTest extends FileTestCase { variable_set('file_test_hook_file_url_alter', 'cdn'); $file = $this->createFile(); $url = file_create_url($file->uri); - $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.')); + $public_directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(); + $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.')); // Test alteration of file URLs to use root-relative URLs. variable_set('file_test_hook_file_url_alter', 'root-relative'); $file = $this->createFile(); $url = file_create_url($file->uri); - $this->assertEqual(base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.')); + $this->assertEqual(base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.')); // Test alteration of file URLs to use a protocol-relative URLs. variable_set('file_test_hook_file_url_alter', 'protocol-relative'); $file = $this->createFile(); $url = file_create_url($file->uri); - $this->assertEqual('/' . base_path() . '/' . file_directory_path() . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.')); + $this->assertEqual('/' . base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.')); } } @@ -2301,9 +2431,7 @@ class FileNameMungingTest extends FileTestCase { function testUnMunge() { $munged_name = file_munge_filename($this->name, '', FALSE); $unmunged_name = file_unmunge_filename($munged_name); - // @TODO uncomment when this tests passes reliably, see - // http://drupal.org/node/368502 - // $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name))); + $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name))); } } @@ -2451,17 +2579,13 @@ class StreamWrapperTest extends DrupalWebTestCase { $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.')); $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.')); - // Test file_build_uri() and file_directory_path(). + // Test file_build_uri() and DrupalLocalStreamWrapper::getDirectoryPath(). $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', t('Expected scheme was added.')); - $this->assertEqual(file_directory_path(), variable_get('file_public_path'), t('Expected default directory path was returned.')); - $this->assertEqual(file_directory_path('public'), variable_get('file_public_path'), t('Expected public directory path was returned.')); - $this->assertEqual(file_directory_path('temporary'), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.')); - $this->assertEqual(file_directory_path($this->scheme), variable_get('stream_public_path', 'sites/default/files'), t('Expected dummy directory path was returned.')); - $this->assertFalse(file_directory_path('non-existent'), t('No directory path returned for invalid scheme.')); + $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), variable_get('file_public_path'), t('Expected default directory path was returned.')); + $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.')); + variable_set('file_default_scheme', 'private'); $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', t('Got a valid URI from foo/bar.txt.')); - $this->assertEqual(file_directory_path(), variable_get('file_private_path'), t('Expected default directory path was returned.')); - $this->assertEqual(file_directory_path('private'), variable_get('file_private_path'), t('Expected private directory path was returned.')); } /** diff --git a/modules/simpletest/tests/file_test.info b/modules/simpletest/tests/file_test.info index 5b2f8474f78f4227d6fe31ce564f4eaced6d7b91..76d53eba7d2a91a87c5dfb463999d23f7a14ff58 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module index 033c43d2f149a71eb778440a54bd827a9075cd05..3624ccec949dd192b92dbb4afe99a81497252565 100644 --- a/modules/simpletest/tests/file_test.module +++ b/modules/simpletest/tests/file_test.module @@ -1,5 +1,5 @@ <?php -// $Id: file_test.module,v 1.23 2010/06/26 19:55:47 dries Exp $ +// $Id: file_test.module,v 1.25 2010/08/22 13:52:58 dries Exp $ /** * @file @@ -113,7 +113,7 @@ function _file_test_form_submit(&$form, &$form_state) { if ($form_state['values']['allow_all_extensions']) { $validators['file_validate_extensions'] = array(); } - else if (!empty($form_state['values']['extensions'])) { + elseif (!empty($form_state['values']['extensions'])) { $validators['file_validate_extensions'] = array($form_state['values']['extensions']); } @@ -143,7 +143,6 @@ function file_test_reset() { 'load' => array(), 'validate' => array(), 'download' => array(), - 'references' => array(), 'insert' => array(), 'update' => array(), 'copy' => array(), @@ -156,7 +155,6 @@ function file_test_reset() { $return = array( 'validate' => array(), 'download' => NULL, - 'references' => NULL, ); variable_set('file_test_return', $return); } @@ -167,7 +165,7 @@ function file_test_reset() { * * @param $op * One of the hook_file_* operations: 'load', 'validate', 'download', - * 'references', 'insert', 'update', 'copy', 'move', 'delete'. + * 'insert', 'update', 'copy', 'move', 'delete'. * * @return * Array of the parameters passed to each call. @@ -184,9 +182,9 @@ function file_test_get_calls($op) { * Get an array with the calls for all hooks. * * @return - * An array keyed by hook name ('load', 'validate', 'download', - * 'references', 'insert', 'update', 'copy', 'move', 'delete') with values - * being arrays of parameters passed to each call. + * An array keyed by hook name ('load', 'validate', 'download', 'insert', + * 'update', 'copy', 'move', 'delete') with values being arrays of parameters + * passed to each call. */ function file_test_get_all_calls() { return variable_get('file_test_results', array()); @@ -197,7 +195,7 @@ function file_test_get_all_calls() { * * @param $op * One of the hook_file_* operations: 'load', 'validate', 'download', - * 'references', 'insert', 'update', 'copy', 'move', 'delete'. + * 'insert', 'update', 'copy', 'move', 'delete'. * @param $args * Values passed to hook. * @@ -214,7 +212,7 @@ function _file_test_log_call($op, $args) { * Load the appropriate return value. * * @param $op - * One of the hook_file_[validate,download,references] operations. + * One of the hook_file_[validate,download] operations. * * @return * Value set by file_test_set_return(). @@ -231,7 +229,7 @@ function _file_test_get_return($op) { * Assign a return value for a given operation. * * @param $op - * One of the hook_file_[validate,download,references] operations. + * One of the hook_file_[validate,download] operations. * @param $value * Value for the hook to return. * @@ -272,14 +270,6 @@ function file_test_file_download($uri) { return _file_test_get_return('download'); } -/** - * Implements hook_file_references(). - */ -function file_test_file_references($file) { - _file_test_log_call('references', array($file)); - return _file_test_get_return('references'); -} - /** * Implements hook_file_insert(). */ diff --git a/modules/simpletest/tests/filetransfer.test b/modules/simpletest/tests/filetransfer.test index b9a79f70923ac94db8f8687bedce338af33d7150..0ddc436e3ef5f322475f8bd4ce118022a546100b 100644 --- a/modules/simpletest/tests/filetransfer.test +++ b/modules/simpletest/tests/filetransfer.test @@ -1,5 +1,5 @@ <?php -// $Id: filetransfer.test,v 1.6 2009/11/10 17:27:53 webchick Exp $ +// $Id: filetransfer.test,v 1.9 2010/09/01 20:08:17 dries Exp $ class FileTranferTest extends DrupalWebTestCase { @@ -36,7 +36,7 @@ class FileTranferTest extends DrupalWebTestCase { } function _buildFakeModule() { - $location = file_directory_path('temporary') . '/fake'; + $location = 'temporary://fake'; if (is_dir($location)) { $ret = 0; $output = array(); @@ -80,7 +80,7 @@ class FileTranferTest extends DrupalWebTestCase { $gotit = TRUE; try { - $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. file_directory_path()); + $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. variable_get('file_public_path', conf_path() . '/files')); } catch (FileTransferException $e) { $gotit = FALSE; diff --git a/modules/simpletest/tests/filter_test.info b/modules/simpletest/tests/filter_test.info index 6c63a666bb6345c252fa2a1d9da015973d3260e4..e46f6c7ad20080364948a841928175a62c6a9650 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/filter_test.module b/modules/simpletest/tests/filter_test.module index 7730f6b17cd437ae16b906138ab9c301c5ebe255..076c14a490dddbf8a53afa22488071369441afa6 100644 --- a/modules/simpletest/tests/filter_test.module +++ b/modules/simpletest/tests/filter_test.module @@ -1,5 +1,5 @@ <?php -// $Id: filter_test.module,v 1.4 2009/12/04 16:49:47 dries Exp $ +// $Id: filter_test.module,v 1.5 2010/08/22 12:55:04 dries Exp $ /** * @file @@ -36,6 +36,28 @@ function filter_test_filter_info() { 'description' => 'Does nothing, but makes a text format uncacheable.', 'cache' => FALSE, ); + $filters['filter_test_replace'] = array( + 'title' => 'Testing filter', + 'description' => 'Replaces all content with filter and text format information.', + 'process callback' => 'filter_test_replace', + ); return $filters; } +/** + * Process handler for filter_test_replace filter. + * + * Replaces all text with filter and text format information. + */ +function filter_test_replace($text, $filter, $format, $langcode, $cache, $cache_id) { + $text = array(); + $text[] = 'Filter: ' . $filter->title . ' (' . $filter->name . ')'; + $text[] = 'Format: ' . $format->name . ' (' . $format->format . ')'; + $text[] = 'Language: ' . $langcode; + $text[] = 'Cache: ' . ($cache ? 'Enabled' : 'Disabled'); + if ($cache_id) { + $text[] = 'Cache ID: ' . $cache_id; + } + return implode("<br />\n", $text); +} + diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index efaac4b09c6358be3a43e40da4b4a42687d12d4f..17eea5133c93eef0aace948ec04eb1181a4f08aa 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -1,5 +1,5 @@ <?php -// $Id: form.test,v 1.53 2010/07/02 12:37:57 dries Exp $ +// $Id: form.test,v 1.64 2010/09/13 06:41:23 webchick Exp $ /** * @file @@ -42,12 +42,15 @@ class FormsTestCase extends DrupalWebTestCase { $elements['password']['empty_values'] = $empty_strings; $elements['password_confirm']['element'] = array('#title' => $this->randomName(), '#type' => 'password_confirm'); - $elements['password_confirm']['empty_values'] = $empty_strings; + // Provide empty values for both password fields. + foreach ($empty_strings as $key => $value) { + $elements['password_confirm']['empty_values'][$key] = array('pass1' => $value, 'pass2' => $value); + } $elements['textarea']['element'] = array('#title' => $this->randomName(), '#type' => 'textarea'); $elements['textarea']['empty_values'] = $empty_strings; - $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); + $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); $elements['radios']['empty_values'] = $empty_arrays; $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName()); @@ -56,7 +59,7 @@ class FormsTestCase extends DrupalWebTestCase { $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); $elements['checkboxes']['empty_values'] = $empty_arrays; - $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); + $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); $elements['select']['empty_values'] = $empty_strings; $elements['file']['element'] = array('#title' => $this->randomName(), '#type' => 'file'); @@ -77,8 +80,7 @@ class FormsTestCase extends DrupalWebTestCase { $element = $data['element']['#title']; $form[$element] = $data['element']; $form[$element]['#required'] = $required; - $form_state['values'][$element] = $empty; - $form_state['input'] = $form_state['values']; + $form_state['input'][$element] = $empty; $form_state['input']['form_id'] = $form_id; $form_state['method'] = 'post'; drupal_prepare_form($form_id, $form, $form_state); @@ -185,29 +187,110 @@ class FormsTestCase extends DrupalWebTestCase { // element. drupalPost() emulates a browser by not submitting input for // disabled elements, so we need to un-disable those elements first. $this->drupalGet('form-test/disabled-elements'); + $disabled_elements = array(); foreach ($this->xpath('//*[@disabled]') as $element) { + $disabled_elements[] = (string) $element['name']; unset($element['disabled']); } + + // All the elements should be marked as disabled, including the ones below + // the disabled container. + $this->assertEqual(count($disabled_elements), 32, t('The correct elements have the disabled property in the HTML code.')); + $this->drupalPost(NULL, $edit, t('Submit')); $returned_values['hijacked'] = drupal_json_decode($this->content); // Ensure that the returned values match the form's default values in both // cases. foreach ($returned_values as $type => $values) { - foreach (element_children($form) as $key) { - if (isset($form[$key]['#default_value'])) { + $this->assertFormValuesDefault($values, $form); + } + } + + /** + * Assert that the values submitted to a form matches the default values of the elements. + */ + function assertFormValuesDefault($values, $form) { + foreach (element_children($form) as $key) { + if (isset($form[$key]['#default_value'])) { + if (isset($form[$key]['#expected_value'])) { + $expected_value = $form[$key]['#expected_value']; + } + else { $expected_value = $form[$key]['#default_value']; + } - if ($key == 'checkboxes_multiple') { - // Checkboxes values are not filtered out. - $values[$key] = array_filter($values[$key]); - } - $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE)))); + if ($key == 'checkboxes_multiple') { + // Checkboxes values are not filtered out. + $values[$key] = array_filter($values[$key]); } + $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE)))); } + + // Recurse children. + $this->assertFormValuesDefault($values, $form[$key]); } } + /** + * Verify markup for disabled form elements. + * + * @see _form_test_disabled_elements() + */ + function testDisabledMarkup() { + $this->drupalGet('form-test/disabled-elements'); + $form_state = array(); + $form = _form_test_disabled_elements(array(), $form_state); + $type_map = array( + 'textarea' => 'textarea', + 'select' => 'select', + 'weight' => 'select', + 'date' => 'select', + ); + + foreach ($form as $name => $item) { + // Skip special #types. + if (!isset($item['#type']) || in_array($item['#type'], array('hidden', 'text_format'))) { + continue; + } + // Setup XPath and CSS class depending on #type. + if (in_array($item['#type'], array('image_button', 'button', 'submit'))) { + $path = "//!type[contains(@class, :div-class) and @value=:value]"; + $class = 'form-button-disabled'; + } + else { + // starts-with() required for checkboxes. + $path = "//div[contains(@class, :div-class)]/descendant::!type[starts-with(@name, :name)]"; + $class = 'form-disabled'; + } + // Replace DOM element name in $path according to #type. + $type = 'input'; + if (isset($type_map[$item['#type']])) { + $type = $type_map[$item['#type']]; + } + $path = strtr($path, array('!type' => $type)); + // Verify that the element exists. + $element = $this->xpath($path, array( + ':name' => check_plain($name), + ':div-class' => $class, + ':value' => isset($item['#value']) ? $item['#value'] : '', + )); + $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => $item['#type']))); + } + + // Verify special element #type text-format. + $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::textarea[@name=:name]', array( + ':name' => 'text_format[value]', + ':div-class' => 'form-disabled', + )); + $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[value]'))); + $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::select[@name=:name]', array( + ':name' => 'text_format[format]', + ':div-class' => 'form-disabled', + )); + $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[format]'))); + } + /** * Test Form API protections against input forgery. * @@ -324,6 +407,10 @@ class FormValidationTestCase extends DrupalWebTestCase { $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); $this->assertText('Test element is invalid'); + // Ensure not validated values are not available to submit handlers. + $this->drupalPost($path, array('title' => '', 'test' => 'valid'), t('Partial validate')); + $this->assertText('Only validated values appear in the form values.'); + // Now test full form validation and ensure that the #element_validate // handler is still triggered. $this->drupalPost($path, $edit, t('Full validate')); @@ -535,11 +622,11 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { ); // Test with a valid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1')); + list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('row1' => 'row1'))); $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for checkboxes.')); // Test with an invalid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value')); + list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('non_existing_value' => 'non_existing_value'))); $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for checkboxes.')); } @@ -924,6 +1011,10 @@ class FormsRebuildTestCase extends DrupalWebTestCase { $this->drupalPost(NULL, array(), t('Save')); $this->assertText('Title field is required.', t('Non-AJAX submission correctly triggered a validation error.')); + // Ensure that the form contains two items in the multi-valued field, so we + // know we're testing a form that was correctly retrieved from cache. + $this->assert(count($this->xpath('//form[contains(@id, "page-node-form")]//div[contains(@class, "form-item-field-ajax-test")]//input[@type="text"]')) == 2, t('Form retained its state from cache.')); + // Ensure that the form's action is correct. $forms = $this->xpath('//form[contains(@class, "node-page-form")]'); $this->assert(count($forms) == 1 && $forms[0]['action'] == url('node/add/page'), t('Re-rendered form contains the correct action value.')); @@ -956,10 +1047,19 @@ class FormsProgrammaticTestCase extends DrupalWebTestCase { $current_batch = $batch =& batch_get(); $batch = array(); - $this->submitForm(); - $this->submitForm('test 1'); - $this->submitForm(); - $this->submitForm('test 2'); + // Test that a programmatic form submission is rejected when a required + // textfield is omitted and correctly processed when it is provided. + $this->submitForm(array(), FALSE); + $this->submitForm(array('textfield' => 'test 1'), TRUE); + $this->submitForm(array(), FALSE); + $this->submitForm(array('textfield' => 'test 2'), TRUE); + + // Test that a programmatic form submission can turn on and off checkboxes + // which are, by default, checked. + $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => 2)), TRUE); + $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => NULL)), TRUE); + $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE); + $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE); // Restore the current batch status. $batch = $current_batch; @@ -967,30 +1067,36 @@ class FormsProgrammaticTestCase extends DrupalWebTestCase { /** * Helper function used to programmatically submit the form defined in - * form_test.module with the given value. + * form_test.module with the given values. * - * @param string $value - * The field value to be submitted. + * @param $values + * An array of field values to be submitted. + * @param $valid_input + * A boolean indicating whether or not the form submission is expected to + * be valid. */ - private function submitForm($value = NULL) { - // Programmatically submit the given value. - $form_state = array('values' => array('submitted_field' => $value)); + private function submitForm($values, $valid_input) { + // Programmatically submit the given values. + $form_state = array('values' => $values); drupal_form_submit('form_test_programmatic_form', $form_state); + // Check that the form returns an error when expected, and vice versa. $errors = form_get_errors(); $valid_form = empty($errors); - $valid_input = !empty($value); - - // If no value was passed the form should return an error and viceversa. - $args = array('%value' => $value, '%errors' => $valid_form ? '' : implode(' ', $errors)); - $this->assertTrue($valid_input == $valid_form, t('Input value: %value<br/>Validation handler errors: %errors', $args)); + $args = array( + '%values' => print_r($values, TRUE), + '%errors' => $valid_form ? t('None') : implode(' ', $errors), + ); + $this->assertTrue($valid_input == $valid_form, t('Input values: %values<br/>Validation handler errors: %errors', $args)); // We check submitted values only if we have a valid input. if ($valid_input) { - // By fetching the value from $form_state['storage'] we ensure that the + // By fetching the values from $form_state['storage'] we ensure that the // submission handler was properly executed. - $submitted_value = $form_state['storage']['programmatic_form_submit']; - $this->assertTrue($submitted_value == $value, t('Submission handler correctly executed: %submitted_value', array('%submitted_value' => $submitted_value))); + $stored_values = $form_state['storage']['programmatic_form_submit']; + foreach ($values as $key => $value) { + $this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, t('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE)))); + } } } } @@ -1158,3 +1264,37 @@ class FormsArbitraryRebuildTestCase extends DrupalWebTestCase { $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.'); } } + +/** + * Tests form API file inclusion. + */ +class FormsFileInclusionTestCase extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Form API file inclusion', + 'description' => 'Tests form API file inclusion.', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Tests loading an include specified in hook_menu(). + */ + function testLoadMenuInclude() { + $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu'); + $this->assertText('Submit callback called.'); + } + + /** + * Tests loading a custom specified inlcude. + */ + function testLoadCustomInclude() { + $this->drupalPost('form-test/load-include-custom', array(), t('Save')); + $this->assertText('Submit callback called.'); + } +} diff --git a/modules/simpletest/tests/form_test.file.inc b/modules/simpletest/tests/form_test.file.inc new file mode 100644 index 0000000000000000000000000000000000000000..aeef156f5fc99e01f6bd44efa02bef72e8cc721c --- /dev/null +++ b/modules/simpletest/tests/form_test.file.inc @@ -0,0 +1,40 @@ +<?php +// $Id: form_test.file.inc,v 1.1 2010/07/17 18:52:39 dries Exp $ + +/** + * @file + * An include file to test loading it with the form API. + */ + +/** + * Form constructor for testing FAPI file inclusion of the file specified in + * hook_menu(). + */ +function form_test_load_include_menu($form, &$form_state) { + // Submit the form via AJAX. That way the FAPI has to care about including + // the file specified in hook_menu(). + $form['button'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#submit' => array('form_test_load_include_submit'), + '#ajax' => array( + 'callback' => 'form_test_load_include_menu_ajax', + ), + ); + return $form; +} + +/** + * Submit callback for the form API file inclusion test forms. + */ +function form_test_load_include_submit($form, $form_state) { + drupal_set_message('Submit callback called.'); +} + +/** + * Ajax callback for the file inclusion via menu test. We don't need to return + * anything as the messages are added automatically. + */ +function form_test_load_include_menu_ajax($form) { + return ''; +} diff --git a/modules/simpletest/tests/form_test.info b/modules/simpletest/tests/form_test.info index 1dcf59e2f8d48fd63064b90b46435ca1891423f5..17b1a2725acedc47c31e100fb012359a6c828757 100644 --- a/modules/simpletest/tests/form_test.info +++ b/modules/simpletest/tests/form_test.info @@ -1,14 +1,15 @@ -; $Id: form_test.info,v 1.1 2009/01/28 07:43:26 webchick Exp $ +; $Id: form_test.info,v 1.2 2010/07/17 18:52:39 dries Exp $ name = "FormAPI Test" description = "Support module for Form API tests." package = Testing version = VERSION core = 7.x files[] = form_test.module +files[] = form_test.file.inc hidden = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 065df578c32fdf2fe1dc859e530570d0ae427b90..bbfd9414d5a004ab071e63d32b68c17890e5ef11 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.42 2010/07/02 12:37:57 dries Exp $ +// $Id: form_test.module,v 1.48 2010/08/27 11:54:32 webchick Exp $ /** * @file @@ -153,6 +153,23 @@ function form_test_menu() { ); } + $items['form-test/load-include-menu'] = array( + 'title' => 'FAPI test loading includes', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_load_include_menu'), + 'access callback' => TRUE, + 'file' => 'form_test.file.inc', + 'type' => MENU_CALLBACK, + ); + + $items['form-test/load-include-custom'] = array( + 'title' => 'FAPI test loading includes', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_load_include_custom'), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -296,7 +313,7 @@ function form_test_limit_validation_errors_form($form, &$form_state) { $form['actions']['partial'] = array( '#type' => 'submit', '#limit_validation_errors' => array(array('test')), - '#submit' => array(), + '#submit' => array('form_test_limit_validation_errors_form_partial_submit'), '#value' => t('Partial validate'), ); $form['actions']['full'] = array( @@ -315,6 +332,17 @@ function form_test_limit_validation_errors_element_validate_test(&$element, &$fo } } +/** + * Form submit handler for the partial validation submit button. + */ +function form_test_limit_validation_errors_form_partial_submit($form, $form_state) { + // The title has not been validated, thus its value - in case of the test case + // an empty string - may not be set. + if (!isset($form_state['values']['title']) && isset($form_state['values']['test'])) { + drupal_set_message('Only validated values appear in the form values.'); + } +} + /** * Create a header and options array. Helper function for callbacks. */ @@ -474,7 +502,7 @@ function form_test_storage_form($form, &$form_state) { } // Count how often the form is constructed. $_SESSION['constructions']++; - drupal_set_message("Form constructions: ". $_SESSION['constructions']); + drupal_set_message("Form constructions: " . $_SESSION['constructions']); $form['title'] = array( '#type' => 'textfield', @@ -555,8 +583,8 @@ function form_storage_test_form_continue_validate($form, &$form_state) { * Form submit handler to finish multi-step form. */ function form_test_storage_form_submit($form, &$form_state) { - drupal_set_message("Title: ". check_plain($form_state['values']['title'])); - drupal_set_message("Form constructions: ". $_SESSION['constructions']); + drupal_set_message("Title: " . check_plain($form_state['values']['title'])); + drupal_set_message("Form constructions: " . $_SESSION['constructions']); if (isset($form_state['storage']['thing']['changed'])) { drupal_set_message("The thing has been changed."); } @@ -852,6 +880,77 @@ function _form_test_disabled_elements($form, &$form_state) { ), ); + // The #disabled state should propagate to children. + $form['disabled_container'] = array( + '#disabled' => TRUE, + ); + foreach (array('textfield', 'textarea', 'hidden') as $type) { + $form['disabled_container']['disabled_container_' . $type] = array( + '#type' => $type, + '#title' => $type, + '#default_value' => $type, + '#test_hijack_value' => 'HIJACK', + ); + } + + // Text format. + $form['text_format'] = array( + '#type' => 'text_format', + '#title' => 'Text format', + '#disabled' => TRUE, + '#default_value' => 'Text value', + '#format' => 1, + '#expected_value' => array( + 'value' => 'Text value', + 'format' => 1, + ), + '#test_hijack_value' => array( + 'value' => 'HIJACK', + 'format' => 2, + ), + ); + + // Password fields. + $form['password'] = array( + '#type' => 'password', + '#title' => 'Password', + '#disabled' => TRUE, + ); + $form['password_confirm'] = array( + '#type' => 'password_confirm', + '#title' => 'Password confirm', + '#disabled' => TRUE, + ); + + // Files. + $form['file'] = array( + '#type' => 'file', + '#title' => 'File', + '#disabled' => TRUE, + ); + $form['managed_file'] = array( + '#type' => 'managed_file', + '#title' => 'Managed file', + '#disabled' => TRUE, + ); + + // Buttons. + $form['image_button'] = array( + '#type' => 'image_button', + '#value' => 'Image button', + '#disabled' => TRUE, + ); + $form['button'] = array( + '#type' => 'button', + '#value' => 'Button', + '#disabled' => TRUE, + ); + $form['submit_disabled'] = array( + '#type' => 'submit', + '#value' => 'Submit', + '#disabled' => TRUE, + ); + $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), @@ -1021,10 +1120,20 @@ function form_test_form_form_test_state_persist_alter(&$form, &$form_state) { * Form builder to test programmatic form submissions. */ function form_test_programmatic_form($form, &$form_state) { - $form['submitted_field'] = array( - '#title' => 'Submitted', + $form['textfield'] = array( + '#title' => 'Textfield', '#type' => 'textfield', ); + $form['checkboxes'] = array( + '#type' => 'checkboxes', + '#options' => array( + 1 => 'First checkbox', + 2 => 'Second checkbox', + ), + // Both checkboxes are selected by default so that we can test the ability + // of programmatic form submissions to uncheck them. + '#default_value' => array(1, 2), + ); return $form; } @@ -1036,8 +1145,8 @@ function form_test_programmatic_form($form, &$form_state) { * explicitly required here. */ function form_test_programmatic_form_validate($form, &$form_state) { - if (empty($form_state['values']['submitted_field'])) { - form_set_error('submitted_field', t('Submitted field is required.')); + if (empty($form_state['values']['textfield'])) { + form_set_error('textfield', t('Textfield is required.')); } } @@ -1045,10 +1154,10 @@ function form_test_programmatic_form_validate($form, &$form_state) { * Form submit handler for programmatic form submissions. * * To test that the submission handler is correctly executed, we store the - * submitted value in a place we can access from the caller context. + * submitted values in a place we can access from the caller context. */ function form_test_programmatic_form_submit($form, &$form_state) { - $form_state['storage']['programmatic_form_submit'] = $form_state['values']['submitted_field']; + $form_state['storage']['programmatic_form_submit'] = $form_state['values']; } /** @@ -1169,3 +1278,20 @@ function form_test_two_instances() { $return['node_form_2'] = drupal_get_form('page_node_form', $node2); return $return; } + +/** + * Menu callback for testing custom form includes. + */ +function form_test_load_include_custom($form, &$form_state) { + $form['button'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#submit' => array('form_test_load_include_submit'), + ); + // Specify the include file and enable form caching. That way the form is + // cached when it is submitted, but needs to find the specified submit handler + // in the include. + $form_state['build_info']['files'][] = array('module' => 'form_test', 'name' => 'form_test.file'); + $form_state['cache'] = TRUE; + return $form; +} diff --git a/modules/simpletest/tests/graph.test b/modules/simpletest/tests/graph.test index 1751fe2ca554ad74d5b88d434047c6519c326b3b..0c9014cdb0a97243fb1b52711fd63a83229a58f0 100644 --- a/modules/simpletest/tests/graph.test +++ b/modules/simpletest/tests/graph.test @@ -1,5 +1,5 @@ <?php -// $Id: graph.test,v 1.7 2010/05/18 18:11:13 dries Exp $ +// $Id: graph.test,v 1.10 2010/09/04 13:33:53 dries Exp $ /** * @file @@ -18,13 +18,15 @@ class GraphUnitTest extends DrupalUnitTestCase { ); } + function setUp() { + require_once DRUPAL_ROOT . '/includes/graph.inc'; + parent::setUp(); + } + /** * Test depth-first-search features. */ function testDepthFirstSearch() { - // Provoke the inclusion of graph.inc. - require_once DRUPAL_ROOT . '/includes/graph.inc'; - // The sample graph used is: // 1 --> 2 --> 3 5 ---> 6 // | ^ ^ diff --git a/modules/simpletest/tests/image.test b/modules/simpletest/tests/image.test index d1b7fc79c943d68800df24f2f94e0d8418c075fe..88ba242cee0fd1f39bb68a6465c668436320cc86 100644 --- a/modules/simpletest/tests/image.test +++ b/modules/simpletest/tests/image.test @@ -1,5 +1,5 @@ <?php -// $Id: image.test,v 1.13 2010/04/23 05:10:35 webchick Exp $ +// $Id: image.test,v 1.16 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -444,7 +444,7 @@ class ImageToolkitGdTestCase extends DrupalWebTestCase { $correct_colors = $this->colorsAreEqual($color, $corner); } - $directory = file_directory_path() . '/imagetests'; + $directory = file_default_scheme() . '://imagetests'; file_prepare_directory($directory, FILE_CREATE_DIRECTORY); image_save($image, $directory . '/' . $op . '.' . $image->info['extension']); diff --git a/modules/simpletest/tests/image_test.info b/modules/simpletest/tests/image_test.info index 8dce514b3bfb75f3d7b094d68f02bd5a83a0b22b..7903c885b7e3c6caee3d92cfdb50f0f9604eac7b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/lock.test b/modules/simpletest/tests/lock.test index 5df032bd675863b3348f516e2b752271a735f7e0..efa71646cdf60ac5e2e06ba474cd93b95694534a 100644 --- a/modules/simpletest/tests/lock.test +++ b/modules/simpletest/tests/lock.test @@ -1,5 +1,5 @@ <?php -// $Id: lock.test,v 1.1 2009/08/17 20:32:30 dries Exp $ +// $Id: lock.test,v 1.3 2010/08/05 23:53:38 webchick Exp $ /** * Tests for the lock system. diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test index f5cb76a577c948180a48b9e40b0d1adbca838f3c..655ce98ba78af3169b89f60a5a72e4639806c8f4 100644 --- a/modules/simpletest/tests/mail.test +++ b/modules/simpletest/tests/mail.test @@ -1,5 +1,5 @@ <?php -// $Id: mail.test,v 1.3 2010/04/11 18:33:44 dries Exp $ +// $Id: mail.test,v 1.5 2010/08/05 23:53:38 webchick Exp $ /** * Test the Drupal mailing system. diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test index b108d06bca4258eb6bb88e442988126b4686fba6..91058d5eccaa146ca8fc7f32c9687ae24d42bf4b 100644 --- a/modules/simpletest/tests/menu.test +++ b/modules/simpletest/tests/menu.test @@ -1,5 +1,5 @@ <?php -// $Id: menu.test,v 1.31 2010/07/08 03:41:27 webchick Exp $ +// $Id: menu.test,v 1.34 2010/08/08 19:35:49 dries Exp $ /** * @file @@ -20,8 +20,8 @@ class MenuRouterTestCase extends DrupalWebTestCase { parent::setUp('menu_test'); // Make the tests below more robust by explicitly setting the default theme // and administrative theme that they expect. - theme_enable(array('garland')); - variable_set('theme_default', 'garland'); + theme_enable(array('bartik')); + variable_set('theme_default', 'bartik'); variable_set('admin_theme', 'seven'); } @@ -81,7 +81,7 @@ class MenuRouterTestCase extends DrupalWebTestCase { // For a regular user, the fact that the site is in maintenance mode means // we expect the theme callback system to be bypassed entirely. $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertRaw('garland/style.css', t("The maintenance theme's CSS appears on the page.")); + $this->assertRaw('bartik/css/style.css', t("The maintenance theme's CSS appears on the page.")); // An administrator, however, should continue to see the requested theme. $admin_user = $this->drupalCreateUser(array('access site in maintenance mode')); @@ -130,8 +130,8 @@ class MenuRouterTestCase extends DrupalWebTestCase { function testThemeCallbackOptionalTheme() { // Request a theme that is not enabled. $this->drupalGet('menu-test/theme-callback/use-stark-theme'); - $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that is not enabled is requested.')); - $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page.")); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when a theme that is not enabled is requested.')); + $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); // Now enable the theme and request it again. theme_enable(array('stark')); @@ -145,8 +145,8 @@ class MenuRouterTestCase extends DrupalWebTestCase { */ function testThemeCallbackFakeTheme() { $this->drupalGet('menu-test/theme-callback/use-fake-theme'); - $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when a theme that does not exist is requested.')); - $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page.")); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when a theme that does not exist is requested.')); + $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); } /** @@ -154,8 +154,8 @@ class MenuRouterTestCase extends DrupalWebTestCase { */ function testThemeCallbackNoThemeRequested() { $this->drupalGet('menu-test/theme-callback/no-theme-requested'); - $this->assertText('Custom theme: NONE. Actual theme: garland.', t('The theme callback system falls back on the default theme when no theme is requested.')); - $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page.")); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when no theme is requested.')); + $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); } /** diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info index bcf19e8c3901a32cba2c958bac200d72c240606e..6ad9c2b4059b56b71750207f6b6a737e7e28b7bf 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test index a9b16c493022add02f6f9dcc98b5b773c223341d..88b2a9426f052537fdf1fd15cb950433ebe1d717 100644 --- a/modules/simpletest/tests/module.test +++ b/modules/simpletest/tests/module.test @@ -1,5 +1,5 @@ <?php -// $Id: module.test,v 1.21 2010/04/22 18:56:31 dries Exp $ +// $Id: module.test,v 1.23 2010/08/05 23:53:38 webchick Exp $ /** * @file diff --git a/modules/simpletest/tests/module_test.info b/modules/simpletest/tests/module_test.info index d5dd42e47a50c9462b66cc6fd7dfda13e82f1812..d40aa343fb22fcfd04e14c3243d2c4dd1f4bc4c4 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test index 5fb8ced72e6f3c1664a89e5f9357dc9c6f7bf00f..56f5a7e3709d5a555d5a7709f3d2671446f628e4 100644 --- a/modules/simpletest/tests/path.test +++ b/modules/simpletest/tests/path.test @@ -1,5 +1,5 @@ <?php -// $Id: path.test,v 1.5 2010/04/06 19:49:03 dries Exp $ +// $Id: path.test,v 1.7 2010/08/05 23:53:38 webchick Exp $ /** * @file diff --git a/modules/simpletest/tests/registry.test b/modules/simpletest/tests/registry.test index e822768972916a80ffe9c63a55d772626249fc8c..421678f781fa57723a62aaca124c92a879cf1710 100644 --- a/modules/simpletest/tests/registry.test +++ b/modules/simpletest/tests/registry.test @@ -1,5 +1,5 @@ <?php -// $Id: registry.test,v 1.17 2010/05/01 08:12:23 dries Exp $ +// $Id: registry.test,v 1.21 2010/09/01 20:08:17 dries Exp $ class RegistryParseFileTestCase extends DrupalWebTestCase { public static function getInfo() { @@ -64,17 +64,18 @@ class RegistryParseFilesTestCase extends DrupalWebTestCase { foreach ($this->fileTypes as $fileType) { $chrs = hash('sha256', microtime() . mt_rand()); $this->$fileType = new stdClass(); - $this->$fileType->fileName = file_directory_path() . '/registry_test_' . substr($chrs, 0, 16); + $this->$fileType->fileName = 'public://registry_test_' . substr($chrs, 0, 16); $this->$fileType->className = 'registry_test_class' . substr($chrs, 16, 16); $this->$fileType->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16); $this->$fileType->contents = $this->getFileContents($fileType); file_save_data($this->$fileType->contents, $this->$fileType->fileName); if ($fileType == 'existing_changed') { + // Add a record with an incorrect hash. + $this->$fileType->fakeHash = hash('sha256', mt_rand()); db_insert('registry_file') ->fields(array( - 'filectime' => mt_rand(1, 1000000), - 'filemtime' => mt_rand(1, 1000000), + 'hash' => $this->$fileType->fakeHash, 'filename' => $this->$fileType->fileName, )) ->execute(); @@ -104,10 +105,9 @@ class RegistryParseFilesTestCase extends DrupalWebTestCase { $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$fileType->$resource))->fetchField(); $this->assertTrue($this->$fileType->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$fileType->$resource))); } - // Test that we have the right file creation and modification dates. - $dates = db_query('SELECT filectime, filemtime FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchObject(); - $this->assertEqual($dates->filectime, filectime($this->$fileType->fileName), t('File creation date matches for %filename.', array('%filename' => $this->$fileType->fileName))); - $this->assertEqual($dates->filemtime, filemtime($this->$fileType->fileName), t('File modification date matches for %filename.', array('%filename' => $this->$fileType->fileName))); + // Test that we have the right hash. + $hash = db_query('SELECT hash FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchField(); + $this->assertTrue(hash('sha256', $this->$fileType->contents) == $hash, t('sha-256 for "@filename" matched.' . $fileType . $hash, array('@filename' => $this->$fileType->fileName))); } } @@ -119,8 +119,7 @@ class RegistryParseFilesTestCase extends DrupalWebTestCase { foreach ($this->fileTypes as $fileType) { $files[$this->$fileType->fileName] = array('module' => '', 'weight' => 0); if ($fileType == 'existing_changed') { - $files[$this->$fileType->fileName]['filectime'] = mt_rand(1, 1000000); - $files[$this->$fileType->fileName]['filemtime'] = mt_rand(1, 1000000); + $files[$this->$fileType->fileName]['hash'] = $this->$fileType->fakeHash; } } return $files; diff --git a/modules/simpletest/tests/requirements1_test.info b/modules/simpletest/tests/requirements1_test.info index ca606b5dcd507e0512d3e894ae0c0d5b6027f258..9e269ab5b4f37ffe011445148de1bf5249786611 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/requirements2_test.info b/modules/simpletest/tests/requirements2_test.info index 0eb03a5cc05577891e7801dfc433b4ae2f1921cf..ba6e576e8b8a880137d1581651b8a50ce74db650 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/schema.test b/modules/simpletest/tests/schema.test index c8c297e3eb5952a22efd9591de6080727f5d1ad2..0f815b7ab5e6bacfff99acf997581557e8973551 100644 --- a/modules/simpletest/tests/schema.test +++ b/modules/simpletest/tests/schema.test @@ -1,5 +1,5 @@ <?php -// $Id: schema.test,v 1.19 2010/03/28 11:45:11 dries Exp $ +// $Id: schema.test,v 1.21 2010/08/05 23:53:38 webchick Exp $ /** * @file diff --git a/modules/simpletest/tests/session.test b/modules/simpletest/tests/session.test index 552f8a8092851ce596d314bdb6c6f55a171506ca..6c07a8ce3c61e7b6c63c5c00211879bfbd682541 100644 --- a/modules/simpletest/tests/session.test +++ b/modules/simpletest/tests/session.test @@ -1,5 +1,5 @@ <?php -// $Id: session.test,v 1.29 2010/06/14 12:31:46 dries Exp $ +// $Id: session.test,v 1.32 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -191,7 +191,7 @@ class SessionTestCase extends DrupalWebTestCase { $this->loggedInUser = FALSE; // Change cookie file for user. - $this->cookieFile = file_directory_path('temporary') . '/cookie.' . $uid . '.txt'; + $this->cookieFile = file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath() . '/cookie.' . $uid . '.txt'; $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile; $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE; $this->drupalGet('session-test/get'); diff --git a/modules/simpletest/tests/session_test.info b/modules/simpletest/tests/session_test.info index 962c8b9e1a7354799db4a7de36302b3ceb42b76f..570a15f46a4602a27b519831de9dd148c93bb2ac 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/system_dependencies_test.info b/modules/simpletest/tests/system_dependencies_test.info index 6adb07d3b5d5aea60a3b65860daa4c537be1361e..77ba2a83f31b347c5da9030df1d3dd0884aaae16 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/system_test.info b/modules/simpletest/tests/system_test.info index 6a29e371998a97503f66bc7a2c123b2dceb828d0..079d22fd53ad6a18bd74357fe3cb8e4d9877218b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module index 355655a2f29304444dc79d39a20468c16b777fe3..f7833c45f859a34e1edef624b2c8bd75701919d6 100644 --- a/modules/simpletest/tests/system_test.module +++ b/modules/simpletest/tests/system_test.module @@ -1,5 +1,5 @@ <?php -// $Id: system_test.module,v 1.30 2010/06/28 20:27:34 dries Exp $ +// $Id: system_test.module,v 1.32 2010/08/01 23:35:01 dries Exp $ /** * Implements hook_menu(). @@ -263,11 +263,11 @@ function system_test_page_build(&$page) { $page['footer'] = drupal_set_page_content(); $page['footer']['main']['#markup'] = '<div id="system-test-content">' . $page['footer']['main']['#markup'] . '</div>'; } - else if ($menu_item['path'] == 'system-test/main-content-fallback') { + elseif ($menu_item['path'] == 'system-test/main-content-fallback') { drupal_set_page_content(); $main_content_display = FALSE; } - else if ($menu_item['path'] == 'system-test/main-content-duplication') { + elseif ($menu_item['path'] == 'system-test/main-content-duplication') { drupal_set_page_content(); } } @@ -306,7 +306,7 @@ function _system_test_second_shutdown_function($arg1, $arg2) { // Throw an exception with an HTML tag. Since this is called in a shutdown // function, it will not bubble up to the default exception handler but will - // be catched in _drupal_shutdown_function() and be displayed through + // be caught in _drupal_shutdown_function() and be displayed through // _drupal_render_exception_safe(). throw new Exception('Drupal is <blink>awesome</blink>.'); } diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info index 10ac2b8b32d1a0753d4807396fadef3c79e251dd..94b2c01373d2a7b43ed4b403385365d7514478bb 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index 2d27c260b93fb410febd16bb7d1b339a58d1ea09..1b5c1c84d7de35f7f74149be4dff54eadb195cfd 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -1,5 +1,5 @@ <?php -// $Id: theme.test,v 1.16 2010/04/29 05:22:06 webchick Exp $ +// $Id: theme.test,v 1.20 2010/08/22 12:46:21 dries Exp $ /** * @file @@ -66,6 +66,14 @@ class ThemeUnitTest extends DrupalWebTestCase { $_GET['q'] = $q; $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.')); } + + /** + * Ensures theme hook_*_alter() implementations can run before anything is rendered. + */ + function testAlter() { + $this->drupalGet('theme-test/alter'); + $this->assertText('The altered data is test_theme_theme_test_alter_alter was invoked.', t('The theme was able to implement an alter hook during page building before anything was rendered.')); + } } /** @@ -170,7 +178,6 @@ class ThemeHookInitUnitTest extends DrupalWebTestCase { function setUp() { parent::setUp('theme_test'); - variable_set('theme_default', 'garland'); } /** @@ -179,6 +186,33 @@ class ThemeHookInitUnitTest extends DrupalWebTestCase { function testThemeInitializationHookInit() { $this->drupalGet('theme-test/hook-init'); $this->assertRaw('Themed output generated in hook_init()', t('Themed output generated in hook_init() correctly appears on the page.')); - $this->assertRaw('garland/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init().")); + $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init().")); + } +} + +/** + * Tests autocompletion not loading registry. + */ +class ThemeFastTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Theme fast initialization', + 'description' => 'Test that autocompletion does not load the registry.', + 'group' => 'Theme' + ); + } + + function setUp() { + parent::setUp('theme_test'); + $this->account = $this->drupalCreateUser(array('access user profiles')); + } + + /** + * Tests access to user autocompletion and verify the correct results. + */ + function testUserAutocomplete() { + $this->drupalLogin($this->account); + $this->drupalGet('user/autocomplete/' . $this->account->name); + $this->assertText('registry not initialized', t('The registry was not initialized')); } } diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info index cbd0558f0e419b25683aab5336a50edf0a946fff..ab59809d428ad272dd2bd0d14a9807687a1b0743 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module index 6815a1cfd696ae06b7d8796c22d751cd755b2bac..63539d655ce851cff58b745c14707c9c1b736e6e 100644 --- a/modules/simpletest/tests/theme_test.module +++ b/modules/simpletest/tests/theme_test.module @@ -1,5 +1,5 @@ <?php -// $Id: theme_test.module,v 1.2 2010/04/29 05:22:06 webchick Exp $ +// $Id: theme_test.module,v 1.4 2010/08/22 12:46:21 dries Exp $ /** * Implements hook_menu(). @@ -12,6 +12,13 @@ function theme_test_menu() { 'theme callback' => '_theme_custom_theme', 'type' => MENU_CALLBACK, ); + $items['theme-test/alter'] = array( + 'title' => 'Suggestion', + 'page callback' => '_theme_test_alter', + 'access arguments' => array('access content'), + 'theme callback' => '_theme_custom_theme', + 'type' => MENU_CALLBACK, + ); $items['theme-test/hook-init'] = array( 'page callback' => 'theme_test_hook_init_page_callback', 'access callback' => TRUE, @@ -24,15 +31,36 @@ function theme_test_menu() { * Implements hook_init(). */ function theme_test_init() { - // First, force the theme registry to be rebuilt on this page request. This - // allows us to test a full initialization of the theme system in the code - // below. - drupal_theme_rebuild(); - // Next, initialize the theme system by storing themed text in a global - // variable. We will use this later in theme_test_hook_init_page_callback() - // to test that even when the theme system is initialized this early, it is - // still capable of returning output and theming the page as a whole. - $GLOBALS['theme_test_output'] = theme('more_link', array('url' => url('user'), 'title' => 'Themed output generated in hook_init()')); + if (arg(0) == 'theme-test' && arg(1) == 'hook-init') { + // First, force the theme registry to be rebuilt on this page request. This + // allows us to test a full initialization of the theme system in the code + // below. + drupal_theme_rebuild(); + // Next, initialize the theme system by storing themed text in a global + // variable. We will use this later in theme_test_hook_init_page_callback() + // to test that even when the theme system is initialized this early, it is + // still capable of returning output and theming the page as a whole. + $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()')); + } +} + +/** + * Implements hook_exit(). + */ +function theme_test_exit() { + if (arg(0) == 'user') { + // Register a fake registry loading callback. If it gets called by + // theme_get_registry(), the registry has not been initialized yet. + _theme_registry_callback('_theme_test_load_registry', array()); + print theme_get_registry() ? 'registry initialized' : 'registry not initialized'; + } +} + +/** + * Fake registry loading callback. + */ +function _theme_test_load_registry() { + return array(); } /** @@ -49,6 +77,19 @@ function _theme_custom_theme() { return 'test_theme'; } +/** + * Page callback, calls drupal_alter(). + * + * This is for testing that the theme can have hook_*_alter() implementations + * that run during page callback execution, even before theme() is called for + * the first time. + */ +function _theme_test_alter() { + $data = 'foo'; + drupal_alter('theme_test_alter', $data); + return "The altered data is $data."; +} + /** * Page callback, calls a theme hook suggestion. */ diff --git a/modules/simpletest/tests/unicode.test b/modules/simpletest/tests/unicode.test index 42cb98762f68e9f7298cd9d0ddd1c1ea4c9947aa..7e6f0348c676bf748b8f4fc5378c40c296d5110a 100644 --- a/modules/simpletest/tests/unicode.test +++ b/modules/simpletest/tests/unicode.test @@ -1,5 +1,5 @@ <?php -// $Id: unicode.test,v 1.6 2010/06/10 15:20:48 dries Exp $ +// $Id: unicode.test,v 1.9 2010/08/11 10:58:22 dries Exp $ /** * @file @@ -218,36 +218,6 @@ class UnicodeUnitTest extends DrupalWebTestCase { } } - function testDecodeEntitiesExclusion() { - $testcase = array( - 'Drupal' => 'Drupal', - '<script>' => '<script>', - '<script>' => '<script>', - '<script>' => '<script>', - '&lt;script&gt;' => '&lt;script&gt;', - '"' => '"', - '"' => '"', - '&#34;' => '&#34;', - '"' => '"', - '&quot;' => '&quot;', - "'" => "'", - ''' => "'", - '&#39;' => '&#39;', - '©' => '©', - '©' => '©', - '©' => '©', - '→' => '→', - '→' => '→', - 'âž¼' => 'âž¼', - '➼' => 'âž¼', - '€' => '€', - ); - $exclude = array('<', '&', '"'); - foreach ($testcase as $input => $output) { - $this->assertIdentical(decode_entities($input, $exclude), $output, t('Make sure the decoded entity of %input, excluding %excludes, is %output', array('%input' => $input, '%excludes' => implode(',', $exclude), '%output' => $output))); - } - } - /** * Tests truncate_utf8(). */ diff --git a/modules/simpletest/tests/update.test b/modules/simpletest/tests/update.test index 944dba7f6c2b9244fae2866a5e58ff5a3de703e7..a9b0e75e14b518f26e863966c17d684b9d7a3f58 100644 --- a/modules/simpletest/tests/update.test +++ b/modules/simpletest/tests/update.test @@ -1,5 +1,5 @@ <?php -// $Id: update.test,v 1.2 2010/04/28 05:28:22 webchick Exp $ +// $Id: update.test,v 1.4 2010/08/05 23:53:38 webchick Exp $ /** * @file diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info index ad5b98b23343c87882d0e3e2324d594c4c27f1ce..350de123e785a012883a9981a33cc9db730bd401 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info index fe1f585f1c400c6ffb6d2a1040ef2dcebb0c3b00..90595f0ea3064e9b7e09db102a25a28355272ae5 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info index 8e0d7cb1171d55956bd492cdee5f4a367cc13b32..015493b7724f6b200a9ddb7951532843ec14fc7b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/upgrade/drupal-6.comments.database.php b/modules/simpletest/tests/upgrade/drupal-6.comments.database.php new file mode 100644 index 0000000000000000000000000000000000000000..7da250407c817f42e033686cb32cf09b89f95e1c --- /dev/null +++ b/modules/simpletest/tests/upgrade/drupal-6.comments.database.php @@ -0,0 +1,40 @@ +<?php +db_update('node')->fields(array( + 'comment' => 2 + )) + ->condition('nid', 1) + ->execute(); + +db_insert('comments')->fields(array( + 'cid', + 'pid', + 'nid', + 'uid', + 'subject', + 'comment', + 'hostname', + 'timestamp', + 'status', + 'format', + 'thread', + 'name', + 'mail', + 'homepage', +)) +->values(array( + 'cid' => 1, + 'pid' => 0, + 'nid' => 1, + 'uid' => 3, + 'subject' => 'Comment title 1', + 'comment' => 'Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1', + 'hostname' => '127.0.0.1', + 'timestamp' => 1008617630, + 'status' => 0, + 'format' => 1, + 'thread' => '01/', + 'name' => NULL, + 'mail' => NULL, + 'homepage' => '', +)) +->execute(); diff --git a/modules/simpletest/tests/upgrade/drupal-6.filled.database.php b/modules/simpletest/tests/upgrade/drupal-6.filled.database.php new file mode 100644 index 0000000000000000000000000000000000000000..d603da94c76424ff4c0c03555d11d43fba701b44 --- /dev/null +++ b/modules/simpletest/tests/upgrade/drupal-6.filled.database.php @@ -0,0 +1,20360 @@ +<?php +// $Id: drupal-6.filled.database.php,v 1.3 2010/09/11 00:39:49 webchick Exp $ + +/** + * @file + * Filled installation of Drupal 6.17, for test purposes. + * + * This file was generated by the dump-database-d6.sh tool, from an + * installation of Drupal 6, filled with data using the generate-d6-content.sh + * tool. It has the following modules installed: + * - block + * - color + * - comment + * - dblog + * - filter + * - help + * - menu + * - node + * - path + * - poll + * - system + * - taxonomy + * - update + * - user + */ + +db_create_table('access', array( + 'fields' => array( + 'aid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'mask' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'aid', + ), + 'module' => 'user', + 'name' => 'access', +)); + +db_create_table('actions', array( + 'fields' => array( + 'aid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '0', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'callback' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'parameters' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '0', + ), + ), + 'primary key' => array( + 'aid', + ), + 'module' => 'system', + 'name' => 'actions', +)); +db_insert('actions')->fields(array( + 'aid', + 'type', + 'callback', + 'parameters', + 'description', +)) +->values(array( + 'aid' => 'comment_publish_action', + 'type' => 'comment', + 'callback' => 'comment_publish_action', + 'parameters' => '', + 'description' => 'Publish comment', +)) +->values(array( + 'aid' => 'comment_unpublish_action', + 'type' => 'comment', + 'callback' => 'comment_unpublish_action', + 'parameters' => '', + 'description' => 'Unpublish comment', +)) +->values(array( + 'aid' => 'node_make_sticky_action', + 'type' => 'node', + 'callback' => 'node_make_sticky_action', + 'parameters' => '', + 'description' => 'Make post sticky', +)) +->values(array( + 'aid' => 'node_make_unsticky_action', + 'type' => 'node', + 'callback' => 'node_make_unsticky_action', + 'parameters' => '', + 'description' => 'Make post unsticky', +)) +->values(array( + 'aid' => 'node_promote_action', + 'type' => 'node', + 'callback' => 'node_promote_action', + 'parameters' => '', + 'description' => 'Promote post to front page', +)) +->values(array( + 'aid' => 'node_publish_action', + 'type' => 'node', + 'callback' => 'node_publish_action', + 'parameters' => '', + 'description' => 'Publish post', +)) +->values(array( + 'aid' => 'node_save_action', + 'type' => 'node', + 'callback' => 'node_save_action', + 'parameters' => '', + 'description' => 'Save post', +)) +->values(array( + 'aid' => 'node_unpromote_action', + 'type' => 'node', + 'callback' => 'node_unpromote_action', + 'parameters' => '', + 'description' => 'Remove post from front page', +)) +->values(array( + 'aid' => 'node_unpublish_action', + 'type' => 'node', + 'callback' => 'node_unpublish_action', + 'parameters' => '', + 'description' => 'Unpublish post', +)) +->values(array( + 'aid' => 'user_block_ip_action', + 'type' => 'user', + 'callback' => 'user_block_ip_action', + 'parameters' => '', + 'description' => 'Ban IP address of current user', +)) +->values(array( + 'aid' => 'user_block_user_action', + 'type' => 'user', + 'callback' => 'user_block_user_action', + 'parameters' => '', + 'description' => 'Block current user', +)) +->execute(); + +db_create_table('actions_aid', array( + 'fields' => array( + 'aid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + ), + 'primary key' => array( + 'aid', + ), + 'module' => 'system', + 'name' => 'actions_aid', +)); + +db_create_table('authmap', array( + 'fields' => array( + 'aid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'authname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'unique keys' => array( + 'authname' => array( + 'authname', + ), + ), + 'primary key' => array( + 'aid', + ), + 'module' => 'user', + 'name' => 'authmap', +)); + +db_create_table('batch', array( + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'token' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + ), + 'batch' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + ), + 'primary key' => array( + 'bid', + ), + 'indexes' => array( + 'token' => array( + 'token', + ), + ), + 'module' => 'system', + 'name' => 'batch', +)); + +db_create_table('blocks', array( + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'delta' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '0', + ), + 'theme' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'region' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'custom' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'throttle' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'visibility' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'pages' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'cache' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 1, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'bid', + ), + 'unique keys' => array( + 'tmd' => array( + 'theme', + 'module', + 'delta', + ), + ), + 'indexes' => array( + 'list' => array( + 'theme', + 'status', + 'region', + 'weight', + 'module', + ), + ), + 'module' => 'block', + 'name' => 'blocks', +)); +db_insert('blocks')->fields(array( + 'bid', + 'module', + 'delta', + 'theme', + 'status', + 'weight', + 'region', + 'custom', + 'throttle', + 'visibility', + 'pages', + 'title', + 'cache', +)) +->values(array( + 'bid' => '1', + 'module' => 'user', + 'delta' => '0', + 'theme' => 'garland', + 'status' => '1', + 'weight' => '0', + 'region' => 'left', + 'custom' => '0', + 'throttle' => '0', + 'visibility' => '0', + 'pages' => '', + 'title' => '', + 'cache' => '-1', +)) +->values(array( + 'bid' => '2', + 'module' => 'user', + 'delta' => '1', + 'theme' => 'garland', + 'status' => '1', + 'weight' => '0', + 'region' => 'left', + 'custom' => '0', + 'throttle' => '0', + 'visibility' => '0', + 'pages' => '', + 'title' => '', + 'cache' => '-1', +)) +->values(array( + 'bid' => '3', + 'module' => 'system', + 'delta' => '0', + 'theme' => 'garland', + 'status' => '1', + 'weight' => '10', + 'region' => 'footer', + 'custom' => '0', + 'throttle' => '0', + 'visibility' => '0', + 'pages' => '', + 'title' => '', + 'cache' => '-1', +)) +->execute(); + +db_create_table('blocks_roles', array( + 'fields' => array( + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + ), + 'delta' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), + 'rid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + ), + 'primary key' => array( + 'module', + 'delta', + 'rid', + ), + 'indexes' => array( + 'rid' => array( + 'rid', + ), + ), + 'module' => 'block', + 'name' => 'blocks_roles', +)); + +db_create_table('boxes', array( + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'body' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + 'info' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'format' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'unique keys' => array( + 'info' => array( + 'info', + ), + ), + 'primary key' => array( + 'bid', + ), + 'module' => 'block', + 'name' => 'boxes', +)); + +db_create_table('cache', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'system', + 'name' => 'cache', +)); + +db_create_table('cache_block', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'block', + 'name' => 'cache_block', +)); + +db_create_table('cache_filter', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'filter', + 'name' => 'cache_filter', +)); + +db_create_table('cache_form', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'system', + 'name' => 'cache_form', +)); + +db_create_table('cache_menu', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'system', + 'name' => 'cache_menu', +)); + +db_create_table('cache_page', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'system', + 'name' => 'cache_page', +)); + +db_create_table('cache_update', array( + 'fields' => array( + 'cid' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'headers' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'serialized' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'update', + 'name' => 'cache_update', +)); + +db_create_table('comments', array( + 'fields' => array( + 'cid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'pid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'subject' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'comment' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'hostname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'format' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + 'thread' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 60, + 'not null' => FALSE, + ), + 'mail' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => FALSE, + ), + 'homepage' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'pid' => array( + 'pid', + ), + 'nid' => array( + 'nid', + ), + 'status' => array( + 'status', + ), + ), + 'primary key' => array( + 'cid', + ), + 'module' => 'comment', + 'name' => 'comments', +)); + +db_create_table('files', array( + 'fields' => array( + 'fid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'filename' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'filepath' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'filemime' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'filesize' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'timestamp' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'uid' => array( + 'uid', + ), + 'status' => array( + 'status', + ), + 'timestamp' => array( + 'timestamp', + ), + ), + 'primary key' => array( + 'fid', + ), + 'module' => 'system', + 'name' => 'files', +)); + +db_create_table('filter_formats', array( + 'fields' => array( + 'format' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'roles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'cache' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'format', + ), + 'unique keys' => array( + 'name' => array( + 'name', + ), + ), + 'module' => 'filter', + 'name' => 'filter_formats', +)); +db_insert('filter_formats')->fields(array( + 'format', + 'name', + 'roles', + 'cache', +)) +->values(array( + 'format' => '1', + 'name' => 'Filtered HTML', + 'roles' => ',1,2,', + 'cache' => '1', +)) +->values(array( + 'format' => '2', + 'name' => 'Full HTML', + 'roles' => '', + 'cache' => '1', +)) +->execute(); + +db_create_table('filters', array( + 'fields' => array( + 'fid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'format' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'fid', + ), + 'unique keys' => array( + 'fmd' => array( + 'format', + 'module', + 'delta', + ), + ), + 'indexes' => array( + 'list' => array( + 'format', + 'weight', + 'module', + 'delta', + ), + ), + 'module' => 'filter', + 'name' => 'filters', +)); +db_insert('filters')->fields(array( + 'fid', + 'format', + 'module', + 'delta', + 'weight', +)) +->values(array( + 'fid' => '1', + 'format' => '1', + 'module' => 'filter', + 'delta' => '2', + 'weight' => '0', +)) +->values(array( + 'fid' => '2', + 'format' => '1', + 'module' => 'filter', + 'delta' => '0', + 'weight' => '1', +)) +->values(array( + 'fid' => '3', + 'format' => '1', + 'module' => 'filter', + 'delta' => '1', + 'weight' => '2', +)) +->values(array( + 'fid' => '4', + 'format' => '1', + 'module' => 'filter', + 'delta' => '3', + 'weight' => '10', +)) +->values(array( + 'fid' => '5', + 'format' => '2', + 'module' => 'filter', + 'delta' => '2', + 'weight' => '0', +)) +->values(array( + 'fid' => '6', + 'format' => '2', + 'module' => 'filter', + 'delta' => '1', + 'weight' => '1', +)) +->values(array( + 'fid' => '7', + 'format' => '2', + 'module' => 'filter', + 'delta' => '3', + 'weight' => '10', +)) +->execute(); + +db_create_table('flood', array( + 'fields' => array( + 'fid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'event' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'hostname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'fid', + ), + 'indexes' => array( + 'allow' => array( + 'event', + 'hostname', + 'timestamp', + ), + ), + 'module' => 'system', + 'name' => 'flood', +)); + +db_create_table('history', array( + 'fields' => array( + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'uid', + 'nid', + ), + 'indexes' => array( + 'nid' => array( + 'nid', + ), + ), + 'module' => 'system', + 'name' => 'history', +)); + +db_create_table('menu_custom', array( + 'fields' => array( + 'menu_name' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'description' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + ), + 'primary key' => array( + 'menu_name', + ), + 'module' => 'menu', + 'name' => 'menu_custom', +)); +db_insert('menu_custom')->fields(array( + 'menu_name', + 'title', + 'description', +)) +->values(array( + 'menu_name' => 'navigation', + 'title' => 'Navigation', + 'description' => 'The navigation menu is provided by Drupal and is the main interactive menu for any site. It is usually the only menu that contains personalized links for authenticated users, and is often not even visible to anonymous users.', +)) +->values(array( + 'menu_name' => 'primary-links', + 'title' => 'Primary links', + 'description' => 'Primary links are often used at the theme layer to show the major sections of a site. A typical representation for primary links would be tabs along the top.', +)) +->values(array( + 'menu_name' => 'secondary-links', + 'title' => 'Secondary links', + 'description' => 'Secondary links are often used for pages like legal notices, contact details, and other secondary navigation items that play a lesser role than primary links', +)) +->execute(); + +db_create_table('menu_links', array( + 'fields' => array( + 'menu_name' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'mlid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'plid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'link_path' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'router_path' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'link_title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'options' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => 'system', + ), + 'hidden' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'external' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'has_children' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'expanded' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'depth' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'customized' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'p1' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p2' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p3' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p4' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p5' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p6' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p7' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p8' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'p9' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'updated' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + ), + 'indexes' => array( + 'path_menu' => array( + array( + 'link_path', + 128, + ), + 'menu_name', + ), + 'menu_plid_expand_child' => array( + 'menu_name', + 'plid', + 'expanded', + 'has_children', + ), + 'menu_parents' => array( + 'menu_name', + 'p1', + 'p2', + 'p3', + 'p4', + 'p5', + 'p6', + 'p7', + 'p8', + 'p9', + ), + 'router_path' => array( + array( + 'router_path', + 128, + ), + ), + ), + 'primary key' => array( + 'mlid', + ), + 'module' => 'system', + 'name' => 'menu_links', +)); +db_insert('menu_links')->fields(array( + 'menu_name', + 'mlid', + 'plid', + 'link_path', + 'router_path', + 'link_title', + 'options', + 'module', + 'hidden', + 'external', + 'has_children', + 'expanded', + 'weight', + 'depth', + 'customized', + 'p1', + 'p2', + 'p3', + 'p4', + 'p5', + 'p6', + 'p7', + 'p8', + 'p9', + 'updated', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '1', + 'plid' => '0', + 'link_path' => 'batch', + 'router_path' => 'batch', + 'link_title' => '', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '1', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '2', + 'plid' => '0', + 'link_path' => 'admin', + 'router_path' => 'admin', + 'link_title' => 'Administer', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '9', + 'depth' => '1', + 'customized' => '0', + 'p1' => '2', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '3', + 'plid' => '0', + 'link_path' => 'node', + 'router_path' => 'node', + 'link_title' => 'Content', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '3', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '4', + 'plid' => '0', + 'link_path' => 'logout', + 'router_path' => 'logout', + 'link_title' => 'Log out', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '10', + 'depth' => '1', + 'customized' => '0', + 'p1' => '4', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '5', + 'plid' => '0', + 'link_path' => 'rss.xml', + 'router_path' => 'rss.xml', + 'link_title' => 'RSS feed', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '5', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '6', + 'plid' => '0', + 'link_path' => 'user', + 'router_path' => 'user', + 'link_title' => 'User account', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '6', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '7', + 'plid' => '0', + 'link_path' => 'node/%', + 'router_path' => 'node/%', + 'link_title' => '', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '7', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '8', + 'plid' => '2', + 'link_path' => 'admin/compact', + 'router_path' => 'admin/compact', + 'link_title' => 'Compact mode', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '8', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '9', + 'plid' => '0', + 'link_path' => 'filter/tips', + 'router_path' => 'filter/tips', + 'link_title' => 'Compose tips', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '9', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '10', + 'plid' => '2', + 'link_path' => 'admin/content', + 'router_path' => 'admin/content', + 'link_title' => 'Content management', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:27:\"Manage your site's content.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '-10', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '11', + 'plid' => '0', + 'link_path' => 'node/add', + 'router_path' => 'node/add', + 'link_title' => 'Create content', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '1', + 'depth' => '1', + 'customized' => '0', + 'p1' => '11', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '12', + 'plid' => '0', + 'link_path' => 'comment/delete', + 'router_path' => 'comment/delete', + 'link_title' => 'Delete comment', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '12', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '13', + 'plid' => '0', + 'link_path' => 'comment/edit', + 'router_path' => 'comment/edit', + 'link_title' => 'Edit comment', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '13', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '14', + 'plid' => '0', + 'link_path' => 'system/files', + 'router_path' => 'system/files', + 'link_title' => 'File download', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '14', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '15', + 'plid' => '2', + 'link_path' => 'admin/reports', + 'router_path' => 'admin/reports', + 'link_title' => 'Reports', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:59:"View reports from system logs and other status information.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '5', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '16', + 'plid' => '2', + 'link_path' => 'admin/build', + 'router_path' => 'admin/build', + 'link_title' => 'Site building', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:38:"Control how your site looks and feels.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '-10', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '17', + 'plid' => '2', + 'link_path' => 'admin/settings', + 'router_path' => 'admin/settings', + 'link_title' => 'Site configuration', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:40:"Adjust basic site configuration options.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '-5', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '18', + 'plid' => '0', + 'link_path' => 'user/autocomplete', + 'router_path' => 'user/autocomplete', + 'link_title' => 'User autocomplete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '18', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '19', + 'plid' => '2', + 'link_path' => 'admin/user', + 'router_path' => 'admin/user', + 'link_title' => 'User management', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:61:\"Manage your site's users, groups and access to site features.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '20', + 'plid' => '0', + 'link_path' => 'user/%', + 'router_path' => 'user/%', + 'link_title' => 'My account', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '20', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '21', + 'plid' => '19', + 'link_path' => 'admin/user/rules', + 'router_path' => 'admin/user/rules', + 'link_title' => 'Access rules', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:80:"List and create rules to disallow usernames, e-mail addresses, and IP addresses.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '21', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '22', + 'plid' => '17', + 'link_path' => 'admin/settings/actions', + 'router_path' => 'admin/settings/actions', + 'link_title' => 'Actions', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:41:"Manage the actions defined for your site.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '22', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '23', + 'plid' => '17', + 'link_path' => 'admin/settings/admin', + 'router_path' => 'admin/settings/admin', + 'link_title' => 'Administration theme', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:55:"Settings for how your administrative pages should look.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '23', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '24', + 'plid' => '16', + 'link_path' => 'admin/build/block', + 'router_path' => 'admin/build/block', + 'link_title' => 'Blocks', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:79:\"Configure what block content appears in your site's sidebars and other regions.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '24', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '25', + 'plid' => '17', + 'link_path' => 'admin/settings/clean-urls', + 'router_path' => 'admin/settings/clean-urls', + 'link_title' => 'Clean URLs', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:43:"Enable or disable clean URLs for your site.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '25', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '26', + 'plid' => '10', + 'link_path' => 'admin/content/comment', + 'router_path' => 'admin/content/comment', + 'link_title' => 'Comments', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:61:"List and edit site comments and the comment moderation queue.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '26', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '27', + 'plid' => '10', + 'link_path' => 'admin/content/node', + 'router_path' => 'admin/content/node', + 'link_title' => 'Content', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:43:\"View, edit, and delete your site's content.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '27', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '28', + 'plid' => '10', + 'link_path' => 'admin/content/types', + 'router_path' => 'admin/content/types', + 'link_title' => 'Content types', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:82:"Manage posts by content type, including default status, front page promotion, etc.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '28', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '29', + 'plid' => '17', + 'link_path' => 'admin/settings/date-time', + 'router_path' => 'admin/settings/date-time', + 'link_title' => 'Date and time', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:89:\"Settings for how Drupal displays date and time, as well as the system's default timezone.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '29', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '30', + 'plid' => '0', + 'link_path' => 'node/%/delete', + 'router_path' => 'node/%/delete', + 'link_title' => 'Delete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '1', + 'depth' => '1', + 'customized' => '0', + 'p1' => '30', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '31', + 'plid' => '20', + 'link_path' => 'user/%/delete', + 'router_path' => 'user/%/delete', + 'link_title' => 'Delete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '20', + 'p2' => '31', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '32', + 'plid' => '17', + 'link_path' => 'admin/settings/error-reporting', + 'router_path' => 'admin/settings/error-reporting', + 'link_title' => 'Error reporting', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:93:"Control how Drupal deals with errors including 403/404 errors as well as PHP error reporting.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '32', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '33', + 'plid' => '17', + 'link_path' => 'admin/settings/file-system', + 'router_path' => 'admin/settings/file-system', + 'link_title' => 'File system', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:68:"Tell Drupal where to store uploaded files and how they are accessed.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '33', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '34', + 'plid' => '17', + 'link_path' => 'admin/settings/image-toolkit', + 'router_path' => 'admin/settings/image-toolkit', + 'link_title' => 'Image toolkit', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:74:"Choose which image toolkit to use if you have installed optional toolkits.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '34', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '35', + 'plid' => '17', + 'link_path' => 'admin/settings/filters', + 'router_path' => 'admin/settings/filters', + 'link_title' => 'Input formats', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:127:"Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '35', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '36', + 'plid' => '17', + 'link_path' => 'admin/settings/logging', + 'router_path' => 'admin/settings/logging', + 'link_title' => 'Logging and alerts', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:156:\"Settings for logging and alerts modules. Various modules can route Drupal's system events to different destination, such as syslog, database, email, ...etc.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '36', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '37', + 'plid' => '16', + 'link_path' => 'admin/build/menu', + 'router_path' => 'admin/build/menu', + 'link_title' => 'Menus', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:116:\"Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '1', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '38', + 'plid' => '16', + 'link_path' => 'admin/build/modules', + 'router_path' => 'admin/build/modules', + 'link_title' => 'Modules', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:47:"Enable or disable add-on modules for your site.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '38', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '39', + 'plid' => '17', + 'link_path' => 'admin/settings/performance', + 'router_path' => 'admin/settings/performance', + 'link_title' => 'Performance', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:101:"Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '39', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '40', + 'plid' => '19', + 'link_path' => 'admin/user/permissions', + 'router_path' => 'admin/user/permissions', + 'link_title' => 'Permissions', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:64:"Determine access to features by selecting permissions for roles.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '40', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '41', + 'plid' => '10', + 'link_path' => 'admin/content/node-settings', + 'router_path' => 'admin/content/node-settings', + 'link_title' => 'Post settings', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:126:"Control posting behavior, such as teaser length, requiring previews before posting, and the number of posts on the front page.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '41', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '42', + 'plid' => '10', + 'link_path' => 'admin/content/rss-publishing', + 'router_path' => 'admin/content/rss-publishing', + 'link_title' => 'RSS publishing', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:92:"Configure the number of items per feed and whether feeds should be titles/teasers/full-text.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '42', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '43', + 'plid' => '0', + 'link_path' => 'comment/reply/%', + 'router_path' => 'comment/reply/%', + 'link_title' => 'Reply to comment', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '43', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '44', + 'plid' => '19', + 'link_path' => 'admin/user/roles', + 'router_path' => 'admin/user/roles', + 'link_title' => 'Roles', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:30:"List, edit, or add user roles.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '44', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '45', + 'plid' => '17', + 'link_path' => 'admin/settings/site-information', + 'router_path' => 'admin/settings/site-information', + 'link_title' => 'Site information', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:107:"Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '45', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '46', + 'plid' => '17', + 'link_path' => 'admin/settings/site-maintenance', + 'router_path' => 'admin/settings/site-maintenance', + 'link_title' => 'Site maintenance', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:63:"Take the site off-line for maintenance or bring it back online.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '46', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '47', + 'plid' => '15', + 'link_path' => 'admin/reports/status', + 'router_path' => 'admin/reports/status', + 'link_title' => 'Status report', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:74:\"Get a status report about your site's operation and any detected problems.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '10', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '47', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '48', + 'plid' => '16', + 'link_path' => 'admin/build/themes', + 'router_path' => 'admin/build/themes', + 'link_title' => 'Themes', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:57:"Change which theme your site uses or allows users to set.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '48', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '49', + 'plid' => '19', + 'link_path' => 'admin/user/settings', + 'router_path' => 'admin/user/settings', + 'link_title' => 'User settings', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:101:"Configure default behavior of users, including registration requirements, e-mails, and user pictures.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '49', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '50', + 'plid' => '19', + 'link_path' => 'admin/user/user', + 'router_path' => 'admin/user/user', + 'link_title' => 'Users', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:26:"List, add, and edit users.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '50', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '51', + 'plid' => '35', + 'link_path' => 'admin/settings/filters/%', + 'router_path' => 'admin/settings/filters/%', + 'link_title' => '', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '35', + 'p4' => '51', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '52', + 'plid' => '25', + 'link_path' => 'admin/settings/clean-urls/check', + 'router_path' => 'admin/settings/clean-urls/check', + 'link_title' => 'Clean URL check', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '25', + 'p4' => '52', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '53', + 'plid' => '22', + 'link_path' => 'admin/settings/actions/configure', + 'router_path' => 'admin/settings/actions/configure', + 'link_title' => 'Configure an advanced action', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '22', + 'p4' => '53', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '54', + 'plid' => '24', + 'link_path' => 'admin/build/block/configure', + 'router_path' => 'admin/build/block/configure', + 'link_title' => 'Configure block', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '24', + 'p4' => '54', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '55', + 'plid' => '16', + 'link_path' => 'admin/build/menu-customize/%', + 'router_path' => 'admin/build/menu-customize/%', + 'link_title' => 'Customize menu', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '55', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '56', + 'plid' => '29', + 'link_path' => 'admin/settings/date-time/lookup', + 'router_path' => 'admin/settings/date-time/lookup', + 'link_title' => 'Date and time lookup', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '29', + 'p4' => '56', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '57', + 'plid' => '24', + 'link_path' => 'admin/build/block/delete', + 'router_path' => 'admin/build/block/delete', + 'link_title' => 'Delete block', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '24', + 'p4' => '57', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '58', + 'plid' => '35', + 'link_path' => 'admin/settings/filters/delete', + 'router_path' => 'admin/settings/filters/delete', + 'link_title' => 'Delete input format', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '35', + 'p4' => '58', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '59', + 'plid' => '21', + 'link_path' => 'admin/user/rules/delete', + 'router_path' => 'admin/user/rules/delete', + 'link_title' => 'Delete rule', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '21', + 'p4' => '59', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '60', + 'plid' => '44', + 'link_path' => 'admin/user/roles/edit', + 'router_path' => 'admin/user/roles/edit', + 'link_title' => 'Edit role', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '44', + 'p4' => '60', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '61', + 'plid' => '21', + 'link_path' => 'admin/user/rules/edit', + 'router_path' => 'admin/user/rules/edit', + 'link_title' => 'Edit rule', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '19', + 'p3' => '21', + 'p4' => '61', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '62', + 'plid' => '47', + 'link_path' => 'admin/reports/status/php', + 'router_path' => 'admin/reports/status/php', + 'link_title' => 'PHP', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '47', + 'p4' => '62', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '63', + 'plid' => '41', + 'link_path' => 'admin/content/node-settings/rebuild', + 'router_path' => 'admin/content/node-settings/rebuild', + 'link_title' => 'Rebuild permissions', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '41', + 'p4' => '63', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '64', + 'plid' => '22', + 'link_path' => 'admin/settings/actions/orphan', + 'router_path' => 'admin/settings/actions/orphan', + 'link_title' => 'Remove orphans', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '22', + 'p4' => '64', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '65', + 'plid' => '47', + 'link_path' => 'admin/reports/status/run-cron', + 'router_path' => 'admin/reports/status/run-cron', + 'link_title' => 'Run cron', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '47', + 'p4' => '65', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '66', + 'plid' => '47', + 'link_path' => 'admin/reports/status/sql', + 'router_path' => 'admin/reports/status/sql', + 'link_title' => 'SQL', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '47', + 'p4' => '66', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '67', + 'plid' => '22', + 'link_path' => 'admin/settings/actions/delete/%', + 'router_path' => 'admin/settings/actions/delete/%', + 'link_title' => 'Delete action', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:17:"Delete an action.";}}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '22', + 'p4' => '67', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '68', + 'plid' => '0', + 'link_path' => 'admin/build/menu-customize/%/delete', + 'router_path' => 'admin/build/menu-customize/%/delete', + 'link_title' => 'Delete menu', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '68', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '69', + 'plid' => '24', + 'link_path' => 'admin/build/block/list/js', + 'router_path' => 'admin/build/block/list/js', + 'link_title' => 'JavaScript List Form', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '24', + 'p4' => '69', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '70', + 'plid' => '38', + 'link_path' => 'admin/build/modules/list/confirm', + 'router_path' => 'admin/build/modules/list/confirm', + 'link_title' => 'List', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '38', + 'p4' => '70', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '71', + 'plid' => '0', + 'link_path' => 'user/reset/%/%/%', + 'router_path' => 'user/reset/%/%/%', + 'link_title' => 'Reset password', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '71', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '72', + 'plid' => '38', + 'link_path' => 'admin/build/modules/uninstall/confirm', + 'router_path' => 'admin/build/modules/uninstall/confirm', + 'link_title' => 'Uninstall', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '38', + 'p4' => '72', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '73', + 'plid' => '0', + 'link_path' => 'node/%/revisions/%/delete', + 'router_path' => 'node/%/revisions/%/delete', + 'link_title' => 'Delete earlier revision', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '73', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '74', + 'plid' => '0', + 'link_path' => 'node/%/revisions/%/revert', + 'router_path' => 'node/%/revisions/%/revert', + 'link_title' => 'Revert to earlier revision', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '74', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '75', + 'plid' => '0', + 'link_path' => 'node/%/revisions/%/view', + 'router_path' => 'node/%/revisions/%/view', + 'link_title' => 'Revisions', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '75', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '76', + 'plid' => '37', + 'link_path' => 'admin/build/menu/item/%/delete', + 'router_path' => 'admin/build/menu/item/%/delete', + 'link_title' => 'Delete menu item', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '76', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '77', + 'plid' => '37', + 'link_path' => 'admin/build/menu/item/%/edit', + 'router_path' => 'admin/build/menu/item/%/edit', + 'link_title' => 'Edit menu item', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '77', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '78', + 'plid' => '37', + 'link_path' => 'admin/build/menu/item/%/reset', + 'router_path' => 'admin/build/menu/item/%/reset', + 'link_title' => 'Reset menu item', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '78', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '79', + 'plid' => '37', + 'link_path' => 'admin/build/menu-customize/navigation', + 'router_path' => 'admin/build/menu-customize/%', + 'link_title' => 'Navigation', + 'options' => 'a:0:{}', + 'module' => 'menu', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '79', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '80', + 'plid' => '37', + 'link_path' => 'admin/build/menu-customize/primary-links', + 'router_path' => 'admin/build/menu-customize/%', + 'link_title' => 'Primary links', + 'options' => 'a:0:{}', + 'module' => 'menu', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '80', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '81', + 'plid' => '37', + 'link_path' => 'admin/build/menu-customize/secondary-links', + 'router_path' => 'admin/build/menu-customize/%', + 'link_title' => 'Secondary links', + 'options' => 'a:0:{}', + 'module' => 'menu', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '37', + 'p4' => '81', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '82', + 'plid' => '0', + 'link_path' => 'taxonomy/autocomplete', + 'router_path' => 'taxonomy/autocomplete', + 'link_title' => 'Autocomplete taxonomy', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '82', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '83', + 'plid' => '15', + 'link_path' => 'admin/reports/updates', + 'router_path' => 'admin/reports/updates', + 'link_title' => 'Available updates', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:82:"Get a status report about available updates for your installed modules and themes.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '10', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '83', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '84', + 'plid' => '11', + 'link_path' => 'node/add/page', + 'router_path' => 'node/add/page', + 'link_title' => 'Page', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:296:\"A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '11', + 'p2' => '84', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '85', + 'plid' => '15', + 'link_path' => 'admin/reports/dblog', + 'router_path' => 'admin/reports/dblog', + 'link_title' => 'Recent log entries', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:43:"View events that have recently been logged.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '-1', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '85', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '86', + 'plid' => '11', + 'link_path' => 'node/add/story', + 'router_path' => 'node/add/story', + 'link_title' => 'Story', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:392:\"A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '11', + 'p2' => '86', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '87', + 'plid' => '10', + 'link_path' => 'admin/content/taxonomy', + 'router_path' => 'admin/content/taxonomy', + 'link_title' => 'Taxonomy', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:67:"Manage tagging, categorization, and classification of your content.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '87', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '88', + 'plid' => '0', + 'link_path' => 'taxonomy/term/%', + 'router_path' => 'taxonomy/term/%', + 'link_title' => 'Taxonomy term', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '88', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '89', + 'plid' => '15', + 'link_path' => 'admin/reports/access-denied', + 'router_path' => 'admin/reports/access-denied', + 'link_title' => "Top 'access denied' errors", + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:35:\"View 'access denied' errors (403s).\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '89', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '90', + 'plid' => '15', + 'link_path' => 'admin/reports/page-not-found', + 'router_path' => 'admin/reports/page-not-found', + 'link_title' => "Top 'page not found' errors", + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:36:\"View 'page not found' errors (404s).\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '90', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '91', + 'plid' => '16', + 'link_path' => 'admin/build/path', + 'router_path' => 'admin/build/path', + 'link_title' => 'URL aliases', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:46:\"Change your site's URL paths by aliasing them.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '91', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '92', + 'plid' => '36', + 'link_path' => 'admin/settings/logging/dblog', + 'router_path' => 'admin/settings/logging/dblog', + 'link_title' => 'Database logging', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:169:"Settings for logging to the Drupal database logs. This is the most common method for small to medium sites on shared hosting. The logs are viewable from the admin pages.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '17', + 'p3' => '36', + 'p4' => '92', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '93', + 'plid' => '91', + 'link_path' => 'admin/build/path/delete', + 'router_path' => 'admin/build/path/delete', + 'link_title' => 'Delete alias', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '91', + 'p4' => '93', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '94', + 'plid' => '15', + 'link_path' => 'admin/reports/event/%', + 'router_path' => 'admin/reports/event/%', + 'link_title' => 'Details', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '94', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '95', + 'plid' => '91', + 'link_path' => 'admin/build/path/edit', + 'router_path' => 'admin/build/path/edit', + 'link_title' => 'Edit alias', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '16', + 'p3' => '91', + 'p4' => '95', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '96', + 'plid' => '87', + 'link_path' => 'admin/content/taxonomy/%', + 'router_path' => 'admin/content/taxonomy/%', + 'link_title' => 'List terms', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '87', + 'p4' => '96', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '97', + 'plid' => '83', + 'link_path' => 'admin/reports/updates/check', + 'router_path' => 'admin/reports/updates/check', + 'link_title' => 'Manual update check', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '15', + 'p3' => '83', + 'p4' => '97', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '98', + 'plid' => '10', + 'link_path' => 'admin/content/node-type/page', + 'router_path' => 'admin/content/node-type/page', + 'link_title' => 'Page', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '98', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '99', + 'plid' => '10', + 'link_path' => 'admin/content/node-type/story', + 'router_path' => 'admin/content/node-type/story', + 'link_title' => 'Story', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '99', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '100', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/page/delete', + 'router_path' => 'admin/content/node-type/page/delete', + 'link_title' => 'Delete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '100', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '101', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/delete', + 'router_path' => 'admin/content/node-type/story/delete', + 'link_title' => 'Delete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '101', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '102', + 'plid' => '87', + 'link_path' => 'admin/content/taxonomy/edit/term', + 'router_path' => 'admin/content/taxonomy/edit/term', + 'link_title' => 'Edit term', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '87', + 'p4' => '102', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '103', + 'plid' => '87', + 'link_path' => 'admin/content/taxonomy/edit/vocabulary/%', + 'router_path' => 'admin/content/taxonomy/edit/vocabulary/%', + 'link_title' => 'Edit vocabulary', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '87', + 'p4' => '103', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '104', + 'plid' => '0', + 'link_path' => 'poll', + 'router_path' => 'poll', + 'link_title' => 'Polls', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '104', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '105', + 'plid' => '104', + 'link_path' => 'poll/js', + 'router_path' => 'poll/js', + 'link_title' => 'Javascript Choice Form', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '104', + 'p2' => '105', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '106', + 'plid' => '11', + 'link_path' => 'node/add/poll', + 'router_path' => 'node/add/poll', + 'link_title' => 'Poll', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:191:"A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '11', + 'p2' => '106', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '107', + 'plid' => '10', + 'link_path' => 'admin/content/node-type/poll', + 'router_path' => 'admin/content/node-type/poll', + 'link_title' => 'Poll', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '10', + 'p3' => '107', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '108', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/poll/delete', + 'router_path' => 'admin/content/node-type/poll/delete', + 'link_title' => 'Delete', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '108', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '109', + 'plid' => '2', + 'link_path' => 'admin/help', + 'router_path' => 'admin/help', + 'link_title' => 'Help', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '9', + 'depth' => '2', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '110', + 'plid' => '109', + 'link_path' => 'admin/help/block', + 'router_path' => 'admin/help/block', + 'link_title' => 'block', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '110', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '111', + 'plid' => '109', + 'link_path' => 'admin/help/color', + 'router_path' => 'admin/help/color', + 'link_title' => 'color', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '111', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '112', + 'plid' => '109', + 'link_path' => 'admin/help/comment', + 'router_path' => 'admin/help/comment', + 'link_title' => 'comment', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '112', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '113', + 'plid' => '109', + 'link_path' => 'admin/help/dblog', + 'router_path' => 'admin/help/dblog', + 'link_title' => 'dblog', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '113', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '114', + 'plid' => '109', + 'link_path' => 'admin/help/filter', + 'router_path' => 'admin/help/filter', + 'link_title' => 'filter', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '114', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '115', + 'plid' => '109', + 'link_path' => 'admin/help/help', + 'router_path' => 'admin/help/help', + 'link_title' => 'help', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '115', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '116', + 'plid' => '109', + 'link_path' => 'admin/help/menu', + 'router_path' => 'admin/help/menu', + 'link_title' => 'menu', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '116', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '117', + 'plid' => '109', + 'link_path' => 'admin/help/node', + 'router_path' => 'admin/help/node', + 'link_title' => 'node', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '117', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '118', + 'plid' => '109', + 'link_path' => 'admin/help/path', + 'router_path' => 'admin/help/path', + 'link_title' => 'path', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '118', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '119', + 'plid' => '109', + 'link_path' => 'admin/help/poll', + 'router_path' => 'admin/help/poll', + 'link_title' => 'poll', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '119', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '120', + 'plid' => '109', + 'link_path' => 'admin/help/system', + 'router_path' => 'admin/help/system', + 'link_title' => 'system', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '120', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '121', + 'plid' => '109', + 'link_path' => 'admin/help/taxonomy', + 'router_path' => 'admin/help/taxonomy', + 'link_title' => 'taxonomy', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '121', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '122', + 'plid' => '109', + 'link_path' => 'admin/help/update', + 'router_path' => 'admin/help/update', + 'link_title' => 'update', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '122', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '123', + 'plid' => '109', + 'link_path' => 'admin/help/user', + 'router_path' => 'admin/help/user', + 'link_title' => 'user', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '2', + 'p2' => '109', + 'p3' => '123', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->execute(); + +db_create_table('menu_router', array( + 'fields' => array( + 'path' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'load_functions' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'to_arg_functions' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'access_callback' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'access_arguments' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'page_callback' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'page_arguments' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'fit' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'number_parts' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'small', + ), + 'tab_parent' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'tab_root' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'title_callback' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'title_arguments' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'block_callback' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'description' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'position' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'file' => array( + 'type' => 'text', + 'size' => 'medium', + ), + ), + 'indexes' => array( + 'fit' => array( + 'fit', + ), + 'tab_parent' => array( + 'tab_parent', + ), + 'tab_root_weight_title' => array( + array( + 'tab_root', + 64, + ), + 'weight', + 'title', + ), + ), + 'primary key' => array( + 'path', + ), + 'module' => 'system', + 'name' => 'menu_router', +)); +db_insert('menu_router')->fields(array( + 'path', + 'load_functions', + 'to_arg_functions', + 'access_callback', + 'access_arguments', + 'page_callback', + 'page_arguments', + 'fit', + 'number_parts', + 'tab_parent', + 'tab_root', + 'title', + 'title_callback', + 'title_arguments', + 'type', + 'block_callback', + 'description', + 'position', + 'weight', + 'file', +)) +->values(array( + 'path' => 'admin', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_main_admin_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'admin', + 'title' => 'Administer', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '9', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_admin_menu_block_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/build', + 'title' => 'Site building', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Control how your site looks and feels.', + 'position' => 'right', + 'weight' => '-10', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/build/block', + 'title' => 'Blocks', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Configure what block content appears in your site's sidebars and other regions.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:20:"block_add_block_form";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/block', + 'tab_root' => 'admin/build/block', + 'title' => 'Add block', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/configure', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"block_admin_configure";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/build/block/configure', + 'title' => 'Configure block', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:16:"block_box_delete";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/build/block/delete', + 'title' => 'Delete block', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/block', + 'tab_root' => 'admin/build/block', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/bluemarine', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:10:"bluemarine";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Bluemarine', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/chameleon', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:9:"chameleon";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Chameleon', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/garland', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:7:"garland";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Garland', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/js', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:17:"administer blocks";}', + 'page_callback' => 'block_admin_display_js', + 'page_arguments' => 'a:0:{}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/build/block/list/js', + 'title' => 'JavaScript List Form', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/marvin', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:6:"marvin";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Marvin', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/minnelli', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:8:"minnelli";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Minnelli', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/block/list/pushbutton', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_block_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'block_admin_display', + 'page_arguments' => 'a:1:{i:0;s:10:"pushbutton";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/block/list', + 'tab_root' => 'admin/build/block', + 'title' => 'Pushbutton', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/block/block.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'menu_overview_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu', + 'title' => 'Menus', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu-customize/%', + 'load_functions' => 'a:1:{i:3;s:9:"menu_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:18:"menu_overview_form";i:1;i:3;}', + 'fit' => '14', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu-customize/%', + 'title' => 'Customize menu', + 'title_callback' => 'menu_overview_title', + 'title_arguments' => 'a:1:{i:0;i:3;}', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu-customize/%/add', + 'load_functions' => 'a:1:{i:3;s:9:"menu_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:4:{i:0;s:14:"menu_edit_item";i:1;s:3:"add";i:2;N;i:3;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/menu-customize/%', + 'tab_root' => 'admin/build/menu-customize/%', + 'title' => 'Add item', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu-customize/%/delete', + 'load_functions' => 'a:1:{i:3;s:9:"menu_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'menu_delete_menu_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu-customize/%/delete', + 'title' => 'Delete menu', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu-customize/%/edit', + 'load_functions' => 'a:1:{i:3;s:9:"menu_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:3:{i:0;s:14:"menu_edit_menu";i:1;s:4:"edit";i:2;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/menu-customize/%', + 'tab_root' => 'admin/build/menu-customize/%', + 'title' => 'Edit menu', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu-customize/%/list', + 'load_functions' => 'a:1:{i:3;s:9:"menu_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:18:"menu_overview_form";i:1;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/menu-customize/%', + 'tab_root' => 'admin/build/menu-customize/%', + 'title' => 'List items', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:14:"menu_edit_menu";i:1;s:3:"add";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/menu', + 'tab_root' => 'admin/build/menu', + 'title' => 'Add menu', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/item/%/delete', + 'load_functions' => 'a:1:{i:4;s:14:"menu_link_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'menu_item_delete_page', + 'page_arguments' => 'a:1:{i:0;i:4;}', + 'fit' => '61', + 'number_parts' => '6', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu/item/%/delete', + 'title' => 'Delete menu item', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/item/%/edit', + 'load_functions' => 'a:1:{i:4;s:14:"menu_link_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:4:{i:0;s:14:"menu_edit_item";i:1;s:4:"edit";i:2;i:4;i:3;N;}', + 'fit' => '61', + 'number_parts' => '6', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu/item/%/edit', + 'title' => 'Edit menu item', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/item/%/reset', + 'load_functions' => 'a:1:{i:4;s:14:"menu_link_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:23:"menu_reset_item_confirm";i:1;i:4;}', + 'fit' => '61', + 'number_parts' => '6', + 'tab_parent' => '', + 'tab_root' => 'admin/build/menu/item/%/reset', + 'title' => 'Reset menu item', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'menu_overview_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/menu', + 'tab_root' => 'admin/build/menu', + 'title' => 'List menus', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/menu/settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:15:"administer menu";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"menu_configure";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/menu', + 'tab_root' => 'admin/build/menu', + 'title' => 'Settings', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '5', + 'file' => 'modules/menu/menu.admin.inc', +)) +->values(array( + 'path' => 'admin/build/modules', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"system_modules";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/build/modules', + 'title' => 'Modules', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Enable or disable add-on modules for your site.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/modules/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"system_modules";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/modules', + 'tab_root' => 'admin/build/modules', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/modules/list/confirm', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"system_modules";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/build/modules/list/confirm', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/modules/uninstall', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:24:"system_modules_uninstall";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/modules', + 'tab_root' => 'admin/build/modules', + 'title' => 'Uninstall', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/modules/uninstall/confirm', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:24:"system_modules_uninstall";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/build/modules/uninstall/confirm', + 'title' => 'Uninstall', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/path', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer url aliases";}', + 'page_callback' => 'path_admin_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/build/path', + 'title' => 'URL aliases', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Change your site's URL paths by aliasing them.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/path/path.admin.inc', +)) +->values(array( + 'path' => 'admin/build/path/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer url aliases";}', + 'page_callback' => 'path_admin_edit', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/path', + 'tab_root' => 'admin/build/path', + 'title' => 'Add alias', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/path/path.admin.inc', +)) +->values(array( + 'path' => 'admin/build/path/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer url aliases";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"path_admin_delete_confirm";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/build/path/delete', + 'title' => 'Delete alias', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/path/path.admin.inc', +)) +->values(array( + 'path' => 'admin/build/path/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer url aliases";}', + 'page_callback' => 'path_admin_edit', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/build/path/edit', + 'title' => 'Edit alias', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/path/path.admin.inc', +)) +->values(array( + 'path' => 'admin/build/path/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer url aliases";}', + 'page_callback' => 'path_admin_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/path', + 'tab_root' => 'admin/build/path', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/path/path.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:18:"system_themes_form";i:1;N;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/build/themes', + 'title' => 'Themes', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Change which theme your site uses or allows users to set.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/select', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:18:"system_themes_form";i:1;N;}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/themes', + 'tab_root' => 'admin/build/themes', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => 'Select the default theme.', + 'position' => '', + 'weight' => '-1', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"system_theme_settings";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/themes', + 'tab_root' => 'admin/build/themes', + 'title' => 'Configure', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/bluemarine', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:10:"bluemarine";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Bluemarine', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/chameleon', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:9:"chameleon";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Chameleon', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/garland', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:7:"garland";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Garland', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/global', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"system_theme_settings";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Global settings', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-1', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/marvin', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:6:"marvin";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Marvin', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/minnelli', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:8:"minnelli";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Minnelli', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/build/themes/settings/pushbutton', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_system_themes_access', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:10:"pushbutton";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/build/themes/settings', + 'tab_root' => 'admin/build/themes', + 'title' => 'Pushbutton', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/by-module', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_admin_by_module', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => 'admin', + 'tab_root' => 'admin', + 'title' => 'By module', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '2', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/by-task', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_main_admin_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => 'admin', + 'tab_root' => 'admin', + 'title' => 'By task', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/compact', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_admin_compact_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/compact', + 'title' => 'Compact mode', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/content', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_admin_menu_block_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/content', + 'title' => 'Content management', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Manage your site's content.", + 'position' => 'left', + 'weight' => '-10', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/content/comment', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer comments";}', + 'page_callback' => 'comment_admin', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/comment', + 'title' => 'Comments', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'List and edit site comments and the comment moderation queue.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/comment/comment.admin.inc', +)) +->values(array( + 'path' => 'admin/content/comment/approval', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer comments";}', + 'page_callback' => 'comment_admin', + 'page_arguments' => 'a:1:{i:0;s:8:"approval";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/comment', + 'tab_root' => 'admin/content/comment', + 'title' => 'Approval queue', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/comment/comment.admin.inc', +)) +->values(array( + 'path' => 'admin/content/comment/new', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer comments";}', + 'page_callback' => 'comment_admin', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/comment', + 'tab_root' => 'admin/content/comment', + 'title' => 'Published comments', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/comment/comment.admin.inc', +)) +->values(array( + 'path' => 'admin/content/node', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer nodes";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:18:"node_admin_content";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node', + 'title' => 'Content', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "View, edit, and delete your site's content.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.admin.inc', +)) +->values(array( + 'path' => 'admin/content/node-settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer nodes";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"node_configure";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-settings', + 'title' => 'Post settings', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Control posting behavior, such as teaser length, requiring previews before posting, and the number of posts on the front page.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.admin.inc', +)) +->values(array( + 'path' => 'admin/content/node-settings/rebuild', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:30:"node_configure_rebuild_confirm";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-settings/rebuild', + 'title' => 'Rebuild permissions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.admin.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/page', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:14:\"node_type_form\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:4:\"page\";s:4:\"name\";s:4:\"Page\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:296:\"A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:4:\"page\";}}", + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/page', + 'title' => 'Page', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/page/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:24:\"node_type_delete_confirm\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:4:\"page\";s:4:\"name\";s:4:\"Page\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:296:\"A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:4:\"page\";}}", + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/page/delete', + 'title' => 'Delete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/page/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:14:\"node_type_form\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:4:\"page\";s:4:\"name\";s:4:\"Page\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:296:\"A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:4:\"page\";}}", + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/node-type/page', + 'tab_root' => 'admin/content/node-type/page', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/poll', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"name";s:4:"Poll";s:6:"module";s:4:"poll";s:11:"description";s:191:"A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.";s:11:"title_label";s:8:"Question";s:8:"has_body";b:0;s:4:"type";s:4:"poll";s:9:"has_title";b:1;s:4:"help";s:0:"";s:14:"min_word_count";i:0;s:6:"custom";b:0;s:8:"modified";b:0;s:6:"locked";b:1;s:9:"orig_type";s:4:"poll";s:6:"is_new";b:1;}}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/poll', + 'title' => 'Poll', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/poll/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:24:"node_type_delete_confirm";i:1;O:8:"stdClass":14:{s:4:"name";s:4:"Poll";s:6:"module";s:4:"poll";s:11:"description";s:191:"A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.";s:11:"title_label";s:8:"Question";s:8:"has_body";b:0;s:4:"type";s:4:"poll";s:9:"has_title";b:1;s:4:"help";s:0:"";s:14:"min_word_count";i:0;s:6:"custom";b:0;s:8:"modified";b:0;s:6:"locked";b:1;s:9:"orig_type";s:4:"poll";s:6:"is_new";b:1;}}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/poll/delete', + 'title' => 'Delete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/poll/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"name";s:4:"Poll";s:6:"module";s:4:"poll";s:11:"description";s:191:"A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.";s:11:"title_label";s:8:"Question";s:8:"has_body";b:0;s:4:"type";s:4:"poll";s:9:"has_title";b:1;s:4:"help";s:0:"";s:14:"min_word_count";i:0;s:6:"custom";b:0;s:8:"modified";b:0;s:6:"locked";b:1;s:9:"orig_type";s:4:"poll";s:6:"is_new";b:1;}}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/node-type/poll', + 'tab_root' => 'admin/content/node-type/poll', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/story', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:14:\"node_type_form\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:5:\"story\";s:4:\"name\";s:5:\"Story\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:392:\"A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:5:\"story\";}}", + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/story', + 'title' => 'Story', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/story/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:24:\"node_type_delete_confirm\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:5:\"story\";s:4:\"name\";s:5:\"Story\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:392:\"A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:5:\"story\";}}", + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/content/node-type/story/delete', + 'title' => 'Delete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node-type/story/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => "a:2:{i:0;s:14:\"node_type_form\";i:1;O:8:\"stdClass\":14:{s:4:\"type\";s:5:\"story\";s:4:\"name\";s:5:\"Story\";s:6:\"module\";s:4:\"node\";s:11:\"description\";s:392:\"A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.\";s:4:\"help\";s:0:\"\";s:9:\"has_title\";s:1:\"1\";s:11:\"title_label\";s:5:\"Title\";s:8:\"has_body\";s:1:\"1\";s:10:\"body_label\";s:4:\"Body\";s:14:\"min_word_count\";s:1:\"0\";s:6:\"custom\";s:1:\"1\";s:8:\"modified\";s:1:\"1\";s:6:\"locked\";s:1:\"0\";s:9:\"orig_type\";s:5:\"story\";}}", + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/node-type/story', + 'tab_root' => 'admin/content/node-type/story', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/node/overview', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer nodes";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:18:"node_admin_content";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/node', + 'tab_root' => 'admin/content/node', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/node/node.admin.inc', +)) +->values(array( + 'path' => 'admin/content/rss-publishing', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"system_rss_feeds_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/rss-publishing', + 'title' => 'RSS publishing', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Configure the number of items per feed and whether feeds should be titles/teasers/full-text.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:30:"taxonomy_overview_vocabularies";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/taxonomy', + 'title' => 'Taxonomy', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Manage tagging, categorization, and classification of your content.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/%', + 'load_functions' => 'a:1:{i:3;s:24:"taxonomy_vocabulary_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:23:"taxonomy_overview_terms";i:1;i:3;}', + 'fit' => '14', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/content/taxonomy/%', + 'title' => 'List terms', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/%/add/term', + 'load_functions' => 'a:1:{i:3;s:24:"taxonomy_vocabulary_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'taxonomy_add_term_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '59', + 'number_parts' => '6', + 'tab_parent' => 'admin/content/taxonomy/%', + 'tab_root' => 'admin/content/taxonomy/%', + 'title' => 'Add term', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/%/list', + 'load_functions' => 'a:1:{i:3;s:24:"taxonomy_vocabulary_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:23:"taxonomy_overview_terms";i:1;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/taxonomy/%', + 'tab_root' => 'admin/content/taxonomy/%', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/add/vocabulary', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:24:"taxonomy_form_vocabulary";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/taxonomy', + 'tab_root' => 'admin/content/taxonomy', + 'title' => 'Add vocabulary', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/edit/term', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'taxonomy_admin_term_edit', + 'page_arguments' => 'a:0:{}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/content/taxonomy/edit/term', + 'title' => 'Edit term', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/edit/vocabulary/%', + 'load_functions' => 'a:1:{i:5;s:24:"taxonomy_vocabulary_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'taxonomy_admin_vocabulary_edit', + 'page_arguments' => 'a:1:{i:0;i:5;}', + 'fit' => '62', + 'number_parts' => '6', + 'tab_parent' => '', + 'tab_root' => 'admin/content/taxonomy/edit/vocabulary/%', + 'title' => 'Edit vocabulary', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/taxonomy/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer taxonomy";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:30:"taxonomy_overview_vocabularies";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/taxonomy', + 'tab_root' => 'admin/content/taxonomy', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/taxonomy/taxonomy.admin.inc', +)) +->values(array( + 'path' => 'admin/content/types', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'node_overview_types', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/content/types', + 'title' => 'Content types', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Manage posts by content type, including default status, front page promotion, etc.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/types/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:14:"node_type_form";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/types', + 'tab_root' => 'admin/content/types', + 'title' => 'Add content type', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/content/types/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', + 'page_callback' => 'node_overview_types', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/content/types', + 'tab_root' => 'admin/content/types', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/node/content_types.inc', +)) +->values(array( + 'path' => 'admin/help', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_main', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/help', + 'title' => 'Help', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '9', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/block', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/block', + 'title' => 'block', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/color', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/color', + 'title' => 'color', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/comment', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/comment', + 'title' => 'comment', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/dblog', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/dblog', + 'title' => 'dblog', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/filter', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/filter', + 'title' => 'filter', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/help', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/help', + 'title' => 'help', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/menu', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/menu', + 'title' => 'menu', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/node', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/node', + 'title' => 'node', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/path', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/path', + 'title' => 'path', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/poll', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/poll', + 'title' => 'poll', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/system', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/system', + 'title' => 'system', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/taxonomy', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/taxonomy', + 'title' => 'taxonomy', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/update', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/update', + 'title' => 'update', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/help/user', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'help_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/help/user', + 'title' => 'user', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/help/help.admin.inc', +)) +->values(array( + 'path' => 'admin/reports', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"access site reports";}', + 'page_callback' => 'system_admin_menu_block_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/reports', + 'title' => 'Reports', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'View reports from system logs and other status information.', + 'position' => 'left', + 'weight' => '5', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/access-denied', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"access site reports";}', + 'page_callback' => 'dblog_top', + 'page_arguments' => 'a:1:{i:0;s:13:"access denied";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/access-denied', + 'title' => "Top 'access denied' errors", + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "View 'access denied' errors (403s).", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/dblog/dblog.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/dblog', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"access site reports";}', + 'page_callback' => 'dblog_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/dblog', + 'title' => 'Recent log entries', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'View events that have recently been logged.', + 'position' => '', + 'weight' => '-1', + 'file' => 'modules/dblog/dblog.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/event/%', + 'load_functions' => 'a:1:{i:3;N;}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"access site reports";}', + 'page_callback' => 'dblog_event', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '14', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/event/%', + 'title' => 'Details', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/dblog/dblog.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/page-not-found', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"access site reports";}', + 'page_callback' => 'dblog_top', + 'page_arguments' => 'a:1:{i:0;s:14:"page not found";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/page-not-found', + 'title' => "Top 'page not found' errors", + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "View 'page not found' errors (404s).", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/dblog/dblog.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/status', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_status', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/status', + 'title' => 'Status report', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Get a status report about your site's operation and any detected problems.", + 'position' => '', + 'weight' => '10', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/status/php', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_php', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/status/php', + 'title' => 'PHP', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/status/run-cron', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_run_cron', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/status/run-cron', + 'title' => 'Run cron', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/status/sql', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_sql', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/status/sql', + 'title' => 'SQL', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/reports/updates', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'update_status', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/updates', + 'title' => 'Available updates', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Get a status report about available updates for your installed modules and themes.', + 'position' => '', + 'weight' => '10', + 'file' => 'modules/update/update.report.inc', +)) +->values(array( + 'path' => 'admin/reports/updates/check', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'update_manual_status', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/reports/updates/check', + 'title' => 'Manual update check', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/update/update.fetch.inc', +)) +->values(array( + 'path' => 'admin/reports/updates/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'update_status', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/reports/updates', + 'tab_root' => 'admin/reports/updates', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/update/update.report.inc', +)) +->values(array( + 'path' => 'admin/reports/updates/settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:15:"update_settings";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/reports/updates', + 'tab_root' => 'admin/reports/updates', + 'title' => 'Settings', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/update/update.settings.inc', +)) +->values(array( + 'path' => 'admin/settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_settings_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/settings', + 'title' => 'Site configuration', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Adjust basic site configuration options.', + 'position' => 'right', + 'weight' => '-5', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/actions', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer actions";}', + 'page_callback' => 'system_actions_manage', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/actions', + 'title' => 'Actions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Manage the actions defined for your site.', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/actions/configure', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer actions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:24:"system_actions_configure";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/actions/configure', + 'title' => 'Configure an advanced action', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/actions/delete/%', + 'load_functions' => 'a:1:{i:4;s:12:"actions_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer actions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:26:"system_actions_delete_form";i:1;i:4;}', + 'fit' => '30', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/actions/delete/%', + 'title' => 'Delete action', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => 'Delete an action.', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/actions/manage', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer actions";}', + 'page_callback' => 'system_actions_manage', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/actions', + 'tab_root' => 'admin/settings/actions', + 'title' => 'Manage actions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => 'Manage the actions defined for your site.', + 'position' => '', + 'weight' => '-2', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/actions/orphan', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer actions";}', + 'page_callback' => 'system_actions_remove_orphans', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/actions/orphan', + 'title' => 'Remove orphans', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/admin', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:27:"system_admin_theme_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/admin', + 'title' => 'Administration theme', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => 'system_admin_theme_settings', + 'description' => 'Settings for how your administrative pages should look.', + 'position' => 'left', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/clean-urls', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"system_clean_url_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/clean-urls', + 'title' => 'Clean URLs', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Enable or disable clean URLs for your site.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/clean-urls/check', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'drupal_json', + 'page_arguments' => 'a:1:{i:0;a:1:{s:6:"status";b:1;}}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/clean-urls/check', + 'title' => 'Clean URL check', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/date-time', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"system_date_time_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/date-time', + 'title' => 'Date and time', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Settings for how Drupal displays date and time, as well as the system's default timezone.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/date-time/lookup', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_date_time_lookup', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/date-time/lookup', + 'title' => 'Date and time lookup', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/error-reporting', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:31:"system_error_reporting_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/error-reporting', + 'title' => 'Error reporting', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Control how Drupal deals with errors including 403/404 errors as well as PHP error reporting.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/file-system', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:27:"system_file_system_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/file-system', + 'title' => 'File system', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Tell Drupal where to store uploaded files and how they are accessed.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"filter_admin_overview";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/filters', + 'title' => 'Input formats', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/%', + 'load_functions' => 'a:1:{i:3;s:18:"filter_format_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'filter_admin_format_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '14', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/filters/%', + 'title' => '', + 'title_callback' => 'filter_admin_format_title', + 'title_arguments' => 'a:1:{i:0;i:3;}', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/%/configure', + 'load_functions' => 'a:1:{i:3;s:18:"filter_format_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'filter_admin_configure_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/filters/%', + 'tab_root' => 'admin/settings/filters/%', + 'title' => 'Configure', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '1', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/%/edit', + 'load_functions' => 'a:1:{i:3;s:18:"filter_format_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'filter_admin_format_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/filters/%', + 'tab_root' => 'admin/settings/filters/%', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/%/order', + 'load_functions' => 'a:1:{i:3;s:18:"filter_format_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'filter_admin_order_page', + 'page_arguments' => 'a:1:{i:0;i:3;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/filters/%', + 'tab_root' => 'admin/settings/filters/%', + 'title' => 'Rearrange', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '2', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'filter_admin_format_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/filters', + 'tab_root' => 'admin/settings/filters', + 'title' => 'Add input format', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '1', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:19:"filter_admin_delete";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/filters/delete', + 'title' => 'Delete input format', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/filters/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"filter_admin_overview";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/filters', + 'tab_root' => 'admin/settings/filters', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/image-toolkit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:29:"system_image_toolkit_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/image-toolkit', + 'title' => 'Image toolkit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Choose which image toolkit to use if you have installed optional toolkits.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/logging', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'system_logging_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/logging', + 'title' => 'Logging and alerts', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destination, such as syslog, database, email, ...etc.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/logging/dblog', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:20:"dblog_admin_settings";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/logging/dblog', + 'title' => 'Database logging', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Settings for logging to the Drupal database logs. This is the most common method for small to medium sites on shared hosting. The logs are viewable from the admin pages.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/dblog/dblog.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/performance', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:27:"system_performance_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/performance', + 'title' => 'Performance', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/site-information', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:32:"system_site_information_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/site-information', + 'title' => 'Site information', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/site-maintenance', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:32:"system_site_maintenance_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/site-maintenance', + 'title' => 'Site maintenance', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Take the site off-line for maintenance or bring it back online.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/user', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:27:"access administration pages";}', + 'page_callback' => 'system_admin_menu_block_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'admin/user', + 'title' => 'User management', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "Manage your site's users, groups and access to site features.", + 'position' => 'left', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/user/permissions', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:15:"user_admin_perm";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/user/permissions', + 'title' => 'Permissions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Determine access to features by selecting permissions for roles.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/roles', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:19:"user_admin_new_role";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/user/roles', + 'title' => 'Roles', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'List, edit, or add user roles.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/roles/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:15:"user_admin_role";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/user/roles/edit', + 'title' => 'Edit role', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'user_admin_access', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/user/rules', + 'title' => 'Access rules', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'List and create rules to disallow usernames, e-mail addresses, and IP addresses.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'user_admin_access_add', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/user/rules', + 'tab_root' => 'admin/user/rules', + 'title' => 'Add rule', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules/check', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'user_admin_access_check', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/user/rules', + 'tab_root' => 'admin/user/rules', + 'title' => 'Check rules', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:32:"user_admin_access_delete_confirm";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/user/rules/delete', + 'title' => 'Delete rule', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'user_admin_access_edit', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => '', + 'tab_root' => 'admin/user/rules/edit', + 'title' => 'Edit rule', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/rules/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:22:"administer permissions";}', + 'page_callback' => 'user_admin_access', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/user/rules', + 'tab_root' => 'admin/user/rules', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/settings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer users";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:19:"user_admin_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/user/settings', + 'title' => 'User settings', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Configure default behavior of users, including registration requirements, e-mails, and user pictures.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/user', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer users";}', + 'page_callback' => 'user_admin', + 'page_arguments' => 'a:1:{i:0;s:4:"list";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/user/user', + 'title' => 'Users', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'List, add, and edit users.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/user/create', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer users";}', + 'page_callback' => 'user_admin', + 'page_arguments' => 'a:1:{i:0;s:6:"create";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/user/user', + 'tab_root' => 'admin/user/user', + 'title' => 'Add user', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'admin/user/user/list', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer users";}', + 'page_callback' => 'user_admin', + 'page_arguments' => 'a:1:{i:0;s:4:"list";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/user/user', + 'tab_root' => 'admin/user/user', + 'title' => 'List', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/user/user.admin.inc', +)) +->values(array( + 'path' => 'batch', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'system_batch_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'batch', + 'title' => '', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'comment/delete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"administer comments";}', + 'page_callback' => 'comment_delete', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'comment/delete', + 'title' => 'Delete comment', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/comment/comment.admin.inc', +)) +->values(array( + 'path' => 'comment/edit', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:13:"post comments";}', + 'page_callback' => 'comment_edit', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'comment/edit', + 'title' => 'Edit comment', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/comment/comment.pages.inc', +)) +->values(array( + 'path' => 'comment/reply/%', + 'load_functions' => 'a:1:{i:2;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:4:"view";i:1;i:2;}', + 'page_callback' => 'comment_reply', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '6', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'comment/reply/%', + 'title' => 'Reply to comment', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/comment/comment.pages.inc', +)) +->values(array( + 'path' => 'filter/tips', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'filter_tips_long', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'filter/tips', + 'title' => 'Compose tips', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '20', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/filter/filter.pages.inc', +)) +->values(array( + 'path' => 'logout', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_is_logged_in', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'user_logout', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'logout', + 'title' => 'Log out', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '10', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'node', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'node_page_default', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'node', + 'title' => 'Content', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'node/%', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:4:"view";i:1;i:1;}', + 'page_callback' => 'node_page_view', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '2', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'node/%', + 'title' => '', + 'title_callback' => 'node_page_title', + 'title_arguments' => 'a:1:{i:0;i:1;}', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'node/%/delete', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:6:"delete";i:1;i:1;}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:19:"node_delete_confirm";i:1;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'node/%/delete', + 'title' => 'Delete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '1', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/%/edit', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:6:"update";i:1;i:1;}', + 'page_callback' => 'node_page_edit', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'node/%', + 'tab_root' => 'node/%', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '1', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/%/results', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => '_poll_menu_access', + 'access_arguments' => 'a:3:{i:0;i:1;i:1;s:14:"access content";i:2;b:1;}', + 'page_callback' => 'poll_results', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'node/%', + 'tab_root' => 'node/%', + 'title' => 'Results', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '3', + 'file' => 'modules/poll/poll.pages.inc', +)) +->values(array( + 'path' => 'node/%/revisions', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => '_node_revision_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'node_revision_overview', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'node/%', + 'tab_root' => 'node/%', + 'title' => 'Revisions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '2', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/%/revisions/%/delete', + 'load_functions' => 'a:2:{i:1;a:1:{s:9:"node_load";a:1:{i:0;i:3;}}i:3;N;}', + 'to_arg_functions' => '', + 'access_callback' => '_node_revision_access', + 'access_arguments' => 'a:2:{i:0;i:1;i:1;s:6:"delete";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:28:"node_revision_delete_confirm";i:1;i:1;}', + 'fit' => '21', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'node/%/revisions/%/delete', + 'title' => 'Delete earlier revision', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/%/revisions/%/revert', + 'load_functions' => 'a:2:{i:1;a:1:{s:9:"node_load";a:1:{i:0;i:3;}}i:3;N;}', + 'to_arg_functions' => '', + 'access_callback' => '_node_revision_access', + 'access_arguments' => 'a:2:{i:0;i:1;i:1;s:6:"update";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:28:"node_revision_revert_confirm";i:1;i:1;}', + 'fit' => '21', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'node/%/revisions/%/revert', + 'title' => 'Revert to earlier revision', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/%/revisions/%/view', + 'load_functions' => 'a:2:{i:1;a:1:{s:9:"node_load";a:1:{i:0;i:3;}}i:3;N;}', + 'to_arg_functions' => '', + 'access_callback' => '_node_revision_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'node_show', + 'page_arguments' => 'a:3:{i:0;i:1;i:1;N;i:2;b:1;}', + 'fit' => '21', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'node/%/revisions/%/view', + 'title' => 'Revisions', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'node/%/view', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:4:"view";i:1;i:1;}', + 'page_callback' => 'node_page_view', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'node/%', + 'tab_root' => 'node/%', + 'title' => 'View', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => '', +)) +->values(array( + 'path' => 'node/%/votes', + 'load_functions' => 'a:1:{i:1;s:9:"node_load";}', + 'to_arg_functions' => '', + 'access_callback' => '_poll_menu_access', + 'access_arguments' => 'a:3:{i:0;i:1;i:1;s:17:"inspect all votes";i:2;b:0;}', + 'page_callback' => 'poll_votes', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'node/%', + 'tab_root' => 'node/%', + 'title' => 'Votes', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '3', + 'file' => 'modules/poll/poll.pages.inc', +)) +->values(array( + 'path' => 'node/add', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '_node_add_access', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'node_add_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'node/add', + 'title' => 'Create content', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '1', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/add/page', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:4:"page";}', + 'page_callback' => 'node_add', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'node/add/page', + 'title' => 'Page', + 'title_callback' => 'check_plain', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/add/poll', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:4:"poll";}', + 'page_callback' => 'node_add', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'node/add/poll', + 'title' => 'Poll', + 'title_callback' => 'check_plain', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'node/add/story', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'node_access', + 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:5:"story";}', + 'page_callback' => 'node_add', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'node/add/story', + 'title' => 'Story', + 'title_callback' => 'check_plain', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => "A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.", + 'position' => '', + 'weight' => '0', + 'file' => 'modules/node/node.pages.inc', +)) +->values(array( + 'path' => 'poll', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'poll_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'poll', + 'title' => 'Polls', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '20', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/poll/poll.pages.inc', +)) +->values(array( + 'path' => 'poll/js', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'poll_choice_js', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'poll/js', + 'title' => 'Javascript Choice Form', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'rss.xml', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'node_feed', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'rss.xml', + 'title' => 'RSS feed', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'system/files', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'file_download', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'system/files', + 'title' => 'File download', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( + 'path' => 'taxonomy/autocomplete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'taxonomy_autocomplete', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'taxonomy/autocomplete', + 'title' => 'Autocomplete taxonomy', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.pages.inc', +)) +->values(array( + 'path' => 'taxonomy/term/%', + 'load_functions' => 'a:1:{i:2;N;}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'taxonomy_term_page', + 'page_arguments' => 'a:1:{i:0;i:2;}', + 'fit' => '6', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'taxonomy/term/%', + 'title' => 'Taxonomy term', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/taxonomy/taxonomy.pages.inc', +)) +->values(array( + 'path' => 'user', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'user_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '1', + 'number_parts' => '1', + 'tab_parent' => '', + 'tab_root' => 'user', + 'title' => 'User account', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/%', + 'load_functions' => 'a:1:{i:1;s:22:"user_uid_optional_load";}', + 'to_arg_functions' => 'a:1:{i:1;s:24:"user_uid_optional_to_arg";}', + 'access_callback' => 'user_view_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'user_view', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '2', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'user/%', + 'title' => 'My account', + 'title_callback' => 'user_page_title', + 'title_arguments' => 'a:1:{i:0;i:1;}', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/%/delete', + 'load_functions' => 'a:1:{i:1;s:9:"user_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:16:"administer users";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:2:{i:0;s:19:"user_confirm_delete";i:1;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'user/%/delete', + 'title' => 'Delete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/%/edit', + 'load_functions' => 'a:1:{i:1;a:1:{s:18:"user_category_load";a:2:{i:0;s:4:"%map";i:1;s:6:"%index";}}}', + 'to_arg_functions' => '', + 'access_callback' => 'user_edit_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'user_edit', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'user/%', + 'tab_root' => 'user/%', + 'title' => 'Edit', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/%/edit/account', + 'load_functions' => 'a:1:{i:1;a:1:{s:18:"user_category_load";a:2:{i:0;s:4:"%map";i:1;s:6:"%index";}}}', + 'to_arg_functions' => '', + 'access_callback' => 'user_edit_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'user_edit', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '11', + 'number_parts' => '4', + 'tab_parent' => 'user/%/edit', + 'tab_root' => 'user/%', + 'title' => 'Account', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/%/view', + 'load_functions' => 'a:1:{i:1;s:9:"user_load";}', + 'to_arg_functions' => '', + 'access_callback' => 'user_view_access', + 'access_arguments' => 'a:1:{i:0;i:1;}', + 'page_callback' => 'user_view', + 'page_arguments' => 'a:1:{i:0;i:1;}', + 'fit' => '5', + 'number_parts' => '3', + 'tab_parent' => 'user/%', + 'tab_root' => 'user/%', + 'title' => 'View', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/autocomplete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:20:"access user profiles";}', + 'page_callback' => 'user_autocomplete', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'user/autocomplete', + 'title' => 'User autocomplete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/login', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_is_anonymous', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'user_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => 'user', + 'tab_root' => 'user', + 'title' => 'Log in', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/password', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_is_anonymous', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:9:"user_pass";}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => 'user', + 'tab_root' => 'user', + 'title' => 'Request new password', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/register', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_register_access', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:13:"user_register";}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => 'user', + 'tab_root' => 'user', + 'title' => 'Create new account', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->values(array( + 'path' => 'user/reset/%/%/%', + 'load_functions' => 'a:3:{i:2;N;i:3;N;i:4;N;}', + 'to_arg_functions' => '', + 'access_callback' => '1', + 'access_arguments' => 'a:0:{}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:4:{i:0;s:15:"user_pass_reset";i:1;i:2;i:2;i:3;i:3;i:4;}', + 'fit' => '24', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'user/reset/%/%/%', + 'title' => 'Reset password', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/user/user.pages.inc', +)) +->execute(); + +db_create_table('node', array( + 'fields' => array( + 'nid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 1, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'comment' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'promote' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'moderate' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'sticky' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'tnid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'translate' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'node_changed' => array( + 'changed', + ), + 'node_created' => array( + 'created', + ), + 'node_moderate' => array( + 'moderate', + ), + 'node_promote_status' => array( + 'promote', + 'status', + ), + 'node_status_type' => array( + 'status', + 'type', + 'nid', + ), + 'node_title_type' => array( + 'title', + array( + 'type', + 4, + ), + ), + 'node_type' => array( + array( + 'type', + 4, + ), + ), + 'uid' => array( + 'uid', + ), + 'tnid' => array( + 'tnid', + ), + 'translate' => array( + 'translate', + ), + ), + 'unique keys' => array( + 'vid' => array( + 'vid', + ), + ), + 'primary key' => array( + 'nid', + ), + 'module' => 'node', + 'name' => 'node', +)); +db_insert('node')->fields(array( + 'nid', + 'vid', + 'type', + 'language', + 'title', + 'uid', + 'status', + 'created', + 'changed', + 'comment', + 'promote', + 'moderate', + 'sticky', + 'tnid', + 'translate', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 1 rev 1 (i=0) rev2 2', + 'uid' => '3', + 'status' => '0', + 'created' => '1262732400', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 2 rev 3 (i=1) rev2 4', + 'uid' => '3', + 'status' => '0', + 'created' => '1262818800', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 3 rev 5 (i=2) rev2 6', + 'uid' => '3', + 'status' => '0', + 'created' => '1262905200', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 4 rev 7 (i=3) rev2 8', + 'uid' => '3', + 'status' => '0', + 'created' => '1262991600', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 5 rev 9 (i=4) rev2 10', + 'uid' => '3', + 'status' => '1', + 'created' => '1263078000', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 6 rev 11 (i=5) rev2 12', + 'uid' => '3', + 'status' => '1', + 'created' => '1263164400', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 7 rev 13 (i=6) rev2 14', + 'uid' => '3', + 'status' => '1', + 'created' => '1263250800', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 8 rev 15 (i=7) rev2 16', + 'uid' => '3', + 'status' => '1', + 'created' => '1263337200', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 9 rev 17 (i=8) rev2 18', + 'uid' => '4', + 'status' => '0', + 'created' => '1263423600', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 10 rev 19 (i=9) rev2 20', + 'uid' => '4', + 'status' => '0', + 'created' => '1263510000', + 'changed' => '1282936266', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 11 rev 21 (i=10) rev2 22', + 'uid' => '4', + 'status' => '0', + 'created' => '1263596400', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'type' => 'page', + 'language' => '', + 'title' => 'node title 12 rev 23 (i=11) rev2 24', + 'uid' => '4', + 'status' => '0', + 'created' => '1263682800', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 13 rev 25 (i=12)', + 'uid' => '4', + 'status' => '1', + 'created' => '1263769200', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 14 rev 26 (i=13)', + 'uid' => '4', + 'status' => '1', + 'created' => '1263855600', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 15 rev 27 (i=14)', + 'uid' => '4', + 'status' => '1', + 'created' => '1263942000', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 16 rev 28 (i=15)', + 'uid' => '4', + 'status' => '1', + 'created' => '1264028400', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 17 rev 29 (i=16)', + 'uid' => '5', + 'status' => '0', + 'created' => '1264114800', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 18 rev 30 (i=17)', + 'uid' => '5', + 'status' => '0', + 'created' => '1264201200', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 19 rev 31 (i=18)', + 'uid' => '5', + 'status' => '0', + 'created' => '1264287600', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 20 rev 32 (i=19)', + 'uid' => '5', + 'status' => '0', + 'created' => '1264374000', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 21 rev 33 (i=20)', + 'uid' => '5', + 'status' => '1', + 'created' => '1264460400', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 22 rev 34 (i=21)', + 'uid' => '5', + 'status' => '1', + 'created' => '1264546800', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 23 rev 35 (i=22)', + 'uid' => '5', + 'status' => '1', + 'created' => '1264633200', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'type' => 'story', + 'language' => '', + 'title' => 'node title 24 rev 36 (i=23)', + 'uid' => '5', + 'status' => '1', + 'created' => '1264719600', + 'changed' => '1282936267', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '25', + 'vid' => '37', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 0', + 'uid' => '3', + 'status' => '0', + 'created' => '1262732400', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '26', + 'vid' => '38', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 1', + 'uid' => '3', + 'status' => '0', + 'created' => '1262775600', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '27', + 'vid' => '39', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 2', + 'uid' => '3', + 'status' => '1', + 'created' => '1262818800', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '28', + 'vid' => '40', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 3', + 'uid' => '3', + 'status' => '1', + 'created' => '1262862000', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '29', + 'vid' => '41', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 4', + 'uid' => '4', + 'status' => '0', + 'created' => '1262905200', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '30', + 'vid' => '42', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 5', + 'uid' => '4', + 'status' => '0', + 'created' => '1262948400', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '31', + 'vid' => '43', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 6', + 'uid' => '4', + 'status' => '1', + 'created' => '1262991600', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '32', + 'vid' => '44', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 7', + 'uid' => '4', + 'status' => '1', + 'created' => '1263034800', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '33', + 'vid' => '45', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 8', + 'uid' => '5', + 'status' => '0', + 'created' => '1263078000', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '34', + 'vid' => '46', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 9', + 'uid' => '5', + 'status' => '0', + 'created' => '1263121200', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '35', + 'vid' => '47', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 10', + 'uid' => '5', + 'status' => '1', + 'created' => '1263164400', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '36', + 'vid' => '48', + 'type' => 'poll', + 'language' => '', + 'title' => 'poll title 11', + 'uid' => '5', + 'status' => '1', + 'created' => '1263207600', + 'changed' => '1282936268', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '37', + 'vid' => '49', + 'type' => 'broken', + 'language' => '', + 'title' => 'node title 24', + 'uid' => '6', + 'status' => '1', + 'created' => '1263769200', + 'changed' => '1279310614', + 'comment' => '0', + 'promote' => '0', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->execute(); + +db_create_table('node_access', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'gid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'realm' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'grant_view' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'grant_update' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'grant_delete' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'nid', + 'gid', + 'realm', + ), + 'module' => 'node', + 'name' => 'node_access', +)); +db_insert('node_access')->fields(array( + 'nid', + 'gid', + 'realm', + 'grant_view', + 'grant_update', + 'grant_delete', +)) +->values(array( + 'nid' => '0', + 'gid' => '0', + 'realm' => 'all', + 'grant_view' => '1', + 'grant_update' => '0', + 'grant_delete' => '0', +)) +->execute(); + +db_create_table('node_comment_statistics', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'last_comment_timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'last_comment_name' => array( + 'type' => 'varchar', + 'length' => 60, + 'not null' => FALSE, + ), + 'last_comment_uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'comment_count' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'nid', + ), + 'indexes' => array( + 'node_comment_timestamp' => array( + 'last_comment_timestamp', + ), + ), + 'module' => 'comment', + 'name' => 'node_comment_statistics', +)); +db_insert('node_comment_statistics')->fields(array( + 'nid', + 'last_comment_timestamp', + 'last_comment_name', + 'last_comment_uid', + 'comment_count', +)) +->values(array( + 'nid' => '1', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '2', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '3', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '4', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '5', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '6', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '7', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '8', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '9', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '10', + 'last_comment_timestamp' => '1282936266', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '11', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '12', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '13', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '14', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '15', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '16', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '17', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '18', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '19', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '20', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '21', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '22', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '23', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '24', + 'last_comment_timestamp' => '1282936267', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '25', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '26', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '27', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '28', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '3', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '29', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '30', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '31', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '32', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '4', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '33', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '34', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '35', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '36', + 'last_comment_timestamp' => '1282936268', + 'last_comment_name' => NULL, + 'last_comment_uid' => '5', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '37', + 'last_comment_timestamp' => '1279310615', + 'last_comment_name' => NULL, + 'last_comment_uid' => '6', + 'comment_count' => '0', +)) +->execute(); + +db_create_table('node_counter', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'totalcount' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'big', + ), + 'daycount' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'medium', + ), + 'timestamp' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'nid', + ), + 'module' => 'node', + 'name' => 'node_counter', +)); + +db_create_table('node_revisions', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'vid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'body' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'teaser' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'log' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'format' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'nid' => array( + 'nid', + ), + 'uid' => array( + 'uid', + ), + ), + 'primary key' => array( + 'vid', + ), + 'module' => 'node', + 'name' => 'node_revisions', +)); +db_insert('node_revisions')->fields(array( + 'nid', + 'vid', + 'uid', + 'title', + 'body', + 'teaser', + 'log', + 'timestamp', + 'format', +)) +->values(array( + 'nid' => '1', + 'vid' => '1', + 'uid' => '3', + 'title' => 'node title 1 rev 1 (i=0)', + 'body' => 'node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0', + 'teaser' => 'node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0', + 'log' => 'added 0 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'uid' => '6', + 'title' => 'node title 1 rev 1 (i=0) rev2 2', + 'body' => 'node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0node revision body (page) - 0', + 'teaser' => 'node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0node body (page) - 0', + 'log' => 'added 0 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '2', + 'vid' => '3', + 'uid' => '3', + 'title' => 'node title 2 rev 3 (i=1)', + 'body' => 'node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1', + 'teaser' => 'node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1', + 'log' => 'added 1 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'uid' => '6', + 'title' => 'node title 2 rev 3 (i=1) rev2 4', + 'body' => 'node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1node revision body (page) - 1', + 'teaser' => 'node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1node body (page) - 1', + 'log' => 'added 1 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '3', + 'vid' => '5', + 'uid' => '3', + 'title' => 'node title 3 rev 5 (i=2)', + 'body' => 'node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2', + 'teaser' => 'node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2', + 'log' => 'added 2 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'uid' => '6', + 'title' => 'node title 3 rev 5 (i=2) rev2 6', + 'body' => 'node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2node revision body (page) - 2', + 'teaser' => 'node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2node body (page) - 2', + 'log' => 'added 2 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '4', + 'vid' => '7', + 'uid' => '3', + 'title' => 'node title 4 rev 7 (i=3)', + 'body' => 'node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3', + 'teaser' => 'node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3', + 'log' => 'added 3 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'uid' => '6', + 'title' => 'node title 4 rev 7 (i=3) rev2 8', + 'body' => 'node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3node revision body (page) - 3', + 'teaser' => 'node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3node body (page) - 3', + 'log' => 'added 3 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '5', + 'vid' => '9', + 'uid' => '3', + 'title' => 'node title 5 rev 9 (i=4)', + 'body' => 'node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4', + 'teaser' => 'node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4', + 'log' => 'added 4 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'uid' => '6', + 'title' => 'node title 5 rev 9 (i=4) rev2 10', + 'body' => 'node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4node revision body (page) - 4', + 'teaser' => 'node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4node body (page) - 4', + 'log' => 'added 4 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '6', + 'vid' => '11', + 'uid' => '3', + 'title' => 'node title 6 rev 11 (i=5)', + 'body' => 'node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5', + 'teaser' => 'node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5', + 'log' => 'added 5 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'uid' => '6', + 'title' => 'node title 6 rev 11 (i=5) rev2 12', + 'body' => 'node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5node revision body (page) - 5', + 'teaser' => 'node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5node body (page) - 5', + 'log' => 'added 5 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '7', + 'vid' => '13', + 'uid' => '3', + 'title' => 'node title 7 rev 13 (i=6)', + 'body' => 'node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6', + 'teaser' => 'node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6', + 'log' => 'added 6 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'uid' => '6', + 'title' => 'node title 7 rev 13 (i=6) rev2 14', + 'body' => 'node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6node revision body (page) - 6', + 'teaser' => 'node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6node body (page) - 6', + 'log' => 'added 6 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '8', + 'vid' => '15', + 'uid' => '3', + 'title' => 'node title 8 rev 15 (i=7)', + 'body' => 'node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7', + 'teaser' => 'node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7', + 'log' => 'added 7 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'uid' => '6', + 'title' => 'node title 8 rev 15 (i=7) rev2 16', + 'body' => 'node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7node revision body (page) - 7', + 'teaser' => 'node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7node body (page) - 7', + 'log' => 'added 7 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '9', + 'vid' => '17', + 'uid' => '4', + 'title' => 'node title 9 rev 17 (i=8)', + 'body' => 'node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8', + 'teaser' => 'node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8', + 'log' => 'added 8 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'uid' => '7', + 'title' => 'node title 9 rev 17 (i=8) rev2 18', + 'body' => 'node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8node revision body (page) - 8', + 'teaser' => 'node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8node body (page) - 8', + 'log' => 'added 8 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '10', + 'vid' => '19', + 'uid' => '4', + 'title' => 'node title 10 rev 19 (i=9)', + 'body' => 'node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9', + 'teaser' => 'node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9', + 'log' => 'added 9 node', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'uid' => '7', + 'title' => 'node title 10 rev 19 (i=9) rev2 20', + 'body' => 'node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9node revision body (page) - 9', + 'teaser' => 'node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9node body (page) - 9', + 'log' => 'added 9 revision', + 'timestamp' => '1282936266', + 'format' => '0', +)) +->values(array( + 'nid' => '11', + 'vid' => '21', + 'uid' => '4', + 'title' => 'node title 11 rev 21 (i=10)', + 'body' => 'node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10', + 'teaser' => 'node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (p', + 'log' => 'added 10 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'uid' => '7', + 'title' => 'node title 11 rev 21 (i=10) rev2 22', + 'body' => 'node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10node revision body (page) - 10', + 'teaser' => 'node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (page) - 10node body (p', + 'log' => 'added 10 revision', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '12', + 'vid' => '23', + 'uid' => '4', + 'title' => 'node title 12 rev 23 (i=11)', + 'body' => 'node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11', + 'teaser' => 'node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (p', + 'log' => 'added 11 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'uid' => '7', + 'title' => 'node title 12 rev 23 (i=11) rev2 24', + 'body' => 'node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11node revision body (page) - 11', + 'teaser' => 'node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (page) - 11node body (p', + 'log' => 'added 11 revision', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'uid' => '4', + 'title' => 'node title 13 rev 25 (i=12)', + 'body' => 'node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12', + 'teaser' => 'node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node body (story) - 12node b', + 'log' => 'added 12 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'uid' => '4', + 'title' => 'node title 14 rev 26 (i=13)', + 'body' => 'node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13', + 'teaser' => 'node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node body (story) - 13node b', + 'log' => 'added 13 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'uid' => '4', + 'title' => 'node title 15 rev 27 (i=14)', + 'body' => 'node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14', + 'teaser' => 'node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node body (story) - 14node b', + 'log' => 'added 14 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'uid' => '4', + 'title' => 'node title 16 rev 28 (i=15)', + 'body' => 'node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15', + 'teaser' => 'node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node body (story) - 15node b', + 'log' => 'added 15 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'uid' => '5', + 'title' => 'node title 17 rev 29 (i=16)', + 'body' => 'node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16', + 'teaser' => 'node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node body (story) - 16node b', + 'log' => 'added 16 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'uid' => '5', + 'title' => 'node title 18 rev 30 (i=17)', + 'body' => 'node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17', + 'teaser' => 'node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node body (story) - 17node b', + 'log' => 'added 17 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'uid' => '5', + 'title' => 'node title 19 rev 31 (i=18)', + 'body' => 'node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18', + 'teaser' => 'node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node body (story) - 18node b', + 'log' => 'added 18 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'uid' => '5', + 'title' => 'node title 20 rev 32 (i=19)', + 'body' => 'node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19', + 'teaser' => 'node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node body (story) - 19node b', + 'log' => 'added 19 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'uid' => '5', + 'title' => 'node title 21 rev 33 (i=20)', + 'body' => 'node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20', + 'teaser' => 'node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node body (story) - 20node b', + 'log' => 'added 20 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'uid' => '5', + 'title' => 'node title 22 rev 34 (i=21)', + 'body' => 'node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21', + 'teaser' => 'node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node body (story) - 21node b', + 'log' => 'added 21 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'uid' => '5', + 'title' => 'node title 23 rev 35 (i=22)', + 'body' => 'node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22', + 'teaser' => 'node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node body (story) - 22node b', + 'log' => 'added 22 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'uid' => '5', + 'title' => 'node title 24 rev 36 (i=23)', + 'body' => 'node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23', + 'teaser' => 'node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node body (story) - 23node b', + 'log' => 'added 23 node', + 'timestamp' => '1282936267', + 'format' => '0', +)) +->values(array( + 'nid' => '25', + 'vid' => '37', + 'uid' => '3', + 'title' => 'poll title 0', + 'body' => '', + 'teaser' => '', + 'log' => 'added 0 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '26', + 'vid' => '38', + 'uid' => '3', + 'title' => 'poll title 1', + 'body' => '', + 'teaser' => '', + 'log' => 'added 1 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '27', + 'vid' => '39', + 'uid' => '3', + 'title' => 'poll title 2', + 'body' => '', + 'teaser' => '', + 'log' => 'added 2 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '28', + 'vid' => '40', + 'uid' => '3', + 'title' => 'poll title 3', + 'body' => '', + 'teaser' => '', + 'log' => 'added 3 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '29', + 'vid' => '41', + 'uid' => '4', + 'title' => 'poll title 4', + 'body' => '', + 'teaser' => '', + 'log' => 'added 4 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '30', + 'vid' => '42', + 'uid' => '4', + 'title' => 'poll title 5', + 'body' => '', + 'teaser' => '', + 'log' => 'added 5 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '31', + 'vid' => '43', + 'uid' => '4', + 'title' => 'poll title 6', + 'body' => '', + 'teaser' => '', + 'log' => 'added 6 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '32', + 'vid' => '44', + 'uid' => '4', + 'title' => 'poll title 7', + 'body' => '', + 'teaser' => '', + 'log' => 'added 7 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '33', + 'vid' => '45', + 'uid' => '5', + 'title' => 'poll title 8', + 'body' => '', + 'teaser' => '', + 'log' => 'added 8 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '34', + 'vid' => '46', + 'uid' => '5', + 'title' => 'poll title 9', + 'body' => '', + 'teaser' => '', + 'log' => 'added 9 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '35', + 'vid' => '47', + 'uid' => '5', + 'title' => 'poll title 10', + 'body' => '', + 'teaser' => '', + 'log' => 'added 10 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '36', + 'vid' => '48', + 'uid' => '5', + 'title' => 'poll title 11', + 'body' => '', + 'teaser' => '', + 'log' => 'added 11 poll', + 'timestamp' => '1282936268', + 'format' => '0', +)) +->values(array( + 'nid' => '37', + 'vid' => '49', + 'uid' => '6', + 'title' => 'node title 24', + 'body' => 'node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37', + 'teaser' => 'node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37node body (broken) - 37no', + 'log' => 'added 12 node', + 'timestamp' => '1279310614', + 'format' => '0', +)) +->execute(); + +db_create_table('node_type', array( + 'fields' => array( + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'description' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + ), + 'help' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + ), + 'has_title' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'size' => 'tiny', + ), + 'title_label' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'has_body' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'size' => 'tiny', + ), + 'body_label' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'min_word_count' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'size' => 'small', + ), + 'custom' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'modified' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'locked' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'orig_type' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'primary key' => array( + 'type', + ), + 'module' => 'node', + 'name' => 'node_type', +)); +db_insert('node_type')->fields(array( + 'type', + 'name', + 'module', + 'description', + 'help', + 'has_title', + 'title_label', + 'has_body', + 'body_label', + 'min_word_count', + 'custom', + 'modified', + 'locked', + 'orig_type', +)) +->values(array( + 'type' => 'page', + 'name' => 'Page', + 'module' => 'node', + 'description' => "A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site's initial home page.", + 'help' => '', + 'has_title' => '1', + 'title_label' => 'Title', + 'has_body' => '1', + 'body_label' => 'Body', + 'min_word_count' => '0', + 'custom' => '1', + 'modified' => '1', + 'locked' => '0', + 'orig_type' => 'page', +)) +->values(array( + 'type' => 'poll', + 'name' => 'Poll', + 'module' => 'poll', + 'description' => 'A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.', + 'help' => '', + 'has_title' => '1', + 'title_label' => 'Question', + 'has_body' => '0', + 'body_label' => '', + 'min_word_count' => '0', + 'custom' => '0', + 'modified' => '0', + 'locked' => '1', + 'orig_type' => 'poll', +)) +->values(array( + 'type' => 'story', + 'name' => 'Story', + 'module' => 'node', + 'description' => "A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site's initial home page, and provides the ability to post comments.", + 'help' => '', + 'has_title' => '1', + 'title_label' => 'Title', + 'has_body' => '1', + 'body_label' => 'Body', + 'min_word_count' => '0', + 'custom' => '1', + 'modified' => '1', + 'locked' => '0', + 'orig_type' => 'story', +)) +->execute(); + +db_create_table('permission', array( + 'fields' => array( + 'pid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'rid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'perm' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'pid', + ), + 'indexes' => array( + 'rid' => array( + 'rid', + ), + ), + 'module' => 'user', + 'name' => 'permission', +)); +db_insert('permission')->fields(array( + 'pid', + 'rid', + 'perm', + 'tid', +)) +->values(array( + 'pid' => '1', + 'rid' => '1', + 'perm' => 'access content', + 'tid' => '0', +)) +->values(array( + 'pid' => '2', + 'rid' => '2', + 'perm' => 'access comments, access content, post comments, post comments without approval', + 'tid' => '0', +)) +->execute(); + +db_create_table('poll', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'runtime' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'active' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'nid', + ), + 'module' => 'poll', + 'name' => 'poll', +)); +db_insert('poll')->fields(array( + 'nid', + 'runtime', + 'active', +)) +->values(array( + 'nid' => '25', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '26', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '27', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '28', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '29', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '30', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '31', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '32', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '33', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '34', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '35', + 'runtime' => '0', + 'active' => '1', +)) +->values(array( + 'nid' => '36', + 'runtime' => '0', + 'active' => '1', +)) +->execute(); + +db_create_table('poll_choices', array( + 'fields' => array( + 'chid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'chtext' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'chvotes' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'chorder' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'nid' => array( + 'nid', + ), + ), + 'primary key' => array( + 'chid', + ), + 'module' => 'poll', + 'name' => 'poll_choices', +)); +db_insert('poll_choices')->fields(array( + 'chid', + 'nid', + 'chtext', + 'chvotes', + 'chorder', +)) +->values(array( + 'chid' => '1', + 'nid' => '25', + 'chtext' => 'Choice 0 for poll 0', + 'chvotes' => '3', + 'chorder' => '0', +)) +->values(array( + 'chid' => '2', + 'nid' => '25', + 'chtext' => 'Choice 1 for poll 0', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '3', + 'nid' => '26', + 'chtext' => 'Choice 0 for poll 1', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '4', + 'nid' => '26', + 'chtext' => 'Choice 1 for poll 1', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '5', + 'nid' => '26', + 'chtext' => 'Choice 2 for poll 1', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '6', + 'nid' => '27', + 'chtext' => 'Choice 0 for poll 2', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '7', + 'nid' => '27', + 'chtext' => 'Choice 1 for poll 2', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '8', + 'nid' => '27', + 'chtext' => 'Choice 2 for poll 2', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '9', + 'nid' => '27', + 'chtext' => 'Choice 3 for poll 2', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '10', + 'nid' => '28', + 'chtext' => 'Choice 0 for poll 3', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '11', + 'nid' => '28', + 'chtext' => 'Choice 1 for poll 3', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '12', + 'nid' => '28', + 'chtext' => 'Choice 2 for poll 3', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '13', + 'nid' => '28', + 'chtext' => 'Choice 3 for poll 3', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '14', + 'nid' => '28', + 'chtext' => 'Choice 4 for poll 3', + 'chvotes' => '1', + 'chorder' => '4', +)) +->values(array( + 'chid' => '15', + 'nid' => '29', + 'chtext' => 'Choice 0 for poll 4', + 'chvotes' => '3', + 'chorder' => '0', +)) +->values(array( + 'chid' => '16', + 'nid' => '29', + 'chtext' => 'Choice 1 for poll 4', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '17', + 'nid' => '30', + 'chtext' => 'Choice 0 for poll 5', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '18', + 'nid' => '30', + 'chtext' => 'Choice 1 for poll 5', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '19', + 'nid' => '30', + 'chtext' => 'Choice 2 for poll 5', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '20', + 'nid' => '31', + 'chtext' => 'Choice 0 for poll 6', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '21', + 'nid' => '31', + 'chtext' => 'Choice 1 for poll 6', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '22', + 'nid' => '31', + 'chtext' => 'Choice 2 for poll 6', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '23', + 'nid' => '31', + 'chtext' => 'Choice 3 for poll 6', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '24', + 'nid' => '32', + 'chtext' => 'Choice 0 for poll 7', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '25', + 'nid' => '32', + 'chtext' => 'Choice 1 for poll 7', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '26', + 'nid' => '32', + 'chtext' => 'Choice 2 for poll 7', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '27', + 'nid' => '32', + 'chtext' => 'Choice 3 for poll 7', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '28', + 'nid' => '32', + 'chtext' => 'Choice 4 for poll 7', + 'chvotes' => '1', + 'chorder' => '4', +)) +->values(array( + 'chid' => '29', + 'nid' => '33', + 'chtext' => 'Choice 0 for poll 8', + 'chvotes' => '3', + 'chorder' => '0', +)) +->values(array( + 'chid' => '30', + 'nid' => '33', + 'chtext' => 'Choice 1 for poll 8', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '31', + 'nid' => '34', + 'chtext' => 'Choice 0 for poll 9', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '32', + 'nid' => '34', + 'chtext' => 'Choice 1 for poll 9', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '33', + 'nid' => '34', + 'chtext' => 'Choice 2 for poll 9', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '34', + 'nid' => '35', + 'chtext' => 'Choice 0 for poll 10', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '35', + 'nid' => '35', + 'chtext' => 'Choice 1 for poll 10', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '36', + 'nid' => '35', + 'chtext' => 'Choice 2 for poll 10', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '37', + 'nid' => '35', + 'chtext' => 'Choice 3 for poll 10', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '38', + 'nid' => '36', + 'chtext' => 'Choice 0 for poll 11', + 'chvotes' => '2', + 'chorder' => '0', +)) +->values(array( + 'chid' => '39', + 'nid' => '36', + 'chtext' => 'Choice 1 for poll 11', + 'chvotes' => '2', + 'chorder' => '1', +)) +->values(array( + 'chid' => '40', + 'nid' => '36', + 'chtext' => 'Choice 2 for poll 11', + 'chvotes' => '2', + 'chorder' => '2', +)) +->values(array( + 'chid' => '41', + 'nid' => '36', + 'chtext' => 'Choice 3 for poll 11', + 'chvotes' => '1', + 'chorder' => '3', +)) +->values(array( + 'chid' => '42', + 'nid' => '36', + 'chtext' => 'Choice 4 for poll 11', + 'chvotes' => '1', + 'chorder' => '4', +)) +->execute(); + +db_create_table('poll_votes', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'chorder' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => -1, + ), + 'hostname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'primary key' => array( + 'nid', + 'uid', + 'hostname', + ), + 'indexes' => array( + 'hostname' => array( + 'hostname', + ), + 'uid' => array( + 'uid', + ), + ), + 'module' => 'poll', + 'name' => 'poll_votes', +)); +db_insert('poll_votes')->fields(array( + 'nid', + 'uid', + 'chorder', + 'hostname', +)) +->values(array( + 'nid' => '25', + 'uid' => '3', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '26', + 'uid' => '3', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '27', + 'uid' => '3', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '28', + 'uid' => '3', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '29', + 'uid' => '4', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '30', + 'uid' => '4', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '31', + 'uid' => '4', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '32', + 'uid' => '4', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '33', + 'uid' => '5', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '34', + 'uid' => '5', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '35', + 'uid' => '5', + 'chorder' => '0', + 'hostname' => '', +)) +->values(array( + 'nid' => '36', + 'uid' => '5', + 'chorder' => '0', + 'hostname' => '', +)) +->execute(); + +db_create_table('role', array( + 'fields' => array( + 'rid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'unique keys' => array( + 'name' => array( + 'name', + ), + ), + 'primary key' => array( + 'rid', + ), + 'module' => 'user', + 'name' => 'role', +)); +db_insert('role')->fields(array( + 'rid', + 'name', +)) +->values(array( + 'rid' => '1', + 'name' => 'anonymous user', +)) +->values(array( + 'rid' => '2', + 'name' => 'authenticated user', +)) +->execute(); + +db_create_table('semaphore', array( + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'expire' => array( + 'type' => 'float', + 'size' => 'big', + 'not null' => TRUE, + ), + ), + 'indexes' => array( + 'expire' => array( + 'expire', + ), + ), + 'primary key' => array( + 'name', + ), + 'module' => 'system', + 'name' => 'semaphore', +)); + +db_create_table('sessions', array( + 'fields' => array( + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'sid' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'hostname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'cache' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'session' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + ), + 'primary key' => array( + 'sid', + ), + 'indexes' => array( + 'timestamp' => array( + 'timestamp', + ), + 'uid' => array( + 'uid', + ), + ), + 'module' => 'system', + 'name' => 'sessions', +)); + +db_create_table('system', array( + 'fields' => array( + 'filename' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'owner' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'throttle' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'bootstrap' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'schema_version' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => -1, + 'size' => 'small', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'info' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + ), + 'primary key' => array( + 'filename', + ), + 'indexes' => array( + 'modules' => array( + array( + 'type', + 12, + ), + 'status', + 'weight', + 'filename', + ), + 'bootstrap' => array( + array( + 'type', + 12, + ), + 'status', + 'bootstrap', + 'weight', + 'filename', + ), + 'type_name' => array( + array( + 'type', + 12, + ), + 'name', + ), + ), + 'module' => 'system', + 'name' => 'system', +)); +db_insert('system')->fields(array( + 'filename', + 'name', + 'type', + 'owner', + 'status', + 'throttle', + 'bootstrap', + 'schema_version', + 'weight', + 'info', +)) +->values(array( + 'filename' => 'modules/aggregator/aggregator.module', + 'name' => 'aggregator', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:10:"Aggregator";s:11:"description";s:57:"Aggregates syndicated content (RSS, RDF, and Atom feeds).";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/block/block.module', + 'name' => 'block', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Block";s:11:"description";s:62:"Controls the boxes that are displayed around the main content.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/blog/blog.module', + 'name' => 'blog', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Blog";s:11:"description";s:69:"Enables keeping easily and regularly updated user web pages or blogs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/blogapi/blogapi.module', + 'name' => 'blogapi', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:8:"Blog API";s:11:"description";s:79:"Allows users to post content using applications that support XML-RPC blog APIs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/book/book.module', + 'name' => 'book', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Book";s:11:"description";s:63:"Allows users to structure site pages in a hierarchy or outline.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/color/color.module', + 'name' => 'color', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Color";s:11:"description";s:61:"Allows the user to change the color scheme of certain themes.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/comment/comment.module', + 'name' => 'comment', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6003', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Comment";s:11:"description";s:57:"Allows users to comment on and discuss published content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/contact/contact.module', + 'name' => 'contact', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Contact";s:11:"description";s:61:"Enables the use of both personal and site-wide contact forms.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/dblog/dblog.module', + 'name' => 'dblog', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6000', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:16:"Database logging";s:11:"description";s:47:"Logs and records system events to the database.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/filter/filter.module', + 'name' => 'filter', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Filter";s:11:"description";s:60:"Handles the filtering of content in preparation for display.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/forum/forum.module', + 'name' => 'forum', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Forum";s:11:"description";s:50:"Enables threaded discussions about general topics.";s:12:"dependencies";a:2:{i:0;s:8:"taxonomy";i:1;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/help/help.module', + 'name' => 'help', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Help";s:11:"description";s:35:"Manages the display of online help.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/locale/locale.module', + 'name' => 'locale', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Locale";s:11:"description";s:119:"Adds language handling functionality and enables the translation of the user interface to languages other than English.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/menu/menu.module', + 'name' => 'menu', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Menu";s:11:"description";s:60:"Allows administrators to customize the site navigation menu.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/node/node.module', + 'name' => 'node', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Node";s:11:"description";s:66:"Allows content to be submitted to the site and displayed on pages.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/openid/openid.module', + 'name' => 'openid', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"OpenID";s:11:"description";s:48:"Allows users to log into your site using OpenID.";s:7:"version";s:8:"6.20-dev";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/path/path.module', + 'name' => 'path', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Path";s:11:"description";s:28:"Allows users to rename URLs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/php/php.module', + 'name' => 'php', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:10:"PHP filter";s:11:"description";s:50:"Allows embedded PHP code/snippets to be evaluated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/ping/ping.module', + 'name' => 'ping', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Ping";s:11:"description";s:51:"Alerts other sites when your site has been updated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/poll/poll.module', + 'name' => 'poll', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Poll";s:11:"description";s:95:"Allows your site to capture votes on different topics in the form of multiple choice questions.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/profile/profile.module', + 'name' => 'profile', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Profile";s:11:"description";s:36:"Supports configurable user profiles.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/search/search.module', + 'name' => 'search', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Search";s:11:"description";s:36:"Enables site-wide keyword searching.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/statistics/statistics.module', + 'name' => 'statistics', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:10:"Statistics";s:11:"description";s:37:"Logs access statistics for your site.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/syslog/syslog.module', + 'name' => 'syslog', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Syslog";s:11:"description";s:41:"Logs and records system events to syslog.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/system/system.module', + 'name' => 'system', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6055', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"System";s:11:"description";s:54:"Handles general site configuration for administrators.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/taxonomy/taxonomy.module', + 'name' => 'taxonomy', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:8:"Taxonomy";s:11:"description";s:38:"Enables the categorization of content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/throttle/throttle.module', + 'name' => 'throttle', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:8:"Throttle";s:11:"description";s:66:"Handles the auto-throttling mechanism, to control site congestion.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/tracker/tracker.module', + 'name' => 'tracker', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Tracker";s:11:"description";s:43:"Enables tracking of recent posts for users.";s:12:"dependencies";a:1:{i:0;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/translation/translation.module', + 'name' => 'translation', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:19:"Content translation";s:11:"description";s:57:"Allows content to be translated into different languages.";s:12:"dependencies";a:1:{i:0;s:6:"locale";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/trigger/trigger.module', + 'name' => 'trigger', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Trigger";s:11:"description";s:90:"Enables actions to be fired on certain system events, such as when new content is created.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/update/update.module', + 'name' => 'update', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6000', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:13:"Update status";s:11:"description";s:88:"Checks the status of available updates for Drupal and your installed modules and themes.";s:7:"version";s:8:"6.20-dev";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/upload/upload.module', + 'name' => 'upload', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Upload";s:11:"description";s:51:"Allows users to upload and attach files to content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'modules/user/user.module', + 'name' => 'user', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"User";s:11:"description";s:47:"Manages the user registration and login system.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/admin_menu/admin_menu.module', + 'name' => 'admin_menu', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:19:"Administration Menu";s:11:"description";s:90:"Renders a menu tree for administrative purposes as dropdown menu at the top of the window.";s:7:"package";s:14:"Administration";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/content.module', + 'name' => 'content', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:7:"Content";s:11:"description";s:50:"Allows administrators to define new content types.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/content_copy/content_copy.module', + 'name' => 'content_copy', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:12:"Content Copy";s:11:"description";s:51:"Enables ability to import/export field definitions.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/content_permissions/content_permissions.module', + 'name' => 'content_permissions', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:19:"Content Permissions";s:11:"description";s:43:"Set field-level permissions for CCK fields.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/fieldgroup/fieldgroup.module', + 'name' => 'fieldgroup', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:10:"Fieldgroup";s:11:"description";s:35:"Create field groups for CCK fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/nodereference/nodereference.module', + 'name' => 'nodereference', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"Node Reference";s:11:"description";s:59:"Defines a field type for referencing one node from another.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/number/number.module', + 'name' => 'number', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Number";s:11:"description";s:28:"Defines numeric field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/optionwidgets/optionwidgets.module', + 'name' => 'optionwidgets', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"Option Widgets";s:11:"description";s:82:"Defines selection, check box and radio button widgets for text and numeric fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/text/text.module', + 'name' => 'text', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:4:"Text";s:11:"description";s:32:"Defines simple text field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/cck/modules/userreference/userreference.module', + 'name' => 'userreference', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"User Reference";s:11:"description";s:56:"Defines a field type for referencing a user from a node.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/devel.module', + 'name' => 'devel', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Devel";s:11:"description";s:52:"Various blocks, pages, and functions for developers.";s:7:"package";s:11:"Development";s:12:"dependencies";a:1:{i:0;s:4:"menu";}s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/devel_generate.module', + 'name' => 'devel_generate', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"Devel generate";s:11:"description";s:48:"Generate dummy users, nodes, and taxonomy terms.";s:7:"package";s:11:"Development";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/devel_node_access.module', + 'name' => 'devel_node_access', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:17:"Devel node access";s:11:"description";s:67:"Developer block and page illustrating relevant node_access records.";s:7:"package";s:11:"Development";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/devel_themer.module', + 'name' => 'devel_themer', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:15:"Theme developer";s:11:"description";s:52:"Essential theme API information for theme developers";s:7:"package";s:11:"Development";s:12:"dependencies";a:1:{i:0;s:5:"devel";}s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/macro.module', + 'name' => 'macro', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Macro";s:11:"description";s:62:"Allows administrators to record and playback form submissions.";s:7:"package";s:11:"Development";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/devel/performance/performance.module', + 'name' => 'performance', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:19:"Performance Logging";s:11:"description";s:91:"Logs detailed and/or summary page generation time and memory consumption for page requests.";s:7:"package";s:11:"Development";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/drush_extras/drush_extras.module', + 'name' => 'drush_extras', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:9:{s:4:"name";s:12:"Drush extras";s:11:"description";s:43:"Useful Drush commands. Requires Drush core.";s:7:"package";s:11:"Development";s:4:"core";s:3:"7.x";s:5:"files";a:7:{i:0;s:19:"drush_extras.module";i:1;s:12:"pm.drush.inc";i:2;s:16:"pm_cvs.drush.inc";i:3;s:16:"pm_svn.drush.inc";i:4;s:20:"simpletest.drush.inc";i:5;s:13:"sql.drush.inc";i:6;s:15:"tools.drush.inc";}s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/mymap/mymap.module', + 'name' => 'mymap', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:7:{s:4:"name";s:6:"My Map";s:11:"description";s:36:"Allows any node to have a Google map";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/plugin_manager.module', + 'name' => 'plugin_manager', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"Plugin Manager";s:11:"description";s:72:"Enables the automated installation of modules and themes from drupal.org";s:4:"core";s:3:"6.x";s:3:"php";s:1:"5";s:7:"package";s:14:"Administration";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;}', +)) +->values(array( + 'filename' => 'sites/all/modules/schema/schema.module', + 'name' => 'schema', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:6:"Schema";s:11:"description";s:65:"The Schema module provides functionality built on the Schema API.";s:7:"package";s:8:"Database";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/views/views.module', + 'name' => 'views', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:5:"Views";s:11:"description";s:55:"Create customized lists and queries from your database.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/views/views_export/views_export.module', + 'name' => 'views_export', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:14:"Views exporter";s:11:"description";s:40:"Allows exporting multiple views at once.";s:7:"package";s:5:"Views";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/views/views_ui.module', + 'name' => 'views_ui', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:8:{s:4:"name";s:8:"Views UI";s:11:"description";s:93:"Administrative interface to views. Without this module, you cannot create or edit your views.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'themes/bluemarine/bluemarine.info', + 'name' => 'bluemarine', + 'type' => 'theme', + 'owner' => 'themes/engines/phptemplate/phptemplate.engine', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'themes/chameleon/chameleon.info', + 'name' => 'chameleon', + 'type' => 'theme', + 'owner' => 'themes/chameleon/chameleon.theme', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'themes/chameleon/marvin/marvin.info', + 'name' => 'marvin', + 'type' => 'theme', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'themes/garland/garland.info', + 'name' => 'garland', + 'type' => 'theme', + 'owner' => 'themes/engines/phptemplate/phptemplate.engine', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'themes/garland/minnelli/minnelli.info', + 'name' => 'minnelli', + 'type' => 'theme', + 'owner' => 'themes/engines/phptemplate/phptemplate.engine', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}', +)) +->values(array( + 'filename' => 'themes/pushbutton/pushbutton.info', + 'name' => 'pushbutton', + 'type' => 'theme', + 'owner' => 'themes/engines/phptemplate/phptemplate.engine', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.20-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}', +)) +->execute(); + +db_create_table('term_data', array( + 'fields' => array( + 'tid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'description' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'tid', + ), + 'indexes' => array( + 'taxonomy_tree' => array( + 'vid', + 'weight', + 'name', + ), + 'vid_name' => array( + 'vid', + 'name', + ), + ), + 'module' => 'taxonomy', + 'name' => 'term_data', +)); +db_insert('term_data')->fields(array( + 'tid', + 'vid', + 'name', + 'description', + 'weight', +)) +->values(array( + 'tid' => '1', + 'vid' => '1', + 'name' => 'term 1 of vocabulary 1 (j=0)', + 'description' => 'description of term 1 of vocabulary 1 (j=0)', + 'weight' => '0', +)) +->values(array( + 'tid' => '2', + 'vid' => '2', + 'name' => 'term 2 of vocabulary 2 (j=0)', + 'description' => 'description of term 2 of vocabulary 2 (j=0)', + 'weight' => '3', +)) +->values(array( + 'tid' => '3', + 'vid' => '2', + 'name' => 'term 3 of vocabulary 2 (j=1)', + 'description' => 'description of term 3 of vocabulary 2 (j=1)', + 'weight' => '4', +)) +->values(array( + 'tid' => '4', + 'vid' => '3', + 'name' => 'term 4 of vocabulary 3 (j=0)', + 'description' => 'description of term 4 of vocabulary 3 (j=0)', + 'weight' => '6', +)) +->values(array( + 'tid' => '5', + 'vid' => '3', + 'name' => 'term 5 of vocabulary 3 (j=1)', + 'description' => 'description of term 5 of vocabulary 3 (j=1)', + 'weight' => '7', +)) +->values(array( + 'tid' => '6', + 'vid' => '3', + 'name' => 'term 6 of vocabulary 3 (j=2)', + 'description' => 'description of term 6 of vocabulary 3 (j=2)', + 'weight' => '8', +)) +->values(array( + 'tid' => '7', + 'vid' => '4', + 'name' => 'term 7 of vocabulary 4 (j=0)', + 'description' => 'description of term 7 of vocabulary 4 (j=0)', + 'weight' => '9', +)) +->values(array( + 'tid' => '8', + 'vid' => '5', + 'name' => 'term 8 of vocabulary 5 (j=0)', + 'description' => 'description of term 8 of vocabulary 5 (j=0)', + 'weight' => '12', +)) +->values(array( + 'tid' => '9', + 'vid' => '5', + 'name' => 'term 9 of vocabulary 5 (j=1)', + 'description' => 'description of term 9 of vocabulary 5 (j=1)', + 'weight' => '13', +)) +->values(array( + 'tid' => '10', + 'vid' => '6', + 'name' => 'term 10 of vocabulary 6 (j=0)', + 'description' => 'description of term 10 of vocabulary 6 (j=0)', + 'weight' => '15', +)) +->values(array( + 'tid' => '11', + 'vid' => '6', + 'name' => 'term 11 of vocabulary 6 (j=1)', + 'description' => 'description of term 11 of vocabulary 6 (j=1)', + 'weight' => '16', +)) +->values(array( + 'tid' => '12', + 'vid' => '6', + 'name' => 'term 12 of vocabulary 6 (j=2)', + 'description' => 'description of term 12 of vocabulary 6 (j=2)', + 'weight' => '17', +)) +->values(array( + 'tid' => '13', + 'vid' => '7', + 'name' => 'term 13 of vocabulary 7 (j=0)', + 'description' => 'description of term 13 of vocabulary 7 (j=0)', + 'weight' => '18', +)) +->values(array( + 'tid' => '14', + 'vid' => '8', + 'name' => 'term 14 of vocabulary 8 (j=0)', + 'description' => 'description of term 14 of vocabulary 8 (j=0)', + 'weight' => '21', +)) +->values(array( + 'tid' => '15', + 'vid' => '8', + 'name' => 'term 15 of vocabulary 8 (j=1)', + 'description' => 'description of term 15 of vocabulary 8 (j=1)', + 'weight' => '22', +)) +->values(array( + 'tid' => '16', + 'vid' => '9', + 'name' => 'term 16 of vocabulary 9 (j=0)', + 'description' => 'description of term 16 of vocabulary 9 (j=0)', + 'weight' => '24', +)) +->values(array( + 'tid' => '17', + 'vid' => '9', + 'name' => 'term 17 of vocabulary 9 (j=1)', + 'description' => 'description of term 17 of vocabulary 9 (j=1)', + 'weight' => '25', +)) +->values(array( + 'tid' => '18', + 'vid' => '9', + 'name' => 'term 18 of vocabulary 9 (j=2)', + 'description' => 'description of term 18 of vocabulary 9 (j=2)', + 'weight' => '26', +)) +->values(array( + 'tid' => '19', + 'vid' => '10', + 'name' => 'term 19 of vocabulary 10 (j=0)', + 'description' => 'description of term 19 of vocabulary 10 (j=0)', + 'weight' => '27', +)) +->values(array( + 'tid' => '20', + 'vid' => '11', + 'name' => 'term 20 of vocabulary 11 (j=0)', + 'description' => 'description of term 20 of vocabulary 11 (j=0)', + 'weight' => '30', +)) +->values(array( + 'tid' => '21', + 'vid' => '11', + 'name' => 'term 21 of vocabulary 11 (j=1)', + 'description' => 'description of term 21 of vocabulary 11 (j=1)', + 'weight' => '31', +)) +->values(array( + 'tid' => '22', + 'vid' => '12', + 'name' => 'term 22 of vocabulary 12 (j=0)', + 'description' => 'description of term 22 of vocabulary 12 (j=0)', + 'weight' => '33', +)) +->values(array( + 'tid' => '23', + 'vid' => '12', + 'name' => 'term 23 of vocabulary 12 (j=1)', + 'description' => 'description of term 23 of vocabulary 12 (j=1)', + 'weight' => '34', +)) +->values(array( + 'tid' => '24', + 'vid' => '12', + 'name' => 'term 24 of vocabulary 12 (j=2)', + 'description' => 'description of term 24 of vocabulary 12 (j=2)', + 'weight' => '35', +)) +->values(array( + 'tid' => '25', + 'vid' => '13', + 'name' => 'term 25 of vocabulary 13 (j=0)', + 'description' => 'description of term 25 of vocabulary 13 (j=0)', + 'weight' => '36', +)) +->values(array( + 'tid' => '26', + 'vid' => '14', + 'name' => 'term 26 of vocabulary 14 (j=0)', + 'description' => 'description of term 26 of vocabulary 14 (j=0)', + 'weight' => '39', +)) +->values(array( + 'tid' => '27', + 'vid' => '14', + 'name' => 'term 27 of vocabulary 14 (j=1)', + 'description' => 'description of term 27 of vocabulary 14 (j=1)', + 'weight' => '40', +)) +->values(array( + 'tid' => '28', + 'vid' => '15', + 'name' => 'term 28 of vocabulary 15 (j=0)', + 'description' => 'description of term 28 of vocabulary 15 (j=0)', + 'weight' => '42', +)) +->values(array( + 'tid' => '29', + 'vid' => '15', + 'name' => 'term 29 of vocabulary 15 (j=1)', + 'description' => 'description of term 29 of vocabulary 15 (j=1)', + 'weight' => '43', +)) +->values(array( + 'tid' => '30', + 'vid' => '15', + 'name' => 'term 30 of vocabulary 15 (j=2)', + 'description' => 'description of term 30 of vocabulary 15 (j=2)', + 'weight' => '44', +)) +->values(array( + 'tid' => '31', + 'vid' => '16', + 'name' => 'term 31 of vocabulary 16 (j=0)', + 'description' => 'description of term 31 of vocabulary 16 (j=0)', + 'weight' => '45', +)) +->values(array( + 'tid' => '32', + 'vid' => '17', + 'name' => 'term 32 of vocabulary 17 (j=0)', + 'description' => 'description of term 32 of vocabulary 17 (j=0)', + 'weight' => '48', +)) +->values(array( + 'tid' => '33', + 'vid' => '17', + 'name' => 'term 33 of vocabulary 17 (j=1)', + 'description' => 'description of term 33 of vocabulary 17 (j=1)', + 'weight' => '49', +)) +->values(array( + 'tid' => '34', + 'vid' => '18', + 'name' => 'term 34 of vocabulary 18 (j=0)', + 'description' => 'description of term 34 of vocabulary 18 (j=0)', + 'weight' => '51', +)) +->values(array( + 'tid' => '35', + 'vid' => '18', + 'name' => 'term 35 of vocabulary 18 (j=1)', + 'description' => 'description of term 35 of vocabulary 18 (j=1)', + 'weight' => '52', +)) +->values(array( + 'tid' => '36', + 'vid' => '18', + 'name' => 'term 36 of vocabulary 18 (j=2)', + 'description' => 'description of term 36 of vocabulary 18 (j=2)', + 'weight' => '53', +)) +->values(array( + 'tid' => '37', + 'vid' => '19', + 'name' => 'term 37 of vocabulary 19 (j=0)', + 'description' => 'description of term 37 of vocabulary 19 (j=0)', + 'weight' => '54', +)) +->values(array( + 'tid' => '38', + 'vid' => '20', + 'name' => 'term 38 of vocabulary 20 (j=0)', + 'description' => 'description of term 38 of vocabulary 20 (j=0)', + 'weight' => '57', +)) +->values(array( + 'tid' => '39', + 'vid' => '20', + 'name' => 'term 39 of vocabulary 20 (j=1)', + 'description' => 'description of term 39 of vocabulary 20 (j=1)', + 'weight' => '58', +)) +->values(array( + 'tid' => '40', + 'vid' => '21', + 'name' => 'term 40 of vocabulary 21 (j=0)', + 'description' => 'description of term 40 of vocabulary 21 (j=0)', + 'weight' => '60', +)) +->values(array( + 'tid' => '41', + 'vid' => '21', + 'name' => 'term 41 of vocabulary 21 (j=1)', + 'description' => 'description of term 41 of vocabulary 21 (j=1)', + 'weight' => '61', +)) +->values(array( + 'tid' => '42', + 'vid' => '21', + 'name' => 'term 42 of vocabulary 21 (j=2)', + 'description' => 'description of term 42 of vocabulary 21 (j=2)', + 'weight' => '62', +)) +->values(array( + 'tid' => '43', + 'vid' => '22', + 'name' => 'term 43 of vocabulary 22 (j=0)', + 'description' => 'description of term 43 of vocabulary 22 (j=0)', + 'weight' => '63', +)) +->values(array( + 'tid' => '44', + 'vid' => '23', + 'name' => 'term 44 of vocabulary 23 (j=0)', + 'description' => 'description of term 44 of vocabulary 23 (j=0)', + 'weight' => '66', +)) +->values(array( + 'tid' => '45', + 'vid' => '23', + 'name' => 'term 45 of vocabulary 23 (j=1)', + 'description' => 'description of term 45 of vocabulary 23 (j=1)', + 'weight' => '67', +)) +->values(array( + 'tid' => '46', + 'vid' => '24', + 'name' => 'term 46 of vocabulary 24 (j=0)', + 'description' => 'description of term 46 of vocabulary 24 (j=0)', + 'weight' => '69', +)) +->values(array( + 'tid' => '47', + 'vid' => '24', + 'name' => 'term 47 of vocabulary 24 (j=1)', + 'description' => 'description of term 47 of vocabulary 24 (j=1)', + 'weight' => '70', +)) +->values(array( + 'tid' => '48', + 'vid' => '24', + 'name' => 'term 48 of vocabulary 24 (j=2)', + 'description' => 'description of term 48 of vocabulary 24 (j=2)', + 'weight' => '71', +)) +->execute(); + +db_create_table('term_hierarchy', array( + 'fields' => array( + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'parent' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'parent' => array( + 'parent', + ), + ), + 'primary key' => array( + 'tid', + 'parent', + ), + 'module' => 'taxonomy', + 'name' => 'term_hierarchy', +)); +db_insert('term_hierarchy')->fields(array( + 'tid', + 'parent', +)) +->values(array( + 'tid' => '1', + 'parent' => '0', +)) +->values(array( + 'tid' => '2', + 'parent' => '0', +)) +->values(array( + 'tid' => '4', + 'parent' => '0', +)) +->values(array( + 'tid' => '7', + 'parent' => '0', +)) +->values(array( + 'tid' => '8', + 'parent' => '0', +)) +->values(array( + 'tid' => '10', + 'parent' => '0', +)) +->values(array( + 'tid' => '13', + 'parent' => '0', +)) +->values(array( + 'tid' => '14', + 'parent' => '0', +)) +->values(array( + 'tid' => '16', + 'parent' => '0', +)) +->values(array( + 'tid' => '19', + 'parent' => '0', +)) +->values(array( + 'tid' => '20', + 'parent' => '0', +)) +->values(array( + 'tid' => '22', + 'parent' => '0', +)) +->values(array( + 'tid' => '25', + 'parent' => '0', +)) +->values(array( + 'tid' => '26', + 'parent' => '0', +)) +->values(array( + 'tid' => '28', + 'parent' => '0', +)) +->values(array( + 'tid' => '31', + 'parent' => '0', +)) +->values(array( + 'tid' => '32', + 'parent' => '0', +)) +->values(array( + 'tid' => '34', + 'parent' => '0', +)) +->values(array( + 'tid' => '37', + 'parent' => '0', +)) +->values(array( + 'tid' => '38', + 'parent' => '0', +)) +->values(array( + 'tid' => '40', + 'parent' => '0', +)) +->values(array( + 'tid' => '43', + 'parent' => '0', +)) +->values(array( + 'tid' => '44', + 'parent' => '0', +)) +->values(array( + 'tid' => '46', + 'parent' => '0', +)) +->values(array( + 'tid' => '3', + 'parent' => '2', +)) +->values(array( + 'tid' => '5', + 'parent' => '4', +)) +->values(array( + 'tid' => '6', + 'parent' => '4', +)) +->values(array( + 'tid' => '6', + 'parent' => '5', +)) +->values(array( + 'tid' => '9', + 'parent' => '8', +)) +->values(array( + 'tid' => '11', + 'parent' => '10', +)) +->values(array( + 'tid' => '12', + 'parent' => '10', +)) +->values(array( + 'tid' => '12', + 'parent' => '11', +)) +->values(array( + 'tid' => '15', + 'parent' => '14', +)) +->values(array( + 'tid' => '17', + 'parent' => '16', +)) +->values(array( + 'tid' => '18', + 'parent' => '16', +)) +->values(array( + 'tid' => '18', + 'parent' => '17', +)) +->values(array( + 'tid' => '21', + 'parent' => '20', +)) +->values(array( + 'tid' => '23', + 'parent' => '22', +)) +->values(array( + 'tid' => '24', + 'parent' => '22', +)) +->values(array( + 'tid' => '24', + 'parent' => '23', +)) +->values(array( + 'tid' => '27', + 'parent' => '26', +)) +->values(array( + 'tid' => '29', + 'parent' => '28', +)) +->values(array( + 'tid' => '30', + 'parent' => '28', +)) +->values(array( + 'tid' => '30', + 'parent' => '29', +)) +->values(array( + 'tid' => '33', + 'parent' => '32', +)) +->values(array( + 'tid' => '35', + 'parent' => '34', +)) +->values(array( + 'tid' => '36', + 'parent' => '34', +)) +->values(array( + 'tid' => '36', + 'parent' => '35', +)) +->values(array( + 'tid' => '39', + 'parent' => '38', +)) +->values(array( + 'tid' => '41', + 'parent' => '40', +)) +->values(array( + 'tid' => '42', + 'parent' => '40', +)) +->values(array( + 'tid' => '42', + 'parent' => '41', +)) +->values(array( + 'tid' => '45', + 'parent' => '44', +)) +->values(array( + 'tid' => '47', + 'parent' => '46', +)) +->values(array( + 'tid' => '48', + 'parent' => '46', +)) +->values(array( + 'tid' => '48', + 'parent' => '47', +)) +->execute(); + +db_create_table('term_node', array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'vid' => array( + 'vid', + ), + 'nid' => array( + 'nid', + ), + ), + 'primary key' => array( + 'tid', + 'vid', + ), + 'module' => 'taxonomy', + 'name' => 'term_node', +)); +db_insert('term_node')->fields(array( + 'nid', + 'vid', + 'tid', +)) +->values(array( + 'nid' => '1', + 'vid' => '1', + 'tid' => '1', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '2', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '3', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '4', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '5', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '6', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '7', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '8', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '9', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '10', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '11', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '12', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '13', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '14', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '15', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '16', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '17', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '18', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '19', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '20', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '21', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '22', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '23', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '24', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '25', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '26', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '27', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '28', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '29', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '30', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '31', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '32', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '33', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '34', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '35', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '36', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '37', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '38', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '39', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '40', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '41', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '42', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '43', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '44', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '45', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '46', +)) +->values(array( + 'nid' => '1', + 'vid' => '2', + 'tid' => '47', +)) +->values(array( + 'nid' => '1', + 'vid' => '1', + 'tid' => '48', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '1', +)) +->values(array( + 'nid' => '2', + 'vid' => '3', + 'tid' => '2', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '3', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '4', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '5', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '6', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '7', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '8', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '9', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '10', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '11', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '12', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '13', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '14', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '15', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '16', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '17', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '18', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '19', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '20', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '21', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '22', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '23', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '24', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '25', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '26', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '27', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '28', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '29', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '30', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '31', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '32', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '33', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '34', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '35', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '36', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '37', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '38', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '39', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '40', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '41', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '42', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '43', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '44', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '45', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '46', +)) +->values(array( + 'nid' => '2', + 'vid' => '3', + 'tid' => '47', +)) +->values(array( + 'nid' => '2', + 'vid' => '4', + 'tid' => '48', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '1', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '2', +)) +->values(array( + 'nid' => '3', + 'vid' => '5', + 'tid' => '3', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '4', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '5', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '6', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '7', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '8', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '9', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '10', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '11', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '12', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '13', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '14', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '15', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '16', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '17', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '18', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '19', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '20', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '21', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '22', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '23', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '24', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '25', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '26', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '27', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '28', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '29', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '30', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '31', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '32', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '33', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '34', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '35', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '36', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '37', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '38', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '39', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '40', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '41', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '42', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '43', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '44', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '45', +)) +->values(array( + 'nid' => '3', + 'vid' => '5', + 'tid' => '46', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '47', +)) +->values(array( + 'nid' => '3', + 'vid' => '6', + 'tid' => '48', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '1', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '2', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '3', +)) +->values(array( + 'nid' => '4', + 'vid' => '7', + 'tid' => '4', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '5', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '6', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '7', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '8', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '9', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '10', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '11', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '12', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '13', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '14', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '15', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '16', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '17', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '18', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '19', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '20', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '21', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '22', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '23', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '24', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '25', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '26', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '27', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '28', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '29', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '30', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '31', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '32', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '33', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '34', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '35', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '36', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '37', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '38', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '39', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '40', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '41', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '42', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '43', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '44', +)) +->values(array( + 'nid' => '4', + 'vid' => '7', + 'tid' => '45', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '46', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '47', +)) +->values(array( + 'nid' => '4', + 'vid' => '8', + 'tid' => '48', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '1', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '2', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '3', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '4', +)) +->values(array( + 'nid' => '5', + 'vid' => '9', + 'tid' => '5', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '6', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '7', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '8', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '9', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '10', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '11', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '12', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '13', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '14', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '15', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '16', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '17', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '18', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '19', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '20', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '21', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '22', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '23', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '24', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '25', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '26', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '27', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '28', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '29', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '30', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '31', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '32', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '33', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '34', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '35', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '36', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '37', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '38', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '39', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '40', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '41', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '42', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '43', +)) +->values(array( + 'nid' => '5', + 'vid' => '9', + 'tid' => '44', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '45', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '46', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '47', +)) +->values(array( + 'nid' => '5', + 'vid' => '10', + 'tid' => '48', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '1', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '2', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '3', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '4', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '5', +)) +->values(array( + 'nid' => '6', + 'vid' => '11', + 'tid' => '6', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '7', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '8', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '9', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '10', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '11', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '12', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '13', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '14', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '15', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '16', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '17', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '18', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '19', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '20', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '21', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '22', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '23', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '24', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '25', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '26', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '27', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '28', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '29', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '30', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '31', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '32', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '33', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '34', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '35', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '36', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '37', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '38', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '39', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '40', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '41', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '42', +)) +->values(array( + 'nid' => '6', + 'vid' => '11', + 'tid' => '43', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '44', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '45', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '46', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '47', +)) +->values(array( + 'nid' => '6', + 'vid' => '12', + 'tid' => '48', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '1', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '2', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '3', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '4', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '5', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '6', +)) +->values(array( + 'nid' => '7', + 'vid' => '13', + 'tid' => '7', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '8', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '9', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '10', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '11', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '12', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '13', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '14', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '15', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '16', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '17', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '18', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '19', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '20', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '21', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '22', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '23', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '24', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '25', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '26', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '27', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '28', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '29', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '30', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '31', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '32', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '33', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '34', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '35', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '36', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '37', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '38', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '39', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '40', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '41', +)) +->values(array( + 'nid' => '7', + 'vid' => '13', + 'tid' => '42', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '43', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '44', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '45', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '46', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '47', +)) +->values(array( + 'nid' => '7', + 'vid' => '14', + 'tid' => '48', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '1', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '2', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '3', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '4', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '5', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '6', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '7', +)) +->values(array( + 'nid' => '8', + 'vid' => '15', + 'tid' => '8', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '9', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '10', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '11', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '12', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '13', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '14', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '15', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '16', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '17', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '18', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '19', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '20', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '21', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '22', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '23', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '24', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '25', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '26', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '27', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '28', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '29', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '30', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '31', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '32', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '33', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '34', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '35', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '36', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '37', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '38', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '39', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '40', +)) +->values(array( + 'nid' => '8', + 'vid' => '15', + 'tid' => '41', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '42', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '43', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '44', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '45', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '46', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '47', +)) +->values(array( + 'nid' => '8', + 'vid' => '16', + 'tid' => '48', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '1', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '2', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '3', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '4', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '5', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '6', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '7', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '8', +)) +->values(array( + 'nid' => '9', + 'vid' => '17', + 'tid' => '9', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '10', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '11', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '12', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '13', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '14', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '15', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '16', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '17', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '18', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '19', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '20', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '21', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '22', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '23', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '24', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '25', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '26', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '27', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '28', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '29', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '30', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '31', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '32', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '33', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '34', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '35', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '36', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '37', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '38', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '39', +)) +->values(array( + 'nid' => '9', + 'vid' => '17', + 'tid' => '40', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '41', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '42', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '43', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '44', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '45', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '46', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '47', +)) +->values(array( + 'nid' => '9', + 'vid' => '18', + 'tid' => '48', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '1', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '2', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '3', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '4', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '5', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '6', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '7', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '8', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '9', +)) +->values(array( + 'nid' => '10', + 'vid' => '19', + 'tid' => '10', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '11', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '12', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '13', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '14', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '15', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '16', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '17', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '18', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '19', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '20', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '21', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '22', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '23', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '24', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '25', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '26', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '27', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '28', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '29', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '30', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '31', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '32', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '33', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '34', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '35', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '36', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '37', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '38', +)) +->values(array( + 'nid' => '10', + 'vid' => '19', + 'tid' => '39', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '40', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '41', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '42', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '43', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '44', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '45', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '46', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '47', +)) +->values(array( + 'nid' => '10', + 'vid' => '20', + 'tid' => '48', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '1', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '2', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '3', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '4', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '5', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '6', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '7', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '8', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '9', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '10', +)) +->values(array( + 'nid' => '11', + 'vid' => '21', + 'tid' => '11', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '12', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '13', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '14', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '15', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '16', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '17', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '18', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '19', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '20', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '21', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '22', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '23', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '24', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '25', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '26', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '27', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '28', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '29', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '30', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '31', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '32', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '33', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '34', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '35', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '36', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '37', +)) +->values(array( + 'nid' => '11', + 'vid' => '21', + 'tid' => '38', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '39', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '40', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '41', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '42', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '43', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '44', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '45', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '46', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '47', +)) +->values(array( + 'nid' => '11', + 'vid' => '22', + 'tid' => '48', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '1', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '2', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '3', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '4', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '5', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '6', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '7', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '8', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '9', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '10', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '11', +)) +->values(array( + 'nid' => '12', + 'vid' => '23', + 'tid' => '12', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '13', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '14', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '15', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '16', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '17', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '18', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '19', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '20', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '21', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '22', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '23', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '24', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '25', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '26', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '27', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '28', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '29', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '30', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '31', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '32', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '33', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '34', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '35', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '36', +)) +->values(array( + 'nid' => '12', + 'vid' => '23', + 'tid' => '37', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '38', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '39', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '40', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '41', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '42', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '43', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '44', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '45', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '46', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '47', +)) +->values(array( + 'nid' => '12', + 'vid' => '24', + 'tid' => '48', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '1', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '2', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '3', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '4', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '5', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '6', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '7', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '8', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '9', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '10', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '11', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '12', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '14', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '15', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '16', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '17', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '18', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '19', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '20', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '21', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '22', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '23', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '24', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '25', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '26', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '27', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '28', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '29', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '30', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '31', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '32', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '33', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '34', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '35', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '37', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '38', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '39', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '40', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '41', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '42', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '43', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '44', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '45', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '46', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '47', +)) +->values(array( + 'nid' => '13', + 'vid' => '25', + 'tid' => '48', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '1', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '2', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '3', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '4', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '5', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '6', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '7', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '8', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '9', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '10', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '11', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '12', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '13', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '15', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '16', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '17', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '18', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '19', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '20', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '21', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '22', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '23', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '24', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '25', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '26', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '27', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '28', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '29', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '30', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '31', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '32', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '33', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '34', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '36', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '37', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '38', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '39', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '40', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '41', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '42', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '43', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '44', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '45', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '46', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '47', +)) +->values(array( + 'nid' => '14', + 'vid' => '26', + 'tid' => '48', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '1', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '2', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '3', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '4', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '5', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '6', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '7', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '8', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '9', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '10', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '11', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '12', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '13', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '14', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '16', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '17', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '18', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '19', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '20', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '21', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '22', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '23', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '24', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '25', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '26', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '27', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '28', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '29', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '30', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '31', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '32', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '33', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '35', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '36', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '37', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '38', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '39', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '40', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '41', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '42', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '43', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '44', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '45', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '46', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '47', +)) +->values(array( + 'nid' => '15', + 'vid' => '27', + 'tid' => '48', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '1', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '2', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '3', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '4', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '5', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '6', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '7', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '8', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '9', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '10', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '11', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '12', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '13', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '14', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '15', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '17', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '18', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '19', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '20', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '21', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '22', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '23', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '24', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '25', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '26', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '27', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '28', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '29', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '30', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '31', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '32', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '34', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '35', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '36', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '37', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '38', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '39', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '40', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '41', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '42', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '43', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '44', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '45', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '46', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '47', +)) +->values(array( + 'nid' => '16', + 'vid' => '28', + 'tid' => '48', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '1', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '2', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '3', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '4', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '5', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '6', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '7', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '8', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '9', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '10', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '11', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '12', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '13', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '14', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '15', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '16', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '18', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '19', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '20', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '21', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '22', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '23', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '24', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '25', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '26', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '27', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '28', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '29', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '30', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '31', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '33', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '34', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '35', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '36', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '37', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '38', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '39', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '40', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '41', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '42', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '43', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '44', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '45', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '46', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '47', +)) +->values(array( + 'nid' => '17', + 'vid' => '29', + 'tid' => '48', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '1', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '2', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '3', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '4', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '5', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '6', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '7', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '8', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '9', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '10', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '11', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '12', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '13', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '14', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '15', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '16', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '17', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '19', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '20', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '21', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '22', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '23', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '24', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '25', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '26', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '27', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '28', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '29', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '30', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '32', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '33', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '34', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '35', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '36', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '37', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '38', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '39', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '40', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '41', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '42', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '43', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '44', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '45', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '46', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '47', +)) +->values(array( + 'nid' => '18', + 'vid' => '30', + 'tid' => '48', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '1', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '2', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '3', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '4', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '5', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '6', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '7', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '8', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '9', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '10', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '11', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '12', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '13', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '14', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '15', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '16', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '17', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '18', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '20', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '21', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '22', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '23', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '24', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '25', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '26', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '27', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '28', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '29', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '31', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '32', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '33', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '34', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '35', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '36', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '37', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '38', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '39', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '40', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '41', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '42', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '43', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '44', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '45', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '46', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '47', +)) +->values(array( + 'nid' => '19', + 'vid' => '31', + 'tid' => '48', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '1', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '2', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '3', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '4', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '5', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '6', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '7', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '8', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '9', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '10', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '11', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '12', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '13', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '14', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '15', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '16', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '17', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '18', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '19', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '21', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '22', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '23', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '24', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '25', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '26', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '27', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '28', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '30', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '31', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '32', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '33', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '34', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '35', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '36', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '37', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '38', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '39', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '40', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '41', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '42', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '43', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '44', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '45', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '46', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '47', +)) +->values(array( + 'nid' => '20', + 'vid' => '32', + 'tid' => '48', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '1', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '2', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '3', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '4', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '5', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '6', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '7', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '8', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '9', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '10', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '11', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '12', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '13', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '14', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '15', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '16', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '17', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '18', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '19', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '20', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '22', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '23', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '24', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '25', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '26', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '27', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '29', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '30', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '31', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '32', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '33', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '34', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '35', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '36', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '37', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '38', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '39', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '40', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '41', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '42', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '43', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '44', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '45', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '46', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '47', +)) +->values(array( + 'nid' => '21', + 'vid' => '33', + 'tid' => '48', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '1', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '2', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '3', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '4', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '5', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '6', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '7', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '8', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '9', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '10', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '11', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '12', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '13', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '14', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '15', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '16', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '17', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '18', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '19', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '20', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '21', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '23', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '24', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '25', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '26', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '28', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '29', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '30', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '31', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '32', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '33', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '34', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '35', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '36', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '37', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '38', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '39', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '40', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '41', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '42', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '43', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '44', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '45', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '46', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '47', +)) +->values(array( + 'nid' => '22', + 'vid' => '34', + 'tid' => '48', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '1', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '2', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '3', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '4', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '5', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '6', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '7', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '8', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '9', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '10', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '11', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '12', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '13', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '14', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '15', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '16', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '17', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '18', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '19', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '20', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '21', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '22', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '24', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '25', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '27', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '28', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '29', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '30', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '31', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '32', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '33', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '34', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '35', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '36', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '37', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '38', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '39', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '40', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '41', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '42', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '43', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '44', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '45', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '46', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '47', +)) +->values(array( + 'nid' => '23', + 'vid' => '35', + 'tid' => '48', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '1', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '2', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '3', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '4', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '5', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '6', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '7', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '8', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '9', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '10', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '11', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '12', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '13', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '14', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '15', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '16', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '17', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '18', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '19', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '20', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '21', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '22', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '23', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '26', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '27', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '28', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '29', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '30', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '31', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '32', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '33', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '34', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '35', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '36', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '37', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '38', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '39', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '40', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '41', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '42', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '43', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '44', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '45', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '46', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '47', +)) +->values(array( + 'nid' => '24', + 'vid' => '36', + 'tid' => '48', +)) +->execute(); + +db_create_table('term_relation', array( + 'fields' => array( + 'trid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'tid1' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'tid2' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'unique keys' => array( + 'tid1_tid2' => array( + 'tid1', + 'tid2', + ), + ), + 'indexes' => array( + 'tid2' => array( + 'tid2', + ), + ), + 'primary key' => array( + 'trid', + ), + 'module' => 'taxonomy', + 'name' => 'term_relation', +)); + +db_create_table('term_synonym', array( + 'fields' => array( + 'tsid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'indexes' => array( + 'tid' => array( + 'tid', + ), + 'name_tid' => array( + 'name', + 'tid', + ), + ), + 'primary key' => array( + 'tsid', + ), + 'module' => 'taxonomy', + 'name' => 'term_synonym', +)); + +db_create_table('url_alias', array( + 'fields' => array( + 'pid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'src' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'dst' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'unique keys' => array( + 'dst_language_pid' => array( + 'dst', + 'language', + 'pid', + ), + ), + 'primary key' => array( + 'pid', + ), + 'indexes' => array( + 'src_language_pid' => array( + 'src', + 'language', + 'pid', + ), + ), + 'module' => 'system', + 'name' => 'url_alias', +)); +db_insert('url_alias')->fields(array( + 'pid', + 'src', + 'dst', + 'language', +)) +->values(array( + 'pid' => '1', + 'src' => 'node/1', + 'dst' => 'content/1262732400', + 'language' => '', +)) +->values(array( + 'pid' => '2', + 'src' => 'node/2', + 'dst' => 'content/1262818800', + 'language' => '', +)) +->values(array( + 'pid' => '3', + 'src' => 'node/3', + 'dst' => 'content/1262905200', + 'language' => '', +)) +->values(array( + 'pid' => '4', + 'src' => 'node/4', + 'dst' => 'content/1262991600', + 'language' => '', +)) +->values(array( + 'pid' => '5', + 'src' => 'node/5', + 'dst' => 'content/1263078000', + 'language' => '', +)) +->values(array( + 'pid' => '6', + 'src' => 'node/6', + 'dst' => 'content/1263164400', + 'language' => '', +)) +->values(array( + 'pid' => '7', + 'src' => 'node/7', + 'dst' => 'content/1263250800', + 'language' => '', +)) +->values(array( + 'pid' => '8', + 'src' => 'node/8', + 'dst' => 'content/1263337200', + 'language' => '', +)) +->values(array( + 'pid' => '9', + 'src' => 'node/9', + 'dst' => 'content/1263423600', + 'language' => '', +)) +->values(array( + 'pid' => '10', + 'src' => 'node/10', + 'dst' => 'content/1263510000', + 'language' => '', +)) +->values(array( + 'pid' => '11', + 'src' => 'node/11', + 'dst' => 'content/1263596400', + 'language' => '', +)) +->values(array( + 'pid' => '12', + 'src' => 'node/12', + 'dst' => 'content/1263682800', + 'language' => '', +)) +->values(array( + 'pid' => '13', + 'src' => 'node/13', + 'dst' => 'content/1263769200', + 'language' => '', +)) +->values(array( + 'pid' => '14', + 'src' => 'node/14', + 'dst' => 'content/1263855600', + 'language' => '', +)) +->values(array( + 'pid' => '15', + 'src' => 'node/15', + 'dst' => 'content/1263942000', + 'language' => '', +)) +->values(array( + 'pid' => '16', + 'src' => 'node/16', + 'dst' => 'content/1264028400', + 'language' => '', +)) +->values(array( + 'pid' => '17', + 'src' => 'node/17', + 'dst' => 'content/1264114800', + 'language' => '', +)) +->values(array( + 'pid' => '18', + 'src' => 'node/18', + 'dst' => 'content/1264201200', + 'language' => '', +)) +->values(array( + 'pid' => '19', + 'src' => 'node/19', + 'dst' => 'content/1264287600', + 'language' => '', +)) +->values(array( + 'pid' => '20', + 'src' => 'node/20', + 'dst' => 'content/1264374000', + 'language' => '', +)) +->values(array( + 'pid' => '21', + 'src' => 'node/21', + 'dst' => 'content/1264460400', + 'language' => '', +)) +->values(array( + 'pid' => '22', + 'src' => 'node/22', + 'dst' => 'content/1264546800', + 'language' => '', +)) +->values(array( + 'pid' => '23', + 'src' => 'node/23', + 'dst' => 'content/1264633200', + 'language' => '', +)) +->values(array( + 'pid' => '24', + 'src' => 'node/24', + 'dst' => 'content/1264719600', + 'language' => '', +)) +->values(array( + 'pid' => '25', + 'src' => 'node/25', + 'dst' => 'content/poll/0', + 'language' => '', +)) +->values(array( + 'pid' => '26', + 'src' => 'node/25/results', + 'dst' => 'content/poll/0/results', + 'language' => '', +)) +->values(array( + 'pid' => '27', + 'src' => 'node/26', + 'dst' => 'content/poll/1', + 'language' => '', +)) +->values(array( + 'pid' => '28', + 'src' => 'node/26/results', + 'dst' => 'content/poll/1/results', + 'language' => '', +)) +->values(array( + 'pid' => '29', + 'src' => 'node/27', + 'dst' => 'content/poll/2', + 'language' => '', +)) +->values(array( + 'pid' => '30', + 'src' => 'node/27/results', + 'dst' => 'content/poll/2/results', + 'language' => '', +)) +->values(array( + 'pid' => '31', + 'src' => 'node/28', + 'dst' => 'content/poll/3', + 'language' => '', +)) +->values(array( + 'pid' => '32', + 'src' => 'node/28/results', + 'dst' => 'content/poll/3/results', + 'language' => '', +)) +->values(array( + 'pid' => '33', + 'src' => 'node/29', + 'dst' => 'content/poll/4', + 'language' => '', +)) +->values(array( + 'pid' => '34', + 'src' => 'node/29/results', + 'dst' => 'content/poll/4/results', + 'language' => '', +)) +->values(array( + 'pid' => '35', + 'src' => 'node/30', + 'dst' => 'content/poll/5', + 'language' => '', +)) +->values(array( + 'pid' => '36', + 'src' => 'node/30/results', + 'dst' => 'content/poll/5/results', + 'language' => '', +)) +->values(array( + 'pid' => '37', + 'src' => 'node/31', + 'dst' => 'content/poll/6', + 'language' => '', +)) +->values(array( + 'pid' => '38', + 'src' => 'node/31/results', + 'dst' => 'content/poll/6/results', + 'language' => '', +)) +->values(array( + 'pid' => '39', + 'src' => 'node/32', + 'dst' => 'content/poll/7', + 'language' => '', +)) +->values(array( + 'pid' => '40', + 'src' => 'node/32/results', + 'dst' => 'content/poll/7/results', + 'language' => '', +)) +->values(array( + 'pid' => '41', + 'src' => 'node/33', + 'dst' => 'content/poll/8', + 'language' => '', +)) +->values(array( + 'pid' => '42', + 'src' => 'node/33/results', + 'dst' => 'content/poll/8/results', + 'language' => '', +)) +->values(array( + 'pid' => '43', + 'src' => 'node/34', + 'dst' => 'content/poll/9', + 'language' => '', +)) +->values(array( + 'pid' => '44', + 'src' => 'node/34/results', + 'dst' => 'content/poll/9/results', + 'language' => '', +)) +->values(array( + 'pid' => '45', + 'src' => 'node/35', + 'dst' => 'content/poll/10', + 'language' => '', +)) +->values(array( + 'pid' => '46', + 'src' => 'node/35/results', + 'dst' => 'content/poll/10/results', + 'language' => '', +)) +->values(array( + 'pid' => '47', + 'src' => 'node/36', + 'dst' => 'content/poll/11', + 'language' => '', +)) +->values(array( + 'pid' => '48', + 'src' => 'node/36/results', + 'dst' => 'content/poll/11/results', + 'language' => '', +)) +->values(array( + 'pid' => '49', + 'src' => 'node/37', + 'dst' => 'content/1263769200', + 'language' => '', +)) +->execute(); + +db_create_table('users', array( + 'fields' => array( + 'uid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 60, + 'not null' => TRUE, + 'default' => '', + ), + 'pass' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'mail' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => FALSE, + 'default' => '', + ), + 'mode' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'sort' => array( + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'size' => 'tiny', + ), + 'threshold' => array( + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'size' => 'tiny', + ), + 'theme' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'signature' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'signature_format' => array( + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'access' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'login' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'timezone' => array( + 'type' => 'varchar', + 'length' => 8, + 'not null' => FALSE, + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'picture' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'init' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => FALSE, + 'default' => '', + ), + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + ), + 'indexes' => array( + 'access' => array( + 'access', + ), + 'created' => array( + 'created', + ), + 'mail' => array( + 'mail', + ), + ), + 'unique keys' => array( + 'name' => array( + 'name', + ), + ), + 'primary key' => array( + 'uid', + ), + 'module' => 'user', + 'name' => 'users', +)); +db_insert('users')->fields(array( + 'uid', + 'name', + 'pass', + 'mail', + 'mode', + 'sort', + 'threshold', + 'theme', + 'signature', + 'signature_format', + 'created', + 'access', + 'login', + 'status', + 'timezone', + 'language', + 'picture', + 'init', + 'data', +)) +->values(array( + 'uid' => 1, + 'name' => '', + 'pass' => '', + 'mail' => '', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '0', + 'access' => '0', + 'login' => '0', + 'status' => '0', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 2, + 'name' => 'admin', + 'pass' => '21232f297a57a5a743894a0e4a801fc3', + 'mail' => 'admin@aexample.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1282936261', + 'access' => '1282936264', + 'login' => '1282936263', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => 'admin@aexample.com', + 'data' => 'a:0:{}', +)) +->values(array( + 'uid' => 4, + 'name' => 'test user 0', + 'pass' => '4dd188028f74622e61048f6683208c2f', + 'mail' => 'test0@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262300400', + 'access' => '1262300400', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 5, + 'name' => 'test user 1', + 'pass' => '388ee5249286bf5eed0dae9205743969', + 'mail' => 'test1@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262386800', + 'access' => '1262386800', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 6, + 'name' => 'test user 2', + 'pass' => 'ff8a825362e1e06dca5c13c60fe407c9', + 'mail' => 'test2@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262473200', + 'access' => '1262473200', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 7, + 'name' => 'test user 3', + 'pass' => '9d4bd7b0570cb513dc7c6ea8de15003a', + 'mail' => 'test3@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262559600', + 'access' => '1262559600', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 8, + 'name' => 'test user 4', + 'pass' => '7903c9dd9d71bcd70e3a18141711cb42', + 'mail' => 'test4@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262646000', + 'access' => '1262646000', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->values(array( + 'uid' => 9, + 'name' => 'test user 5', + 'pass' => '8eb7221204ea80db4e66247b4c748071', + 'mail' => 'test5@example.com', + 'mode' => '0', + 'sort' => '0', + 'threshold' => '0', + 'theme' => '', + 'signature' => '', + 'signature_format' => '0', + 'created' => '1262732400', + 'access' => '1262732400', + 'login' => '0', + 'status' => '1', + 'timezone' => NULL, + 'language' => '', + 'picture' => '', + 'init' => '', + 'data' => NULL, +)) +->execute(); +db_query('UPDATE {users} SET uid = uid - 1'); + +db_create_table('users_roles', array( + 'fields' => array( + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'rid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'uid', + 'rid', + ), + 'indexes' => array( + 'rid' => array( + 'rid', + ), + ), + 'module' => 'user', + 'name' => 'users_roles', +)); + +db_create_table('variable', array( + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + ), + 'primary key' => array( + 'name', + ), + 'module' => 'system', + 'name' => 'variable', +)); +db_insert('variable')->fields(array( + 'name', + 'value', +)) +->values(array( + 'name' => 'clean_url', + 'value' => 's:1:"1";', +)) +->values(array( + 'name' => 'comment_page', + 'value' => 'i:0;', +)) +->values(array( + 'name' => 'cron_last', + 'value' => 'i:1282936266;', +)) +->values(array( + 'name' => 'css_js_query_string', + 'value' => 's:20:"i0000000000000000000";', +)) +->values(array( + 'name' => 'date_default_timezone', + 'value' => 's:6:"-39600";', +)) +->values(array( + 'name' => 'drupal_private_key', + 'value' => 's:64:"c2f015f78636f97527f2ce6c0869fccd1c7a674005ed203d48b106cbd6db3947";', +)) +->values(array( + 'name' => 'file_directory_temp', + 'value' => 's:26:"/Applications/MAMP/tmp/php";', +)) +->values(array( + 'name' => 'filter_html_1', + 'value' => 'i:1;', +)) +->values(array( + 'name' => 'install_profile', + 'value' => 's:7:"default";', +)) +->values(array( + 'name' => 'install_task', + 'value' => 's:4:"done";', +)) +->values(array( + 'name' => 'install_time', + 'value' => 'i:1282936263;', +)) +->values(array( + 'name' => 'javascript_parsed', + 'value' => 'a:0:{}', +)) +->values(array( + 'name' => 'menu_expanded', + 'value' => 'a:0:{}', +)) +->values(array( + 'name' => 'menu_masks', + 'value' => 'a:17:{i:0;i:62;i:1;i:61;i:2;i:59;i:3;i:31;i:4;i:30;i:5;i:29;i:6;i:24;i:7;i:21;i:8;i:15;i:9;i:14;i:10;i:11;i:11;i:7;i:12;i:6;i:13;i:5;i:14;i:3;i:15;i:2;i:16;i:1;}', +)) +->values(array( + 'name' => 'node_options_forum', + 'value' => 'a:1:{i:0;s:6:"status";}', +)) +->values(array( + 'name' => 'node_options_page', + 'value' => 'a:1:{i:0;s:6:"status";}', +)) +->values(array( + 'name' => 'site_mail', + 'value' => 's:21:"site_mail@example.com";', +)) +->values(array( + 'name' => 'site_name', + 'value' => 's:9:"site_name";', +)) +->values(array( + 'name' => 'theme_default', + 'value' => 's:7:"garland";', +)) +->values(array( + 'name' => 'theme_settings', + 'value' => 'a:1:{s:21:"toggle_node_info_page";b:0;}', +)) +->values(array( + 'name' => 'update_last_check', + 'value' => 'i:1282936266;', +)) +->values(array( + 'name' => 'user_email_verification', + 'value' => 'b:1;', +)) +->execute(); + +db_create_table('vocabulary', array( + 'fields' => array( + 'vid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'description' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ), + 'help' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'relations' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'hierarchy' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'multiple' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'required' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'tags' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'module' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array( + 'vid', + ), + 'indexes' => array( + 'list' => array( + 'weight', + 'name', + ), + ), + 'module' => 'taxonomy', + 'name' => 'vocabulary', +)); +db_insert('vocabulary')->fields(array( + 'vid', + 'name', + 'description', + 'help', + 'relations', + 'hierarchy', + 'multiple', + 'required', + 'tags', + 'module', + 'weight', +)) +->values(array( + 'vid' => '1', + 'name' => 'vocabulary 1 (i=0)', + 'description' => 'description of vocabulary 1 (i=0)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '0', +)) +->values(array( + 'vid' => '2', + 'name' => 'vocabulary 2 (i=1)', + 'description' => 'description of vocabulary 2 (i=1)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '1', +)) +->values(array( + 'vid' => '3', + 'name' => 'vocabulary 3 (i=2)', + 'description' => 'description of vocabulary 3 (i=2)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '2', +)) +->values(array( + 'vid' => '4', + 'name' => 'vocabulary 4 (i=3)', + 'description' => 'description of vocabulary 4 (i=3)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '3', +)) +->values(array( + 'vid' => '5', + 'name' => 'vocabulary 5 (i=4)', + 'description' => 'description of vocabulary 5 (i=4)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '4', +)) +->values(array( + 'vid' => '6', + 'name' => 'vocabulary 6 (i=5)', + 'description' => 'description of vocabulary 6 (i=5)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '5', +)) +->values(array( + 'vid' => '7', + 'name' => 'vocabulary 7 (i=6)', + 'description' => 'description of vocabulary 7 (i=6)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '6', +)) +->values(array( + 'vid' => '8', + 'name' => 'vocabulary 8 (i=7)', + 'description' => 'description of vocabulary 8 (i=7)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '7', +)) +->values(array( + 'vid' => '9', + 'name' => 'vocabulary 9 (i=8)', + 'description' => 'description of vocabulary 9 (i=8)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '8', +)) +->values(array( + 'vid' => '10', + 'name' => 'vocabulary 10 (i=9)', + 'description' => 'description of vocabulary 10 (i=9)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '9', +)) +->values(array( + 'vid' => '11', + 'name' => 'vocabulary 11 (i=10)', + 'description' => 'description of vocabulary 11 (i=10)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '10', +)) +->values(array( + 'vid' => '12', + 'name' => 'vocabulary 12 (i=11)', + 'description' => 'description of vocabulary 12 (i=11)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '11', +)) +->values(array( + 'vid' => '13', + 'name' => 'vocabulary 13 (i=12)', + 'description' => 'description of vocabulary 13 (i=12)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '12', +)) +->values(array( + 'vid' => '14', + 'name' => 'vocabulary 14 (i=13)', + 'description' => 'description of vocabulary 14 (i=13)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '13', +)) +->values(array( + 'vid' => '15', + 'name' => 'vocabulary 15 (i=14)', + 'description' => 'description of vocabulary 15 (i=14)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '14', +)) +->values(array( + 'vid' => '16', + 'name' => 'vocabulary 16 (i=15)', + 'description' => 'description of vocabulary 16 (i=15)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '15', +)) +->values(array( + 'vid' => '17', + 'name' => 'vocabulary 17 (i=16)', + 'description' => 'description of vocabulary 17 (i=16)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '0', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '16', +)) +->values(array( + 'vid' => '18', + 'name' => 'vocabulary 18 (i=17)', + 'description' => 'description of vocabulary 18 (i=17)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '1', + 'required' => '0', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '17', +)) +->values(array( + 'vid' => '19', + 'name' => 'vocabulary 19 (i=18)', + 'description' => 'description of vocabulary 19 (i=18)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '18', +)) +->values(array( + 'vid' => '20', + 'name' => 'vocabulary 20 (i=19)', + 'description' => 'description of vocabulary 20 (i=19)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '19', +)) +->values(array( + 'vid' => '21', + 'name' => 'vocabulary 21 (i=20)', + 'description' => 'description of vocabulary 21 (i=20)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '20', +)) +->values(array( + 'vid' => '22', + 'name' => 'vocabulary 22 (i=21)', + 'description' => 'description of vocabulary 22 (i=21)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '0', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '21', +)) +->values(array( + 'vid' => '23', + 'name' => 'vocabulary 23 (i=22)', + 'description' => 'description of vocabulary 23 (i=22)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '1', + 'multiple' => '0', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '22', +)) +->values(array( + 'vid' => '24', + 'name' => 'vocabulary 24 (i=23)', + 'description' => 'description of vocabulary 24 (i=23)', + 'help' => '', + 'relations' => '1', + 'hierarchy' => '2', + 'multiple' => '1', + 'required' => '1', + 'tags' => '0', + 'module' => 'taxonomy', + 'weight' => '23', +)) +->execute(); + +db_create_table('vocabulary_node_types', array( + 'fields' => array( + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'primary key' => array( + 'type', + 'vid', + ), + 'indexes' => array( + 'vid' => array( + 'vid', + ), + ), + 'module' => 'taxonomy', + 'name' => 'vocabulary_node_types', +)); +db_insert('vocabulary_node_types')->fields(array( + 'vid', + 'type', +)) +->values(array( + 'vid' => '13', + 'type' => 'page', +)) +->values(array( + 'vid' => '14', + 'type' => 'page', +)) +->values(array( + 'vid' => '15', + 'type' => 'page', +)) +->values(array( + 'vid' => '16', + 'type' => 'page', +)) +->values(array( + 'vid' => '17', + 'type' => 'page', +)) +->values(array( + 'vid' => '18', + 'type' => 'page', +)) +->values(array( + 'vid' => '19', + 'type' => 'page', +)) +->values(array( + 'vid' => '20', + 'type' => 'page', +)) +->values(array( + 'vid' => '21', + 'type' => 'page', +)) +->values(array( + 'vid' => '22', + 'type' => 'page', +)) +->values(array( + 'vid' => '23', + 'type' => 'page', +)) +->values(array( + 'vid' => '24', + 'type' => 'page', +)) +->execute(); + +db_create_table('watchdog', array( + 'fields' => array( + 'wid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'default' => '', + ), + 'message' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'variables' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'severity' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'link' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'location' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'referer' => array( + 'type' => 'text', + 'not null' => FALSE, + ), + 'hostname' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array( + 'wid', + ), + 'indexes' => array( + 'type' => array( + 'type', + ), + ), + 'module' => 'dblog', + 'name' => 'watchdog', +)); + diff --git a/modules/simpletest/tests/upgrade/upgrade.comment.test b/modules/simpletest/tests/upgrade/upgrade.comment.test new file mode 100644 index 0000000000000000000000000000000000000000..ebf70304f8ea18661822f1a6ddf2d1ca966e1530 --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade.comment.test @@ -0,0 +1,33 @@ +<?php +// $Id: upgrade.comment.test,v 1.1 2010/09/11 21:52:59 webchick Exp $ + +/** + * Upgrade test for comment.module. + */ +class CommentUpgradePathTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Comment upgrade path', + 'description' => 'Comment upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump files. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.comments.database.php', + ); + parent::setUp(); + + $this->uninstallModulesExcept(array('comment')); + } + + /** + * Test a successful upgrade. + */ + public function testCommentUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + } +} diff --git a/modules/simpletest/tests/upgrade/upgrade.node.test b/modules/simpletest/tests/upgrade/upgrade.node.test new file mode 100644 index 0000000000000000000000000000000000000000..a3e2b10f35850cd5f89b5efd3e848d52e8e71b05 --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade.node.test @@ -0,0 +1,99 @@ +<?php +// $Id: upgrade.node.test,v 1.2 2010/09/13 05:50:09 webchick Exp $ + +/** + * Upgrade test for node bodies. + * + * Load a filled installation of Drupal 6 and run the upgrade process on it. + */ +class NodeBodyUpgradePathTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Node body upgrade path', + 'description' => 'Node body upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + ); + parent::setUp(); + } + + /** + * Test a successful upgrade. + */ + public function testNodyBodyUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + $this->drupalGet("content/1263769200"); + $this->assertText('node body (broken) - 37'); + } +} + +/** + * Upgrade test for node type poll. + * + * Load a bare installation of Drupal 6 and run the upgrade process on it. + * + * The install only contains dblog (although it's optional, it's only so that + * another hook_watchdog module can take its place, the site is not functional + * without watchdog) and update. + */ +class PollUpgradePathTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Poll upgrade path', + 'description' => 'Poll upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + ); + parent::setUp(); + + $this->uninstallModulesExcept(array('poll')); + } + + /** + * Test a successful upgrade. + */ + public function testPollUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Check modules page for poll + $this->drupalGet('admin/modules'); + + // Verify that the poll data is still correctly available + for ($i = 0; $i < 12; $i++) { + $this->drupalGet("content/poll/$i"); + + $nbchoices = ($i % 4) + 2; + + for ($c = 0; $c < $nbchoices; $c++) { + $this->assertText("Choice $c for poll $i", t('Choice text is displayed correctly on poll view')); + } + + // Now check that the votes are correct + $this->clickLink(t('Results')); + + for ($c = 0; $c < $nbchoices; $c++) { + $this->assertText("Choice $c for poll $i", t('Choice text is displayed correctly on result view')); + } + + $nbvotes = floor (($i % 4) + 5); + $elements = $this->xpath("//div[@class='percent']"); + for ($c = 0; $c < $nbchoices; $c++) { + $votes = floor($nbvotes / $nbchoices); + if (($nbvotes % $nbchoices) > $c) $votes++; + $this->assertTrue(preg_match("/$votes vote/", $elements[$c]), t('The number of votes is displayed correctly: expected ' . $votes . ', got ' . $elements[$c])); + } + } + } +} diff --git a/modules/simpletest/tests/upgrade/upgrade.poll.test b/modules/simpletest/tests/upgrade/upgrade.poll.test new file mode 100644 index 0000000000000000000000000000000000000000..b1f41eb1c7488a99ce69d75d50d039a802c0c7d7 --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade.poll.test @@ -0,0 +1,67 @@ +<?php +// $Id: upgrade.poll.test,v 1.4 2010/09/13 05:50:09 webchick Exp $ + +/** + * Upgrade test for poll.module. + * + * Load a bare installation of Drupal 6 and run the upgrade process on it. + * + * The install only contains dblog (although it's optional, it's only so that + * another hook_watchdog module can take its place, the site is not functional + * without watchdog) and update. + */ +class PollUpgradePathTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Poll upgrade path', + 'description' => 'Poll upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump files. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + ); + parent::setUp(); + + $this->uninstallModulesExcept(array('poll')); + } + + /** + * Test a successful upgrade. + */ + public function testPollUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Check modules page for poll + $this->drupalGet('admin/modules'); + + // Verify that the poll data is still correctly available + for ($i = 0; $i < 12; $i++) { + $this->drupalGet("content/poll/$i"); + + $nbchoices = ($i % 4) + 2; + + for ($c = 0; $c < $nbchoices; $c++) { + $this->assertText("Choice $c for poll $i", t('Choice text is displayed correctly on poll view')); + } + + // Now check that the votes are correct + $this->clickLink(t('Results')); + + for ($c = 0; $c < $nbchoices; $c++) { + $this->assertText("Choice $c for poll $i", t('Choice text is displayed correctly on result view')); + } + + $nbvotes = floor (($i % 4) + 5); + $elements = $this->xpath("//div[@class='percent']"); + for ($c = 0; $c < $nbchoices; $c++) { + $votes = floor($nbvotes / $nbchoices); + if (($nbvotes % $nbchoices) > $c) $votes++; + $this->assertTrue(preg_match("/$votes vote/", $elements[$c]), t('The number of votes is displayed correctly: expected ' . $votes . ', got ' . $elements[$c])); + } + } + } +} diff --git a/modules/simpletest/tests/upgrade/upgrade.taxonomy.test b/modules/simpletest/tests/upgrade/upgrade.taxonomy.test new file mode 100644 index 0000000000000000000000000000000000000000..3d2d0fb726de588998fdbc7bb362076784ca31ea --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade.taxonomy.test @@ -0,0 +1,147 @@ +<?php +// $Id: upgrade.taxonomy.test,v 1.2 2010/09/13 05:50:09 webchick Exp $ + +/** + * Test taxonomy upgrades. + */ +class UpgradePathTaxonomyTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Taxonomy upgrade path', + 'description' => 'Taxonomy upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + ); + parent::setUp(); + } + + /** + * Retrieve an array mapping allowed vocabulary id to field name for + * all taxonomy_term_reference fields for which an instance exists + * for the specified entity type and bundle. + */ + function instanceVocabularies($entity_type, $bundle) { + $instances = array(); + foreach (field_info_instances($entity_type, $bundle) as $instance) { + $field = field_info_field($instance['field_name']); + if ($field['type'] == 'taxonomy_term_reference') { + foreach ($field['settings']['allowed_values'] as $tree) { + // Prefer valid taxonomy term reference fields for a given vocabulary + // when they exist. + if (empty($instances[$tree['vid']]) || $instances[$tree['vid']] == 'taxonomyextra') { + $instances[$tree['vid']] = $field['field_name']; + } + } + } + } + return $instances; + } + + /** + * Basic tests for the taxonomy upgrade. + */ + public function testTaxonomyUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Visit the front page to assert for PHP warning and errors. + $this->drupalGet(''); + + // Check that taxonomy_vocabulary_node_type and taxonomy_term_node have been + // removed. + $this->assertFalse(db_table_exists('taxonomy_vocabulary_node_type'), t('taxonomy_vocabulary_node_type has been removed.')); + $this->assertFalse(db_table_exists('taxonomy_term_node'), t('taxonomy_term_node has been removed.')); + + // Check that the node type 'page' has been associated to a taxonomy + // reference field for each vocabulary. + $vocabularies = taxonomy_get_vocabularies(); + $voc_keys = array_keys($vocabularies); + $instances = $this->instanceVocabularies('node', 'page'); + $inst_keys = array_keys($instances); + sort($voc_keys); + sort($inst_keys); + $this->assertEqual($voc_keys, $inst_keys, t('Node type page has instances for every vocabulary.')); + + // Node type 'story' was not explicitly in $vocabulary->nodes but + // each node of type 'story' was associated to one or more terms. + // Check that the node type 'story' has been associated only to + // the taxonomyextra field. + $instances = $this->instanceVocabularies('node', 'story'); + $field_names = array_flip($instances); + $this->assertEqual(count($field_names), 1, t('Only one taxonomy term field instance exists for story nodes')); + $this->assertEqual(key($field_names), 'taxonomyextra', t('Only the excess taxonomy term field is used on story nodes')); + + // Check that the node type 'poll' has been associated to no taxonomy + // reference field. + $instances = $this->instanceVocabularies('node', 'poll'); + $this->assertTrue(empty($instances), t('Node type poll has no taxonomy term reference field instances.')); + + // Check that each node of type 'page' and 'story' is associated to all the + // terms except terms whose ID is equal to the node ID or is equal to the + // node ID subtracted from 49. + $nodes = node_load_multiple(array(), array('type' => 'page')); + $nodes += node_load_multiple(array(), array('type' => 'story')); + $terms = db_select('taxonomy_term_data', 'td') + ->fields('td') + ->execute() + ->fetchAllAssoc('tid'); + field_attach_prepare_view('node', $nodes, 'full'); + foreach ($nodes as $nid => $node) { + $node->content = field_attach_view('node', $node, 'full'); + $render = drupal_render($node->content); + $this->drupalSetContent($render); + debug("Testing node $nid"); + $this->verbose($render); + foreach ($terms as $tid => $term) { + $args = array( + '%name' => $term->name, + '@tid' => $tid, + '%nid' => $nid, + ); + + // Use link rather than term name because migrated term names can be + // substrings of other term names. e.g. "term 1 of vocabulary 2" is + // found when "term 1 of vocabulary 20" is output. + $link = l($term->name, 'taxonomy/term/' . $term->tid); + if (($tid == $nid) || ($tid + $nid == 49)) { + $this->assertNoRaw($link, t('Term %name (@tid) is not displayed on node %nid', $args)); + } + else { + $this->assertRaw($link, t('Term %name (@tid) is displayed on node %nid', $args)); + } + } + + // The first 12 nodes have two revisions. For nodes with + // revisions, check that the oldest revision is associated only + // to terms whose ID is equal to the node ID or 49 less the node ID. + $revisions = node_revision_list($node); + if ($node->nid < 13) { + $this->assertEqual(count($revisions), 2, t('Node %nid has two revisions.', $args)); + $last_rev = end($revisions); + $args['%old_vid'] = $last_rev->vid; + + $node_old = node_load($node->nid, $last_rev->vid); + field_attach_prepare_view('node', array($node_old->nid => $node_old), 'full'); + $node_old->content = field_attach_view('node', $node_old, 'full'); + $render = drupal_render($node_old->content); + $this->drupalSetContent($render); + $this->verbose($render); + + $term = $terms[$node->nid]; + $link = l($term->name, 'taxonomy/term/' . $term->tid); + $this->assertRaw($link, t('Term %name (@tid) is displayed on node %nid vid %old_vid.', $args)); + $term = $terms[49-$node->nid]; + $link = l($term->name, 'taxonomy/term/' . $term->tid); + $this->assertRaw($link, t('Term %name (@tid) is displayed on node %nid %old_vid.', $args)); + } + else { + $this->assertEqual(count($revisions), 1, t('Node %nid has one revision.', $args)); + } + } + } +} diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 7f2ddd27d5a04e0faef10486fd085b3f69eef226..c793fc75856e33db0b5cc17df5cbe762eba48416 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.2 2010/06/29 00:21:00 webchick Exp $ +// $Id: upgrade.test,v 1.7 2010/09/13 05:50:09 webchick Exp $ /** * Perform end-to-end tests of the upgrade path. @@ -7,9 +7,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { /** - * The file path to the dumped database to load into the child site. + * The file path(s) to the dumped database(s) to load into the child site. + * + * @var array */ - var $databaseDumpFile = NULL; + var $databaseDumpFiles = array(); /** * Flag that indicates whether the child site has been upgraded. @@ -21,6 +23,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { */ var $upgradeErrors = array(); + /** + * Array of modules loaded when the test starts. + */ + var $loadedModules = array(); + /** * Override of DrupalWebTestCase::setUp() specialized for upgrade testing. */ @@ -31,6 +38,8 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { $this->upgradedSite = FALSE; $this->upgradeErrors = array(); + $this->loadedModules = module_list(); + // Generate a temporary prefixed database to ensure that tests have a clean starting point. $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); db_update('simpletest_test_id') @@ -51,7 +60,7 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { // Store necessary current values before switching to prefixed database. $this->originalLanguage = $language; $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = file_directory_path(); + $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); $this->originalProfile = drupal_get_profile(); $clean_url_original = variable_get('clean_url', 0); @@ -81,7 +90,9 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { $conf = array(); // Load the database from the portable PHP dump. - require $this->databaseDumpFile; + foreach ($this->databaseDumpFiles as $file) { + require $file; + } // Set path variables. $this->variable_set('file_public_path', $public_files_directory); @@ -258,8 +269,14 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { // of the child site directly from this request. $this->upgradedSite = TRUE; - // Reload module list and implementations. - module_list(TRUE); + // Reload module list. For modules that are enabled in the test database, + // but not on the test client, we need to load the code here. + $new_modules = array_diff(module_list(TRUE), $this->loadedModules); + foreach ($new_modules as $module) { + drupal_load('module', $module); + } + + // Reload hook implementations module_implements('', FALSE, TRUE); // Rebuild caches. @@ -276,6 +293,24 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { return TRUE; } + /** + * Force uninstall all modules from a test database, except those listed. + * + * @param $modules + * The list of modules to keep installed. Required core modules will + * always be kept. + */ + protected function uninstallModulesExcept(array $modules) { + $required_modules = array('block', 'dblog', 'filter', 'node', 'system', 'update', 'user'); + + $modules = array_merge($required_modules, $modules); + + db_delete('system') + ->condition('type', 'module') + ->condition('name', $modules, 'NOT IN') + ->execute(); + } + } /** @@ -297,8 +332,10 @@ class BasicUpgradePath extends UpgradePathTestCase { } public function setUp() { - // Path to the database dump. - $this->databaseDumpFile = drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.bare.database.php'; + // Path to the database dump files. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.bare.database.php', + ); parent::setUp(); } diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info index 7b825d6066110b3bd3547ba14a0653142bfc415b..2281be8e077cdf3d6aab53e39d6aa217ac2a67e8 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/simpletest/tests/xmlrpc.test b/modules/simpletest/tests/xmlrpc.test index 7625a8ffcc0fed9b0361958facffd32feaa8392d..8b8f2b55db7cf89d0bcfd17c817c444835679c96 100644 --- a/modules/simpletest/tests/xmlrpc.test +++ b/modules/simpletest/tests/xmlrpc.test @@ -1,5 +1,5 @@ <?php -// $Id: xmlrpc.test,v 1.17 2010/05/06 05:59:31 webchick Exp $ +// $Id: xmlrpc.test,v 1.20 2010/08/14 03:15:01 dries Exp $ /** * Perform basic XML-RPC tests that do not require addition callbacks. @@ -29,7 +29,7 @@ class XMLRPCBasicTestCase extends DrupalWebTestCase { // Invoke XML-RPC call to get list of methods. $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - $methods = xmlrpc($url, 'system.listMethods'); + $methods = xmlrpc($url, array('system.listMethods' => array())); // Ensure that the minimum methods were found. $count = 0; @@ -74,8 +74,8 @@ class XMLRPCBasicTestCase extends DrupalWebTestCase { class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { public static function getInfo() { return array( - 'name' => 'XML-RPC validator', - 'description' => 'See !validator-link.', array('!validator-link' => l('the xmlrpc validator1 specification', 'http://www.xmlrpc.com/validator1Docs')), + 'name' => 'XML-RPC validator', + 'description' => 'See <a href="http://www.xmlrpc.com/validator1Docs">the xmlrpc validator1 specification</a>.', 'group' => 'XML-RPC', ); } @@ -92,7 +92,6 @@ class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { srand(); mt_srand(); - $array_1 = array(array('curly' => mt_rand(-100, 100)), array('curly' => mt_rand(-100, 100)), array('larry' => mt_rand(-100, 100)), @@ -102,29 +101,26 @@ class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { array('larry' => mt_rand(-100, 100))); shuffle($array_1); $l_res_1 = xmlrpc_test_arrayOfStructsTest($array_1); - $r_res_1 = xmlrpc($xml_url, 'validator1.arrayOfStructsTest', $array_1); - $this->assertIdentical($l_res_1, $r_res_1, 'array of structs test: %s'); - + $r_res_1 = xmlrpc($xml_url, array('validator1.arrayOfStructsTest' => array($array_1))); + $this->assertIdentical($l_res_1, $r_res_1); $string_2 = 't\'&>>zf"md>yr>xlcev<h<"k&j<og"w&&>">>uai"np&s>>q\'&b<>"&&&'; $l_res_2 = xmlrpc_test_countTheEntities($string_2); - $r_res_2 = xmlrpc($xml_url, 'validator1.countTheEntities', $string_2); - $this->assertIdentical($l_res_2, $r_res_2, 'count the entities test: %s'); - + $r_res_2 = xmlrpc($xml_url, array('validator1.countTheEntities' => array($string_2))); + $this->assertIdentical($l_res_2, $r_res_2); $struct_3 = array('moe' => mt_rand(-100, 100), 'larry' => mt_rand(-100, 100), 'curly' => mt_rand(-100, 100), 'homer' => mt_rand(-100, 100)); $l_res_3 = xmlrpc_test_easyStructTest($struct_3); - $r_res_3 = xmlrpc($xml_url, 'validator1.easyStructTest', $struct_3); - $this->assertIdentical($l_res_3, $r_res_3, 'easy struct test: %s'); - + $r_res_3 = xmlrpc($xml_url, array('validator1.easyStructTest' => array($struct_3))); + $this->assertIdentical($l_res_3, $r_res_3); $struct_4 = array('sub1' => array('bar' => 13), 'sub2' => 14, 'sub3' => array('foo' => 1, 'baz' => 2), 'sub4' => array('ss' => array('sss' => array('ssss' => 'sssss')))); $l_res_4 = xmlrpc_test_echoStructTest($struct_4); - $r_res_4 = xmlrpc($xml_url, 'validator1.echoStructTest', $struct_4); - $this->assertIdentical($l_res_4, $r_res_4, 'echo struct test: %s'); + $r_res_4 = xmlrpc($xml_url, array('validator1.echoStructTest' => array($struct_4))); + $this->assertIdentical($l_res_4, $r_res_4); $int_5 = mt_rand(-100, 100); $bool_5 = (($int_5 % 2) == 0); @@ -133,12 +129,11 @@ class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { $time_5 = REQUEST_TIME; $base64_5 = $this->randomName(100); $l_res_5 = xmlrpc_test_manyTypesTest($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), $base64_5); - $l_res_5[5] = $l_res_5[5]->data; /* override warpping */ - $r_res_5 = xmlrpc($xml_url, 'validator1.manyTypesTest', $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)); - /* Contains objects, objects are not equal */ // See http://drupal.org/node/37766 why this currently fails - $this->assertEqual($l_res_5, $r_res_5, 'many types test: %s'); - + $l_res_5[5] = $l_res_5[5]->data; + $r_res_5 = xmlrpc($xml_url, array('validator1.manyTypesTest' => array($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)))); + // @todo Contains objects, objects are not equal. + $this->assertEqual($l_res_5, $r_res_5); $size = mt_rand(100, 200); $array_6 = array(); @@ -147,9 +142,8 @@ class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { } $l_res_6 = xmlrpc_test_moderateSizeArrayCheck($array_6); - $r_res_6 = xmlrpc($xml_url, 'validator1.moderateSizeArrayCheck', $array_6); - $this->assertIdentical($l_res_6, $r_res_6, 'moderate size array check: %s'); - + $r_res_6 = xmlrpc($xml_url, array('validator1.moderateSizeArrayCheck' => array($array_6))); + $this->assertIdentical($l_res_6, $r_res_6); $struct_7 = array(); for ($y = 2000; $y < 2002; $y++) { @@ -165,29 +159,29 @@ class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { } } $l_res_7 = xmlrpc_test_nestedStructTest($struct_7); - $r_res_7 = xmlrpc($xml_url, 'validator1.nestedStructTest', $struct_7); - $this->assertIdentical($l_res_7, $r_res_7, 'nested struct test: %s'); + $r_res_7 = xmlrpc($xml_url, array('validator1.nestedStructTest' => array($struct_7))); + $this->assertIdentical($l_res_7, $r_res_7); $int_8 = mt_rand(-100, 100); $l_res_8 = xmlrpc_test_simpleStructReturnTest($int_8); - $r_res_8 = xmlrpc($xml_url, 'validator1.simpleStructReturnTest', $int_8); - $this->assertIdentical($l_res_8, $r_res_8, 'simple struct test: %s'); + $r_res_8 = xmlrpc($xml_url, array('validator1.simpleStructReturnTest' => array($int_8))); + $this->assertIdentical($l_res_8, $r_res_8); /* Now test multicall */ $x = array(); - $x[] = array('validator1.arrayOfStructsTest', $array_1); - $x[] = array('validator1.countTheEntities', $string_2); - $x[] = array('validator1.easyStructTest', $struct_3); - $x[] = array('validator1.echoStructTest', $struct_4); - $x[] = array('validator1.manyTypesTest', $int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)); - $x[] = array('validator1.moderateSizeArrayCheck', $array_6); - $x[] = array('validator1.nestedStructTest', $struct_7); - $x[] = array('validator1.simpleStructReturnTest', $int_8); + $x['validator1.arrayOfStructsTest'] = array($array_1); + $x['validator1.countTheEntities'] = array($string_2); + $x['validator1.easyStructTest'] = array($struct_3); + $x['validator1.echoStructTest'] = array($struct_4); + $x['validator1.manyTypesTest'] = array($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)); + $x['validator1.moderateSizeArrayCheck'] = array($array_6); + $x['validator1.nestedStructTest'] = array($struct_7); + $x['validator1.simpleStructReturnTest'] = array($int_8); $a_l_res = array($l_res_1, $l_res_2, $l_res_3, $l_res_4, $l_res_5, $l_res_6, $l_res_7, $l_res_8); $a_r_res = xmlrpc($xml_url, $x); - $this->assertEqual($a_l_res, $a_r_res, 'multicall equals result'); + $this->assertEqual($a_l_res, $a_r_res); } } @@ -212,7 +206,7 @@ class XMLRPCMessagesTestCase extends DrupalWebTestCase { $sizes = array(8, 80, 160); foreach ($sizes as $size) { $xml_message_l = xmlrpc_test_message_sized_in_kb($size); - $xml_message_r = xmlrpc($xml_url, 'messages.messageSizedInKB', $size); + $xml_message_r = xmlrpc($xml_url, array('messages.messageSizedInKB' => array($size))); $this->assertEqual($xml_message_l, $xml_message_r, t('XML-RPC messages.messageSizedInKB of %s Kb size received', array('%s' => $size))); } diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info index d923ef3f062f0069083b3ee5459aa4d08aac3d06..e7099cfb091714aa12a5f86c311783d528c109f6 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/statistics/statistics.info b/modules/statistics/statistics.info index 2031fa1af91cb0e01d20672d038d14e01e2413c0..581b126db6a92e907d4c918bff52b21052e22d80 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/statistics/statistics.install b/modules/statistics/statistics.install index 02765039d79c15a5106c643144ae87c1be0f0197..649869dd4dfa98224883746afe425c0bea8ff33f 100644 --- a/modules/statistics/statistics.install +++ b/modules/statistics/statistics.install @@ -1,5 +1,5 @@ <?php -// $Id: statistics.install,v 1.26 2010/04/28 05:29:55 webchick Exp $ +// $Id: statistics.install,v 1.27 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -90,7 +90,10 @@ function statistics_schema() { ), 'primary key' => array('aid'), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), + 'visitor' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index 8b059f7e2244103f8855f8873a8232d6a98d2a2b..01f4de76c6d67fee955da380da57be2af1d9b022 100644 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -1,5 +1,5 @@ <?php -// $Id: statistics.module,v 1.336 2010/05/29 07:53:44 dries Exp $ +// $Id: statistics.module,v 1.338 2010/08/30 05:58:46 webchick Exp $ /** * @file @@ -60,7 +60,7 @@ function statistics_exit() { if (variable_get('statistics_count_content_views', 0)) { // We are counting content views. - if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') { + if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) { // A node has been viewed, so update the node's counters. db_merge('node_counter') ->key(array('nid' => arg(1))) @@ -114,19 +114,12 @@ function statistics_permission() { */ function statistics_node_view($node, $view_mode) { if ($view_mode != 'rss') { - $links = array(); if (user_access('view post access counter')) { $statistics = statistics_get($node->nid); if ($statistics) { - $links['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads'); + $node->content['links']['#links']['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads'); } } - - $node->content['links']['statistics'] = array( - '#theme' => 'links__statistics_node', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); } } diff --git a/modules/statistics/statistics.test b/modules/statistics/statistics.test index 2548d86a7206b5e1ed9cd58ce70bd86b919990e7..6a9da4f377c19a3ee6a596214010cc64ce6f5470 100644 --- a/modules/statistics/statistics.test +++ b/modules/statistics/statistics.test @@ -1,5 +1,5 @@ <?php -// $Id: statistics.test,v 1.20 2010/07/08 03:41:27 webchick Exp $ +// $Id: statistics.test,v 1.22 2010/08/05 23:53:39 webchick Exp $ /** * Sets up a base class for the Statistics module. diff --git a/modules/syslog/syslog.info b/modules/syslog/syslog.info index 6cff42ea19c1a393d604447efded585c34f86448..b447adf364d04f1efa2226409f1c2e3d8a84bae2 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/syslog/syslog.install b/modules/syslog/syslog.install new file mode 100644 index 0000000000000000000000000000000000000000..969b1b64deec163c8542f918b4e08cf45f3bb4a4 --- /dev/null +++ b/modules/syslog/syslog.install @@ -0,0 +1,16 @@ +<?php +// $Id: syslog.install,v 1.1 2010/08/18 01:54:36 dries Exp $ + +/** + * @file + * Install, update and uninstall functions for the syslog module. + */ + +/** + * Implements hook_uninstall(). + */ +function syslog_uninstall() { + variable_del('syslog_identity'); + variable_del('syslog_facility'); + variable_del('syslog_format'); +} diff --git a/modules/syslog/syslog.module b/modules/syslog/syslog.module index abe6e22c5883876f1302606ad5db286437a38110..d0a48dfdef6564a7f563217801ea219bd4877e01 100644 --- a/modules/syslog/syslog.module +++ b/modules/syslog/syslog.module @@ -1,5 +1,5 @@ <?php -// $Id: syslog.module,v 1.35 2010/04/21 14:27:03 dries Exp $ +// $Id: syslog.module,v 1.37 2010/08/18 01:54:36 dries Exp $ /** * @file @@ -37,8 +37,14 @@ function syslog_help($path, $arg) { * Implements hook_form_FORM_ID_alter(). */ function syslog_form_system_logging_settings_alter(&$form, &$form_state) { + $help = module_exists('help') ? ' ' . l(t('More information'), 'admin/help/syslog') . '.' : NULL; + $form['syslog_identity'] = array( + '#type' => 'textfield', + '#title' => t('Syslog identity'), + '#default_value' => variable_get('syslog_identity', 'drupal'), + '#description' => t('A string that will be prepended to every message logged to Syslog. If you have multiple sites logging to the same Syslog log file, a unique identity per site makes it easy to tell the log entries apart.') . $help, + ); if (defined('LOG_LOCAL0')) { - $help = module_exists('help') ? ' ' . l(t('More information'), 'admin/help/syslog') . '.' : NULL; $form['syslog_facility'] = array( '#type' => 'select', '#title' => t('Syslog facility'), @@ -46,14 +52,14 @@ function syslog_form_system_logging_settings_alter(&$form, &$form_state) { '#options' => syslog_facility_list(), '#description' => t('Depending on the system configuration, Syslog and other logging tools use this code to identify or filter messages from within the entire system log.') . $help, ); - $form['syslog_format'] = array( - '#type' => 'textarea', - '#title' => t('Syslog format'), - '#default_value' => variable_get('syslog_format', '!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message'), - '#description' => t('Specify the format of the syslog entry. Available variables are: <dl><dt><code>!base_url</code></dt><dd>Base URL of the site.</dd><dt><code>!timestamp</code></dt><dd>Unix timestamp of the log entry.</dd><dt><code>!type</code></dt><dd>The category to which this message belongs.</dd><dt><code>!ip</code></dt><dd>IP address of the user triggering the message.</dd><dt><code>!request_uri</code></dt><dd>The requested URI.</dd><dt><code>!referer</code></dt><dd>HTTP Referer if available.</dd><dt><code>!uid</code></dt><dd>User ID.</dd><dt><code>!link</code></dt><dd>A link to associate with the message.</dd><dt><code>!message</code></dt><dd>The message to store in the log.</dd></dl>'), - ); - $form['actions']['#weight'] = 1; } + $form['syslog_format'] = array( + '#type' => 'textarea', + '#title' => t('Syslog format'), + '#default_value' => variable_get('syslog_format', '!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message'), + '#description' => t('Specify the format of the syslog entry. Available variables are: <dl><dt><code>!base_url</code></dt><dd>Base URL of the site.</dd><dt><code>!timestamp</code></dt><dd>Unix timestamp of the log entry.</dd><dt><code>!type</code></dt><dd>The category to which this message belongs.</dd><dt><code>!ip</code></dt><dd>IP address of the user triggering the message.</dd><dt><code>!request_uri</code></dt><dd>The requested URI.</dd><dt><code>!referer</code></dt><dd>HTTP Referer if available.</dd><dt><code>!uid</code></dt><dd>User ID.</dd><dt><code>!link</code></dt><dd>A link to associate with the message.</dd><dt><code>!message</code></dt><dd>The message to store in the log.</dd></dl>'), + ); + $form['actions']['#weight'] = 1; } /** @@ -85,7 +91,7 @@ function syslog_watchdog(array $log_entry) { if (!$log_init) { $log_init = TRUE; $default_facility = defined('LOG_LOCAL0') ? LOG_LOCAL0 : LOG_USER; - openlog('drupal', LOG_NDELAY, variable_get('syslog_facility', $default_facility)); + openlog(variable_get('syslog_identity', 'drupal'), LOG_NDELAY, variable_get('syslog_facility', $default_facility)); } $message = strtr(variable_get('syslog_format', '!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message'), array( diff --git a/modules/syslog/syslog.test b/modules/syslog/syslog.test index 7d41b6e75833cc92df7c6b23039f733da29f2f68..ec79420723260b1492bcebdf51cbd8b15177c77d 100644 --- a/modules/syslog/syslog.test +++ b/modules/syslog/syslog.test @@ -1,5 +1,5 @@ <?php -// $Id: syslog.test,v 1.12 2010/03/31 20:05:06 dries Exp $ +// $Id: syslog.test,v 1.14 2010/08/05 23:53:39 webchick Exp $ class SyslogTestCase extends DrupalWebTestCase { public static function getInfo() { diff --git a/modules/system/image.gd.inc b/modules/system/image.gd.inc index ebfdcf22515ff1f80381cf5203e650dd7389b52b..d035ab1ee664a66de753a9994b81798013052a93 100644 --- a/modules/system/image.gd.inc +++ b/modules/system/image.gd.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.gd.inc,v 1.15 2010/01/30 07:59:25 dries Exp $ +// $Id: image.gd.inc,v 1.16 2010/07/19 22:20:49 dries Exp $ /** * @file @@ -336,7 +336,7 @@ function image_gd_create_tmp(stdClass $image, $width, $height) { */ function image_gd_get_info(stdClass $image) { $details = FALSE; - $data = getimagesize($image->source); + $data = getimagesize(drupal_realpath($image->source)); if (isset($data) && is_array($data)) { $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png'); diff --git a/modules/system/page.tpl.php b/modules/system/page.tpl.php index f97b34cd7ad9b51716b6535ca34f66c3ebb5cab0..8bdb75d183905772a4b7314aad9df5022e401668 100644 --- a/modules/system/page.tpl.php +++ b/modules/system/page.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: page.tpl.php,v 1.43 2010/01/30 07:59:25 dries Exp $ +// $Id: page.tpl.php,v 1.46 2010/08/23 23:38:06 webchick Exp $ /** * @file @@ -11,7 +11,7 @@ * - $base_path: The base URL path of the Drupal installation. At the very * least, this will always default to /. * - $directory: The directory the template is located in, e.g. modules/system - * or themes/garland. + * or themes/bartik. * - $is_front: TRUE if the current page is the front page. * - $logged_in: TRUE if the user is registered and signed in. * - $is_admin: TRUE if the user has permission to access administration pages. @@ -55,7 +55,7 @@ * * Regions: * - $page['help']: Dynamic help text, mostly for admin pages. - * - $page['highlight']: Items for the highlighted content region. + * - $page['highlighted']: Items for the highlighted content region. * - $page['content']: The main content of the current page. * - $page['sidebar_first']: Items for the first sidebar. * - $page['sidebar_second']: Items for the second sidebar. @@ -102,9 +102,10 @@ </div></div> <!-- /.section, /#header --> - <?php if ($main_menu): ?> + <?php if ($main_menu || $secondary_menu): ?> <div id="navigation"><div class="section"> <?php print theme('links__system_main_menu', array('links' => $main_menu, 'attributes' => array('id' => 'main-menu', 'class' => array('links', 'clearfix')), 'heading' => t('Main menu'))); ?> + <?php print theme('links__system_secondary_menu', array('links' => $secondary_menu, 'attributes' => array('id' => 'secondary-menu', 'class' => array('links', 'clearfix')), 'heading' => t('Secondary menu'))); ?> </div></div> <!-- /.section, /#navigation --> <?php endif; ?> @@ -117,7 +118,7 @@ <div id="main-wrapper"><div id="main" class="clearfix"> <div id="content" class="column"><div class="section"> - <?php if ($page['highlight']): ?><div id="highlight"><?php print render($page['highlight']); ?></div><?php endif; ?> + <?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?> <a id="main-content"></a> <?php print render($title_prefix); ?> <?php if ($title): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?> @@ -144,7 +145,6 @@ </div></div> <!-- /#main, /#main-wrapper --> <div id="footer"><div class="section"> - <?php print theme('links__system_secondary_menu', array('links' => $secondary_menu, 'attributes' => array('id' => 'secondary-menu', 'class' => array('links', 'clearfix')), 'heading' => t('Secondary menu'))); ?> <?php print render($page['footer']); ?> </div></div> <!-- /.section, /#footer --> diff --git a/modules/system/region.tpl.php b/modules/system/region.tpl.php index adb164a30a6488d97bdd7fa04077d3cc695afacb..8ff154910b95993f583219cc258cf4567dd78ab2 100644 --- a/modules/system/region.tpl.php +++ b/modules/system/region.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: region.tpl.php,v 1.1 2009/10/05 02:43:01 webchick Exp $ +// $Id: region.tpl.php,v 1.2 2010/08/23 09:09:23 dries Exp $ /** * @file @@ -27,6 +27,8 @@ * @see template_process() */ ?> -<div class="<?php print $classes; ?>"> - <?php print $content; ?> -</div> +<?php if($content): ?> + <div class="<?php print $classes; ?>"> + <?php print $content; ?> + </div> +<?php endif; ?> diff --git a/modules/system/system-behavior.css b/modules/system/system-behavior.css index 19242abe97d296bf81354f1c167b3a81adf7d43a..72799d499b1479af01b767687bf305baf012a4b3 100644 --- a/modules/system/system-behavior.css +++ b/modules/system/system-behavior.css @@ -1,4 +1,4 @@ -/* $Id: system-behavior.css,v 1.11 2010/06/26 20:10:24 dries Exp $ */ +/* $Id: system-behavior.css,v 1.13 2010/08/09 16:58:15 webchick Exp $ */ /** * Autocomplete @@ -136,6 +136,9 @@ div.tree-child-horizontal { .tabledrag-toggle-weight { font-size: 0.9em; } +body div.tabledrag-changed-warning { + margin-bottom: 0.5em; +} /** * Progress bar @@ -204,7 +207,7 @@ dl.multiselect .form-item { /** * Password strength indicator */ -#password-strength { +.password-strength { width: 17em; float: right; /* LTR */ margin-top: 1.4em; @@ -212,16 +215,16 @@ dl.multiselect .form-item { .password-strength-title { display: inline; } -#password-strength-text { +.password-strength-text { float: right; /* LTR */ font-weight: bold; } -#password-indicator { +.password-indicator { background-color: #C4C4C4; height: 0.3em; width: 100%; } -#password-indicator div { +.password-indicator div { height: 100%; width: 0%; background-color: #47C965; diff --git a/modules/system/system-messages.css b/modules/system/system-messages.css index e68b0b8aa2f7e3bfb28f0f17d51f53478f4b40bb..b1a1406ee4ff013fae0974c20653536dc6c9f72f 100644 --- a/modules/system/system-messages.css +++ b/modules/system/system-messages.css @@ -1,36 +1,55 @@ /* $Id */ div.messages { - background-color: #dfd; - color: #000; - margin-bottom: 0.25em; - padding: 0.25em 0.5em; + background-position: 8px 8px; /* LTR */ + background-repeat: no-repeat; + border: 1px solid; + margin: 6px 0; + padding: 10px 10px 10px 50px; /* LTR */ } - -.error { - color: #e55; +div.status { + background-image: url(../../misc/message-24-ok.png); + border-color: #be7; } - -div.error, -tr.error { - background-color: #fcc; +div.status, +.ok { + color: #234600; } - +div.status, +table tr.ok { + background-color: #f8fff0; +} +div.warning { + background-image: url(../../misc/message-24-warning.png); + border-color: #ed5; +} +div.warning, .warning { - color: #e09010; + color: #840; } - div.warning, -tr.warning { - background-color: #fcfca7; +table tr.warning { + background-color: #fffce5; } - -.ok { - color: #008000; +div.error { + background-image: url(../../misc/message-24-error.png); + border-color: #ed541d; } - -div.ok, -tr.ok { - background-color: #dfd; - color: #020; +div.error, +.error { + color: #8c2e0b; +} +div.error, +table tr.error { + background-color: #fef5f1; +} +div.error p.error { + color: #333; +} +div.messages ul { + margin: 0 0 0 1em; /* LTR */ + padding: 0; +} +div.messages ul li { + list-style-image: none; } diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index e81150b3b4d07f4abba7fa3198c6fdcc22ade25a..3d421f295c3cf8fb286c62a427071dec11be7f95 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: system.admin.inc,v 1.292 2010/07/08 03:41:27 webchick Exp $ +// $Id: system.admin.inc,v 1.304 2010/09/15 04:31:02 webchick Exp $ /** * @file @@ -486,6 +486,10 @@ function system_theme_settings($form, &$form_state, $key = '') { $disabled['toggle_node_user_picture'] = TRUE; $disabled['toggle_comment_user_picture'] = TRUE; } + if (!module_exists('comment')) { + $disabled['toggle_comment_user_picture'] = TRUE; + $disabled['toggle_comment_user_verification'] = TRUE; + } $form['theme_settings'] = array( '#type' => 'fieldset', @@ -672,8 +676,10 @@ function system_theme_settings_validate($form, &$form_state) { } } + $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg')); + // Check for a new uploaded favicon. - $file = file_save_upload('favicon_upload'); + $file = file_save_upload('favicon_upload', $validators); if (isset($file)) { // File upload was attempted. if ($file) { @@ -953,9 +959,14 @@ function system_modules($form, $form_state = array()) { t('Description'), array('data' => t('Operations'), 'colspan' => 3), ), + // Ensure that the "Core" package fieldset comes first. + '#weight' => $package == 'Core' ? -10 : NULL, ); } + // Lastly, sort all fieldsets by title. + uasort($form['modules'], 'element_sort_by_title'); + $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', @@ -1259,8 +1270,6 @@ function system_modules_submit($form, &$form_state) { // Synchronize to catch any actions that were added or removed. actions_synchronize(); - - return; } /** @@ -1507,41 +1516,52 @@ function system_ip_blocking_delete_submit($form, &$form_state) { * @see system_settings_form() */ function system_site_information_settings() { - $form['site_name'] = array( + $form['site_information'] = array( + '#type' => 'fieldset', + '#title' => t('Site details'), + ); + $form['site_information']['site_name'] = array( '#type' => 'textfield', '#title' => t('Site name'), '#default_value' => variable_get('site_name', 'Drupal'), '#required' => TRUE ); - $form['site_slogan'] = array( + $form['site_information']['site_slogan'] = array( '#type' => 'textfield', '#title' => t('Slogan'), '#default_value' => variable_get('site_slogan', ''), '#description' => t("How this is used depends on your site's theme."), ); - $form['site_mail'] = array( + $form['site_information']['site_mail'] = array( '#type' => 'textfield', '#title' => t('E-mail address'), '#default_value' => variable_get('site_mail', ini_get('sendmail_from')), '#description' => t("The <em>From</em> address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"), '#required' => TRUE, ); - $form['site_frontpage'] = array( - '#type' => 'textfield', - '#title' => t('Default front page'), - '#default_value' => drupal_get_path_alias(variable_get('site_frontpage', 'node')), - '#size' => 40, - '#description' => t('The home page displays content from this relative URL. If unsure, specify "node".'), - '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), - '#required' => TRUE, + $form['front_page'] = array( + '#type' => 'fieldset', + '#title' => t('Front page'), ); - $form['default_nodes_main'] = array( + $form['front_page']['default_nodes_main'] = array( '#type' => 'select', '#title' => t('Number of posts on front page'), '#default_value' => variable_get('default_nodes_main', 10), '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)), '#description' => t('The maximum number of posts displayed on overview pages such as the front page.') ); - $form['site_403'] = array( + $form['front_page']['site_frontpage'] = array( + '#type' => 'textfield', + '#title' => t('Default front page'), + '#default_value' => (variable_get('site_frontpage')!='node'?drupal_get_path_alias(variable_get('site_frontpage', 'node')):''), + '#size' => 40, + '#description' => t('Optionally, specify a relative URL to display as the front page. Leave blank to display the default content feed.'), + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), + ); + $form['error_page'] = array( + '#type' => 'fieldset', + '#title' => t('Error pages'), + ); + $form['error_page']['site_403'] = array( '#type' => 'textfield', '#title' => t('Default 403 (access denied) page'), '#default_value' => variable_get('site_403', ''), @@ -1549,7 +1569,7 @@ function system_site_information_settings() { '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'), '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') ); - $form['site_404'] = array( + $form['error_page']['site_404'] = array( '#type' => 'textfield', '#title' => t('Default 404 (not found) page'), '#default_value' => variable_get('site_404', ''), @@ -1557,13 +1577,6 @@ function system_site_information_settings() { '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'), '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') ); - $form['cron_safe_threshold'] = array( - '#type' => 'select', - '#title' => t('Automatically run cron'), - '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD), - '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'), - '#description' => t('When enabled, the site will check whether cron has been run in the configured interval and automatically run it upon the next page request. For more information visit the <a href="@status-report-url">status report page</a>.', array('@status-report-url' => url('admin/reports/status'))), - ); $form['#validate'][] = 'system_site_information_settings_validate'; @@ -1571,21 +1584,92 @@ function system_site_information_settings() { } /** - * Validate the submitted site-information form. + * Validates the submitted site-information form. */ function system_site_information_settings_validate($form, &$form_state) { // Validate the e-mail address. if ($error = user_validate_mail($form_state['values']['site_mail'])) { form_set_error('site_mail', $error); } - // Get the normal path of the front page. - form_set_value($form['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state); + // Check for empty front page path. + if (empty($form_state['values']['site_frontpage'])) { + // Set to default "node". + form_set_value($form['front_page']['site_frontpage'], 'node', $form_state); + } + else { + // Get the normal path of the front page. + form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state); + } // Validate front page path. if (!drupal_valid_path($form_state['values']['site_frontpage'])) { - form_set_error('site_frontpage', t("The path '@path' is either invalid or you do not have access to it.", array('@path' => $form_state['values']['site_frontpage']))); + form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage']))); + } + // Get the normal paths of both error pages. + if (!empty($form_state['values']['site_403'])) { + form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state); + } + if (!empty($form_state['values']['site_404'])) { + form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state); + } + // Validate 403 error path. + if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) { + form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403']))); + } + // Validate 404 error path. + if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) { + form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404']))); } } +/** + * Form builder; Cron form. + * + * @see system_settings_form() + * @ingroup forms + */ +function system_cron_settings() { + $form['description'] = array( + '#markup' => '<p>'.t('Cron takes care of running periodical tasks like checking for updates and indexing content for search.').'</p>', + ); + $form['run'] = array( + '#type' => 'submit', + '#value' => t('Run cron'), + '#submit' => array('system_run_cron_submit'), + ); + $status = '<p>' . t('Last run: %cron-last ago.', array('%cron-last' => format_interval(REQUEST_TIME - variable_get('cron_last')),)) . '</p>'; + $form['status'] = array( + '#markup' => $status, + ); + $form['cron'] = array( + '#type' => 'fieldset', + ); + $form['cron']['cron_safe_threshold'] = array( + '#type' => 'select', + '#title' => t('Run cron every'), + '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD), + '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'), + ); + + return system_settings_form($form, FALSE); +} + +/** + * Submit callback; run cron. + * + * @ingroup forms + */ +function system_run_cron_submit($form, &$form_state) { + // Run cron manually from Cron form. + if (drupal_cron_run()) { + drupal_set_message(t('Cron run successfully.')); + } + else { + drupal_set_message(t('Cron run failed.'), 'error'); + } + + drupal_goto('admin/config/system/cron'); +} + /** * Form builder; Configure error reporting settings. * @@ -1694,6 +1778,9 @@ function system_performance_settings() { $form['#submit'][] = 'drupal_clear_css_cache'; $form['#submit'][] = 'drupal_clear_js_cache'; + // This form allows page compression settings to be changed, which can + // invalidate the page cache, so it needs to be cleared on form submit. + $form['#submit'][] = 'system_clear_page_cache_submit'; return system_settings_form($form); } @@ -1708,6 +1795,15 @@ function system_clear_cache_submit($form, &$form_state) { drupal_set_message(t('Caches cleared.')); } +/** + * Submit callback; clear the page cache. + * + * @ingroup forms + */ +function system_clear_page_cache_submit($form, &$form_state) { + cache_clear_all('*', 'cache_page', TRUE); +} + /** * Form builder; Configure the site file handling. * @@ -1718,7 +1814,7 @@ function system_file_system_settings() { $form['file_public_path'] = array( '#type' => 'textfield', '#title' => t('Public file system path'), - '#default_value' => variable_get('file_public_path', file_directory_path()), + '#default_value' => variable_get('file_public_path', conf_path() . '/files'), '#maxlength' => 255, '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'), '#after_build' => array('system_check_directory'), @@ -1727,7 +1823,7 @@ function system_file_system_settings() { $form['file_private_path'] = array( '#type' => 'textfield', '#title' => t('Private file system path'), - '#default_value' => variable_get('file_private_path', file_directory_path('private')), + '#default_value' => variable_get('file_private_path', ''), '#maxlength' => 255, '#description' => t('A local file system path where private files will be stored. This directory must exist and be writable by Drupal. This directory should not be accessible over the web.'), '#after_build' => array('system_check_directory'), @@ -1736,7 +1832,7 @@ function system_file_system_settings() { $form['file_temporary_path'] = array( '#type' => 'textfield', '#title' => t('Temporary directory'), - '#default_value' => variable_get('file_temporary_path', file_directory_path('temporary')), + '#default_value' => variable_get('file_temporary_path', file_directory_temp()), '#maxlength' => 255, '#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'), '#after_build' => array('system_check_directory'), @@ -2013,7 +2109,8 @@ function theme_system_date_time_settings($variables) { $delete_key = $key . '_delete'; $row = array(); $row[] = $form['format'][$key]['#title']; - unset($form['format'][$key]['#title']); + $form['format'][$key]['#title_display'] = 'invisible'; + $row[] = $form['format'][$key]['#title_display']; $row[] = array('data' => drupal_render($form['format'][$key])); $row[] = array('data' => drupal_render($form['delete'][$delete_key])); $rows[] = $row; @@ -2151,7 +2248,7 @@ function system_site_maintenance_mode() { '#type' => 'checkbox', '#title' => t('Put site into maintenance mode'), '#default_value' => variable_get('maintenance_mode', 0), - '#description' => t('When enabled, only users with the "Access site in maintenance mode" <a href="@permissions-url">permission</a> are able to access your site to perform maintenance; all other visitors see the maintenance mode message configured below. Authorized users can log in directly via the <a href="@user-login">user login</a> page.', array('@permissions-url' => url('admin/people/permissions'), '@user-login' => url('user'))), + '#description' => t('When enabled, only users with the "Use the site in maintenance mode" <a href="@permissions-url">permission</a> are able to access your site to perform maintenance; all other visitors see the maintenance mode message configured below. Authorized users can log in directly via the <a href="@user-login">user login</a> page.', array('@permissions-url' => url('admin/people/permissions'), '@user-login' => url('user'))), ); $form['maintenance_mode_message'] = array( '#type' => 'textarea', @@ -2783,6 +2880,7 @@ function system_configure_date_formats_form($form, &$form_state, $dfid = 0) { $form['date_format'] = array( '#type' => 'textfield', '#title' => t('Format string'), + '#maxlength' => 100, '#description' => t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')), '#default_value' => ($dfid ? $format->format : ''), '#field_suffix' => ' <small id="edit-date-format-suffix">' . $now . '</small>', @@ -3083,9 +3181,8 @@ function system_actions_delete_form_submit($form, &$form_state) { $aid = $form_state['values']['aid']; $action = actions_load($aid); actions_delete($aid); - $label = check_plain($action->label); - watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $label)); - drupal_set_message(t('Action %action was deleted', array('%action' => $label))); + watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label)); + drupal_set_message(t('Action %action was deleted', array('%action' => $action->label))); $form_state['redirect'] = 'admin/config/system/actions/manage'; } diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 26be37141ca7f4146e6cb1271440a5ec555fc2f3..d8450db604bbcea8efe3c59759c90ea5451e72f1 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1,5 +1,5 @@ <?php -// $Id: system.api.php,v 1.178 2010/07/07 08:05:01 webchick Exp $ +// $Id: system.api.php,v 1.193 2010/09/11 14:35:13 dries Exp $ /** * @file @@ -87,7 +87,16 @@ function hook_hook_info_alter(&$hooks) { * - uri callback: A function taking an entity as argument and returning the * uri elements of the entity, e.g. 'path' and 'options'. The actual entity * uri can be constructed by passing these elements to url(). + * - label callback: (optional) A function taking an entity as argument and + * returning the label of the entity; e.g., $node->title or + * $comment->subject. A callback should be specified when the label is the + * result of complex logic. Otherwise, the 'label' property of the + * 'entity keys' the property should be used. * - fieldable: Set to TRUE if you want your entity type to be fieldable. + * - translation: An associative array of modules registered as field + * translation handlers. Array keys are the module names, array values + * can be any data structure the module uses to provide field translation. + * Any empty value disallows the module to appear as a translation handler. * - entity keys: An array describing how the Field API can extract the * information it needs from the objects of the type. Elements: * - id: The name of the property that contains the primary id of the @@ -103,6 +112,11 @@ function hook_hook_info_alter(&$hooks) { * omitted if this entity type exposes a single bundle (all entities have * the same collection of fields). The name of this single bundle will be * the same as the entity type. + * - label: The property name of the entity that contains the label. For + * example, if the entity's label is located in $entity->subject, then + * 'subect' should be specified here. In case complex logic is required to + * build the label, a 'label callback' should be implemented instead. See + * entity_label() for details. * - bundle keys: An array describing how the Field API can extract the * information it needs from the bundle objects for this type (e.g * $vocabulary objects for terms; not applicable for nodes). This entry can @@ -147,11 +161,11 @@ function hook_hook_info_alter(&$hooks) { * described by an array with the following key/value pairs: * - label: The human-readable name of the view mode * - custom settings: A boolean specifying whether the view mode should by - * default use its own custom field display settings. If FALSE, entities - * displayed in this view mode will reuse the 'default' display settings by - * default (e.g. right after the module exposing the view mode is enabled), - * but administrators can later use the Field UI to apply custom display - * settings specific to the view mode. + * default use its own custom field display settings. If FALSE, entities + * displayed in this view mode will reuse the 'default' display settings + * by default (e.g. right after the module exposing the view mode is + * enabled), but administrators can later use the Field UI to apply custom + * display settings specific to the view mode. * * @see entity_load() * @see hook_entity_info_alter() @@ -165,6 +179,9 @@ function hook_entity_info() { 'revision table' => 'node_revision', 'uri callback' => 'node_uri', 'fieldable' => TRUE, + 'translation' => array( + 'locale' => TRUE, + ), 'entity keys' => array( 'id' => 'nid', 'revision' => 'vid', @@ -372,27 +389,20 @@ function hook_entity_prepare_view($entities, $type) { * * This hook will only be called if cron.php is run (e.g. by crontab). * - * Modules that require to schedule some commands to be executed at regular - * intervals can implement hook_cron(). The engine will then call the hook - * at the appropriate intervals defined by the administrator. This interface - * is particularly handy to implement timers or to automate certain tasks. - * Database maintenance, recalculation of settings or parameters are good - * candidates for cron tasks. - * - * Short-running or not resource intensive tasks can be executed directly. - * - * Long-running tasks should use the queue API. To do this, one or more queues - * need to be defined via hook_cron_queue_info(). Items that need to be - * processed are appended to the defined queue, instead of processing them - * directly in hook_cron(). - * Examples of jobs that are good candidates for - * hook_cron_queue_info() include automated mailing, retrieving remote data, and - * intensive file tasks. + * Modules that require some commands to be executed periodically can + * implement hook_cron(). The engine will then call the hook whenever a cron + * run happens, as defined by the administrator. Typical tasks managed by + * hook_cron() are database maintenance, backups, recalculation of settings + * or parameters, automated mailing, and retrieving remote data. * - * @return - * None. + * Short-running or non-resource-intensive tasks can be executed directly in + * the hook_cron() implementation. * - * @see hook_cron_queue_info() + * Long-running tasks and tasks that could time out, such as retrieving remote + * data, sending email, and intensive file tasks, should use the queue API + * instead of executing the tasks directly. To do this, first define one or + * more queues via hook_cron_queue_info(). Then, add items that need to be + * processed to the defined queues. */ function hook_cron() { // Short-running operation example, not using a queue: @@ -888,7 +898,9 @@ function hook_page_build(&$page) { * - "delivery callback": The function to call to package the result of the * page callback function and send it to the browser. Defaults to * drupal_deliver_html_page() unless a value is inherited from a parent menu - * item. + * item. Note that this function is called even if the access checks fail, + * so any custom delivery callback function should take that into account. + * See drupal_deliver_html_page() for an example. * - "access callback": A function returning a boolean value that determines * whether the user has access rights to this menu item. Defaults to * user_access() unless a value is inherited from a parent menu item. @@ -978,6 +990,8 @@ function hook_page_build(&$page) { * - MENU_DEFAULT_LOCAL_TASK: Every set of local tasks should provide one * "default" task, which should display the same page as the parent item. * If the "type" element is omitted, MENU_NORMAL_ITEM is assumed. + * - "options": An array of options to be passed to l() when generating a link + * from this menu item. * * For a detailed usage example, see page_example.module. * For comprehensive documentation on the menu system, see @@ -1146,6 +1160,10 @@ function hook_menu_link_delete($link) { * does not need to be altered if there is more than one tab. * - output: A list of of tabs, each one being an associative array as * described above. + * @param $router_item + * The menu system router item of the page. + * @param $root_path + * The path to the root item for this set of tabs. */ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { // Add an action linking to node/add to all pages. @@ -1205,9 +1223,10 @@ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { * This is a normalized path, which means that an originally passed path of * 'node/123' became 'node/%'. * + * @see hook_contextual_links_view_alter() * @see menu_contextual_links() * @see hook_menu() - * @see system_preprocess() + * @see contextual_preprocess() */ function hook_menu_contextual_links_alter(&$links, $router_item, $root_path) { // Add a link to all contextual links for nodes. @@ -1328,11 +1347,14 @@ function hook_form_alter(&$form, &$form_state, $form_id) { * Nested array of form elements that comprise the form. * @param $form_state * A keyed array containing the current state of the form. + * @param $form_id + * String representing the name of the form itself. Typically this is the + * name of the function that generated the form. * * @see hook_form_alter() * @see drupal_prepare_form() */ -function hook_form_FORM_ID_alter(&$form, &$form_state) { +function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) { // Modification for the form with the given form ID goes here. For example, if // FORM_ID is "user_register_form" this code would run only on the user // registration form. @@ -1345,6 +1367,43 @@ function hook_form_FORM_ID_alter(&$form, &$form_state) { ); } +/** + * Provide a form-specific alteration for shared forms. + * + * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific + * form belonging to multiple form_ids, rather than implementing + * hook_form_alter() and checking for conditions that would identify the + * shared form constructor. + * + * Examples for such forms are node_form() or comment_form(). + * + * Note that this hook fires after hook_form_FORM_ID_alter() and before + * hook_form_alter(). + * + * @param $form + * Nested array of form elements that comprise the form. + * @param $form_state + * A keyed array containing the current state of the form. + * @param $form_id + * String representing the name of the form itself. Typically this is the + * name of the function that generated the form. + * + * @see hook_form_FORM_ID_alter() + * @see drupal_prepare_form() + */ +function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) { + // Modification for the form with the given BASE_FORM_ID goes here. For + // example, if BASE_FORM_ID is "node_form", this code would run on every + // node form, regardless of node type. + + // Add a checkbox to the node form about agreeing to terms of use. + $form['terms_of_use'] = array( + '#type' => 'checkbox', + '#title' => t("I agree with the website's terms and conditions."), + '#required' => TRUE, + ); +} + /** * Map form_ids to form builder functions. * @@ -1433,9 +1492,10 @@ function hook_boot() { * used to set up global parameters which are needed later in the request. * when this hook is called, all modules are already loaded in memory. * - * For example, this hook is a typical place for modules to add CSS or JS - * that should be present on every page. This hook is not run on cached - * pages - though CSS or JS added this way will be present on a cached page. + * This hook is not run on cached pages. + * + * To add CSS or JS that should be present on all pages, modules should not + * implement this hook, but declare these files in their .info file. */ function hook_init() { drupal_add_css(drupal_get_path('module', 'book') . '/book.css'); @@ -1747,19 +1807,18 @@ function hook_theme($existing, $type, $theme, $path) { * * For example: * @code - * $theme_registry['user_profile'] = array( - * 'variables' => array( - * 'account' => NULL, - * ), - * 'template' => 'modules/user/user-profile', - * 'file' => 'modules/user/user.pages.inc', - * 'type' => 'module', - * 'theme path' => 'modules/user', - * 'preprocess functions' => array( - * 0 => 'template_preprocess', - * 1 => 'template_preprocess_user_profile', - * ), - * ) + * $theme_registry['user_profile'] = array( + * 'variables' => array( + * 'account' => NULL, + * ), + * 'template' => 'modules/user/user-profile', + * 'file' => 'modules/user/user.pages.inc', + * 'type' => 'module', + * 'theme path' => 'modules/user', + * 'preprocess functions' => array( + * 0 => 'template_preprocess', + * 1 => 'template_preprocess_user_profile', + * ), * ); * @endcode * @@ -2302,31 +2361,6 @@ function hook_file_move($file, $source) { } -/** - * Report the number of times a file is referenced by a module. - * - * This hook is called to determine if a files is in use. Multiple modules may - * be referencing the same file and to prevent one from deleting a file used by - * another this hook is called. - * - * @param $file - * The file object being checked for references. - * @return - * If the module uses this file return an array with the module name as the - * key and the value the number of times the file is used. - * - * @see file_delete() - * @see upload_file_references() - */ -function hook_file_references($file) { - // If user.module is still using a file, do not let other modules delete it. - $file_used = (bool) db_query_range('SELECT 1 FROM {user} WHERE pictire = :fid', 0, 1, array(':fid' => $file->fid))->fetchField(); - if ($file_used) { - // Return the name of the module and how many references it has to the file. - return array('user' => 1); - } -} - /** * Respond to a file being deleted. * @@ -2579,7 +2613,7 @@ function hook_schema() { 'not null' => TRUE, 'default' => ''), 'title' => array( - 'description' => 'The title of this node, always treated a non-markup plain text.', + 'description' => 'The title of this node, always treated as non-markup plain text.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, @@ -2593,6 +2627,16 @@ function hook_schema() { 'nid_vid' => array('nid', 'vid'), 'vid' => array('vid') ), + 'foreign keys' => array( + 'node_revision' => array( + 'table' => 'node_revision', + 'columns' => array('vid' => 'vid'), + ), + 'node_author' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid') + ), + ), 'primary key' => array('nid'), ); return $schema; @@ -2603,8 +2647,8 @@ function hook_schema() { * * When a module modifies the database structure of another module (by * changing, adding or removing fields, keys or indexes), it should - * implement hook_schema_alter() to update the default $schema to take - * it's changes into account. + * implement hook_schema_alter() to update the default $schema to take its + * changes into account. * * See hook_schema() for details on the schema definition structure. * @@ -3963,7 +4007,7 @@ function hook_filetransfer_backends() { /** * Control site status before menu dispatching. * - * The hook is called after checking whether the site is offline but before + * The hook is called after checking whether the site is offline but before * the current router item is retrieved and executed by * menu_execute_active_handler(). If the site is in offline mode, * $menu_site_status is set to MENU_SITE_OFFLINE. diff --git a/modules/system/system.css b/modules/system/system.css index 1b2de28d3f41069b5ccf96a5c038f56c3c1abf0a..a04e6d63d784c0ab33775f5f39df5a698888e212 100644 --- a/modules/system/system.css +++ b/modules/system/system.css @@ -1,4 +1,4 @@ -/* $Id: system.css,v 1.77 2010/05/23 18:23:32 dries Exp $ */ +/* $Id: system.css,v 1.78 2010/08/02 11:22:22 dries Exp $ */ /* ** HTML elements @@ -397,7 +397,7 @@ table.sticky-header { /* ** Markup free clearing -** Details: http://www.positioniseverything.net/easyclearing.html +** Details: http://perishablepress.com/press/2009/12/06/new-clearfix-hack */ .clearfix:after { content: "."; @@ -406,16 +406,11 @@ table.sticky-header { clear: both; visibility: hidden; } - -.clearfix { - display: inline-block; -} - -/* Hides from IE-mac \*/ +/* IE6 */ * html .clearfix { height: 1%; } -.clearfix { - display: block; +/* IE7 */ +*:first-child + html .clearfix { + min-height: 1%; } -/* End hide from IE-mac */ diff --git a/modules/system/system.info b/modules/system/system.info index a8a37d2455a58664688a989ce6757b2ee705ff3e..b24fa6d114eea9bd2fc9a594b3fbd8db0d3532ff 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/system/system.install b/modules/system/system.install index 89c4edd70672091bacda90b0a5511824f33fae99..d124d6dd62d4933d2a325335686a1abffc8cf033 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -1,5 +1,5 @@ <?php -// $Id: system.install,v 1.488 2010/07/08 03:41:27 webchick Exp $ +// $Id: system.install,v 1.506 2010/09/13 05:50:09 webchick Exp $ /** * @file @@ -141,27 +141,29 @@ function system_requirements($phase) { $requirements['php_extensions']['value'] = $t('Enabled'); } - // Test for PDO (database). - $requirements['database_extensions'] = array( - 'title' => $t('Database support'), - ); + if ($phase == 'install' || $phase == 'update') { + // Test for PDO (database). + $requirements['database_extensions'] = array( + 'title' => $t('Database support'), + ); - // Test for at least one suitable PDO extension, if PDO is available. - $database_ok = extension_loaded('pdo'); - if ($database_ok) { - $drivers = drupal_detect_database_types(); - $database_ok = !empty($drivers); - } + // Test for at least one suitable PDO extension, if PDO is available. + $database_ok = extension_loaded('pdo'); + if ($database_ok) { + $drivers = drupal_detect_database_types(); + $database_ok = !empty($drivers); + } - 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', - )); - } - else { - $requirements['database_extensions']['value'] = $t('Enabled'); + 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', + )); + } + else { + $requirements['database_extensions']['value'] = $t('Enabled'); + } } // Test PHP memory_limit @@ -321,7 +323,7 @@ function system_requirements($phase) { } } else { - if (variable_get('file_default_scheme', 'public') == 'public') { + if (file_default_scheme() == 'public') { $requirements['file system']['value'] = $t('Writable (<em>public</em> download method)'); } else { @@ -414,15 +416,6 @@ function system_requirements($phase) { ); continue; } - // Check for a disabled dependency. - if (!$required_file->status) { - $requirements["$module-$required_module"] = array( - 'title' => $t('Unresolved dependency'), - 'description' => $t('@name requires this module.', array('@name' => $name)), - 'value' => $t('@required_name (Disabled)', array('@required_name' => $required_name)), - 'severity' => REQUIREMENT_ERROR, - ); - } } } } @@ -777,14 +770,14 @@ function system_schema() { 'default' => 0, ), 'filename' => array( - 'description' => 'Name of the file with no path components. This may differ from the basename of the filepath if the file is renamed to avoid overwriting an existing file.', + 'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), 'uri' => array( - 'description' => 'Path of the file relative to Drupal root.', + 'description' => 'The URI to access the file (either local or remote).', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, @@ -805,10 +798,11 @@ function system_schema() { 'default' => 0, ), 'status' => array( - 'description' => 'A bitmapped field indicating the status of the file. The least significant bit indicates temporary (0) or permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', + 'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, + 'size' => 'tiny', ), 'timestamp' => array( 'description' => 'UNIX timestamp for when the file was added.', @@ -828,7 +822,56 @@ function system_schema() { ), 'primary key' => array('fid'), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), + 'file_owner' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + ), + ); + + $schema['file_usage'] = array( + 'description' => 'Track where a file is used.', + 'fields' => array( + 'fid' => array( + 'description' => 'File ID.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'module' => array( + 'description' => 'The name of the module that is using the file.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'description' => 'The name of the object type in which the file is used.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'id' => array( + 'description' => 'The primary key of the object using the file.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'count' => array( + 'description' => 'The number of times this file is used by this object.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('fid', 'type', 'id', 'module'), + 'indexes' => array( + 'type_id' => array('type', 'id'), + 'fid_count' => array('fid', 'count'), + 'fid_module' => array('fid', 'module'), ), ); @@ -1346,17 +1389,11 @@ function system_schema() { 'length' => 255, 'not null' => TRUE, ), - 'filectime' => array( - 'description' => "The creation time of the file when last parsed.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'filemtime' => array( - 'description' => "The modification time of the file when last parsed.", - 'type' => 'int', + 'hash' => array( + 'description' => "sha-256 hash of the file's contents when last parsed.", + 'type' => 'varchar', + 'length' => 64, 'not null' => TRUE, - 'default' => 0, ), ), 'primary key' => array('filename'), @@ -1386,7 +1423,10 @@ function system_schema() { 'not null' => TRUE ), ), - 'indexes' => array('value' => array('value')), + 'indexes' => array( + 'value' => array('value'), + 'expire' => array('expire'), + ), 'primary key' => array('name'), ); @@ -1462,7 +1502,10 @@ function system_schema() { 'ssid' => array('ssid'), ), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), + 'session_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); @@ -1530,8 +1573,7 @@ function system_schema() { ), 'primary key' => array('filename'), 'indexes' => array( - 'bootstrap' => array('status', 'bootstrap', 'type', 'weight', 'name'), - 'system_list' => array('weight', 'name'), + 'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'), 'type_name' => array('type', 'name'), ), ); @@ -1567,12 +1609,10 @@ function system_schema() { 'default' => '', ), ), - 'unique keys' => array( - 'alias_language' => array('alias', 'language'), - ), 'primary key' => array('pid'), 'indexes' => array( - 'source_language' => array('source', 'language'), + 'alias_language_pid' => array('alias', 'language', 'pid'), + 'source_language_pid' => array('source', 'language', 'pid'), ), ); @@ -1637,6 +1677,18 @@ function system_update_last_removed() { return 6055; } +/** + * Implements hook_update_dependencies(). + */ +function system_update_dependencies() { + // Update 7053 adds new blocks, so make sure the block tables are updated. + $dependencies['system'][7053] = array( + 'block' => 7002, + ); + + return $dependencies; +} + /** * @defgroup updates-6.x-to-7.x System updates from 6.x to 7.x * @{ @@ -1776,6 +1828,10 @@ function system_update_7004(&$sandbox) { ), ); + $moved_deltas = array( + 'user' => array('navigation' => 'system'), + ); + // Only run this the first time through the batch update. if (!isset($sandbox['progress'])) { // Rename forum module's block variables. @@ -1791,7 +1847,7 @@ function system_update_7004(&$sandbox) { } } - update_fix_d7_block_deltas($sandbox, $renamed_deltas); + update_fix_d7_block_deltas($sandbox, $renamed_deltas, $moved_deltas); } @@ -1850,14 +1906,14 @@ function system_update_7008() { } /** - * Rename the variables for primary and secondary links. - * + * Rename the variable for primary links. */ function system_update_7009() { - db_update('variable') - ->fields(array('name' => 'main_menu_links_source')) - ->condition('name', 'menu_primary_links_source') - ->execute(); + $current_primary = variable_get('menu_primary_links_source'); + if (isset($current_primary)) { + variable_set('menu_main_links_source', $current_primary); + variable_del('menu_primary_links_source'); + } } /** @@ -1986,12 +2042,12 @@ function system_update_7016() { $datatype = 'bigint'; break; } - db_query('ALTER TABLE ' . $row->table . ' ALTER COLUMN ' . $row->field . ' TYPE ' . $datatype); - db_query('ALTER TABLE ' . $row->table . ' ADD CHECK (' . $row->field . ' >= 0)'); + db_query('ALTER TABLE ' . $row->table . ' ALTER COLUMN "' . $row->field . '" TYPE ' . $datatype); + db_query('ALTER TABLE ' . $row->table . ' ADD CHECK ("' . $row->field . '" >= 0)'); } - db_query('DROP DOMAIN smallint_unsigned'); - db_query('DROP DOMAIN int_unsigned'); - db_query('DROP DOMAIN bigint_unsigned'); + db_query('DROP DOMAIN IF EXISTS smallint_unsigned'); + db_query('DROP DOMAIN IF EXISTS int_unsigned'); + db_query('DROP DOMAIN IF EXISTS bigint_unsigned'); } } @@ -2085,12 +2141,6 @@ function system_update_7029() { ->execute(); } -/** - * Moved to comment_update_7011(). - */ -function system_update_7030() { -} - /** * Removed in favour of Drupal 6 backport. * @@ -2182,10 +2232,11 @@ function system_update_7034() { 'default' => 0, ), 'status' => array( - 'description' => 'A bitmapped field indicating the status of the file the least sigifigant bit indicates temporary (1) or permanent (0). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', + 'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, + 'size' => 'tiny', ), 'timestamp' => array( 'description' => 'UNIX timestamp for when the file was added.', @@ -2286,9 +2337,10 @@ function system_update_7042() { // Rename the fields, and increase their length to 255 characters. db_change_field('url_alias', 'src', 'source', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); db_change_field('url_alias', 'dst', 'alias', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); - // Add indexes back. + // Add indexes back. We replace the unique key with an index since it never + // provided any meaningful unique constraint ('pid' is a primary key). db_add_index('url_alias', 'source_language_pid', array('source', 'language', 'pid')); - db_add_unique_key('url_alias', 'alias_language_pid', array('alias', 'language', 'pid')); + db_add_index('url_alias', 'alias_language_pid', array('alias', 'language', 'pid')); } /** @@ -2335,10 +2387,10 @@ function system_update_7046() { variable_set('theme_garland_settings', $settings); // Remove Garland's color files since they won't match Minnelli's. foreach (variable_get('color_garland_files', array()) as $file) { - @unlink($file); + @drupal_unlink($file); } if (isset($file) && $file = dirname($file)) { - @rmdir($file); + @drupal_rmdir($file); } variable_del('color_garland_palette'); variable_del('color_garland_stylesheets'); @@ -2406,15 +2458,6 @@ function system_update_7052() { * Upgrade standard blocks and menus. */ function system_update_7053() { - // Navigation block is now defined in system module. - if (db_table_exists('block')) { - db_update('block') - ->fields(array('module' => 'system')) - ->condition('module', 'user') - ->condition('delta', 'navigation') - ->execute(); - } - if (db_table_exists('menu_custom')) { // Create the same menus as in menu_install(). db_insert('menu_custom') @@ -2425,6 +2468,29 @@ function system_update_7053() { ->fields(array('menu_name' => 'management', 'title' => 'Management', 'description' => "The <em>Management</em> menu contains links for administrative tasks.")) ->execute(); } + + block_flush_caches(); + + // Show the new menu blocks along the navigation block. + $blocks = db_query("SELECT theme, status, region, weight, visibility, pages FROM {block} WHERE module = 'system' AND delta = 'navigation'"); + $deltas = db_or() + ->condition('delta', 'user-menu') + ->condition('delta', 'management'); + + foreach ($blocks as $block) { + db_update('block') + ->fields(array( + 'status' => $block->status, + 'region' => $block->region, + 'weight' => $block->weight, + 'visibility' => $block->visibility, + 'pages' => $block->pages, + )) + ->condition('theme', $block->theme) + ->condition('module', 'system') + ->condition($deltas) + ->execute(); + } } /** @@ -2529,18 +2595,6 @@ function system_update_7055() { db_change_field('system', 'info', 'info', $spec); } -/** - * Remove pid from indexes and unique keys of {url_alias}. - */ -function system_update_7056() { - // Drop indexes. - db_drop_index('url_alias', 'source_language_pid'); - db_drop_unique_key('url_alias', 'alias_language_pid'); - // Add indexes back. - db_add_index('url_alias', 'source_language', array('source', 'language')); - db_add_unique_key('url_alias', 'alias_language', array('alias', 'language')); -} - /** * Increase the size of session-ids. */ @@ -2562,10 +2616,62 @@ function system_update_7058() { variable_del('cron_semaphore'); } +/** + * Create the {file_usage} table. + */ +function system_update_7059() { + $spec = array( + 'description' => 'Track where a file is used.', + 'fields' => array( + 'fid' => array( + 'description' => 'File ID.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'module' => array( + 'description' => 'The name of the module that is using the file.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'description' => 'The name of the object type in which the file is used.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'id' => array( + 'description' => 'The primary key of the object using the file.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'count' => array( + 'description' => 'The number of times this file is used by this object.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('fid', 'type', 'id', 'module'), + 'indexes' => array( + 'type_id' => array('type', 'id'), + 'fid_count' => array('fid', 'count'), + 'fid_module' => array('fid', 'module'), + ), + ); + db_create_table('file_usage', $spec); +} + /** * Migrate upload.module to file.module. */ -function system_update_7059(&$sandbox) { +function system_update_7060(&$sandbox) { if (!db_table_exists('upload')) { return; } @@ -2613,7 +2719,7 @@ function system_update_7059(&$sandbox) { 'settings' => array( 'display_field' => 1, 'display_default' => variable_get('upload_list_default', 1), - 'uri_scheme' => variable_get('file_default_scheme', 'public'), + 'uri_scheme' => file_default_scheme(), 'default_file' => 0, ), ); @@ -2717,7 +2823,7 @@ function system_update_7059(&$sandbox) { } $basename = variable_get('file_directory_path', conf_path() . '/files'); - $scheme = variable_get('file_default_scheme', 'public') . '://'; + $scheme = file_default_scheme() . '://'; foreach ($node_revisions as $vid => $revision) { // We will convert filepaths to uri using the default scheme // and stripping off the existing file directory path. @@ -2745,6 +2851,10 @@ function system_update_7059(&$sandbox) { // Update the node field with the file URI. $revision['file'][LANGUAGE_NONE][$delta] = $file; + + // Add the usage entry for the file. + $file = (object) $file; + file_usage_add($file, 'file', 'node', $revision['nid']); } // Insert the revision's files into the field_upload table. @@ -2767,6 +2877,15 @@ function system_update_7059(&$sandbox) { } } +/** + * Replace 'system_list' index with 'bootstrap' index on {system}. + */ +function system_update_7061() { + db_drop_index('system', 'bootstrap'); + db_drop_index('system', 'system_list'); + db_add_index('system', 'system_list', array('status', 'bootstrap', 'type', 'weight', 'name')); +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/system/system.module b/modules/system/system.module index c963cb4f8b8bab8de1098bd64087351073bc4a2f..ce489a5cf2e353c005bf2d169d5afdf7e7001acd 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -1,5 +1,5 @@ <?php -// $Id: system.module,v 1.946 2010/07/07 17:56:42 webchick Exp $ +// $Id: system.module,v 1.960 2010/09/11 06:03:12 webchick Exp $ /** * @file @@ -80,7 +80,7 @@ function system_help($path, $arg) { $output .= '<dt>' . t('Managing caching') . '</dt>'; $output .= '<dd>' . t("The System module allows users with the appropriate permissions to manage caching on the <a href='@cache-settings'>Performance settings page</a>. Drupal has a robust caching system that allows the efficient re-use of previously-constructed web pages and web page components. Pages requested by anonymous users are stored in a compressed format; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, the caching system may significantly increase the speed of your site.", array('@cache-settings' => url('admin/config/development/performance'))) . '</dd>'; $output .= '<dt>' . t('Performing system maintenance') . '</dt>'; - $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis. The System module manages this task by making use of a system cron job. You can verify the status of cron tasks by visiting the <a href="@status">Status report page</a>. For more information, see the online handbook entry for <a href="@handbook">configuring cron jobs</a>.', array('@status' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron')) . '</dd>'; + $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis. The System module manages this task by making use of a system cron job. You can verify the status of cron tasks by visiting the <a href="@status">Status report page</a>. For more information, see the online handbook entry for <a href="@handbook">configuring cron jobs</a>. You can set up cron job by visiting <a href="@cron">Cron configuration</a> page', array('@status' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron', '@cron' => url('admin/config/system/cron'))) . '</dd>'; $output .= '<dt>' . t('Configuring basic site settings') . '</dt>'; $output .= '<dd>' . t('The System module also handles basic configuration options for your site, including <a href="@date-time-settings">Date and time settings</a>, <a href="@file-system">File system settings</a>, <a href="@clean-url">Clean URL support</a>, <a href="@site-info">Site name and other information</a>, and a <a href="@maintenance-mode">Maintenance mode</a> for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@clean-url' => url('admin/config/search/clean-urls'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '</dd>'; $output .= '<dt>' . t('Configuring actions') . '</dt>'; @@ -264,6 +264,7 @@ function system_entity_info() { 'base table' => 'file_managed', 'entity keys' => array( 'id' => 'fid', + 'label' => 'filename', ), 'static cache' => FALSE, ), @@ -543,13 +544,14 @@ function system_menu() { 'access arguments' => array('access administration pages'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'system.admin.inc', + 'weight' => -20, ); $items['admin/by-module'] = array( 'title' => 'By module', 'page callback' => 'system_admin_by_module', 'access arguments' => array('access administration pages'), 'type' => MENU_LOCAL_TASK, - 'weight' => 2, + 'weight' => -18, 'file' => 'system.admin.inc', ); @@ -709,7 +711,7 @@ function system_menu() { 'weight' => -10, 'file' => 'system.admin.inc', ); - $items['admin/config/media/image-toolkit'] = array( + $items['admin/config/media/image-toolkit'] = array( 'title' => 'Image toolkit', 'description' => 'Choose which image toolkit to use if you have installed optional toolkits.', 'page callback' => 'drupal_get_form', @@ -957,14 +959,22 @@ function system_menu() { ); $items['admin/config/system/site-information'] = array( 'title' => 'Site information', - 'description' => 'Change site name, e-mail address, slogan, default front page, number of posts per page, error pages and cron.', + 'description' => t('Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('system_site_information_settings'), 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', 'weight' => -20, ); - + $items['admin/config/system/cron'] = array( + 'title' => t('Cron'), + 'description' => t('Manage automatic site maintenance tasks.'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_cron_settings'), + 'access arguments' => array('administer site configuration'), + 'file' => 'system.admin.inc', + 'weight' => 20, + ); // Additional categories $items['admin/config/user-interface'] = array( 'title' => 'User interface', @@ -1066,6 +1076,7 @@ function system_library() { 'js' => array( 'misc/jquery.js' => array('weight' => JS_LIBRARY - 20), ), + 'preprocess' => TRUE, ); // jQuery Once. @@ -1076,6 +1087,7 @@ function system_library() { 'js' => array( 'misc/jquery.once.js' => array('weight' => JS_LIBRARY - 19), ), + 'preprocess' => TRUE, ); // jQuery Form Plugin. @@ -1120,7 +1132,7 @@ function system_library() { 'misc/farbtastic/farbtastic.js' => array(), ), 'css' => array( - 'misc/farbtastic/farbtastic.css' => array('preprocess' => FALSE), + 'misc/farbtastic/farbtastic.css' => array(), ), ); @@ -1816,15 +1828,15 @@ function _system_filetransfer_backend_form_common() { * Implements hook_init(). */ function system_init() { + $path = drupal_get_path('module', 'system'); // Add the CSS for this module. if (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit' || arg(2) == 'delete'))) { - drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', array('weight' => CSS_SYSTEM)); + drupal_add_css($path . '/admin.css', array('weight' => CSS_SYSTEM)); } - drupal_add_css(drupal_get_path('module', 'system') . '/system.css', array('weight' => CSS_SYSTEM)); - drupal_add_css(drupal_get_path('module', 'system') . '/system-behavior.css', array('weight' => CSS_SYSTEM)); - drupal_add_css(drupal_get_path('module', 'system') . '/system-menus.css', array('weight' => CSS_SYSTEM)); - drupal_add_css(drupal_get_path('module', 'system') . '/system-messages.css', array('weight' => CSS_SYSTEM)); - + drupal_add_css($path . '/system.css', array('weight' => CSS_SYSTEM, 'preprocess' => TRUE)); + drupal_add_css($path . '/system-behavior.css', array('weight' => CSS_SYSTEM, 'preprocess' => TRUE)); + drupal_add_css($path . '/system-menus.css', array('weight' => CSS_SYSTEM, 'preprocess' => TRUE)); + drupal_add_css($path . '/system-messages.css', array('weight' => CSS_SYSTEM, 'preprocess' => TRUE)); // Ignore slave database servers for this request. // @@ -1847,6 +1859,29 @@ function system_init() { unset($_SESSION['ignore_slave_server']); } } + + // Add CSS/JS files from module .info files. + system_add_module_assets(); +} + +/** + * Adds CSS and JavaScript files declared in module .info files. + */ +function system_add_module_assets() { + foreach (system_get_info('module') as $module => $info) { + if (!empty($info['stylesheets'])) { + foreach ($info['stylesheets'] as $media => $stylesheets) { + foreach ($stylesheets as $stylesheet) { + drupal_add_css($stylesheet, array('media' => $media, 'preprocess' => TRUE)); + } + } + } + if (!empty($info['scripts'])) { + foreach ($info['scripts'] as $script) { + drupal_add_js($script, array('preprocess' => TRUE)); + } + } + } } /** @@ -1978,6 +2013,16 @@ function system_block_view($delta = '') { } } +/** + * Implements hook_preprocess_block(). + */ +function system_preprocess_block(&$variables) { + // System menu blocks should get the same class as menu module blocks. + if ($variables['block']->module == 'system' && in_array($variables['block']->delta, array_keys(menu_list_system_menus()))) { + $variables['classes_array'][] = 'block-menu'; + } +} + /** * Provide a single block on the administration overview page. * @@ -2195,25 +2240,40 @@ function system_update_files_database(&$files, $type) { } /** - * Returns an array of information about active modules or themes. + * Returns an array of information about enabled modules or themes. * * This function returns the information from the {system} table corresponding * to the cached contents of the .info file for each active module or theme. * * @param $type * Either 'module' or 'theme'. + * @param $name + * (optional) The name of a module or theme whose information shall be + * returned. If omitted, all records for the provided $type will be returned. + * If $name does not exist in the provided $type or is not enabled, an empty + * array will be returned. * * @return - * An associative array of module or theme information keyed by name. + * An associative array of module or theme information keyed by name, or only + * information for $name, if given. If no records are available, an empty + * array is returned. * * @see system_rebuild_module_data() * @see system_rebuild_theme_data() */ -function system_get_info($type) { +function system_get_info($type, $name = NULL) { $info = array(); - $result = db_query('SELECT name, info FROM {system} WHERE type = :type AND status = 1', array(':type' => $type)); - foreach ($result as $item) { - $info[$item->name] = unserialize($item->info); + if ($type == 'module') { + $type = 'module_enabled'; + } + $list = system_list($type); + foreach ($list as $shortname => $item) { + if (!empty($item->status)) { + $info[$shortname] = $item->info; + } + } + if (isset($name)) { + return isset($info[$name]) ? $info[$name] : array(); } return $info; } @@ -2268,6 +2328,15 @@ function _system_rebuild_module_data() { // Merge in defaults and save. $modules[$key]->info = $module->info + $defaults; + // Prefix stylesheets and scripts with module path. + $path = dirname($module->uri); + if (isset($module->info['stylesheets'])) { + $module->info['stylesheets'] = _system_info_add_path($module->info['stylesheets'], $path); + } + if (isset($module->info['scripts'])) { + $module->info['scripts'] = _system_info_add_path($module->info['scripts'], $path); + } + // Install profiles are hidden by default, unless explicitly specified // otherwise in the .info file. if ($key == $profile && !isset($modules[$key]->info['hidden'])) { @@ -2350,7 +2419,7 @@ function _system_rebuild_theme_data() { 'content' => 'Content', 'header' => 'Header', 'footer' => 'Footer', - 'highlight' => 'Highlighted content', + 'highlighted' => 'Highlighted', 'help' => 'Help', 'page_top' => 'Page top', 'page_bottom' => 'Page bottom', @@ -2359,6 +2428,8 @@ function _system_rebuild_theme_data() { 'features' => _system_default_theme_features(), 'screenshot' => 'screenshot.png', 'php' => DRUPAL_MINIMUM_PHP, + 'stylesheets' => array(), + 'scripts' => array(), ); $sub_themes = array(); @@ -2391,28 +2462,14 @@ function _system_rebuild_theme_data() { } } - // Give the stylesheets proper path information. - $pathed_stylesheets = array(); - if (isset($themes[$key]->info['stylesheets'])) { - foreach ($themes[$key]->info['stylesheets'] as $media => $stylesheets) { - foreach ($stylesheets as $stylesheet) { - $pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->uri) . '/' . $stylesheet; - } - } - } - $themes[$key]->info['stylesheets'] = $pathed_stylesheets; + // Prefix stylesheets and scripts with module path. + $path = dirname($theme->uri); + $theme->info['stylesheets'] = _system_info_add_path($theme->info['stylesheets'], $path); + $theme->info['scripts'] = _system_info_add_path($theme->info['scripts'], $path); - // Give the scripts proper path information. - $scripts = array(); - if (isset($themes[$key]->info['scripts'])) { - foreach ($themes[$key]->info['scripts'] as $script) { - $scripts[$script] = dirname($themes[$key]->uri) . '/' . $script; - } - } - $themes[$key]->info['scripts'] = $scripts; // Give the screenshot proper path information. if (!empty($themes[$key]->info['screenshot'])) { - $themes[$key]->info['screenshot'] = dirname($themes[$key]->uri) . '/' . $themes[$key]->info['screenshot']; + $themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot']; } } @@ -2459,6 +2516,41 @@ function system_rebuild_theme_data() { return $themes; } +/** + * Prefixes all values in an .info file array with a given path. + * + * This helper function is mainly used to prefix all array values of an .info + * file property with a single given path (to the module or theme); e.g., to + * prefix all values of the 'stylesheets' or 'scripts' properties with the file + * path to the defining module/theme. + * + * @param $info + * A nested array of data of an .info file to be processed. + * @param $path + * A file path to prepend to each value in $info. + * + * @return + * The $info array with prefixed values. + * + * @see _system_rebuild_module_data() + * @see _system_rebuild_theme_data() + */ +function _system_info_add_path($info, $path) { + foreach ($info as $key => $value) { + // Recurse into nested values until we reach the deepest level. + if (is_array($value)) { + $info[$key] = _system_info_add_path($info[$key], $path); + } + // Unset the original value's key and set the new value with prefix, using + // the original value as key, so original values can still be looked up. + else { + unset($info[$key]); + $info[$value] = $path . '/' . $value; + } + } + return $info; +} + /** * Returns an array of default theme features. */ @@ -2618,9 +2710,6 @@ function system_settings_form_submit($form, &$form_state) { } drupal_set_message(t('The configuration options have been saved.')); - - cache_clear_all(); - drupal_theme_rebuild(); } /** @@ -2653,7 +2742,9 @@ function _system_sort_requirements($a, $b) { * a function like check_plain() or filter_xss(). * * @param $form - * Additional elements to add to the form; for example, hidden elements. + * Additional elements to add to the form. These can be regular form elements, + * #value elements, etc., and their values will be available to the submit + * handler. * @param $question * The question to ask the user (e.g. "Are you sure you want to delete the * block <em>foo</em>?"). The page title will be set to this value. @@ -2792,15 +2883,20 @@ function system_cron() { // Remove temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE. // Use separate placeholders for the status to avoid a bug in some versions // of PHP. See http://drupal.org/node/352956. - $result = db_query('SELECT fid FROM {file_managed} WHERE status & :permanent1 <> :permanent2 AND timestamp < :timestamp', array( - ':permanent1' => FILE_STATUS_PERMANENT, - ':permanent2' => FILE_STATUS_PERMANENT, + $result = db_query('SELECT fid FROM {file_managed} WHERE status <> :permanent AND timestamp < :timestamp', array( + ':permanent' => FILE_STATUS_PERMANENT, ':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE )); foreach ($result as $row) { if ($file = file_load($row->fid)) { - if (!file_delete($file)) { - watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->uri), WATCHDOG_ERROR); + $references = file_usage_list($file); + if (empty($references)) { + if (!file_delete($file)) { + watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->uri), WATCHDOG_ERROR); + } + } + else { + watchdog('file system', 'Did not delete temporary file "%path" during garbage collection, because it is in use by the following modules: %modules.', array('%path' => $file->uri, '%modules' => implode(', ', array_keys($references))), WATCHDOG_INFO); } } } @@ -3042,7 +3138,7 @@ function system_message_action(&$entity, $context = array()) { $context['node'] = $entity; } - $context['message'] = token_replace($context['message'], $context); + $context['message'] = token_replace(filter_xss_admin($context['message']), $context); drupal_set_message($context['message']); } diff --git a/modules/system/system.tar.inc b/modules/system/system.tar.inc index dbf933c76faa1a27fc5382a462c2412f1550d3df..fd1d3c8256a629df40df89a239e2eafc906cf389 100644 --- a/modules/system/system.tar.inc +++ b/modules/system/system.tar.inc @@ -36,10 +36,13 @@ * @author Vincent Blavet <vincent@phpconcept.net> * @copyright 1997-2008 The Authors * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: system.tar.inc,v 1.5 2010/02/09 12:29:39 dries Exp $ + * @version CVS: Id: Tar.php,v 1.43 2008/10/30 17:58:42 dufuz Exp * @link http://pear.php.net/package/Archive_Tar */ +//require_once 'PEAR.php'; +// +// define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001); define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); @@ -47,11 +50,11 @@ define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); * Creates a (compressed) Tar archive * * @author Vincent Blavet <vincent@phpconcept.net> -* @version $Revision: 1.5 $ +* @version Revision: 1.43 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License * @package Archive_Tar */ -class Archive_Tar +class Archive_Tar // extends PEAR { /** * @var string Name of the Tar @@ -98,8 +101,10 @@ class Archive_Tar * boolean value 'true' means 'gz'. * @access public */ +// function Archive_Tar($p_tarname, $p_compress = null) function __construct($p_tarname, $p_compress = null) { +// $this->PEAR(); $this->_compress = false; $this->_compress_type = 'none'; if (($p_compress === null) || ($p_compress == '')) { @@ -150,11 +155,12 @@ class Archive_Tar $extname = 'bz2'; if (!extension_loaded($extname)) { +// PEAR::loadExtension($extname); $this->loadExtension($extname); } if (!extension_loaded($extname)) { die("The extension '$extname' couldn't be found.\n". - "Make sure your version of PHP was built ". + "Please make sure your version of PHP was built ". "with '$extname' support.\n"); return false; } @@ -165,6 +171,7 @@ class Archive_Tar /** * OS independant PHP extension load. Remember to take care * on the correct extension name for case sensitive OSes. + * The function is the copy of PEAR::loadExtension(). * * @param string $ext The extension name * @return bool Success or not on the dl() call @@ -197,12 +204,14 @@ class Archive_Tar // {{{ destructor +// function _Archive_Tar() function __destruct() { $this->_close(); // ----- Look for a local copy to delete if ($this->_temp_tarname != '') - @unlink($this->_temp_tarname); + @drupal_unlink($this->_temp_tarname); +// $this->_PEAR(); } // }}} @@ -622,6 +631,7 @@ class Archive_Tar function _error($p_message) { // ----- To be completed +// $this->raiseError($p_message); throw new Exception($p_message); } // }}} @@ -630,6 +640,7 @@ class Archive_Tar function _warning($p_message) { // ----- To be completed +// $this->raiseError($p_message); throw new Exception($p_message); } // }}} @@ -766,7 +777,7 @@ class Archive_Tar // ----- Look if a local copy need to be erase // Note that it might be interesting to keep the url for a time : ToDo if ($this->_temp_tarname != '') { - @unlink($this->_temp_tarname); + @drupal_unlink($this->_temp_tarname); $this->_temp_tarname = ''; } @@ -782,11 +793,11 @@ class Archive_Tar // ----- Look for a local copy if ($this->_temp_tarname != '') { // ----- Remove the local copy but not the remote tarname - @unlink($this->_temp_tarname); + @drupal_unlink($this->_temp_tarname); $this->_temp_tarname = ''; } else { // ----- Remove the local tarname file - @unlink($this->_tarname); + @drupal_unlink($this->_tarname); } $this->_tarname = ''; @@ -962,7 +973,7 @@ class Archive_Tar } // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false); + $p_filename = $this->_translateWinPath($p_filename, false);; $v_stored_filename = $p_filename; if (strcmp($p_filename, $p_remove_dir) == 0) { return true; @@ -1025,7 +1036,7 @@ class Archive_Tar } // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false); + $p_filename = $this->_translateWinPath($p_filename, false);; if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), time(), 384, "", 0, 0)) @@ -1582,6 +1593,8 @@ class Archive_Tar if ($v_extract_file) { if ($v_header['typeflag'] == "5") { if (!@file_exists($v_header['filename'])) { + // Drupal integration. + // Changed the code to use drupal_mkdir() instead of mkdir(). if (!@drupal_mkdir($v_header['filename'], 0777)) { $this->_error('Unable to create directory {' .$v_header['filename'].'}'); @@ -1590,7 +1603,7 @@ class Archive_Tar } } elseif ($v_header['typeflag'] == "2") { if (@file_exists($v_header['filename'])) { - @unlink($v_header['filename']); + @drupal_unlink($v_header['filename']); } if (!@symlink($v_header['link'], $v_header['filename'])) { $this->_error('Unable to extract symbolic link {' @@ -1727,7 +1740,7 @@ class Archive_Tar @bzclose($v_temp_tar); } - if (!@unlink($this->_tarname.".tmp")) { + if (!@drupal_unlink($this->_tarname.".tmp")) { $this->_error('Error while deleting temporary file \'' .$this->_tarname.'.tmp\''); } @@ -1795,6 +1808,8 @@ class Archive_Tar (!$this->_dirCheck($p_parent_dir))) return false; + // Drupal integration. + // Changed the code to use drupal_mkdir() instead of mkdir(). if (!@drupal_mkdir($p_dir, 0777)) { $this->_error("Unable to create directory '$p_dir'"); return false; diff --git a/modules/system/system.test b/modules/system/system.test index 721edf4c9bdbc226cff9d5f817b16282c4c215b8..3cc68d67ed56bbafb4064ed81f27c056f131ec7d 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -1,5 +1,5 @@ <?php -// $Id: system.test,v 1.131 2010/07/07 08:05:01 webchick Exp $ +// $Id: system.test,v 1.142 2010/09/11 21:14:32 webchick Exp $ /** * Helper class for module test cases. @@ -24,7 +24,7 @@ class ModuleTestCase extends DrupalWebTestCase { * specified base table. Defaults to TRUE. */ function assertTableCount($base_table, $count = TRUE) { - $tables = db_find_tables($base_table . '%'); + $tables = db_find_tables(Database::getConnection()->prefixTables('{' . $base_table . '}') . '%'); if ($count) { return $this->assertTrue($tables, t('Tables matching "@base_table" found.', array('@base_table' => $base_table))); @@ -218,7 +218,7 @@ class ModuleDependencyTestCase extends ModuleTestCase { $edit = array(); $edit['modules[Core][translation][enable]'] = 'translation'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); - $this->assertText(t('Some required modules must be enabled'), t('Dependecy required.')); + $this->assertText(t('Some required modules must be enabled'), t('Dependency required.')); $this->assertModules(array('translation', 'locale'), FALSE); @@ -285,6 +285,39 @@ class ModuleDependencyTestCase extends ModuleTestCase { $this->assertModules(array('comment'), TRUE); } + + /** + * Tests re-enabling forum with taxonomy disabled. + */ + function testEnableForumTaxonomyFieldDependency() { + // Enable the forum module. + $edit = array(); + $edit['modules[Core][forum][enable]'] = 'forum'; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertModules(array('forum'), TRUE); + + // Disable the forum module. + $edit = array(); + $edit['modules[Core][forum][enable]'] = FALSE; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertModules(array('forum'), FALSE); + + // Disable the taxonomy module. + $edit = array(); + $edit['modules[Core][taxonomy][enable]'] = FALSE; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertModules(array('taxonomy'), FALSE); + + // Attempt to re-enable the forum module with taxonomy disabled and ensure + // forum does not try to recreate the taxonomy_forums field. + $edit = array(); + $edit['modules[Core][forum][enable]'] = 'forum'; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertText(t('Some required modules must be enabled'), t('Dependency required.')); + $this->drupalPost(NULL, NULL, t('Continue')); + $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + $this->assertModules(array('taxonomy', 'forum'), TRUE); + } } /** @@ -520,7 +553,7 @@ class CronRunTestCase extends DrupalWebTestCase { // Disable the cron threshold through the interface. $admin_user = $this->drupalCreateUser(array('administer site configuration')); $this->drupalLogin($admin_user); - $this->drupalPost('admin/config/system/site-information', array('cron_safe_threshold' => 0), t('Save configuration')); + $this->drupalPost('admin/config/system/cron', array('cron_safe_threshold' => 0), t('Save configuration')); $this->assertText(t('The configuration options have been saved.')); $this->drupalLogout(); @@ -663,7 +696,7 @@ class AccessDeniedTestCase extends DrupalWebTestCase { // Log back in, set the custom 403 page to /user and remove the block $this->drupalLogin($this->admin_user); variable_set('site_403', 'user'); - $this->drupalPost('admin/structure/block', array('user_login[region]' => '-1'), t('Save blocks')); + $this->drupalPost('admin/structure/block', array('blocks[user_login][region]' => '-1'), t('Save blocks')); // Check that we can log in from the 403 page. $this->drupalLogout(); @@ -1045,7 +1078,6 @@ class PageTitleFiltering extends DrupalWebTestCase { $this->assertRaw($title_filtered . '</title>', 'Check for the filtered version of the title.'); // Test the slogan. - // Currently Garland is not displaying the slogan so this test is escaped. $this->assertNoRaw($slogan, 'Check for the unfiltered version of the slogan.'); $this->assertRaw($slogan_filtered, 'Check for the filtered version of the slogan.'); } @@ -1133,7 +1165,7 @@ class SystemBlockTestCase extends DrupalWebTestCase { // Set the powered-by block to the footer region. $edit = array(); - $edit['system_powered-by[region]'] = 'footer'; + $edit['blocks[system_powered-by][region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to footer region.')); @@ -1143,15 +1175,15 @@ class SystemBlockTestCase extends DrupalWebTestCase { // Set the block to the disabled region. $edit = array(); - $edit['system_powered-by[region]'] = '-1'; + $edit['blocks[system_powered-by][region]'] = '-1'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block is hidden. $this->assertNoRaw('id="block-system-powered-by"', t('Block no longer appears on page.')); - // For convenience of developers, set the block to it's default settings. + // For convenience of developers, set the block to its default settings. $edit = array(); - $edit['system_powered-by[region]'] = 'footer'; + $edit['blocks[system_powered-by][region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->drupalPost('admin/structure/block/manage/system/powered-by/configure', array('title' => ''), t('Save block')); } @@ -1267,13 +1299,14 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase { function testThemeSettings() { // Specify a filesystem path to be used for the logo. $file = current($this->drupalGetTestFiles('image')); + $fullpath = drupal_realpath($file->uri); $edit = array( 'default_logo' => FALSE, - 'logo_path' => $file->uri, + 'logo_path' => $fullpath, ); $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration')); $this->drupalGet('node'); - $this->assertRaw($file->uri, t('Logo path successfully changed.')); + $this->assertRaw($fullpath, t('Logo path successfully changed.')); // Upload a file to use for the logo. $file = current($this->drupalGetTestFiles('image')); @@ -1296,22 +1329,22 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase { variable_set('theme_default', 'stark'); // Enable an administration theme and show it on the node admin pages. $edit = array( - 'admin_theme' => 'garland', + 'admin_theme' => 'seven', 'node_admin_theme' => TRUE, ); $this->drupalPost('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('admin/config'); - $this->assertRaw('themes/garland', t('Administration theme used on an administration page.')); + $this->assertRaw('themes/seven', t('Administration theme used on an administration page.')); $this->drupalGet('node/' . $this->node->nid); $this->assertRaw('themes/stark', t('Site default theme used on node page.')); $this->drupalGet('node/add'); - $this->assertRaw('themes/garland', t('Administration theme used on the add content page.')); + $this->assertRaw('themes/seven', t('Administration theme used on the add content page.')); $this->drupalGet('node/' . $this->node->nid . '/edit'); - $this->assertRaw('themes/garland', t('Administration theme used on the edit content page.')); + $this->assertRaw('themes/seven', t('Administration theme used on the edit content page.')); // Disable the admin theme on the node admin pages. $edit = array( @@ -1320,13 +1353,13 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('admin/config'); - $this->assertRaw('themes/garland', t('Administration theme used on an administration page.')); + $this->assertRaw('themes/seven', t('Administration theme used on an administration page.')); $this->drupalGet('node/add'); $this->assertRaw('themes/stark', t('Site default theme used on the add content page.')); // Reset to the default theme settings. - variable_set('theme_default', 'garland'); + variable_set('theme_default', 'bartik'); $edit = array( 'admin_theme' => '0', 'node_admin_theme' => FALSE, @@ -1334,10 +1367,10 @@ class SystemThemeFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('admin'); - $this->assertRaw('themes/garland', t('Site default theme used on administration page.')); + $this->assertRaw('themes/bartik', t('Site default theme used on administration page.')); $this->drupalGet('node/add'); - $this->assertRaw('themes/garland', t('Site default theme used on the add content page.')); + $this->assertRaw('themes/bartik', t('Site default theme used on the add content page.')); } } @@ -1510,7 +1543,6 @@ class TokenReplaceTestCase extends DrupalWebTestCase { $tests = array(); $tests['[site:name]'] = check_plain(variable_get('site_name', 'Drupal')); $tests['[site:slogan]'] = check_plain(variable_get('site_slogan', '')); - $tests['[site:mission]'] = filter_xss(variable_get('site_mission', '')); $tests['[site:mail]'] = 'simpletest@example.com'; $tests['[site:url]'] = url('<front>', $url_options); $tests['[site:url-brief]'] = preg_replace('!^https?://!', '', url('<front>', $url_options)); @@ -1527,7 +1559,6 @@ class TokenReplaceTestCase extends DrupalWebTestCase { // Generate and test unsanitized tokens. $tests['[site:name]'] = variable_get('site_name', 'Drupal'); $tests['[site:slogan]'] = variable_get('site_slogan', ''); - $tests['[site:mission]'] = variable_get('site_mission', ''); foreach ($tests as $input => $expected) { $output = token_replace($input, array(), array('language' => $language, 'sanitize' => FALSE)); @@ -1686,31 +1717,6 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase { $this->assertResponse(200); } - /** - * Tests the detection of requirements for the update script to proceed. - */ - function testUpdateRequirements() { - $this->drupalLogin($this->update_user); - $this->drupalGet($this->update_url, array('external' => TRUE)); - $this->assertResponse(200); - // Test if disabling a module that another enabled module depends on will - // prevent the update from proceeding. - module_disable(array('block'), FALSE); - $this->assertFalse(module_exists('block'), t('Block module is disabled.')); - $this->assertTrue(module_exists('dashboard'), t('Dashboard module is enabled.')); - $this->drupalGet($this->update_url, array('external' => TRUE)); - $this->assertText(t('Unresolved dependency'), t('The update process cannot proceed when a module dependency is not enabled.')); - - // Test if modules required by the current install profile are not required - // to be enabled for an update to proceed. - module_enable(array('block')); - $this->assertTrue(module_exists('block'), t('Block module is enabled.')); - module_disable(array('overlay')); - $this->assertFalse(module_exists('overlay'), t('Overlay module is disabled.')); - $this->drupalGet($this->update_url, array('external' => TRUE)); - $this->assertNoText(t('Unresolved dependency'), t('The update process can proceed when modules from the install profile are disabled.')); - } - /** * Tests the effect of using the update script on the theme system. */ diff --git a/modules/system/system.tokens.inc b/modules/system/system.tokens.inc index f07adcf86f3943795979c2491d7684cc9bce49f9..78d48e1b8c7d3fc6b0d6ebc2160689e9c3745326 100644 --- a/modules/system/system.tokens.inc +++ b/modules/system/system.tokens.inc @@ -1,5 +1,5 @@ <?php -// $Id: system.tokens.inc,v 1.10 2010/06/29 00:57:19 dries Exp $ +// $Id: system.tokens.inc,v 1.12 2010/08/17 21:31:13 dries Exp $ /** * @file @@ -36,10 +36,6 @@ function system_token_info() { 'name' => t("Slogan"), 'description' => t("The slogan of the site."), ); - $site['mission'] = array( - 'name' => t("Mission"), - 'description' => t("The optional 'mission' of the site."), - ); $site['mail'] = array( 'name' => t("Email"), 'description' => t("The administrative email address for the site."), @@ -160,11 +156,6 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a $replacements[$original] = $sanitize ? check_plain($slogan) : $slogan; break; - case 'mission': - $mission = variable_get('site_mission', ''); - $replacements[$original] = $sanitize ? filter_xss($mission) : $mission; - break; - case 'mail': $replacements[$original] = variable_get('site_mail', ''); break; @@ -211,7 +202,7 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a break; case 'raw': - $replacements[$original] = filter_xss($date); + $replacements[$original] = $sanitize ? check_plain($date) : $date; break; } } @@ -239,15 +230,15 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a break; case 'description': - $replacements[$original] = $sanitize ? filter_xss($file->description) : $file->description; + $replacements[$original] = $sanitize ? check_plain($file->description) : $file->description; break; case 'path': - $replacements[$original] = $sanitize ? filter_xss($file->uri) : $file->uri; + $replacements[$original] = $sanitize ? check_plain($file->uri) : $file->uri; break; case 'mime': - $replacements[$original] = $sanitize ? filter_xss($file->filemime) : $file->filemime; + $replacements[$original] = $sanitize ? check_plain($file->filemime) : $file->filemime; break; case 'size': @@ -255,7 +246,7 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a break; case 'url': - $replacements[$original] = url(file_create_url($file->uri), $url_options); + $replacements[$original] = $sanitize ? check_plain(file_create_url($file->uri)) : file_create_url($file->uri); break; // These tokens are default variations on the chained tokens handled below. @@ -265,7 +256,7 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a case 'owner': $account = user_load($file->uid); - $replacements[$original] = $sanitize ? filter_xss($account->name) : $account->name; + $replacements[$original] = $sanitize ? check_plain($account->name) : $account->name; break; } } diff --git a/modules/system/theme.api.php b/modules/system/theme.api.php index 9943e858b95e9c357502dee023474bc32ed20626..b687ddb2d831a45fa24dbfef9822bc67d1a4fe1d 100644 --- a/modules/system/theme.api.php +++ b/modules/system/theme.api.php @@ -1,5 +1,5 @@ <?php -// $Id: theme.api.php,v 1.3 2010/04/28 20:00:34 dries Exp $ +// $Id: theme.api.php,v 1.4 2010/08/08 19:35:49 dries Exp $ /** * @defgroup themeable Default theme implementations @@ -56,9 +56,9 @@ * themes utilizing an engine will have their well-named theming functions * automatically registered for them. While this can vary based upon the theme * engine, the standard set by phptemplate is that theme functions should be - * named THEMENAME_HOOK. For example, for Drupal's default theme (Garland) to + * named THEMENAME_HOOK. For example, for Drupal's default theme (Bartik) to * implement the 'table' hook, the phptemplate.engine would find - * garland_table(). + * bartik_table(). * * The theme system is described and defined in theme.inc. * diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc index 98d23c7ef7e96887a91f7dd25639d2919380709f..1193b69bb6d5aa8bf09cfbafd5b66b79dbb90525 100644 --- a/modules/taxonomy/taxonomy.admin.inc +++ b/modules/taxonomy/taxonomy.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.admin.inc,v 1.106 2010/06/17 13:44:45 dries Exp $ +// $Id: taxonomy.admin.inc,v 1.107 2010/07/31 13:01:50 webchick Exp $ /** * @file @@ -284,6 +284,7 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) { // An array of the terms to be displayed on this page. $current_page = array(); + $delta = 0; $term_deltas = array(); $tree = taxonomy_get_tree($vocabulary->vid); $term = current($tree); @@ -292,6 +293,7 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) { if (empty($term)) { break; } + $delta++; // Count entries before the current page. if ($page && ($page * $page_increment) > $before_entries && !isset($back_step)) { $before_entries++; @@ -392,6 +394,13 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) { // Same as above, the depth is modified by javascript, so it's a default_value. '#default_value' => $term->depth, ); + $form[$key]['weight'] = array( + '#type' => 'weight', + '#delta' => $delta, + '#title_display' => 'invisible', + '#title' => t('Weight for added term'), + '#default_value' => $term->weight, + ); } $form[$key]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'taxonomy/term/' . $term->tid . '/edit', '#options' => array('query' => drupal_get_destination())); } @@ -446,8 +455,8 @@ function taxonomy_overview_terms_submit($form, &$form_state) { return; } - $order = array_flip(array_keys($form_state['input'])); // Get the $_POST order. - $form_state['values'] = array_merge($order, $form_state['values']); // Update our original form with the new order. + // Sort term order based on weight. + uasort($form_state['values'], 'drupal_sort_weight'); $vocabulary = $form['#vocabulary']; $hierarchy = 0; // Update the current hierarchy type as we go. @@ -562,6 +571,7 @@ function theme_taxonomy_overview_terms($variables) { drupal_add_js(array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)), 'setting'); drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css'); } + drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'term-weight'); $errors = form_get_errors() != FALSE ? form_get_errors() : array(); $rows = array(); @@ -577,8 +587,9 @@ function theme_taxonomy_overview_terms($variables) { $term['depth']['#attributes']['class'] = array('term-depth'); $row[0] .= drupal_render($term['parent']) . drupal_render($term['tid']) . drupal_render($term['depth']); } + $term['weight']['#attributes']['class'] = array('term-weight'); + $row[] = drupal_render($term['weight']); $row[] = drupal_render($term['edit']); - $row = array('data' => $row); $rows[$key] = $row; } @@ -619,7 +630,7 @@ function theme_taxonomy_overview_terms($variables) { $rows[] = array(array('data' => $form['#empty_text'], 'colspan' => '2')); } - $header = array(t('Name'), t('Operations')); + $header = array(t('Name'), t('Weight'), t('Operations')); $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'taxonomy'))); $output .= drupal_render_children($form); $output .= theme('pager', array('tags' => NULL)); diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info index 2460d84b5614e0c7ee87b9e5070234892b158c0f..51a6625704509564d216317921ac3a58767f1038 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/taxonomy/taxonomy.install b/modules/taxonomy/taxonomy.install index 385f556582548411804fd566ab56a4ebd2b39ace..3be5829f7d901cc07ef9b718a021f164d73d3ad0 100644 --- a/modules/taxonomy/taxonomy.install +++ b/modules/taxonomy/taxonomy.install @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.install,v 1.43 2010/05/23 19:10:23 dries Exp $ +// $Id: taxonomy.install,v 1.46 2010/09/05 17:00:04 webchick Exp $ /** * @file @@ -66,7 +66,10 @@ function taxonomy_schema() { ), 'primary key' => array('tid'), 'foreign keys' => array( - 'vid' => array('taxonomy_vocabulary' => 'vid'), + 'vocabulary' => array( + 'table' => 'taxonomy_vocabulary', + 'columns' => array('vid' => 'vid'), + ), ), 'indexes' => array( 'taxonomy_tree' => array('vid', 'weight', 'name'), @@ -97,7 +100,10 @@ function taxonomy_schema() { 'parent' => array('parent'), ), 'foreign keys' => array( - 'tid' => array('taxonomy_term_data' => 'tid'), + 'taxonomy_term_data' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array('tid' => 'tid'), + ), ), 'primary key' => array('tid', 'parent'), ); @@ -201,14 +207,38 @@ function taxonomy_schema() { 'nid' => array('nid'), ), 'foreign keys' => array( - 'node' => 'nid', - 'taxonomy_term_data' => 'tid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'term' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array('tid' => 'tid'), + ), ), ); return $schema; } +/** + * Implements hook_field_schema(). + */ +function taxonomy_field_schema($field) { + return array( + 'columns' => array( + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + ), + ), + 'indexes' => array( + 'tid' => array('tid'), + ), + ); +} + /** * Implements hook_update_dependencies(). */ @@ -328,8 +358,14 @@ function taxonomy_update_7004() { 'nid' => array('nid'), ), 'foreign keys' => array( - 'node' => 'nid', - 'taxonomy_term_data' => 'tid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'term' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array('tid' => 'tid'), + ), ), ); db_create_table('taxonomy_index', $taxonomy_index); @@ -394,7 +430,57 @@ function taxonomy_update_7004() { field_create_instance($instance); } } - db_drop_table('taxonomy_vocabulary_node_type'); + + // Some contrib projects stored term node associations without regard for the + // selections in the taxonomy_vocabulary_node_types table, or have more terms + // for a single node than the vocabulary allowed. We construct the + // taxonomyextra field to store all the extra stuff. + + // Allowed values for this extra vocabs field is every vocabulary. + $allowed_values = array(); + foreach (taxonomy_get_vocabularies() as $vocabulary) { + $allowed_values[] = array( + 'vid' => $vocabulary->vid, + 'parent' => 0, + ); + } + + $field_name = 'taxonomyextra'; + $field = array( + 'field_name' => $field_name, + 'type' => 'taxonomy_term_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'settings' => array( + 'required' => FALSE, + 'allowed_values' => $allowed_values, + ), + ); + field_create_field($field); + + foreach (node_type_get_types() as $bundle) { + $instance = array( + 'label' => 'Taxonomy upgrade extras', + 'field_name' => $field_name, + 'bundle' => $bundle->type, + 'entity_type' => 'node', + 'description' => 'Debris left over after upgrade from Drupal 6', + 'widget' => array( + 'type' => 'taxonomy_autocomplete', + ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), + ); + field_create_instance($instance); + } + $fields = array('help', 'multiple', 'required', 'tags'); foreach ($fields as $field) { db_drop_field('taxonomy_vocabulary', $field); @@ -403,16 +489,46 @@ function taxonomy_update_7004() { /** * Migrate {taxonomy_term_node} table to field storage. + * + * @todo: This function can possibly be made much faster by wrapping a + * transaction around all the inserts. */ function taxonomy_update_7005(&$sandbox) { - // Since we are upgrading from Drupal 6, we know that only - // field_sql_storage.module will be enabled. - $field = field_info_field($field['field_name']); - $data_table = _field_sql_storage_tablename($field); - $revision_table = _field_sql_storage_revision_tablename($field); - $etid = _field_sql_storage_etid('node'); - $value_column = $field['field_name'] . '_value'; - $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta', $value_column); + // $sandbox contents: + // - total: The total number of term_node relationships to migrate. + // - count: The number of term_node relationships that have been + // migrated so far. + // - last: The db_query_range() offset to use when querying + // term_node; this field is incremented in quantities of $batch + // (1000) but at the end of each call to this function, last and + // count are the same. + // - vocabularies: An associative array mapping vocabulary id and node + // type to field name. If a voc id/node type pair does not appear + // in this array but a term_node relationship exists mapping a + // term in voc id to node of that type, the relationship is + // assigned to the taxonomymyextra field which allows terms of all + // vocabularies. + // - cursor[values], cursor[deltas]: The contents of $values and + // $deltas at the end of the previous call to this function. These + // need to be preserved across calls because a single batch of + // 1000 rows from term_node may end in the middle of the terms for + // a single node revision. + // + // $values is the array of values about to be/most recently inserted + // into the SQL data table for the taxonomy_term_reference + // field. Before $values is constructed for each record, the + // $values from the previous insert is checked to see if the two + // records are for the same node revision id; this enables knowing + // when to reset the delta counters which are incremented across all + // terms for a single field on a single revision, but reset for each + // new field and revision. + // + // $deltas is an associative array mapping field name to the number + // of term references stored so far for the current revision, which + // provides the delta value for each term reference data insert. The + // deltas are reset for each new revision. + + $field_info = field_info_fields(); // This is a multi-pass update. On the first call we need to initialize some // variables. @@ -422,47 +538,164 @@ function taxonomy_update_7005(&$sandbox) { $query = db_select('taxonomy_term_node', 't'); $sandbox['total'] = $query->countQuery()->execute()->fetchField(); - $found = (bool) $sandbox['total']; - } - else { - // We do each pass in batches of 1000, this should result in a - // maximum of 2000 insert queries each operation. - $batch = 1000 + $sandbox['last']; - // Query and save data for the current revision. - $result = db_query_range('SELECT td.tid, tn.nid, td.weight, tn.vid, n2.type, n2.created, n2.sticky FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n2 ON tn.nid = n2.nid INNER JOIN {node} n ON tn.vid = n.vid AND td.vid = :vocabulary_id ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid), $sandbox['last'], $batch); - $deltas = array(); + // Use an inline version of Drupal 6 taxonomy_get_vocabularies() here since + // we can no longer rely on $vocabulary->nodes from the API function. + $result = db_query('SELECT v.vid, v.machine_name, n.type FROM {taxonomy_vocabulary} v INNER JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid'); + $vocabularies = array(); foreach ($result as $record) { - $found = TRUE; - $sandbox['count'] += 1; - // Start deltas from 0, and increment by one for each - // term attached to a node. - $deltas[$record->nid] = isset($deltas[$record->nid]) ? ++$deltas[$record->nid] : 0; - $values = array($etid, $record->nid, $record->vid, $record->type, $deltas[$record->nid], $record->tid); - db_insert($data_table)->fields($columns)->values($values)->execute(); - - // Update the {taxonomy_index} table. - db_insert('taxonomy_index') - ->fields(array('nid', 'tid', 'sticky', 'created',)) - ->values(array($record->nid, $record->tid, $record->sticky, $record->created)) - ->execute(); + + // If no node types are associated with a vocabulary, the LEFT JOIN will + // return a NULL value for type. + if (isset($record->type)) { + $vocabularies[$record->vid][$record->type] = 'taxonomy_'. $record->machine_name; + } + } + + if (!empty($vocabularies)) { + $sandbox['vocabularies'] = $vocabularies; } + } + else { + $etid = _field_sql_storage_etid('node'); + + // We do each pass in batches of 1000. + $batch = 1000; - // Query and save data for all revisions. - $result = db_query('SELECT td.tid, tn.nid, td.weight, tn.vid, n.type FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid AND td.vid = :vocabulary_id INNER JOIN {node} n ON tn.nid = n.nid ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid), $sandbox['last'][$batch]); - $deltas = array(); + // Query selects all revisions at once and processes them in revision and + // term weight order. Join types: + // + // - INNER JOIN term_node ON tn.tid: We are migrating term-node + // relationships. If there are none for a term, we do not need the + // term_data row. + // - INNER JOIN {node} n ON n.nid: If a term-node relationship exists for a + // nid that does not exist, we cannot migrate it as we have no node to + // relate it to; thus we do not need that row from term_node. + // - LEFT JOIN {node} n2 ON n2.vid: If the current term-node relationship + // is for the current revision of the node, this left join will match and + // is_current will be non-NULL (we also get the current sticky and + // created in this case). This tells us whether to insert into the + // current data tables in addition to the revision data tables. + // + // This query must return a consistent ordering across multiple calls. We + // need them ordered by node vid (since we use that to decide when to reset + // the delta counters) and by term weight so they appear within each node + // in weight order. However, tn.vid,td.weight is not guaranteed to be + // unique, so we add tn.tid as an additional sort key because tn.tid,tn.vid + // is the primary key of the D6 term_node table and so is guaranteed + // unique. Unfortunately it also happens to be in the wrong order which is + // less efficient, but c'est la vie. + $query = 'SELECT td.vid AS vocab_id, td.tid, tn.nid, tn.vid, n.type, n2.created, n2.sticky, n2.nid AS is_current FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n ON tn.nid = n.nid LEFT JOIN {node} n2 ON tn.vid = n2.vid ORDER BY tn.vid, td.weight ASC, tn.tid'; + $result = db_query_range($query, $sandbox['last'], $batch); + if (isset($sandbox['cursor'])) { + $values = $sandbox['cursor']['values']; + $deltas = $sandbox['cursor']['deltas']; + } + else { + $deltas = array(); + } foreach ($result as $record) { - $found = TRUE; $sandbox['count'] += 1; - // Start deltas at 0, and increment by one for each term attached to a revision. - $deltas[$record->vid] = isset($deltas[$record->vid]) ? ++$deltas[$record->vid] : 0; - $values = array($etid, $record->nid, $record->vid, $record->type, $deltas[$record->vid], $record->tid); - db_insert($revision_table)->fields($columns)->values($values)->execute(); + + // Use the valid field for this vocabulary and node type or use the + // overflow vocabulary if there is no valid field. + $field_name = isset($sandbox['vocabularies'][$record->vocab_id][$record->type]) ? $sandbox['vocabularies'][$record->vocab_id][$record->type] : 'taxonomyextra'; + $field = $field_info[$field_name]; + + // Start deltas from 0, and increment by one for each term attached to a + // node. + if (!isset($deltas[$field_name])) { + $deltas[$field_name] = 0; + } + + if (isset($values)) { + + // If the last inserted revision_id is the same as the current record, + // use the previous deltas to calculate the next delta. + if ($record->vid == $values[2]) { + + // see field_default_validate(). + if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ($deltas[$field_name] + 1) >= $field['cardinality']) { + + // For excess values of a single-term vocabulary, switch over to + // the overflow field. + $field_name = 'taxonomyextra'; + $field = $field_info[$field_name]; + if (!isset($deltas[$field_name])) { + $deltas[$field_name] = 0; + } + } + } + else { + + // When the record is a new revision, empty the deltas array. + $deltas = array($field_name => 0); + } + } + + // Table and column found in the field's storage details. During upgrades, + // it's always SQL. + $table = key($field['storage']['details']['sql'][FIELD_LOAD_REVISION]); + $value_column = $field['storage']['details']['sql'][FIELD_LOAD_REVISION][$table]['tid']; + + // Column names and values in field storage are the same for current and + // revision. + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'language', 'delta', $value_column); + $values = array($etid, $record->nid, $record->vid, $record->type, LANGUAGE_NONE, $deltas[$field_name]++, $record->tid); + + // Insert rows into the revision table. + db_insert($table)->fields($columns)->values($values)->execute(); + + // is_current column is a node ID if this revision is also current. + if ($record->is_current) { + $table = key($field['storage']['details']['sql'][FIELD_LOAD_CURRENT]); + db_insert($table)->fields($columns)->values($values)->execute(); + + // Update the {taxonomy_index} table. + db_insert('taxonomy_index') + ->fields(array('nid', 'tid', 'sticky', 'created',)) + ->values(array($record->nid, $record->tid, $record->sticky, $record->created)) + ->execute(); + } } - $sandbox['last'] = $batch; + + // Store the set of inserted values and the current revision's deltas in the + // sandbox. + $sandbox['cursor'] = array( + 'values' => $values, + 'deltas' => $deltas, + ); + $sandbox['last'] += $batch; } - if (!$found) { - db_drop_table('taxonomy_term_node'); + + if ($sandbox['count'] < $sandbox['total']) { + $sandbox['#finished'] = FALSE; + } + else { + db_drop_table('taxonomy_vocabulary_node_type'); + db_drop_table('taxonomy_term_node'); + + // If there are no vocabs, we're done. + $sandbox['#finished'] = TRUE; + + // Determine necessity of taxonomyextras field. + $field = $field_info['taxonomyextra']; + $table = key($field['storage']['details']['sql'][FIELD_LOAD_REVISION]); + $node_types = db_select($table)->distinct()->fields($table, array('bundle')) + ->execute()->fetchCol(); + + if (empty($node_types)) { + // Delete the overflow field if there are no rows in the revision table. + field_delete_field('taxonomyextra'); + } + else { + // Remove instances which are not actually used. + $bundles = array_diff($field['bundles']['node'], $node_types); + foreach ($bundles as $bundle) { + $instance = field_info_instance('node', 'taxonomyextra', $bundle); + field_delete_instance($instance); + } + } } } diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 7a2123413f83bb0e78338450c0dc11d820aff87a..d220848fd1742f8ac521e3c62938d95f270c34c0 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -1,11 +1,19 @@ <?php -// $Id: taxonomy.module,v 1.597 2010/06/27 16:49:58 dries Exp $ +// $Id: taxonomy.module,v 1.605 2010/09/11 06:03:12 webchick Exp $ /** * @file * Enables the organization of content into categories. */ +/** + * Users can create new terms in a free-tagging vocabulary when + * submitting a taxonomy_autocomplete_widget. We store a term object + * whose tid is 'autocreate' as a field data item during widget + * validation and then actually create the term if/when that field + * data item makes it to taxonomy_field_insert/update(). + */ + /** * Implements hook_help(). */ @@ -92,6 +100,7 @@ function taxonomy_entity_info() { 'entity keys' => array( 'id' => 'tid', 'bundle' => 'vocabulary_machine_name', + 'label' => 'name', ), 'bundle keys' => array( 'bundle' => 'machine_name', @@ -123,6 +132,7 @@ function taxonomy_entity_info() { 'base table' => 'taxonomy_vocabulary', 'entity keys' => array( 'id' => 'vid', + 'label' => 'name', ), 'fieldable' => FALSE, ); @@ -485,25 +495,24 @@ function taxonomy_term_save($term) { module_invoke_all('taxonomy_term_presave', $term); if (empty($term->tid)) { + $op = 'insert'; $status = drupal_write_record('taxonomy_term_data', $term); field_attach_insert('taxonomy_term', $term); - module_invoke_all('taxonomy_term_insert', $term); - module_invoke_all('entity_insert', $term, 'taxonomy_term'); if (!isset($term->parent)) { $term->parent = array(0); } } else { + $op = 'update'; $status = drupal_write_record('taxonomy_term_data', $term, 'tid'); field_attach_update('taxonomy_term', $term); - module_invoke_all('taxonomy_term_update', $term); - module_invoke_all('entity_update', $term, 'taxonomy_term'); if (isset($term->parent)) { db_delete('taxonomy_term_hierarchy') ->condition('tid', $term->tid) ->execute(); } } + if (isset($term->parent)) { if (!is_array($term->parent)) { $term->parent = array($term->parent); @@ -528,8 +537,14 @@ function taxonomy_term_save($term) { } $query->execute(); } + + // Reset the taxonomy term static variables. taxonomy_terms_static_reset(); + // Invoke the taxonomy hooks. + module_invoke_all("taxonomy_term_$op", $term); + module_invoke_all("entity_$op", $term, 'taxonomy_term'); + return $status; } @@ -557,25 +572,24 @@ function taxonomy_term_delete($tid) { } } - $term = taxonomy_term_load($tid); - - db_delete('taxonomy_term_data') - ->condition('tid', $tid) - ->execute(); - db_delete('taxonomy_term_hierarchy') - ->condition('tid', $tid) - ->execute(); - - field_attach_delete('taxonomy_term', $term); - module_invoke_all('taxonomy_term_delete', $term); + if ($term = taxonomy_term_load($tid)) { + db_delete('taxonomy_term_data') + ->condition('tid', $tid) + ->execute(); + db_delete('taxonomy_term_hierarchy') + ->condition('tid', $tid) + ->execute(); + + field_attach_delete('taxonomy_term', $term); + module_invoke_all('taxonomy_term_delete', $term); + taxonomy_terms_static_reset(); + } } $tids = $orphans; } cache_clear_all(); - taxonomy_terms_static_reset(); - return SAVED_DELETED; } @@ -625,7 +639,7 @@ function template_preprocess_taxonomy_term(&$variables) { $uri = entity_uri('taxonomy_term', $term); $variables['term_url'] = url($uri['path'], $uri['options']); $variables['term_name'] = check_plain($term->name); - $variables['page'] = taxonomy_term_is_page($term); + $variables['page'] = $variables['view_mode'] == 'full' && taxonomy_term_is_page($term); // Flatten the term object's member fields. $variables = array_merge((array) $term, $variables); @@ -641,14 +655,12 @@ function template_preprocess_taxonomy_term(&$variables) { // languages. field_attach_preprocess('taxonomy_term', $term, $variables['content'], $variables); + // Gather classes, and clean up name so there are no underscores. $vocabulary_name_css = str_replace('_', '-', $term->vocabulary_machine_name); - - // Gather classes. $variables['classes_array'][] = 'vocabulary-' . $vocabulary_name_css; - // Clean up name so there are no underscores. - $variables['theme_hook_suggestions'][] = 'taxonomy-term__' . $vocabulary_name_css; - $variables['theme_hook_suggestions'][] = 'taxonomy-term__' . $term->tid; + $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->vocabulary_machine_name; + $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->tid; } /** @@ -668,7 +680,10 @@ function taxonomy_term_is_page($term) { function taxonomy_terms_static_reset() { drupal_static_reset('taxonomy_term_count_nodes'); drupal_static_reset('taxonomy_get_tree'); + drupal_static_reset('taxonomy_get_tree:parents'); + drupal_static_reset('taxonomy_get_tree:terms'); drupal_static_reset('taxonomy_get_parents'); + drupal_static_reset('taxonomy_get_parents_all'); drupal_static_reset('taxonomy_get_children'); entity_get_controller('taxonomy_term')->resetCache(); } @@ -695,35 +710,30 @@ function taxonomy_vocabulary_get_names() { } /** - * Find all parents of a given term ID. + * Finds all parents of a given term ID. + * + * @param $tid + * A taxonomy term ID. + * + * @return + * An array of term objects which are the parents of the term $tid. */ -function taxonomy_get_parents($tid, $key = 'tid') { - if ($tid) { - $tids = &drupal_static(__FUNCTION__, array()); - if (isset($tids[$key][$tid])) { - $parents = $tids[$key][$tid]; - } - else { - $query = db_select('taxonomy_term_data', 't'); - $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid'); - $result = $query - ->addTag('translatable') - ->addTag('term_access') - ->fields('t') - ->condition('h.tid', $tid) - ->orderBy('weight') - ->orderBy('name') - ->execute(); - $parents = array(); - foreach ($result as $parent) { - $parents[$parent->$key] = $parent; - } - } - return $parents; - } - else { - return array(); +function taxonomy_get_parents($tid) { + $parents = &drupal_static(__FUNCTION__, array()); + + if ($tid && !isset($parents[$tid])) { + $query = db_select('taxonomy_term_data', 't'); + $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid'); + $query->addField('t', 'tid'); + $query->condition('h.tid', $tid); + $query->addTag('term_access'); + $query->orderBy('t.weight'); + $query->orderBy('t.name'); + $tids = $query->execute()->fetchCol(); + $parents[$tid] = taxonomy_term_load_multiple($tids); } + + return isset($parents[$tid]) ? $parents[$tid] : array(); } /** @@ -752,35 +762,35 @@ function taxonomy_get_parents_all($tid) { } /** - * Find all children of a term ID. + * Finds all children of a term ID. + * + * @param $tid + * A taxonomy term ID. + * @param $vid + * An optional vocabulary ID to restrict the child search. + * + * @return + * An array of term objects which are the children of the term $tid. */ -function taxonomy_get_children($tid, $vid = 0, $key = 'tid') { - $tids = &drupal_static(__FUNCTION__, array()); - if (isset($tids[$vid][$tid])) { - $children = $tids[$vid][$tid]; - } - else { +function taxonomy_get_children($tid, $vid = 0) { + $children = &drupal_static(__FUNCTION__, array()); + + if ($tid && !isset($children[$tid])) { $query = db_select('taxonomy_term_data', 't'); $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); - $query - ->addTag('translatable') - ->addTag('term_access') - ->fields('t') - ->condition('parent', $tid) - ->orderBy('weight') - ->orderBy('name'); + $query->addField('t', 'tid'); + $query->condition('h.parent', $tid); if ($vid) { $query->condition('t.vid', $vid); } - $result = $query->execute(); - - $children = array(); - foreach ($result as $term) { - $children[$term->$key] = $term; - } - $tids[$vid][$tid] = $children; + $query->addTag('term_access'); + $query->orderBy('t.weight'); + $query->orderBy('t.name'); + $tids = $query->execute()->fetchCol(); + $children[$tid] = taxonomy_term_load_multiple($tids); } - return $children; + + return isset($children[$tid]) ? $children[$tid] : array(); } /** @@ -803,8 +813,8 @@ function taxonomy_get_children($tid, $vid = 0, $key = 'tid') { */ function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) { $children = &drupal_static(__FUNCTION__, array()); - $parents = &drupal_static(__FUNCTION__ . 'parents', array()); - $terms = &drupal_static(__FUNCTION__ . 'terms', array()); + $parents = &drupal_static(__FUNCTION__ . ':parents', array()); + $terms = &drupal_static(__FUNCTION__ . ':terms', array()); $depth++; @@ -817,19 +827,18 @@ 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'); - $result = $query - ->addTag('translatable') - ->addTag('term_access') - ->fields('t') - ->fields('h', array('parent')) - ->condition('t.vid', $vid) - ->orderBy('weight') - ->orderBy('name') - ->execute(); - foreach ($result as $term) { - $children[$vid][$term->parent][] = $term->tid; - $parents[$vid][$term->tid][] = $term->parent; - $terms[$vid][$term->tid] = $term; + $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; } } @@ -839,8 +848,6 @@ function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) { foreach ($children[$vid][$parent] as $child) { $term = clone $terms[$vid][$child]; $term->depth = $depth; - // The "parent" attribute is not useful, as it would show one parent only. - unset($term->parent); $term->parents = $parents[$vid][$child]; $tree[] = $term; if (!empty($children[$vid][$child])) { @@ -1112,24 +1119,6 @@ function taxonomy_options_list($field) { return $function($field); } -/** - * Implements hook_field_schema(). - */ -function taxonomy_field_schema($field) { - return array( - 'columns' => array( - 'tid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'tid' => array('tid'), - ), - ); -} - /** * Implements hook_field_validate(). * @@ -1142,21 +1131,21 @@ function taxonomy_field_schema($field) { * - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values. */ function taxonomy_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { - // Build an array of term IDs so they can be loaded with + // Build an array of existing term IDs so they can be loaded with // taxonomy_term_load_multiple(); foreach ($items as $delta => $item) { - if (!empty($item['tid'])) { + if (!empty($item['tid']) && $item['tid'] != 'autocreate') { $tids[] = $item['tid']; } } if (!empty($tids)) { $terms = taxonomy_term_load_multiple($tids); - // Check each item to ensure it can be found in the allowed values for this - // field. + // Check each existing item to ensure it can be found in the + // allowed values for this field. foreach ($items as $delta => $item) { $validate = TRUE; - if (!empty($item['tid'])) { + if (!empty($item['tid']) && $item['tid'] != 'autocreate') { $validate = FALSE; foreach ($field['settings']['allowed_values'] as $settings) { // If no parent is specified, check if the term is in the vocabulary. @@ -1221,25 +1210,36 @@ function taxonomy_field_formatter_info() { function taxonomy_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $element = array(); + // Terms whose tid is 'autocreate' do not exist + // yet and $item['taxonomy_term'] is not set. Theme such terms as + // just their name. + switch ($display['type']) { case 'taxonomy_term_reference_link': foreach ($items as $delta => $item) { - $term = $item['taxonomy_term']; - $uri = entity_uri('taxonomy_term', $term); - $element[$delta] = array( - '#type' => 'link', - '#title' => $term->name, - '#href' => $uri['path'], - '#options' => $uri['options'], - ); + if ($item['tid'] == 'autocreate') { + $element[$delta] = array( + '#markup' => check_plain($item['name']), + ); + } + else { + $term = $item['taxonomy_term']; + $uri = entity_uri('taxonomy_term', $term); + $element[$delta] = array( + '#type' => 'link', + '#title' => $term->name, + '#href' => $uri['path'], + '#options' => $uri['options'], + ); + } } break; case 'taxonomy_term_reference_plain': foreach ($items as $delta => $item) { - $term = $item['taxonomy_term']; + $name = ($item['tid'] != 'autocreate' ? $item['taxonomy_term']->name : $item['name']); $element[$delta] = array( - '#markup' => check_plain($term->name), + '#markup' => check_plain($name), ); } break; @@ -1282,7 +1282,9 @@ function taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { // Force the array key to prevent duplicates. - $tids[$item['tid']] = $item['tid']; + if ($item['tid'] != 'autocreate') { + $tids[$item['tid']] = $item['tid']; + } } } if ($tids) { @@ -1298,6 +1300,10 @@ function taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, // Replace the instance value with the term data. $items[$id][$delta]['taxonomy_term'] = $terms[$item['tid']]; } + // Terms to be created are not in $terms, but are still legitimate. + else if ($item['tid'] == 'autocreate') { + // Leave the item in place. + } // Otherwise, unset the instance value, since the term does not exist. else { unset($items[$id][$delta]); @@ -1351,6 +1357,7 @@ function taxonomy_field_widget_form(&$form, &$form_state, $field, $instance, $la function taxonomy_autocomplete_validate($element, &$form_state) { // Autocomplete widgets do not send their tids in the form, so we must detect // them here and process them independently. + $value = array(); if ($tags = $element['#value']) { // Collect candidate vocabularies. $field = $form_state['field'][$element['#field_name']][$element['#language']]['field']; @@ -1361,28 +1368,23 @@ function taxonomy_autocomplete_validate($element, &$form_state) { // Translate term names into actual terms. $typed_terms = drupal_explode_tags($tags); - $values = array(); foreach ($typed_terms as $typed_term) { // See if the term exists in the chosen vocabulary and return the tid; - // otherwise, create a new term. + // otherwise, create a new 'autocreate' term for insert/update. if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => $vids))) { $term = array_pop($possibilities); } else { $vocabulary = taxonomy_vocabulary_load($vids[0]); - $term = (object) array( + $term = array( + 'tid' => 'autocreate', 'vid' => $vids[0], 'name' => $typed_term, 'vocabulary_machine_name' => $vocabulary->machine_name, ); - taxonomy_term_save($term); } - $values[] = $term->tid; + $value[] = (array)$term; } - $value = options_array_transpose(array('tid' => $values)); - } - else { - $value = array(); } form_set_value($element, $value, $form_state); @@ -1489,6 +1491,22 @@ function taxonomy_rdf_mapping() { * @{ */ +/** + * Implements hook_field_presave(). + * + * Create any new terms defined in a freetagging vocabulary. + */ +function taxonomy_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { + foreach ($items as $delta => $item) { + if ($item['tid'] == 'autocreate') { + $term = (object) $item; + unset($term->tid); + taxonomy_term_save($term); + $items[$delta]['tid'] = $term->tid; + } + } +} + /** * Implements hook_field_insert(). */ diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index dd6e8a05d3e04d4aea43fdbb0b5e0f82f5dc0200..43509ed2a04bf27ed3f6e56e435c736624312305 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.pages.inc,v 1.51 2010/02/10 06:28:10 webchick Exp $ +// $Id: taxonomy.pages.inc,v 1.53 2010/08/22 13:53:37 dries Exp $ /** * @file @@ -27,13 +27,17 @@ function taxonomy_term_page($term) { $breadcrumb[] = l(t('Home'), NULL); $breadcrumb = array_reverse($breadcrumb); drupal_set_breadcrumb($breadcrumb); - drupal_add_feed(url('taxonomy/term/' . $term->tid . '/feed'), 'RSS - ' . $term->name); + drupal_add_feed('taxonomy/term/' . $term->tid . '/feed', 'RSS - ' . $term->name); - $build['term_heading'] = array( - '#prefix' => '<div class="term-listing-heading">', - '#suffix' => '</div>', - 'term' => taxonomy_term_view($term, 'full'), - ); + $build = array(); + // Add term heading if the term has a description + if (!empty($term->description)) { + $build['term_heading'] = array( + '#prefix' => '<div class="term-listing-heading">', + '#suffix' => '</div>', + 'term' => taxonomy_term_view($term, 'full'), + ); + } if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) { $nodes = node_load_multiple($nids); diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index 5778abb82dfb4047f69aa476b74b65157119ca14..5e961047dc991a1e33a6bf5e9db7c44a18f9aad7 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.test,v 1.81 2010/07/01 00:42:34 dries Exp $ +// $Id: taxonomy.test,v 1.90 2010/08/22 15:45:03 dries Exp $ /** * @file @@ -337,6 +337,18 @@ class TaxonomyTermUnitTest extends TaxonomyWebTestCase { 'group' => 'Taxonomy', ); } + + function testTermDelete() { + $vocabulary = $this->createVocabulary(); + $valid_term = $this->createTerm($vocabulary); + // Delete a valid term. + taxonomy_term_delete($valid_term->tid); + $terms = taxonomy_term_load_multiple(array(), array('vid' => $vocabulary->vid)); + $this->assertTrue(empty($terms), 'Vocabulary is empty after deletion'); + + // Delete an invalid term. Should not throw any notices. + taxonomy_term_delete(42); + } } /** @@ -451,6 +463,12 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $this->drupalGet('node/' . $node->nid); $this->assertText($term2->name, t('Term is displayed when viewing 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')); + $this->assertNoUniqueText($term2->name, t('Term is displayed when previewing the node again.')); } /** @@ -475,6 +493,19 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Insert the terms in a comma separated list. Vocabulary 1 is a // free-tagging field created by the default profile. $edit[$instance['field_name'] . "[$langcode]"] = implode(', ', $terms); + + // Preview and verify the terms appear but are not created. + $this->drupalPost('node/add/page', $edit, t('Preview')); + foreach ($terms as $term) { + $this->assertText($term, t('The term appears on the node preview')); + } + $tree = taxonomy_get_tree($this->vocabulary->vid); + $this->assertTrue(empty($tree), t('The terms are not created on preview.')); + + // taxonomy.module does not maintain its static caches. + drupal_static_reset(); + + // Save, creating the terms. $this->drupalPost('node/add/page', $edit, t('Save')); $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), t('The node was created successfully')); foreach ($terms as $term) { @@ -547,6 +578,14 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $this->assertText($edit['name'], t('The randomly generated term name is present.')); $this->assertText($edit['description[value]'], t('The randomly generated term description is present.')); + // Did this page request display a 'term-listing-heading'? + $this->assertPattern('|class="term-listing-heading"|', 'Term page displayed the term description element.'); + // Check that it does NOT show a description when description is blank. + $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.'); + // Check that the term feed page is working. $this->drupalGet('taxonomy/term/' . $term->tid . '/feed'); @@ -574,29 +613,25 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { drupal_static_reset('taxonomy_get_treeterms'); list($term1, $term2, $term3) = taxonomy_get_tree($this->vocabulary->vid); - // Change the order to term2, term3, term1. Emulate the reordering done by - // tabledrag.js by changing the page HTML source. Each term has three hidden - // fields, "tid:1:0[tid]", "tid:1:0[parent]", and "tid:1:0[depth]". The - // order of the input fields in the page is used when the form is processed. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name); - $reorder = array( - 'tid:' . $term1->tid . ':0' => 'tid:' . $term2->tid . ':0', - 'tid:' . $term2->tid . ':0' => 'tid:' . $term3->tid . ':0', - 'tid:' . $term3->tid . ':0' => 'tid:' . $term1->tid . ':0', - ); - $this->drupalSetContent(strtr($this->drupalGetContent(), $reorder)); - - // Make term3 a child of term2, and update all hidden fields. + + // Each term has four hidden fields, "tid:1:0[tid]", "tid:1:0[parent]", + // "tid:1:0[depth]", and "tid:1:0[weight]". Change the order to term2, + // term3, term1 by setting weight property, make term3 a child of term2 by + // setting the parent and depth properties, and update all hidden fields. $edit = array( 'tid:' . $term2->tid . ':0[tid]' => $term2->tid, 'tid:' . $term2->tid . ':0[parent]' => 0, 'tid:' . $term2->tid . ':0[depth]' => 0, + 'tid:' . $term2->tid . ':0[weight]' => 0, 'tid:' . $term3->tid . ':0[tid]' => $term3->tid, 'tid:' . $term3->tid . ':0[parent]' => $term2->tid, 'tid:' . $term3->tid . ':0[depth]' => 1, + 'tid:' . $term3->tid . ':0[weight]' => 1, 'tid:' . $term1->tid . ':0[tid]' => $term1->tid, 'tid:' . $term1->tid . ':0[parent]' => 0, 'tid:' . $term1->tid . ':0[depth]' => 0, + 'tid:' . $term1->tid . ':0[weight]' => 2, ); $this->drupalPost(NULL, $edit, t('Save')); @@ -990,7 +1025,22 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $edit[$this->instance['field_name'] . '[' . $this->langcode . '][]'] = $term2->tid; $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); - // Generate and test sanitized tokens. + // Generate and test sanitized tokens for term1. + $tests = array(); + $tests['[term:tid]'] = $term1->tid; + $tests['[term:name]'] = check_plain($term1->name); + $tests['[term:description]'] = check_markup($term1->description, $term1->format); + $tests['[term:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE)); + $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))); + } + + // Generate and test sanitized tokens for term2. $tests = array(); $tests['[term:tid]'] = $term2->tid; $tests['[term:name]'] = check_plain($term2->name); @@ -998,6 +1048,8 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests['[term:url]'] = url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE)); $tests['[term:node-count]'] = 1; $tests['[term:parent:name]'] = check_plain($term1->name); + $tests['[term:parent:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE)); + $tests['[term:parent:parent:name]'] = '[term:parent:parent:name]'; $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name); // Test to make sure that we generated something for each token. diff --git a/modules/taxonomy/taxonomy.tokens.inc b/modules/taxonomy/taxonomy.tokens.inc index f9e934616d99c8912e9760c633211207d61584c1..8066a2659e001b036901c51ddaec37cf2da238a2 100644 --- a/modules/taxonomy/taxonomy.tokens.inc +++ b/modules/taxonomy/taxonomy.tokens.inc @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.tokens.inc,v 1.7 2010/07/01 00:42:34 dries Exp $ +// $Id: taxonomy.tokens.inc,v 1.9 2010/08/08 02:06:56 dries Exp $ /** * @file @@ -111,7 +111,8 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options = break; case 'url': - $replacements[$original] = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE)); + $uri = entity_uri('taxonomy_term', $term); + $replacements[$original] = url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))); break; case 'node-count': @@ -128,9 +129,10 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options = break; case 'parent': - $parents = taxonomy_get_parents($term->tid); - $parent = array_pop($parents); - $replacements[$original] = check_plain($parent->name); + if ($parents = taxonomy_get_parents($term->tid)) { + $parent = array_pop($parents); + $replacements[$original] = check_plain($parent->name); + } break; } } @@ -140,8 +142,7 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options = $replacements += token_generate('vocabulary', $vocabulary_tokens, array('vocabulary' => $vocabulary), $options); } - if ($vocabulary_tokens = token_find_with_prefix($tokens, 'parent')) { - $parents = taxonomy_get_parents($term->tid); + if (($vocabulary_tokens = token_find_with_prefix($tokens, 'parent')) && $parents = taxonomy_get_parents($term->tid)) { $parent = array_pop($parents); $replacements += token_generate('term', $vocabulary_tokens, array('term' => $parent), $options); } diff --git a/modules/toolbar/toolbar.css b/modules/toolbar/toolbar.css index d5fced7604bf15caaf65a61c2125002bfb8e6466..800518f7c9fe53e3aa7bb443fa6c0ef6d93025d8 100644 --- a/modules/toolbar/toolbar.css +++ b/modules/toolbar/toolbar.css @@ -1,4 +1,4 @@ -/* $Id: toolbar.css,v 1.22 2010/05/23 18:23:32 dries Exp $ */ +/* $Id: toolbar.css,v 1.26 2010/09/11 14:45:23 dries Exp $ */ body.toolbar { padding-top: 2.2em; @@ -27,9 +27,12 @@ body.toolbar-drawer { /** * Base styles. + * + * We use a keyword for the toolbar font size to make it display consistently + * across different themes, while still allowing browsers to resize the text. */ #toolbar { - font: normal 0.9em "Lucida Grande", Verdana, sans-serif; + font: normal small "Lucida Grande", Verdana, sans-serif; background: #666; color: #ccc; position: fixed; @@ -52,8 +55,9 @@ body.toolbar-drawer { } #toolbar a { - text-decoration: none; color: #fff; + font-size: .846em; + text-decoration: none; } #toolbar ul li, @@ -100,10 +104,18 @@ body.toolbar-drawer { width: 25px; height: 25px; } +#toolbar div.toolbar-menu a.toggle:focus, +#toolbar div.toolbar-menu a.toggle:hover { +background-position: -50px -20px; +} #toolbar div.toolbar-menu a.toggle-active { background-position: -25px -20px; } +#toolbar div.toolbar-menu a.toggle-active.toggle:focus, +#toolbar div.toolbar-menu a.toggle-active.toggle:hover { +background-position: -75px -20px; +} #toolbar div.toolbar-menu ul li a { -moz-border-radius: 10px; @@ -114,11 +126,13 @@ body.toolbar-drawer { #toolbar div.toolbar-menu ul li a:focus, #toolbar div.toolbar-menu ul li a:hover, +#toolbar div.toolbar-menu ul li a:active, #toolbar div.toolbar-menu ul li a.active:focus { background: #444; } #toolbar div.toolbar-menu ul li a.active:hover, +#toolbar div.toolbar-menu ul li a.active:active, #toolbar div.toolbar-menu ul li a.active, #toolbar div.toolbar-menu ul li.active-trail a { text-shadow: #333 0 1px 0; @@ -137,7 +151,7 @@ body.toolbar-drawer { * IE 6 Fix. * * IE 6 shows elements with position:fixed as position:static so we replace - * it with position:absolute; toolbar needs it's z-index to stay above overlay. + * it with position:absolute; toolbar needs its z-index to stay above overlay. */ * html #toolbar { position: absolute; diff --git a/modules/toolbar/toolbar.info b/modules/toolbar/toolbar.info index 740cb27b9f93028c8cdd29fe0df3b1ed4c042f7b..4f86211c0a6077f560b347a7caad86d9e5b8e83e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info index fd7a8e8773f0cddf74cb32cc8af1dca38f0936ef..8ed689039e268c36ffdc9fa7834f61e1fc35d20e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/tracker/tracker.install b/modules/tracker/tracker.install index cb17dc687803deff431b4bfadd181da20bae8db5..a47f5a1a5274358f5c944667cfbe4fe35c6aa6b7 100644 --- a/modules/tracker/tracker.install +++ b/modules/tracker/tracker.install @@ -1,5 +1,5 @@ <?php -// $Id: tracker.install,v 1.4 2010/03/20 15:06:51 dries Exp $ +// $Id: tracker.install,v 1.5 2010/08/22 13:55:53 dries Exp $ /** * Implements hook_uninstall(). @@ -56,7 +56,10 @@ function tracker_schema() { ), 'primary key' => array('nid'), 'foreign keys' => array( - 'node' => 'nid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), ), ); @@ -96,8 +99,14 @@ function tracker_schema() { ), 'primary key' => array('nid', 'uid'), 'foreign keys' => array( - 'node' => 'nid', - 'users' => 'uid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'tracked_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); @@ -143,7 +152,10 @@ function tracker_update_7000() { ), 'primary key' => array('nid'), 'foreign keys' => array( - 'node' => 'nid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), ), ); @@ -183,8 +195,14 @@ function tracker_update_7000() { ), 'primary key' => array('nid', 'uid'), 'foreign keys' => array( - 'node' => 'nid', - 'users' => 'uid', + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'tracked_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); diff --git a/modules/tracker/tracker.pages.inc b/modules/tracker/tracker.pages.inc index 661b8a694fbbe4ea72c3055e6b349dcddfcf23e1..fab5c94784d659d9d9271bd040c2ad958ff1226c 100644 --- a/modules/tracker/tracker.pages.inc +++ b/modules/tracker/tracker.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: tracker.pages.inc,v 1.31 2010/01/14 06:23:40 webchick Exp $ +// $Id: tracker.pages.inc,v 1.32 2010/07/30 02:47:28 dries Exp $ /** * @file @@ -113,7 +113,7 @@ function tracker_page($account = NULL, $set_title = FALSE) { '#theme' => 'table', '#empty' => t('No content available.'), '#attached' => array( - 'css' => array(drupal_get_path('module', 'tracker') . '/tracker.css' => array('preprocess' => FALSE)), + 'css' => array(drupal_get_path('module', 'tracker') . '/tracker.css' => array()), ), ); $page['pager'] = array( diff --git a/modules/tracker/tracker.test b/modules/tracker/tracker.test index f3e1ffb24c81ca1b325af132b7c562dd5fc84e52..8eeb86c91f8e55d8672f97a6b393bbd12631ce8e 100644 --- a/modules/tracker/tracker.test +++ b/modules/tracker/tracker.test @@ -1,5 +1,5 @@ <?php -// $Id: tracker.test,v 1.18 2010/01/09 21:54:01 webchick Exp $ +// $Id: tracker.test,v 1.20 2010/08/05 23:53:39 webchick Exp $ class TrackerTest extends DrupalWebTestCase { protected $user; diff --git a/modules/translation/translation.info b/modules/translation/translation.info index ec937f25982b483d5235bfe0be3980ddc52b7273..2efa3bc933af2d66cc6d5cf47e05999c39053836 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/translation/translation.module b/modules/translation/translation.module index 883810a7c0ba120e0df166484d35a28998e9b91c..486422ed1400d908bf01d3c72f5256178df3c39c 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -1,5 +1,5 @@ <?php -// $Id: translation.module,v 1.80 2010/06/06 00:24:16 webchick Exp $ +// $Id: translation.module,v 1.84 2010/09/09 23:01:48 dries Exp $ /** * @file @@ -123,8 +123,8 @@ function translation_form_node_type_form_alter(&$form, &$form_state) { * - Alters language fields on node forms when a translation * is about to be created. */ -function translation_form_alter(&$form, &$form_state, $form_id) { - if (!empty($form['#node_edit_form']) && translation_supported_type($form['#node']->type)) { +function translation_form_node_form_alter(&$form, &$form_state) { + if (translation_supported_type($form['#node']->type)) { $node = $form['#node']; if (!empty($node->translation_source)) { // We are creating a translation. Add values and lock language field. @@ -181,18 +181,14 @@ function translation_form_alter(&$form, &$form_state, $form_id) { * is part of a translation set. */ function translation_node_view($node, $view_mode) { - if (isset($node->tnid) && $translations = translation_node_get_translations($node->tnid)) { + 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']['translation'] = array( - '#theme' => 'links__translation_node', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); + $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links); } } } @@ -209,7 +205,7 @@ function translation_node_prepare($node) { user_access('translate content') && // And the $_GET variables are set properly. isset($_GET['translation']) && - isset($_GET['language']) && + isset($_GET['target']) && is_numeric($_GET['translation'])) { $source_node = node_load($_GET['translation']); @@ -221,7 +217,8 @@ function translation_node_prepare($node) { } $language_list = language_list(); - if (!isset($language_list[$_GET['language']]) || ($source_node->language == $_GET['language'])) { + $langcode = $_GET['target']; + if (!isset($language_list[$langcode]) || ($source_node->language == $langcode)) { // If not supported language, or same language as source node, break. return; } @@ -229,14 +226,14 @@ function translation_node_prepare($node) { // Ensure we don't have an existing translation in this language. if (!empty($source_node->tnid)) { $translations = translation_node_get_translations($source_node->tnid); - if (isset($translations[$_GET['language']])) { - drupal_set_message(t('A translation of %title in %language already exists, a new %type will be created instead of a translation.', array('%title' => $source_node->title, '%language' => $language_list[$_GET['language']]->name, '%type' => $node->type)), 'error'); + if (isset($translations[$langcode])) { + drupal_set_message(t('A translation of %title in %language already exists, a new %type will be created instead of a translation.', array('%title' => $source_node->title, '%language' => $language_list[$langcode]->name, '%type' => $node->type)), 'error'); return; } } // Populate fields based on source node. - $node->language = $_GET['language']; + $node->language = $langcode; $node->translation_source = $source_node; $node->title = $source_node->title; diff --git a/modules/translation/translation.pages.inc b/modules/translation/translation.pages.inc index b407ef039956eb654d576f9de3e90c6c399e9b52..66754a003fde638bf694dad6f4b0e9701c47fb60 100644 --- a/modules/translation/translation.pages.inc +++ b/modules/translation/translation.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: translation.pages.inc,v 1.15 2010/01/30 07:59:26 dries Exp $ +// $Id: translation.pages.inc,v 1.16 2010/07/17 11:31:36 dries Exp $ /** * @file @@ -47,7 +47,7 @@ function translation_node_overview($node) { // No such translation in the set yet: help user to create it. $title = t('n/a'); if (node_access('create', $node)) { - $options[] = l(t('add translation'), 'node/add/' . str_replace('_', '-', $node->type), array('query' => array('translation' => $node->nid, 'language' => $language->language))); + $options[] = l(t('add translation'), 'node/add/' . str_replace('_', '-', $node->type), array('query' => array('translation' => $node->nid, 'target' => $language->language))); } $status = t('Not translated'); } diff --git a/modules/translation/translation.test b/modules/translation/translation.test index 1a50fe3303cf3cf9bd4673c94d5247ce484110f0..2b4f6d2268b2e6649d7e30eb23ec72bffa1efa5a 100644 --- a/modules/translation/translation.test +++ b/modules/translation/translation.test @@ -1,5 +1,5 @@ <?php -// $Id: translation.test,v 1.26 2010/06/06 00:24:16 webchick Exp $ +// $Id: translation.test,v 1.29 2010/08/05 23:53:39 webchick Exp $ class TranslationTestCase extends DrupalWebTestCase { protected $book; @@ -53,7 +53,7 @@ class TranslationTestCase extends DrupalWebTestCase { // Attempt to submit a duplicate translation by visiting the node/add page // with identical query string. $languages = language_list(); - $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'language' => 'es'))); + $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => 'es'))); $this->assertRaw(t('A translation of %title in %language already exists', array('%title' => $node_title, '%language' => $languages['es']->name)), t('Message regarding attempted duplicate translation is displayed.')); // Attempt a resubmission of the form - this emulates using the back button @@ -154,7 +154,7 @@ class TranslationTestCase extends DrupalWebTestCase { * @param string $language Language code. */ function createTranslation($node, $title, $body, $language) { - $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'language' => $language))); + $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $language))); $body_key = "body[$language][0][value]"; $this->assertFieldByXPath('//input[@id="edit-title"]', $node->title, "Original title value correctly populated."); diff --git a/modules/trigger/tests/trigger_test.info b/modules/trigger/tests/trigger_test.info index 965efc6c093a1e70d5a24d2efe635ea4314a6bba..b3e4ad817d2ed28cce1c543c486348bd7eb2e702 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/trigger/trigger.admin.inc b/modules/trigger/trigger.admin.inc index c9c02115a39f7557954c3d23783aae72d4052604..b156a3e71d1991b0a54a780d1db2509a4f5cd10d 100644 --- a/modules/trigger/trigger.admin.inc +++ b/modules/trigger/trigger.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: trigger.admin.inc,v 1.26 2010/05/01 08:12:23 dries Exp $ +// $Id: trigger.admin.inc,v 1.27 2010/08/22 11:04:09 dries Exp $ /** * @file @@ -90,7 +90,7 @@ function trigger_unassign_submit($form, &$form_state) { ->condition('aid', $aid) ->execute(); $actions = actions_get_all_actions(); - watchdog('actions', 'Action %action has been unassigned.', array('%action' => check_plain($actions[$aid]['label']))); + watchdog('actions', 'Action %action has been unassigned.', array('%action' => $actions[$aid]['label'])); drupal_set_message(t('Action %action has been unassigned.', array('%action' => $actions[$aid]['label']))); $form_state['redirect'] = 'admin/structure/trigger/' . $form_state['values']['module']; } @@ -291,7 +291,7 @@ function theme_trigger_display($variables) { $rows = array(); foreach ($element['assigned']['#value'] as $aid => $info) { $rows[] = array( - $info['label'], + check_plain($info['label']), $info['link'] ); } diff --git a/modules/trigger/trigger.info b/modules/trigger/trigger.info index a122cfbc490afd4102309376724e047febb89425..7881d0e0965e7114e10d8d6596a84a442e198b31 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/trigger/trigger.install b/modules/trigger/trigger.install index 14a30bffc61d3a7b7105614bf8e7e63287eff32a..1f42515b2f0b121050a4a9edacd2b016e666cf84 100644 --- a/modules/trigger/trigger.install +++ b/modules/trigger/trigger.install @@ -1,5 +1,5 @@ <?php -// $Id: trigger.install,v 1.16 2009/12/04 16:49:47 dries Exp $ +// $Id: trigger.install,v 1.17 2010/08/22 13:55:53 dries Exp $ /** * @file @@ -36,7 +36,10 @@ function trigger_schema() { ), 'primary key' => array('hook', 'aid'), 'foreign keys' => array( - 'aid' => array('actions' => 'aid'), + 'action' => array( + 'table' => 'actions', + 'columns' => array('aid' => 'aid'), + ), ), ); return $schema; diff --git a/modules/trigger/trigger.module b/modules/trigger/trigger.module index bca7c52a7022be275ce2b60fdb699ef74498a00e..808bb90f6488d65e2d3ce512a996d264e17cc03f 100644 --- a/modules/trigger/trigger.module +++ b/modules/trigger/trigger.module @@ -1,5 +1,5 @@ <?php -// $Id: trigger.module,v 1.63 2010/06/29 18:24:10 dries Exp $ +// $Id: trigger.module,v 1.64 2010/07/10 01:44:28 dries Exp $ /** * @file @@ -467,7 +467,7 @@ function _trigger_normalize_user_context($type, $account) { // If a single node is being viewed, return the node. case 'node': // If we are viewing an individual node, return the node. - if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) { + if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) { return node_load(array('nid' => arg(1))); } break; diff --git a/modules/trigger/trigger.test b/modules/trigger/trigger.test index 0188e7d75198cebe1d5187dfb628da8f6fb4527a..b579127a6665b685a6c0d5b4ee3b625f493e7be6 100644 --- a/modules/trigger/trigger.test +++ b/modules/trigger/trigger.test @@ -1,5 +1,5 @@ <?php -// $Id: trigger.test,v 1.33 2010/06/29 18:24:10 dries Exp $ +// $Id: trigger.test,v 1.36 2010/08/05 23:53:39 webchick Exp $ /** * Provides common helper methods. @@ -567,7 +567,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase { $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), 'trigger-user-insert-assign-form'); // Set action variable to FALSE. - variable_set( $action_id, FALSE ); + variable_set($action_id, FALSE); // Create an unblocked user $web_user = $this->drupalCreateUser(array('administer users')); diff --git a/modules/update/tests/aaa_update_test.info b/modules/update/tests/aaa_update_test.info index 9900353e5cc07c6a421be1db8e7f2e35b525a57c..e2668c7c13183065329ccb421e11d35d18222769 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/update/tests/bbb_update_test.info b/modules/update/tests/bbb_update_test.info index 72ec11c31f61deb777063def6fe6ad34a81ef4d5..6b36719edf273b1245ce155867796e0a6ad17502 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/update/tests/ccc_update_test.info b/modules/update/tests/ccc_update_test.info index 139d2ad0892c71eb383c9d1a1391a873491c2eaf..65ea06ae0901af2e5fa91faf3860d69b76973681 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/update/tests/update_test.info b/modules/update/tests/update_test.info index 5b2e0e141de28cffd68bfe6e442afc74ad81b548..c7445a8b6e7343d049775f4f3837058c48f1a9ab 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/update/update.info b/modules/update/update.info index ee88b84b617ce566efea42c854be78664b8d66e6..fab0777c8f4783c47996c091a03d452bbfcbcf61 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc index 7c437b9a68abe4ae19960f5ec71bb9a7bb3802f0..24a4cf26c770ee8326ab09888e2a5e6f3ab606c5 100644 --- a/modules/update/update.manager.inc +++ b/modules/update/update.manager.inc @@ -1,5 +1,5 @@ <?php -// $Id: update.manager.inc,v 1.23 2010/05/26 11:50:58 dries Exp $ +// $Id: update.manager.inc,v 1.24 2010/07/29 02:27:43 dries Exp $ /** * @file @@ -457,17 +457,9 @@ function update_manager_update_ready_form_submit($form, &$form_state) { function update_manager_install_form($form, &$form_state, $context) { $form = array(); - // Collect all the supported archive file extensions for the UI text. - $extensions = array(); - $archiver_info = archiver_get_info(); - foreach ($archiver_info as $info) { - if (!empty($info['extensions'])) { - $extensions += $info['extensions']; - } - } $form['help_text'] = array( '#prefix' => '<p>', - '#markup' => t('To install a new module or theme, either enter the URL of an archive file you wish to install, or upload the archive file that you have downloaded. You can find <a href="@module_url">modules</a> and <a href="@theme_url">themes</a> at <a href="@drupal_org_url">http://drupal.org</a>. The following archive extensions are supported: %extensions', array('@module_url' => 'http://drupal.org/project/modules', '@theme_url' => 'http://drupal.org/project/themes', '@drupal_org_url' => 'http://drupal.org', '%extensions' => implode(', ', $extensions))), + '#markup' => t('To install a new module or theme, either enter the URL of an archive file you wish to install, or upload the archive file that you have downloaded. You can find <a href="@module_url">modules</a> and <a href="@theme_url">themes</a> at <a href="@drupal_org_url">http://drupal.org</a>.<br/>The following archive extensions are supported: %extensions.', array('@module_url' => 'http://drupal.org/project/modules', '@theme_url' => 'http://drupal.org/project/themes', '@drupal_org_url' => 'http://drupal.org', '%extensions' => archiver_get_extensions())), '#suffix' => '</p>', ); @@ -538,10 +530,13 @@ function update_manager_install_form_submit($form, &$form_state) { } } elseif ($_FILES['files']['name']['project_upload']) { + $validators = array('file_validate_extensions' => array(archiver_get_extensions())); $field = 'project_upload'; - // @todo: add some validators here. - $finfo = file_save_upload($field, array(), NULL, FILE_EXISTS_REPLACE); - // @todo: find out if the module is already instealled, if so, throw an error. + if (!($finfo = file_save_upload($field, $validators, NULL, FILE_EXISTS_REPLACE))) { + // Failed to upload the file. file_save_upload() calls form_set_error() on + // failure. + return; + } $local_cache = $finfo->uri; } diff --git a/modules/update/update.report.inc b/modules/update/update.report.inc index 304f53997312a1e7475712556259ce00fc991b2f..7344de442e35f75956f8afd78b238e57538bddac 100644 --- a/modules/update/update.report.inc +++ b/modules/update/update.report.inc @@ -1,5 +1,5 @@ <?php -// $Id: update.report.inc,v 1.31 2010/04/13 15:23:03 dries Exp $ +// $Id: update.report.inc,v 1.32 2010/08/17 13:50:52 dries Exp $ /** * @file @@ -168,7 +168,7 @@ function theme_update_report($variables) { foreach ($project['extra'] as $key => $value) { $row .= '<div class="' . implode(' ', $value['class']) . '">'; $row .= check_plain($value['label']) . ': '; - $row .= drupal_placeholder(array('text' => $value['data'])); + $row .= drupal_placeholder($value['data']); $row .= "</div>\n"; } $row .= "</div>\n"; // extra div. @@ -203,7 +203,7 @@ function theme_update_report($variables) { break; default: - $base_themes[] = drupal_placeholder(array('text' => $base_theme)); + $base_themes[] = drupal_placeholder($base_theme); } } $row .= t('Depends on: !basethemes', array('!basethemes' => implode(', ', $base_themes))); diff --git a/modules/update/update.test b/modules/update/update.test index fdb2d67fbf5fc1074b377a8ef422a4ca5baf98be..e2a3bf2849513480da9ff46735f7203d6c60d7f6 100644 --- a/modules/update/update.test +++ b/modules/update/update.test @@ -1,5 +1,5 @@ <?php -// $Id: update.test,v 1.13 2010/01/04 21:31:52 webchick Exp $ +// $Id: update.test,v 1.16 2010/08/05 23:53:39 webchick Exp $ /** * @file @@ -493,3 +493,44 @@ class UpdateTestContribCase extends UpdateTestHelper { } +class UpdateTestUploadCase extends UpdateTestHelper { + public static function getInfo() { + return array( + 'name' => 'Upload and extract module functionality', + 'description' => 'Tests the update module\'s upload and extraction functionality.', + 'group' => 'Update', + ); + } + + public function setUp() { + parent::setUp('update'); + variable_set('allow_authorize_operations', TRUE); + $admin_user = $this->drupalCreateUser(array('administer software updates', 'administer site configuration')); + $this->drupalLogin($admin_user); + } + + /** + * Tests upload and extraction of a module. + */ + public function testUploadModule() { + // Images are not valid archives, so get one and try to install it. + $invalidArchiveFile = reset($this->drupalGetTestFiles('image')); + $edit = array( + 'files[project_upload]' => $invalidArchiveFile->uri, + ); + // This also checks that the correct archive extensions are allowed. + $this->drupalPost('admin/modules/install', $edit, t('Install')); + $this->assertText(t('Only files with the following extensions are allowed: @archive_extensions.', array('@archive_extensions' => archiver_get_extensions())),'Only valid archives can be uploaded.'); + + // Check to ensure an existing module can't be reinstalled. Also checks that + // the archive was extracted since we can't know if the module is already + // installed until after extraction. + $validArchiveFile = drupal_get_path('module', 'update') . '/tests/aaa_update_test.tar.gz'; + $edit = array( + 'files[project_upload]' => $validArchiveFile, + ); + $this->drupalPost('admin/modules/install', $edit, t('Install')); + $this->assertText(t('@module_name is already installed.', array('@module_name' => 'AAA Update test')), 'Existing module was extracted and not reinstalled.'); + } +} + diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc index 50e4a5ed2a903cb0ac4144a45e461acbcd89f7a9..06bfe2fc98ef51b7a72d5eff38105e568ff103ff 100644 --- a/modules/user/user.admin.inc +++ b/modules/user/user.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: user.admin.inc,v 1.114 2010/07/07 17:56:42 webchick Exp $ +// $Id: user.admin.inc,v 1.117 2010/09/01 20:08:17 dries Exp $ /** * @file @@ -341,7 +341,7 @@ function user_admin_settings() { ); // If picture support is enabled, check whether the picture directory exists. if (variable_get('user_pictures', 0)) { - $picture_path = variable_get('file_default_scheme', 'public') . '://' . variable_get('user_picture_path', 'pictures'); + $picture_path = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures'); if (!file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY)) { form_set_error('user_picture_path', t('The directory %directory does not exist or is not writable.', array('%directory' => $picture_path))); watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $picture_path), WATCHDOG_ERROR); @@ -369,7 +369,7 @@ function user_admin_settings() { '#default_value' => variable_get('user_picture_path', 'pictures'), '#size' => 30, '#maxlength' => 255, - '#description' => t('Subdirectory in the directory %dir where pictures will be stored.', array('%dir' => file_directory_path() . '/')), + '#description' => t('Subdirectory in the file upload directory where pictures will be stored.'), ); $form['personalization']['pictures']['user_picture_default'] = array( '#type' => 'textfield', @@ -395,7 +395,7 @@ function user_admin_settings() { '#size' => 10, '#maxlength' => 10, '#field_suffix' => ' ' . t('pixels'), - '#description' => t('Maximum allowed dimensions for uploaded pictures.'), + '#description' => t('Pictures larger than this will be scaled down to this size.'), ); $form['personalization']['pictures']['user_picture_file_size'] = array( '#type' => 'textfield', @@ -404,7 +404,7 @@ function user_admin_settings() { '#size' => 10, '#maxlength' => 10, '#field_suffix' => ' ' . t('KB'), - '#description' => t('Maximum allowed file size for uploaded pictures.'), + '#description' => t('Maximum allowed file size for uploaded pictures. Upload size is normally limited only by the PHP maximum post and file upload settings, and images are automatically scaled down to the dimensions specified above.'), ); $form['personalization']['pictures']['user_picture_guidelines'] = array( '#type' => 'textarea', @@ -758,7 +758,9 @@ function theme_user_admin_permissions($variables) { 'class' => array('permission'), ); foreach (element_children($form['checkboxes']) as $rid) { - $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox'), 'title' => $roles[$rid] . ' : ' . t($key)); + $form['checkboxes'][$rid][$key]['#title'] = $roles[$rid] . ': ' . t($key); + $form['checkboxes'][$rid][$key]['#title_display'] = 'invisible'; + $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox')); } } $rows[] = $row; diff --git a/modules/user/user.info b/modules/user/user.info index 7fd1d34c6ce50cae0f9965a911e22dd419b3d926..a6ec491e5392734684c389da2b21d624edfd404d 100644 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -1,4 +1,4 @@ -; $Id: user.info,v 1.13 2009/11/17 21:24:19 dries Exp $ +; $Id: user.info,v 1.14 2010/09/05 02:21:38 dries Exp $ name = User description = Manages the user registration and login system. package = Core @@ -12,9 +12,10 @@ files[] = user.test files[] = user.tokens.inc required = TRUE configure = admin/config/people +stylesheets[all][] = user.css -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/modules/user/user.install b/modules/user/user.install index 6ed94a7d566454a82d989ab4b69c59a99760ab00..7b40e90141fd540a60449da34ac7a070986c68ec 100644 --- a/modules/user/user.install +++ b/modules/user/user.install @@ -1,5 +1,5 @@ <?php -// $Id: user.install,v 1.54 2010/06/28 14:59:24 webchick Exp $ +// $Id: user.install,v 1.63 2010/09/13 05:50:09 webchick Exp $ /** * @file @@ -45,7 +45,10 @@ function user_schema() { ), 'primary key' => array('aid'), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), + 'user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), ), ); @@ -60,7 +63,7 @@ function user_schema() { ), 'permission' => array( 'type' => 'varchar', - 'length' => 64, + 'length' => 128, 'not null' => TRUE, 'default' => '', 'description' => 'A single permission granted to the role identified by rid.', @@ -78,7 +81,10 @@ function user_schema() { 'permission' => array('permission'), ), 'foreign keys' => array( - 'rid' => array('role' => 'rid'), + 'role' => array( + 'table' => 'roles', + 'columns' => array('rid' => 'rid'), + ), ), ); @@ -236,7 +242,10 @@ function user_schema() { ), 'primary key' => array('uid'), 'foreign keys' => array( - 'signature_format' => array('filter_format' => 'format'), + 'signature_format' => array( + 'table' => 'filter_format', + 'columns' => array('signature_format' => 'format'), + ), ), ); @@ -263,8 +272,14 @@ function user_schema() { 'rid' => array('rid'), ), 'foreign keys' => array( - 'uid' => array('users' => 'uid'), - 'rid' => array('role' => 'rid'), + 'user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + 'role' => array( + 'table' => 'roles', + 'columns' => array('rid' => 'rid'), + ), ), ); @@ -334,12 +349,44 @@ function user_update_dependencies() { // user_update_7006 relies on filter_update_7002. // TODO: move user_update_7006 down below in the upgrade process. $dependencies['user'][7006] = array( - 'filter' => 7002, + 'filter' => 7003, + ); + // user_update_7013 relies on system_update_7060. + $dependencies['user'][7013] = array( + 'system' => 7059, ); return $dependencies; } +/** + * Utility function: grant a set of permissions to a role during update. + * + * This function is valid for a database schema version 7000. + * + * @param $rid + * The role ID. + * @param $permissions + * An array of permissions names. + * @param $module + * The name of the module defining the permissions. + * @ingroup update-api-6.x-to-7.x + */ +function _update_7000_user_role_grant_permissions($rid, array $permissions, $module) { + // Grant new permissions for the role. + foreach ($permissions as $name) { + db_merge('role_permission') + ->key(array( + 'rid' => $rid, + 'permission' => $name, + )) + ->fields(array( + 'module' => $module, + )) + ->execute(); + } +} + /** * @defgroup user-updates-6.x-to-7.x User updates from 6.x to 7.x * @{ @@ -501,77 +548,10 @@ function user_update_7003() { } /** - * Add the user's pictures to the {file_managed} table and make them managed - * files. + * Moved to user_update_7012(). */ -function user_update_7004(&$sandbox) { - - $picture_field = array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => "Foreign key: {file_managed}.fid of user's picture.", - ); - - if (!isset($sandbox['progress'])) { - // Check that the field hasn't been updated in an aborted run of this - // update. - if (!db_field_exists('users', 'picture_fid')) { - // Add a new field for the fid. - db_add_field('users', 'picture_fid', $picture_field); - } - - // Initialize batch update information. - $sandbox['progress'] = 0; - $sandbox['last_user_processed'] = -1; - $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE picture <> ''")->fetchField(); - } - - // As a batch operation move the photos into the {file_managed} table and - // update the {users} records. - $limit = 500; - $result = db_query_range("SELECT uid, picture FROM {users} WHERE picture <> '' AND uid > :uid ORDER BY uid", 0, $limit, array(':uid' => $sandbox['last_user_processed'])); - foreach ($result as $user) { - // Don't bother adding files that don't exist. - if (file_exists($user->picture)) { - - // Check if the file already exists. - $files = file_load_multiple(array(), array('uri' => $user->picture)); - if (count($files)) { - $file = reset($files); - } - else { - // Create a file object. - $file = new stdClass(); - $file->uri = $user->picture; - $file->filename = basename($file->uri); - $file->filemime = file_get_mimetype($file->uri); - $file->uid = $user->uid; - $file->status = FILE_STATUS_PERMANENT; - $file = file_save($file); - } - - db_update('users') - ->fields(array('picture_fid' => $file->fid)) - ->condition('uid', $user->uid) - ->execute(); - } - - // Update our progress information for the batch update. - $sandbox['progress']++; - $sandbox['last_user_processed'] = $user->uid; - } - - // Indicate our current progress to the batch update system. If there's no - // max value then there's nothing to update and we're finished. - $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); - - // When we're finished, drop the old picture field and rename the new one to - // replace it. - if (isset($sandbox['#finished']) && $sandbox['#finished'] == 1) { - db_drop_field('users', 'picture'); - db_change_field('users', 'picture_fid', 'picture', $picture_field); - } +function user_update_7004() { + // This doesn't affect any subsequent user updates. } /** @@ -714,6 +694,108 @@ function user_update_7011() { return $message; } +/** + * Add the user's pictures to the {file_managed} table and make them managed + * files. + */ +function user_update_7012(&$sandbox) { + + $picture_field = array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => "Foreign key: {file_managed}.fid of user's picture.", + ); + + if (!isset($sandbox['progress'])) { + // Check that the field hasn't been updated in an aborted run of this + // update. + if (!db_field_exists('users', 'picture_fid')) { + // Add a new field for the fid. + db_add_field('users', 'picture_fid', $picture_field); + } + + // Initialize batch update information. + $sandbox['progress'] = 0; + $sandbox['last_user_processed'] = -1; + $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE picture <> ''")->fetchField(); + } + + // As a batch operation move the photos into the {file_managed} table and + // update the {users} records. + $limit = 500; + $result = db_query_range("SELECT uid, picture FROM {users} WHERE picture <> '' AND uid > :uid ORDER BY uid", 0, $limit, array(':uid' => $sandbox['last_user_processed'])); + foreach ($result as $user) { + // Don't bother adding files that don't exist. + if (file_exists($user->picture)) { + + // Check if the file already exists. + $files = file_load_multiple(array(), array('uri' => $user->picture)); + if (count($files)) { + $file = reset($files); + } + else { + // Create a file object. + $file = new stdClass(); + $file->uri = $user->picture; + $file->filename = basename($file->uri); + $file->filemime = file_get_mimetype($file->uri); + $file->uid = $user->uid; + $file->status = FILE_STATUS_PERMANENT; + $file = file_save($file); + } + + db_update('users') + ->fields(array('picture_fid' => $file->fid)) + ->condition('uid', $user->uid) + ->execute(); + } + + // Update our progress information for the batch update. + $sandbox['progress']++; + $sandbox['last_user_processed'] = $user->uid; + } + + // Indicate our current progress to the batch update system. If there's no + // max value then there's nothing to update and we're finished. + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); + + // When we're finished, drop the old picture field and rename the new one to + // replace it. + if (isset($sandbox['#finished']) && $sandbox['#finished'] == 1) { + db_drop_field('users', 'picture'); + db_change_field('users', 'picture_fid', 'picture', $picture_field); + } +} + +/** + * Add user module file usage entries. + */ +function user_update_7013(&$sandbox) { + if (!isset($sandbox['progress'])) { + // Initialize batch update information. + $sandbox['progress'] = 0; + $sandbox['last_uid_processed'] = -1; + $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} u WHERE u.picture <> 0")->fetchField(); + } + + // Add usage entries for the user picture files. + $limit = 500; + $result = db_query_range('SELECT f.*, u.uid as user_uid FROM {users} u INNER JOIN {file_managed} f ON u.picture = f.fid WHERE u.picture <> 0 AND u.uid > :uid ORDER BY u.uid', 0, $limit, array(':uid' => $sandbox['last_uid_processed']))->fetchAllAssoc('fid', PDO::FETCH_ASSOC); + foreach ($result as $row) { + $uid = $row['user_uid']; + $file = (object) $row; + file_usage_add($file, 'user', 'user', $uid); + + // Update our progress information for the batch update. + $sandbox['progress']++; + $sandbox['last_uid_processed'] = $uid; + } + + // Indicate our current progress to the batch update system. + $sandbox['#finished'] = empty($sandbox['max']) || ($sandbox['progress'] / $sandbox['max']); +} + /** * @} 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.js b/modules/user/user.js index 410a70edbcacb22e2fbcf1ba819f4d22ca62d1ce..1336d44735da3f1e9139ee44fcf21712c63839e8 100644 --- a/modules/user/user.js +++ b/modules/user/user.js @@ -1,4 +1,4 @@ -// $Id: user.js,v 1.23 2010/02/16 01:05:52 webchick Exp $ +// $Id: user.js,v 1.24 2010/07/20 10:17:59 dries Exp $ (function ($) { /** @@ -13,9 +13,7 @@ Drupal.behaviors.password = { var innerWrapper = $(this).parent(); var outerWrapper = $(this).parent().parent(); - // Add the password strength layers. - var passwordStrength = $('span.password-strength', innerWrapper); - var passwordResult = $('span.password-result', passwordStrength); + // Add identifying class to password element parent. innerWrapper.addClass('password-parent'); // Add the password confirmation layer. @@ -25,10 +23,10 @@ Drupal.behaviors.password = { var confirmChild = $('span', confirmResult); // Add the description box. - var passwordMeter = '<div id="password-strength"><div id="password-strength-text" aria-live="assertive"></div><div class="password-strength-title">' + translate.strengthTitle + '</div><div id="password-indicator"><div id="indicator"></div></div></div>'; + var passwordMeter = '<div class="password-strength"><div class="password-strength-text" aria-live="assertive"></div><div class="password-strength-title">' + translate['strengthTitle'] + '</div><div class="password-indicator"><div class="indicator"></div></div></div>'; $(confirmInput).parent().after('<div class="password-suggestions description"></div>'); $(innerWrapper).prepend(passwordMeter); - var passwordDescription = $("div.password-suggestions", outerWrapper).hide(); + var passwordDescription = $('div.password-suggestions', outerWrapper).hide(); // Check the password strength. var passwordCheck = function () { @@ -50,10 +48,10 @@ Drupal.behaviors.password = { } // Adjust the length of the strength indicator. - $('#indicator').css('width', result.strength + '%'); + $(innerWrapper).find('.indicator').css('width', result.strength + '%'); // Update the strength indication text. - $("#password-strength-text").html(result.indicatorText); + $(innerWrapper).find('.password-strength-text').html(result.indicatorText); passwordCheckMatch(); }; diff --git a/modules/user/user.module b/modules/user/user.module index fcb915a24cfe18d6e6c22f793bf7803d768a43e4..2e84c6e169867906e65a436b36ae603a9c96c501 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -1,5 +1,5 @@ <?php -// $Id: user.module,v 1.1180 2010/07/07 08:05:01 webchick Exp $ +// $Id: user.module,v 1.1200 2010/09/11 06:03:12 webchick Exp $ /** * @file @@ -59,7 +59,7 @@ function user_help($path, $arg) { return '<p>' . t('Permissions let you control what users can do and see on your site. You can define a specific set of permissions for each role. (See the <a href="@role">Roles</a> page to create a role). Two important roles to consider are Authenticated Users and Administrators. Any permissions granted to the Authenticated Users role will be given to any user who can log into your site. You can make any role the Administrator role for the site, meaning this will be granted all new permissions automatically. You can do this on the <a href="@settings">User Settings</a> page. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('@role' => url('admin/people/permissions/roles'), '@settings' => url('admin/config/people/accounts'))) . '</p>'; case 'admin/people/permissions/roles': $output = '<p>' . t('Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined on the <a href="@permissions">permissions page</a>. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the names and order of the roles on your site. It is recommended to order your roles from least permissive (anonymous user) to most permissive (administrator). To delete a role choose "edit role".', array('@permissions' => url('admin/people/permissions'))) . '</p>'; - $output .= '<p>'. t('By default, Drupal comes with two user roles:') . '</p>'; + $output .= '<p>' . t('By default, Drupal comes with two user roles:') . '</p>'; $output .= '<ul>'; $output .= '<li>' . t("Anonymous user: this role is used for users that don't have a user account or that are not authenticated.") . '</li>'; $output .= '<li>' . t('Authenticated user: this role is automatically granted to all logged in users.') . '</li>'; @@ -146,6 +146,7 @@ function user_entity_info() { 'controller class' => 'UserController', 'base table' => 'users', 'uri callback' => 'user_uri', + 'label callback' => 'format_username', 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'uid', @@ -427,24 +428,34 @@ function user_save($account, $edit = array(), $category = 'account') { } // Process picture uploads. - if (!empty($edit['picture']->fid)) { + if (!$delete_previous_picture = empty($edit['picture']->fid)) { $picture = $edit['picture']; // If the picture is a temporary file move it to its final location and // make it permanent. - if (($picture->status & FILE_STATUS_PERMANENT) == 0) { + if (!$picture->status) { $info = image_get_info($picture->uri); - $picture_directory = variable_get('file_default_scheme', 'public') . '://' . variable_get('user_picture_path', 'pictures'); + $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures'); // Prepare the pictures directory. file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY); - $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '.' . $info['extension']); + $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']); - if ($picture = file_move($picture, $destination, FILE_EXISTS_REPLACE)) { - $picture->status |= FILE_STATUS_PERMANENT; + // Move the temporary file into the final location. + if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) { + $delete_previous_picture = TRUE; + $picture->status = FILE_STATUS_PERMANENT; $edit['picture'] = file_save($picture); + file_usage_add($picture, 'user', 'user', $account->uid); } } } + + // Delete the previous picture if it was deleted or replaced. + if ($delete_previous_picture && !empty($account->picture->fid)) { + file_usage_delete($account->picture, 'user', 'user', $account->uid); + file_delete($account->picture); + } + $edit['picture'] = empty($edit['picture']->fid) ? 0 : $edit['picture']->fid; // Do not allow 'uid' to be changed. @@ -457,13 +468,6 @@ function user_save($account, $edit = array(), $category = 'account') { return FALSE; } - // If the picture changed or was unset, remove the old one. This step needs - // to occur after updating the {users} record so that user_file_references() - // doesn't report it in use and block the deletion. - if (!empty($account->picture->fid) && ($edit['picture'] != $account->picture->fid)) { - file_delete($account->picture); - } - // Reload user roles if provided. if (isset($edit['roles']) && is_array($edit['roles'])) { db_delete('users_roles') @@ -829,15 +833,18 @@ function user_file_download($uri) { } /** - * Implements hook_file_references(). + * Implements hook_file_move(). */ -function user_file_references($file) { - // Determine if the file is used by this module. - $file_used = (bool) db_query_range('SELECT 1 FROM {users} WHERE picture = :fid', 0, 1, array(':fid' => $file->fid))->fetchField(); - if ($file_used) { - // Return the name of the module and how many references it has to the file. - // If file is still used then 1 is enough to indicate this. - return array('user' => 1); +function user_file_move($file, $source) { + // If a user's picture is replaced with a new one, update the record in + // the users table. + if (isset($file->fid) && isset($source->fid) && $file->fid != $source->fid) { + db_update('users') + ->fields(array( + 'picture' => $file->fid, + )) + ->condition('picture', $source->fid) + ->execute(); } } @@ -871,7 +878,7 @@ function user_search_access() { /** * Implements hook_search_execute(). */ -function user_search_execute($keys = NULL) { +function user_search_execute($keys = NULL, $conditions = NULL) { $find = array(); // Replace wildcards with MySQL/PostgreSQL wildcards. $keys = preg_replace('!\*+!', '%', $keys); @@ -946,7 +953,7 @@ function user_user_view($account) { * @see user_account_form_validate() * @see user_validate_current_pass() * @see user_validate_picture() - * @see user_validate_email() + * @see user_validate_mail() */ function user_account_form(&$form, &$form_state) { global $user; @@ -960,8 +967,6 @@ function user_account_form(&$form, &$form_state) { // Account information. $form['account'] = array( - '#type' => 'fieldset', - '#title' => t('Account information'), '#weight' => -10, ); // Only show name field on registration form or user can change own username. @@ -1115,7 +1120,7 @@ function user_account_form(&$form, &$form_state) { '#type' => 'file', '#title' => t('Upload picture'), '#size' => 48, - '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')), + '#description' => t('Your virtual face or picture. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')), ); $form['#validate'][] = 'user_validate_picture'; } @@ -1783,10 +1788,15 @@ function user_menu_site_status_alter(&$menu_site_status, $path) { } /** - * Implements hook_init(). + * Implements hook_admin_paths(). */ -function user_init() { - drupal_add_css(drupal_get_path('module', 'user') . '/user.css'); +function user_admin_paths() { + $paths = array( + 'user/*/cancel' => TRUE, + 'user/*/edit' => TRUE, + 'user/*/edit/*' => TRUE, + ); + return $paths; } /** @@ -2282,10 +2292,7 @@ function _user_cancel($edit, $account, $method) { if (!empty($edit['user_cancel_notify'])) { _user_mail_notify('status_blocked', $account); } - db_update('users') - ->fields(array('status' => 0)) - ->condition('uid', $account->uid) - ->execute(); + user_save($account, array('status' => 0)); drupal_set_message(t('%name has been disabled.', array('%name' => $account->name))); watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE); break; @@ -2307,9 +2314,6 @@ function _user_cancel($edit, $account, $method) { // Destroy the current session, and reset $user to the anonymous user. session_destroy(); } - else { - drupal_session_destroy_uid($account->uid); - } // Clear the cache for anonymous users. cache_clear_all(); @@ -2338,6 +2342,7 @@ function user_delete_multiple(array $uids) { foreach ($accounts as $uid => $account) { module_invoke_all('user_delete', $account); field_attach_delete('user', $account); + drupal_session_destroy_uid($account->uid); } db_delete('users') @@ -2542,7 +2547,7 @@ Your account at [site:name] has been activated. You may now log in by clicking this link or copying and pasting it into your browser: -[site:login-url] +[user:one-time-login-url] This link can only be used once to log in and will lead you to a page where you can set your password. @@ -2860,6 +2865,8 @@ function user_role_grant_permissions($rid, array $permissions = array()) { ->key(array( 'rid' => $rid, 'permission' => $name, + )) + ->fields(array( 'module' => $modules[$name], )) ->execute(); @@ -3199,13 +3206,13 @@ function user_build_filter_query(SelectQuery $query) { if (user_access($value, $account)) { continue; } - $user_role_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); - $permission_alias = $query->join('role_permission', 'p', $user_role_alias . '.rid = %alias.rid'); + $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); + $permission_alias = $query->join('role_permission', 'p', $users_roles_alias . '.rid = %alias.rid'); $query->condition($permission_alias . '.permission', $value); } - else if ($key == 'role') { - $user_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); - $query->condition($user_role_alias . '.rid' , $value); + elseif ($key == 'role') { + $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); + $query->condition($users_roles_alias . '.rid' , $value); } else { $query->condition($filters[$key]['field'], $value); @@ -3361,7 +3368,12 @@ function user_form_process_password_confirm($element) { ); $element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js'; - $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting'); + // Ensure settings are only added once per page. + static $already_added = FALSE; + if (!$already_added) { + $already_added = TRUE; + $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting'); + } return $element; } @@ -3438,12 +3450,8 @@ function user_block_user_action(&$entity, $context = array()) { else { $uid = $GLOBALS['user']->uid; } - db_update('users') - ->fields(array('status' => 0)) - ->condition('uid', $uid) - ->execute(); - drupal_session_destroy_uid($uid); $account = user_load($uid); + $account = user_save($account, array('status' => 0)); watchdog('action', 'Blocked user %name.', array('%name' => $account->name)); } @@ -3471,8 +3479,6 @@ function user_register_form($form, &$form_state) { $form['#attached']['library'][] = array('system', 'cookie'); $form['#attributes']['class'][] = 'user-info-from-cookie'; - $form['#pre_render'] = array('user_register_form_pre_render'); - // Start with the default user account fields. user_account_form($form, $form_state); @@ -3494,20 +3500,6 @@ function user_register_form($form, &$form_state) { return $form; } -/** - * Form pre-render callback to clean up the user_register_form. - * - * If the "account" fieldset is the only element at the top level (apart from - * the submit button), its borders are hidden for aesthetic reasons. - */ -function user_register_form_pre_render($form) { - $visible_children = element_get_visible_children($form); - if (!count(array_diff($visible_children, array('account', 'submit')))) { - $form['account']['#theme_wrappers'] = array(); - } - return $form; -} - /** * Submit handler for the user registration form. * @@ -3677,3 +3669,12 @@ function user_rdf_mapping() { ), ); } + +/** + * Implements hook_file_download_access(). + */ +function user_file_download_access($field, $entity_type, $entity) { + if ($entity_type == 'user') { + return user_view_access($entity); + } +} diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 65f96dd48d97c3b0359ed1b5e5702e06cd0cded2..0f60a3a69e16f24347b8de94babcbdd11d88e36e 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: user.pages.inc,v 1.73 2010/06/17 13:44:45 dries Exp $ +// $Id: user.pages.inc,v 1.75 2010/08/27 11:28:45 webchick Exp $ /** * @file @@ -302,13 +302,19 @@ function user_profile_form_submit($form, &$form_state) { // Remove unneeded values. form_state_values_clean($form_state); + // Before updating the account entity, keep an unchanged copy for use with + // user_save() later. This is necessary for modules implementing the user + // hooks to be able to react on changes by comparing the values of $account + // and $edit. + $account_unchanged = clone $account; + entity_form_submit_build_entity('user', $account, $form, $form_state); // Populate $edit with the properties of $account, which have been edited on // this form by taking over all values, which appear in the form values too. $edit = array_intersect_key((array) $account, $form_state['values']); - user_save($account, $edit, $category); + user_save($account_unchanged, $edit, $category); $form_state['values']['uid'] = $account->uid; if ($category == 'account' && !empty($edit['pass'])) { @@ -491,7 +497,6 @@ function user_cancel_methods() { '#return_value' => $name, '#default_value' => $default_method, '#parents' => array('user_cancel_method'), - '#required' => TRUE, ); } return $form; diff --git a/modules/user/user.test b/modules/user/user.test index 75da3a92975333112fd7cc712f5ab9ef30e188dd..d89dac1d4bb45adbb42e9425b9f287c24b8c6044 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -1,5 +1,5 @@ <?php -// $Id: user.test,v 1.95 2010/06/10 06:57:20 dries Exp $ +// $Id: user.test,v 1.101 2010/08/27 11:28:45 webchick Exp $ class UserRegistrationTestCase extends DrupalWebTestCase { public static function getInfo() { @@ -136,14 +136,6 @@ class UserRegistrationTestCase extends DrupalWebTestCase { $this->assertEqual($new_user->language, '', t('Correct language field.')); $this->assertEqual($new_user->picture, '', t('Correct picture field.')); $this->assertEqual($new_user->init, $mail, t('Correct init field.')); - - // Make the user timezone configurable, which will create a second fieldset - // on the registration page and cause the account information elements to - // be put in a fieldset. - variable_set('user_default_timezone', DRUPAL_USER_TIMEZONE_SELECT); - $this->drupalLogout(); - $this->drupalGet('user/register'); - $this->assertText(t('Account information'), t('Account settings fieldset was not hidden.')); } } @@ -914,6 +906,13 @@ class UserPictureTestCase extends DrupalWebTestCase { // Check if file is located in proper directory. $this->assertTrue(is_file($pic_path), t('File is located in proper directory')); + + // Set new picture dimensions. + $test_dim = ($info['width'] + 5) . 'x' . ($info['height'] + 5); + variable_set('user_picture_dimensions', $test_dim); + + $pic_path2 = $this->saveUserPicture($image); + $this->assertNotEqual($pic_path, $pic_path2, t('Filename of second picture is different.')); } } @@ -921,11 +920,9 @@ class UserPictureTestCase extends DrupalWebTestCase { $edit = array('files[picture_upload]' => drupal_realpath($image->uri)); $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); - $img_info = image_get_info($image->uri); - $picture_dir = variable_get('user_picture_path', 'pictures'); - $pic_path = 'public://' . $picture_dir . '/picture-' . $this->user->uid . '.' . $img_info['extension']; - - return $pic_path; + // Load actual user data from database. + $account = user_load($this->user->uid, TRUE); + return isset($account->picture) ? $account->picture->uri : NULL; } } @@ -1065,19 +1062,48 @@ class UserAdminTestCase extends DrupalWebTestCase { $this->drupalPost('admin/people', $edit, t('Filter')); // Check if the correct users show up. - $this->assertNoText($user_a->name, t('User A not on filtered by perm admin users page')); + $this->assertNoText($user_a->name, t('User A not on filtered by perm admin users page')); $this->assertText($user_b->name, t('Found user B on filtered by perm admin users page')); $this->assertText($user_c->name, t('Found user C on filtered by perm admin users page')); + // Filter the users by role. Grab the system-generated role name for User C. + $edit['role'] = max(array_flip($user_c->roles)); + $this->drupalPost('admin/people', $edit, t('Refine')); + + // Check if the correct users show up when filtered by role. + $this->assertNoText($user_a->name, t('User A not on filtered by role on admin users page')); + $this->assertNoText($user_b->name, t('User B not on filtered by role on admin users page')); + $this->assertText($user_c->name, t('User C on filtered by role on admin users page')); + // Test blocking of a user. - $account = user_load($user_b->uid); - $this->assertEqual($account->status, 1, 'User B not blocked'); + $account = user_load($user_c->uid); + $this->assertEqual($account->status, 1, 'User C not blocked'); $edit = array(); $edit['operation'] = 'block'; $edit['accounts[' . $account->uid . ']'] = TRUE; $this->drupalPost('admin/people', $edit, t('Update')); - $account = user_load($user_b->uid, TRUE); - $this->assertEqual($account->status, 0, 'User B blocked'); + $account = user_load($user_c->uid, TRUE); + $this->assertEqual($account->status, 0, 'User C blocked'); + + // Test unblocking of a user from /admin/people page and sending of activation mail + $editunblock = array(); + $editunblock['operation'] = 'unblock'; + $editunblock['accounts[' . $account->uid . ']'] = TRUE; + $this->drupalPost('admin/people', $editunblock, t('Update')); + $account = user_load($user_c->uid, TRUE); + $this->assertEqual($account->status, 1, 'User C unblocked'); + $this->assertMail("to", $account->mail, "Activation mail sent to user C"); + + // Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail + $user_d = $this->drupalCreateUser(array()); + $account1 = user_load($user_d->uid, TRUE); + $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => 0), t('Save')); + $account1 = user_load($user_d->uid, TRUE); + $this->assertEqual($account1->status, 0, 'User D blocked'); + $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => TRUE), t('Save')); + $account1 = user_load($user_d->uid, TRUE); + $this->assertEqual($account1->status, 1, 'User D unblocked'); + $this->assertMail("to", $account1->mail, "Activation mail sent to user D"); } } diff --git a/profiles/minimal/minimal.info b/profiles/minimal/minimal.info index 8d0a77ea4f1097a3883974e22bebd53fad2a7381..740efaee8da49dc6463bdd7a126c9252be32540e 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/profiles/minimal/minimal.profile b/profiles/minimal/minimal.profile index 2cadf8822188c00970bc23d312c36073c64979e8..8fb1b82f6940f90cc51a58f9e326c73b6532acb8 100644 --- a/profiles/minimal/minimal.profile +++ b/profiles/minimal/minimal.profile @@ -1,11 +1,12 @@ <?php -// $Id: minimal.profile,v 1.1 2010/01/04 23:08:34 webchick Exp $ +// $Id: minimal.profile,v 1.2 2010/07/22 16:16:42 dries Exp $ /** - * Implements hook_form_alter(). + * Implements hook_form_FORM_ID_alter(). * * Allows the profile to alter the site configuration form. */ function minimal_form_install_configure_form_alter(&$form, $form_state) { + // Pre-populate the site name with the server name. $form['site_information']['site_name']['#default_value'] = $_SERVER['SERVER_NAME']; } diff --git a/profiles/standard/standard.info b/profiles/standard/standard.info index f64d09c8972c4806ee9ee5b0b0238f7c0977d3a3..3624742c617ebc69e3790447e7886d6c3a72a7c8 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install index cf459ce33eac7601992d876237d4ce6299df07b5..a93b6b3bdd2bf5759baff9f52278ece817acd4d1 100644 --- a/profiles/standard/standard.install +++ b/profiles/standard/standard.install @@ -1,5 +1,5 @@ <?php -// $Id: standard.install,v 1.18 2010/07/08 03:41:27 webchick Exp $ +// $Id: standard.install,v 1.23 2010/09/07 18:01:32 webchick Exp $ /** * Implements hook_install(). @@ -247,7 +247,7 @@ function standard_install() { 'mapping' => array( 'rdftype' => array('sioc:Item', 'foaf:Document'), 'field_image' => array( - 'predicates' => array('rdfs:seeAlso'), + 'predicates' => array('og:image', 'rdfs:seeAlso'), 'type' => 'rel', ), 'field_tags' => array( @@ -359,7 +359,7 @@ function standard_install() { 'entity_type' => 'node', 'label' => 'Image', 'bundle' => 'article', - 'description' => 'Upload an image to go with this article.', + 'description' => st('Upload an image to go with this article.'), 'required' => FALSE, 'settings' => array( @@ -384,12 +384,14 @@ function standard_install() { 'display' => array( 'default' => array( 'label' => 'hidden', - 'type' => 'image__large', + 'type' => 'image', + 'settings' => array('image_style' => 'large', 'image_link' => ''), 'weight' => -1, ), 'teaser' => array( 'label' => 'hidden', - 'type' => 'image_link_content__medium', + 'type' => 'image', + 'settings' => array('image_style' => 'medium', 'image_link' => 'content'), 'weight' => -1, ), ), @@ -398,7 +400,7 @@ function standard_install() { // Enable default permissions for system roles. $filtered_html_permission = filter_permission_name($filtered_html_format); - user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content', $filtered_html_permission)); + user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content', 'access comments', $filtered_html_permission)); user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access content', 'access comments', 'post comments', 'post comments without approval', $filtered_html_permission)); // Create a default role for site administrators, with all available permissions assigned. @@ -415,6 +417,14 @@ function standard_install() { ->fields(array('uid' => 1, 'rid' => $admin_role->rid)) ->execute(); + // Create a Home link in the main menu. + $item = array( + 'link_title' => 'Home', + 'link_path' => '<front>', + 'menu_name' => 'main-menu', + ); + menu_link_save($item); + // Update the menu router information. menu_rebuild(); diff --git a/profiles/standard/standard.profile b/profiles/standard/standard.profile index 8fb2f45091810ffca2a0546952780011eb38a196..97d5d8768a910288664c463baf10eaf6de0db4f9 100644 --- a/profiles/standard/standard.profile +++ b/profiles/standard/standard.profile @@ -1,14 +1,12 @@ <?php -// $Id: standard.profile,v 1.1 2010/01/04 23:08:34 webchick Exp $ +// $Id: standard.profile,v 1.2 2010/07/22 16:16:42 dries Exp $ /** - * Implements hook_form_alter(). + * Implements hook_form_FORM_ID_alter(). * * Allows the profile to alter the site configuration form. */ -function standard_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'install_configure_form') { - // Set default for site name field. - $form['site_information']['site_name']['#default_value'] = $_SERVER['SERVER_NAME']; - } +function standard_form_install_configure_form_alter(&$form, $form_state) { + // Pre-populate the site name with the server name. + $form['site_information']['site_name']['#default_value'] = $_SERVER['SERVER_NAME']; } diff --git a/profiles/testing/testing.info b/profiles/testing/testing.info new file mode 100644 index 0000000000000000000000000000000000000000..e0948bcb276a815f565b99b2ae452f5796418feb --- /dev/null +++ b/profiles/testing/testing.info @@ -0,0 +1,12 @@ +; $Id: testing.info,v 1.2 2010/08/27 12:36:53 webchick Exp $ +name = Testing +description = Minimal profile for running tests. Includes absolutely required modules only. +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" +project = "drupal" +datestamp = "1284599761" + diff --git a/profiles/testing/testing.install b/profiles/testing/testing.install new file mode 100644 index 0000000000000000000000000000000000000000..6426140c858d8f1b52ac7f05451439d9d11a340a --- /dev/null +++ b/profiles/testing/testing.install @@ -0,0 +1,16 @@ +<?php +// $Id: testing.install,v 1.2 2010/08/27 12:36:53 webchick Exp $ + +/** + * Implements hook_install(). + * + * Perform actions to set up the site for this profile. + */ +function testing_install() { + // Allow visitor account creation, but with administrative approval. + variable_set('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL); + + // Enable default permissions for system roles. + user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content')); + user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access content')); +} diff --git a/profiles/testing/testing.profile b/profiles/testing/testing.profile new file mode 100644 index 0000000000000000000000000000000000000000..5d804dec3dd93d83cd0b5aaada0ebbe6a5734c9d --- /dev/null +++ b/profiles/testing/testing.profile @@ -0,0 +1,3 @@ +<?php +// $Id: testing.profile,v 1.1 2010/08/22 15:31:18 dries Exp $ + diff --git a/scripts/dump-database-d6.sh b/scripts/dump-database-d6.sh index 4ea0e36009daee0541ab141b71b5897c366471b2..0feb63471369f53ac1818620166fd152f3bee153 100644 --- a/scripts/dump-database-d6.sh +++ b/scripts/dump-database-d6.sh @@ -1,6 +1,6 @@ #!/usr/bin/env php <?php -// $Id: dump-database-d6.sh,v 1.2 2010/06/29 00:21:00 webchick Exp $ +// $Id: dump-database-d6.sh,v 1.3 2010/07/30 01:28:00 dries Exp $ /** * Dump a Drupal 6 database into a Drupal 7 PHP script to test the upgrade @@ -32,7 +32,24 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); include_once dirname(__FILE__) . '/../includes/utility.inc'; // Output the PHP header. -$output = "<?php\n\n"; +$output = <<<ENDOFHEADER +<?php +// \$Id\$ + +/** + * @file + * Filled installation of Drupal 6.17, for test purposes. + * + * This file was generated by the dump-database-d6.sh tool, from an + * installation of Drupal 6, filled with data using the generate-d6-content.sh + * tool. It has the following modules installed: + +ENDOFHEADER; + +foreach (module_list() as $module) { + $output .= " * - $module\n"; +} +$output .= " */\n\n"; // Get the current schema, order it by table name. $schema = drupal_get_schema(); diff --git a/scripts/generate-d6-content.sh b/scripts/generate-d6-content.sh new file mode 100644 index 0000000000000000000000000000000000000000..b58197558b7b917aca036220dadeaddbda5a0540 --- /dev/null +++ b/scripts/generate-d6-content.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env php +<?php +// $Id: generate-d6-content.sh,v 1.3 2010/09/11 00:39:49 webchick Exp $ + +/** + * Generate content for a Drupal 6 database to test the upgrade process. + * + * Run this script at the root of an existing Drupal 6 installation. + * Steps to use this generation script: + * - Install drupal 6. + * - Run this script from your Drupal ROOT directory. + * - Use the dump-database-d6.sh to generate the D7 file + * modules/simpletest/tests/upgrade/database.filled.php + */ + +// Define settings. +$cmd = 'index.php'; +$_SERVER['HTTP_HOST'] = 'default'; +$_SERVER['PHP_SELF'] = '/index.php'; +$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; +$_SERVER['SERVER_SOFTWARE'] = NULL; +$_SERVER['REQUEST_METHOD'] = 'GET'; +$_SERVER['QUERY_STRING'] = ''; +$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/'; +$_SERVER['HTTP_USER_AGENT'] = 'console'; +$modules_to_enable = array('path', 'poll'); + +// Bootstrap Drupal. +include_once './includes/bootstrap.inc'; +drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + +// Enable requested modules +include_once './modules/system/system.admin.inc'; +$form = system_modules(); +foreach ($modules_to_enable as $module) { + $form_state['values']['status'][$module] = TRUE; +} +$form_state['values']['disabled_modules'] = $form['disabled_modules']; +system_modules_submit(NULL, $form_state); +unset($form_state); + +// Run cron after installing +drupal_cron_run(); + +// Create six users +for ($i = 0; $i < 6; $i++) { + $name = "test user $i"; + $pass = md5("test PassW0rd $i !(.)"); + $mail = "test$i@example.com"; + $now = mktime(0, 0, 0, 1, $i + 1, 2010); + db_query("INSERT INTO {users} (name, pass, mail, status, created, access) VALUES ('%s', '%s', '%s', %d, %d, %d)", $name, $pass, $mail, 1, $now, $now); +} + + +// Create vocabularies and terms + +$terms = array(); + +// All possible combinations of these vocabulary properties. +$hierarchy = array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2); +$multiple = array(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1); +$required = array(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + +$voc_id = 0; +$term_id = 0; +for ($i = 0; $i < 24; $i++) { + $vocabulary = array(); + ++$voc_id; + $vocabulary['name'] = "vocabulary $voc_id (i=$i)"; + $vocabulary['description'] = "description of ". $vocabulary['name']; + $vocabulary['nodes'] = $i > 11 ? array('page' => TRUE) : array(); + $vocabulary['multiple'] = $multiple[$i % 12]; + $vocabulary['required'] = $required[$i % 12]; + $vocabulary['relations'] = 1; + $vocabulary['hierarchy'] = $hierarchy[$i % 12]; + $vocabulary['weight'] = $i; + taxonomy_save_vocabulary($vocabulary); + $parents = array(); + // Vocabularies without hierarcy get one term, single parent vocabularies get + // one parent and one child term. Multiple parent vocabularies get three + // terms: t0, t1, t2 where t0 is a parent of both t1 and t2. + for ($j = 0; $j < $vocabulary['hierarchy'] + 1; $j++) { + $term = array(); + $term['vid'] = $vocabulary['vid']; + // For multiple parent vocabularies, omit the t0-t1 relation, otherwise + // every parent in the vocabulary is a parent. + $term['parent'] = $vocabulary['hierarchy'] == 2 && i == 1 ? array() : $parents; + ++$term_id; + $term['name'] = "term $term_id of vocabulary $voc_id (j=$j)"; + $term['description'] = 'description of ' . $term['name']; + $term['weight'] = $i * 3 + $j; + taxonomy_save_term($term); + $terms[] = $term['tid']; + $parents[] = $term['tid']; + } +} + +$node_id = 0; +$revision_id = 0; +module_load_include('inc', 'node', 'node.pages'); +for ($i = 0; $i < 24; $i++) { + $uid = intval($i / 8) + 3; + $user = user_load($uid); + $node = new stdClass; + $node->uid = $uid; + $node->type = $i < 12 ? 'page' : 'story'; + $node->sticky = 0; + ++$node_id; + ++$revision_id; + $node->title = "node title $node_id rev $revision_id (i=$i)"; + $type = node_get_types('type', $node->type); + if ($type->has_body) { + $node->body = str_repeat("node body ($node->type) - $i", 100); + $node->teaser = node_teaser($node->body); + $node->filter = variable_get('filter_default_format', 1); + $node->format = FILTER_FORMAT_DEFAULT; + } + $node->status = intval($i / 4) % 2; + $node->language = ''; + $node->revision = $i < 12; + $node->promote = $i % 2; + $node->created = $now + $i * 86400; + $node->log = "added $i node"; + // Make every term association different a little. For nodes with revisions, + // make the initial revision have a different set of terms than the + // newest revision. + $node_terms = $terms; + unset($node_terms[$i], $node_terms[47 - $i]); + if ($node->revision) { + $node->taxonomy = array($i => $terms[$i], 47-$i => $terms[47 - $i]); + } + else { + $node->taxonomy = $node_terms; + } + node_save($node); + path_set_alias("node/$node->nid", "content/$node->created"); + if ($node->revision) { + $user = user_load($uid + 3); + ++$revision_id; + $node->title .= " rev2 $revision_id"; + $node->body = str_repeat("node revision body ($node->type) - $i", 100); + $node->log = "added $i revision"; + $node->taxonomy = $node_terms; + node_save($node); + } +} + +// Create poll content +for ($i = 0; $i < 12; $i++) { + $uid = intval($i / 4) + 3; + $user = user_load($uid); + $node = new stdClass; + $node->uid = $uid; + $node->type = 'poll'; + $node->sticky = 0; + $node->title = "poll title $i"; + $type = node_get_types('type', $node->type); + if ($type->has_body) { + $node->body = str_repeat("node body ($node->type) - $i", 100); + $node->teaser = node_teaser($node->body); + $node->filter = variable_get('filter_default_format', 1); + $node->format = FILTER_FORMAT_DEFAULT; + } + $node->status = intval($i / 2) % 2; + $node->language = ''; + $node->revision = 1; + $node->promote = $i % 2; + $node->created = $now + $i * 43200; + $node->log = "added $i poll"; + + $nbchoices = ($i % 4) + 2; + for ($c = 0; $c < $nbchoices; $c++) { + $node->choice[] = array('chtext' => "Choice $c for poll $i"); + } + node_save($node); + path_set_alias("node/$node->nid", "content/poll/$i"); + path_set_alias("node/$node->nid/results", "content/poll/$i/results"); + + // Add some votes + for ($v = 0; $v < ($i % 4) + 5; $v++) { + $c = $v % $nbchoices; + $form_state = array(); + $form_state['values']['choice'] = $c; + $form_state['values']['op'] = t('Vote'); + drupal_execute('poll_view_voting', $form_state, $node); + } +} + +$uid = 6; +$user = user_load($uid); +$node = new stdClass; +$node->uid = $uid; +$node->type = 'broken'; +$node->sticky = 0; +$node->title = "node title 24"; +$node->body = str_repeat("node body ($node->type) - 37", 100); +$node->teaser = node_teaser($node->body); +$node->filter = variable_get('filter_default_format', 1); +$node->format = FILTER_FORMAT_DEFAULT; +$node->status = 1; +$node->language = ''; +$node->revision = 0; +$node->promote = 0; +$node->created = 1263769200; +$node->log = "added $i node"; +node_save($node); +path_set_alias("node/$node->nid", "content/1263769200"); diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index aa39ae8a9e4e8dd3a11617ef248e790b366cc571..d1bb23ad72cff19cd2e2558e053fcc9fefc7b624 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -1,5 +1,5 @@ <?php -// $Id: default.settings.php,v 1.48 2010/06/28 19:57:34 dries Exp $ +// $Id: default.settings.php,v 1.50 2010/08/08 19:35:49 dries Exp $ /** * @file @@ -53,7 +53,7 @@ * * Each database connection is specified as an array of settings, * similar to the following: - * + * @code * array( * 'driver' => 'mysql', * 'database' => 'databasename', @@ -62,7 +62,9 @@ * 'host' => 'localhost', * 'port' => 3306, * 'prefix' => 'myprefix_', + * 'collation' => 'utf8_general_ci', * ); + * @endcode * * The "driver" property indicates what Drupal database driver the * connection should use. This is usually the same as the name of the @@ -86,11 +88,12 @@ * fall back to the single master server. * * The general format for the $databases array is as follows: - * + * @code * $databases['default']['default'] = $info_array; * $databases['default']['slave'][] = $info_array; * $databases['default']['slave'][] = $info_array; * $databases['extra']['default'] = $info_array; + * @endcode * * In the above example, $info_array is an array of settings described above. * The first line sets a "default" database that has one master database @@ -100,7 +103,7 @@ * "extra". * * For a single database configuration, the following is sufficient: - * + * @code * $databases['default']['default'] = array( * 'driver' => 'mysql', * 'database' => 'databasename', @@ -108,7 +111,9 @@ * 'password' => 'password', * 'host' => 'localhost', * 'prefix' => 'main_', + * 'collation' => 'utf8_general_ci', * ); + * @endcode * * You can optionally set prefixes for some or all database table names * by using the 'prefix' setting. If a prefix is specified, the table @@ -117,14 +122,14 @@ * are desired, leave it as an empty string ''. * * To have all database names prefixed, set 'prefix' as a string: - * + * @code * 'prefix' => 'main_', - * + * @endcode * To provide prefixes for specific tables, set 'prefix' as an array. * The array's keys are the table names and the values are the prefixes. * The 'default' element is mandatory and holds the prefix for any tables * not specified elsewhere in the array. Example: - * + * @code * 'prefix' => array( * 'default' => 'main_', * 'users' => 'shared_', @@ -132,13 +137,13 @@ * 'role' => 'shared_', * 'authmap' => 'shared_', * ), - * + * @endcode * You can also use a reference to a schema/database as a prefix. This maybe * useful if your Drupal installation exists in a schema that is not the default * or you want to access several databases from the same code base at the same * time. * Example: - * + * @code * 'prefix' => array( * 'default' => 'main.', * 'users' => 'shared.', @@ -146,10 +151,11 @@ * 'role' => 'shared.', * 'authmap' => 'shared.', * ); - * + * @endcode * NOTE: MySQL and SQLite's definition of a schema is a database. * * Database configuration format: + * @code * $databases['default']['default'] = array( * 'driver' => 'mysql', * 'database' => 'databasename', @@ -170,6 +176,7 @@ * 'driver' => 'sqlite', * 'database' => '/path/to/databasefilename', * ); + * @endcode */ $databases = array(); @@ -312,7 +319,7 @@ ini_set('session.cookie_lifetime', 2000000); * theme. It is located inside 'modules/system/maintenance-page.tpl.php'. * Note: This setting does not apply to installation and update pages. */ -# $conf['maintenance_theme'] = 'garland'; +# $conf['maintenance_theme'] = 'bartik'; /** * Enable this setting to determine the correct IP address of the remote diff --git a/themes/bartik/bartik.info b/themes/bartik/bartik.info index 92e2fd0446d612c250eac272b46a48fb3e74ae57..7885faa2a4ce15acbeb841a253bed876d129fbfe 100644 --- a/themes/bartik/bartik.info +++ b/themes/bartik/bartik.info @@ -1,4 +1,4 @@ -; $Id: bartik.info,v 1.1 2010/07/06 05:25:51 webchick Exp $ +; $Id: bartik.info,v 1.4 2010/09/05 02:21:38 dries Exp $ name = Bartik description = A flexible, recolorable theme with many regions. @@ -11,15 +11,12 @@ stylesheets[all][] = css/layout.css stylesheets[all][] = css/style.css stylesheets[all][] = css/colors.css stylesheets[print][] = css/print.css -stylesheets[all][] = css/maintenance-page.css - -scripts[] = scripts/search.js regions[header] = Header regions[help] = Help regions[page_top] = Page top regions[page_bottom] = Page bottom -regions[highlight] = Highlighted +regions[highlighted] = Highlighted regions[featured] = Featured regions[content] = Content @@ -39,8 +36,8 @@ regions[footer] = Footer settings[shortcut_module_link] = 0 -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/bartik/color/color.inc b/themes/bartik/color/color.inc index 4b8fa17f83e635485ff9ff697016ad8dc0343fc3..f71c9f44e7f6d18b10e0238880fb13fa3e650c4e 100644 --- a/themes/bartik/color/color.inc +++ b/themes/bartik/color/color.inc @@ -1,5 +1,5 @@ <?php -// $Id: color.inc,v 1.1 2010/07/06 05:25:51 webchick Exp $ +// $Id: color.inc,v 1.3 2010/08/18 00:28:08 dries Exp $ // Put the logo path into JavaScript for the live preview. drupal_add_js(array('color' => array('logo' => theme_get_setting('logo', 'bartik'))), 'setting'); @@ -23,31 +23,17 @@ $info = array( 'title' => t('Blue Lagoon (default)'), 'colors' => array( 'bg' => '#ffffff', - 'link' => '#288CC9', + 'link' => '#288cc9', 'top' => '#0779bf', 'bottom' => '#48a9e4', 'text' => '#3b3b3b', 'sidebar' => '#f6f6f2', 'sidebarborders' => '#f9f9f9', - 'footer' => '#2d1e0f', - 'titleslogan' => '#fffeff', - ), - ), - 'Slate' => array( - 'title' => t('Slate'), - 'colors' => array( - 'bg' => '#ffffff', - 'link' => '#0073b6', - 'top' => '#4a4a4a', - 'bottom' => '#4e4e4e', - 'text' => '#3b3b3b', - 'sidebar' => '#feffff', - 'sidebarborders' => '#d0d0d0', - 'footer' => '#161617', - 'titleslogan' => '#fffeff', + 'footer' => '#292929', + 'titleslogan' => '#ffffff', ), ), - 'Firehouse' => array( + 'firehouse' => array( 'title' => t('Firehouse'), 'colors' => array( 'bg' => '#ffffff', @@ -61,7 +47,21 @@ $info = array( 'titleslogan' => '#fffeff', ), ), - 'Plum' => array( + 'ice' => array( + 'title' => t('Ice'), + 'colors' => array( + 'bg' => '#ffffff', + 'link' => '#019dbf', + 'top' => '#d0d0d0', + 'bottom' => '#c2c4c5', + 'text' => '#4a4a4a', + 'sidebar' => '#ffffff', + 'sidebarborders' => '#cccccc', + 'footer' => '#24272c', + 'titleslogan' => '#000000', + ), + ), + 'plum' => array( 'title' => t('Plum'), 'colors' => array( 'bg' => '#fffdf7', @@ -71,22 +71,22 @@ $info = array( 'text' => '#301313', 'sidebar' => '#f8f3e7', 'sidebarborders' => '#e4e3d4', - 'footer' => '#2C2C28', - 'titleslogan' => '#fffeff', + 'footer' => '#2c2c28', + 'titleslogan' => '#ffffff', ), ), - 'Ice' => array( - 'title' => t('Ice'), + 'slate' => array( + 'title' => t('Slate'), 'colors' => array( 'bg' => '#ffffff', - 'link' => '#019DBF', - 'top' => '#d0d0d0', - 'bottom' => '#c2c4c5', - 'text' => '#4A4A4A', - 'sidebar' => '#feffff', - 'sidebarborders' => '#cccccc', - 'footer' => '#24272c', - 'titleslogan' => '#0b0b0b', + 'link' => '#0073b6', + 'top' => '#4a4a4a', + 'bottom' => '#4e4e4e', + 'text' => '#3b3b3b', + 'sidebar' => '#ffffff', + 'sidebarborders' => '#d0d0d0', + 'footer' => '#161617', + 'titleslogan' => '#ffffff', ), ), ), diff --git a/themes/bartik/css/colors.css b/themes/bartik/css/colors.css index 4bbda70ed8bbf9ae8302eccb0a9018d86ecb806c..911d8caed4fff8f1d0fdb0235763d6999ffefa97 100644 --- a/themes/bartik/css/colors.css +++ b/themes/bartik/css/colors.css @@ -1,23 +1,27 @@ -/* $Id: colors.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: colors.css,v 1.5 2010/09/09 15:27:08 webchick Exp $ */ /* ---------- Color Module Styles ----------- */ body { - background-color: #2d1e0f; + background-color: #292929; color: #ffffff; } -html, #page-wrapper, +#main, body.overlay { background-color: #ffffff; color: #3b3b3b; } -#navigation ul.links li.active-trail a { +#main-menu ul.links li.active-trail a { background: #ffffff; } .tabs ul.primary li a.active { background-color: #ffffff; } +.tabs ul.primary li.active a { + background-color: #ffffff; + border-bottom: 1px solid #ffffff; +} #header { background-color: #48a9e4; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#0779bf), to(#48a9e4)); @@ -38,9 +42,10 @@ a:active { border-color: #f9f9f9; } #footer-wrapper { - background: #2d1e0f; + background: #292929; } #header #name-and-slogan, -#header #name-and-slogan a { +#header #name-and-slogan a, +#secondary-menu-links li a { color: #fffeff; } diff --git a/themes/bartik/css/ie-rtl.css b/themes/bartik/css/ie-rtl.css index 8fefa7d0848f96405ce899978c6163805e8ba510..85836bc0f6358da48c6660d676111834751dcba0 100644 --- a/themes/bartik/css/ie-rtl.css +++ b/themes/bartik/css/ie-rtl.css @@ -1,11 +1,8 @@ -/* $Id: ie-rtl.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: ie-rtl.css,v 1.2 2010/08/22 13:58:29 dries Exp $ */ fieldset legend { left: 6px; } -.tabs ul.primary li a.active { - padding: 0 10px 0 7px; -} ul.action-links li a { zoom: 1; } diff --git a/themes/bartik/css/ie.css b/themes/bartik/css/ie.css index 4cbddeee5a6ff5a89b3392bbf0668c2e76399670..3972f451de84aef2c540ac77f83306ec2bf78a99 100644 --- a/themes/bartik/css/ie.css +++ b/themes/bartik/css/ie.css @@ -1,4 +1,4 @@ -/* $Id: ie.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: ie.css,v 1.3 2010/09/02 20:43:18 dries Exp $ */ .block { zoom: 1; @@ -16,9 +16,11 @@ fieldset legend { .tabs ul.primary { height: auto; } -.tabs ul.primary li a.active { - padding: 0 7px 0 10px; /* LTR */ -} #footer-wrapper #footer .block { height: 100%; } +#search-block-form input.form-submit, +#search-form input.form-submit { + text-transform: capitalize; /* Trigger text indent. */ + height: 26px; +} diff --git a/themes/bartik/css/ie6.css b/themes/bartik/css/ie6.css index c664228f0dd35024d3bedcb09af3bbe909f2873d..980f7ed5dd1c04c2dd553eee8f160f98845bcfcd 100644 --- a/themes/bartik/css/ie6.css +++ b/themes/bartik/css/ie6.css @@ -1,4 +1,4 @@ -/* $Id: ie6.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: ie6.css,v 1.2 2010/09/02 20:43:18 dries Exp $ */ #content { overflow: hidden; @@ -9,3 +9,6 @@ .tabs ul.primary { zoom: 1; } +#block-search-form .form-item-search-block-form input { + width: 67%; +} diff --git a/themes/bartik/css/layout-rtl.css b/themes/bartik/css/layout-rtl.css index 40aae3d04723a3b99e447ae6f5a5031d753b8308..6c31f87bf5221f27cd0100f86e410ea1acabb5b7 100644 --- a/themes/bartik/css/layout-rtl.css +++ b/themes/bartik/css/layout-rtl.css @@ -1,15 +1,19 @@ -/* $Id: layout-rtl.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: layout-rtl.css,v 1.3 2010/09/09 15:27:08 webchick Exp $ */ /* ---------- Basic Layout RTL Styles ----------- */ #content, .sidebar, -#triptych-first, -#triptych-middle, -#triptych-last, -#footer-firstcolumn, -#footer-secondcolumn, -#footer-thirdcolumn, -#footer-fourthcolumn { +.region-triptych-first, +.region-triptych-middle, +.region-triptych-last, +.region-footer-firstcolumn, +.region-footer-secondcolumn, +.region-footer-thirdcolumn, +.region-footer-fourthcolumn { float: right; } +#secondary-menu { + left: 0; + right: auto; +} diff --git a/themes/bartik/css/layout.css b/themes/bartik/css/layout.css index b0f138a8ce8a4adaafbd3321be671cbd637c74a3..4e5e8351807ee3fb69e6bb5e81cbf2ae206bf04a 100644 --- a/themes/bartik/css/layout.css +++ b/themes/bartik/css/layout.css @@ -1,9 +1,8 @@ -/* $Id: layout.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: layout.css,v 1.3 2010/09/09 15:27:08 webchick Exp $ */ /* ---------- Basic Layout Styles ----------- */ #header div.section, -#navigation div.section, #featured div.section, #messages, #main, @@ -14,15 +13,24 @@ margin-left: auto; margin-right: auto; } +#header div.section { + position: relative; +} +#secondary-menu { + position: absolute; + right: 0; + top: 0; + width: 480px; +} #content, .sidebar, -#triptych-first, -#triptych-middle, -#triptych-last, -#footer-firstcolumn, -#footer-secondcolumn, -#footer-thirdcolumn, -#footer-fourthcolumn { +.region-triptych-first, +.region-triptych-middle, +.region-triptych-last, +.region-footer-firstcolumn, +.region-footer-secondcolumn, +.region-footer-thirdcolumn, +.region-footer-fourthcolumn { display: inline; float: left; /* LTR */ position: relative; @@ -51,19 +59,19 @@ #breadcrumb { margin: 0 15px; } -#triptych-first, -#triptych-middle, -#triptych-last { +.region-triptych-first, +.region-triptych-middle, +.region-triptych-last { margin: 20px 20px 30px; width: 280px; } #footer-wrapper { padding: 35px 5px 30px; } -#footer-firstcolumn, -#footer-secondcolumn, -#footer-thirdcolumn, -#footer-fourthcolumn { +.region-footer-firstcolumn, +.region-footer-secondcolumn, +.region-footer-thirdcolumn, +.region-footer-fourthcolumn { padding: 0 10px; width: 220px; } diff --git a/themes/bartik/css/maintenance-page.css b/themes/bartik/css/maintenance-page.css index 9c7ca38fc28f44a74d2e1537ddf1e29e57bb2dee..f0aa0a7c679eb50c13af985b5f90fd7d7bf2861b 100644 --- a/themes/bartik/css/maintenance-page.css +++ b/themes/bartik/css/maintenance-page.css @@ -1,4 +1,4 @@ -/* $Id: maintenance-page.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: maintenance-page.css,v 1.4 2010/09/09 15:27:08 webchick Exp $ */ body.maintenance-page { background-color: #fff; @@ -22,9 +22,8 @@ body.maintenance-page { .maintenance-page #main { width: auto; } -.maintenance-page #header div.section, -.maintenance-page #navigation div.section, -.maintenance-page #messages, +.maintenance-page #header div.section, +.maintenance-page #messages, .maintenance-page #main { width: 700px; } @@ -40,13 +39,14 @@ body.maintenance-page { } .maintenance-page #header #name-and-slogan { margin-bottom: 50px; + margin-left: 0; padding-top: 20px; font-size: 90%; } -.maintenance-page #header, -.maintenance-page #header a, -.maintenance-page #header a:hover, -.maintenance-page #header a:hover { +.maintenance-page #header #name-and-slogan, +.maintenance-page #header #name-and-slogan a, +.maintenance-page #header #name-and-slogan a:hover, +.maintenance-page #header #name-and-slogan a:hover { color: #777; } .maintenance-page h1#page-title { diff --git a/themes/bartik/css/print.css b/themes/bartik/css/print.css index f6bf5e70b4f5108c2f1363a9fdfd19c9a8dec050..d6f3131ba8e83c831f4fdb5dcde5ef094240ada5 100644 --- a/themes/bartik/css/print.css +++ b/themes/bartik/css/print.css @@ -1,4 +1,4 @@ -/* $Id: print.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: print.css,v 1.2 2010/09/09 15:27:08 webchick Exp $ */ /* ---------- General Layout ---------- */ @@ -17,7 +17,7 @@ body { } #sidebar-first, #sidebar-second, -#navigation, +.navigation, #toolbar, #footer-wrapper, .tabs, diff --git a/themes/bartik/css/style-rtl.css b/themes/bartik/css/style-rtl.css index ff1b1c777ed382ba7b38823b2450adad2fd1f7c0..ca8f9c467d1b45dd6418e0b8dd85de3423ea2903 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.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: style-rtl.css,v 1.7 2010/09/09 15:27:08 webchick Exp $ */ /* ------------------ Reset Styles ------------------ */ @@ -39,27 +39,22 @@ ul.tips { } /* Menus when in the header region. */ #header div.block-menu, -#header div#block-system-main-menu, -#header div#block-system-secondary-menu { +#header div#block-system-main-menu { float: right; } #header .block-menu li a, -#header #block-system-main-menu li a, -#header #block-system-secondary-menu li a { +#header #block-system-main-menu li a { float: right; border-left: 1px solid #555; - border-right: none; } #header .block-menu li.first a, -#header #block-system-main-menu li.first a, -#header #block-system-secondary-menu li.first a { +#header #block-system-main-menu li.first a { padding-right: 0; padding-left: 12px; } #header .block-menu li.last a, -#header #block-system-main-menu li.last a, -#header #block-system-secondary-menu li.last a { +#header #block-system-main-menu li.last a { padding-left: 0; padding-right: 12px; border-left: none; @@ -82,13 +77,19 @@ ul.tips { margin-right: 10px; } -/* --------------- Main Navigation ------------ */ +/* --------------- Main Menu ------------ */ -#navigation ul.links li.first { +#main-menu ul.links li.first { padding-right: 2px; padding-left: 0; } +/* --------------- Secondary Menu ------------ */ + +#secondary-menu-links { + float: left; +} + /* ----------------- Content ------------------ */ #block-system-main .submitted .user-picture img { @@ -128,6 +129,9 @@ ul.tips { margin-right: 40px; margin-left: 0; } +#comments ul.links li { + padding: 0 0 0.5em; +} /* -------------- Password Meter ------------- */ @@ -144,6 +148,26 @@ ul.tips { clear: right; } +/* ----------------- Buttons ------------------ */ + +input.form-submit, +a.button { + margin-right: 0; + margin-left: 1em; +} + +/* --------------- Search Form ---------------- */ + +#search-form input#edit-keys, +#block-search-form .form-item-search-block-form input { + float: right; +} +#search-block-form input.form-submit, +#search-form input.form-submit { + margin-left: 0; + margin-right: 5px; +} + /* ------------------ Footer ------------------ */ #footer-columns ul { diff --git a/themes/bartik/css/style.css b/themes/bartik/css/style.css index 1ba022d19c78d8e5f621378bd795d549bef6a6a7..3cd9df8344710e4cccd0b589f09ab6267fad0e50 100644 --- a/themes/bartik/css/style.css +++ b/themes/bartik/css/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.1 2010/07/06 05:25:51 webchick Exp $ */ +/* $Id: style.css,v 1.13 2010/09/10 17:56:20 webchick Exp $ */ /* ---------- Overall Specifications ---------- */ @@ -17,6 +17,11 @@ a:link, a:visited { text-decoration: none; } +a:hover, +a:active, +a:focus{ + text-decoration: underline; +} h1, h2, h3, @@ -67,9 +72,11 @@ body, #header, #footer-wrapper, #preview #preview-header, +#skip-link, ul.contextual-links, ul.links, ul.primary, +.item-list .pager, div.field-type-taxonomy-term-reference, div.messages, div.meta, @@ -174,7 +181,8 @@ tr.even { .block ul { margin: 0; } -ul.contextual-links { +ul.contextual-links, +.item-list .pager { font-size: 90%; } ul.menu li { @@ -196,8 +204,44 @@ ul.tips { /* ------------------ Header ------------------ */ #skip-link { + left: 50%; + margin-left: -5.25em; + margin-top: 0; + position: absolute; + width: auto !important; + z-index: 50; +} +#skip-link a, +#skip-link a:link, +#skip-link a:visited { + background: #444; + background: rgba(0, 0, 0, 0.6); + color: #fff; + display: block; + font-size: 0.94em; + height: 1px; + line-height: 1.7; position: absolute; - top: -9999px; + text-decoration: none; + top: -10000px; + width: 1px; + -khtml-border-radius: 0 0 10px 10px; + -moz-border-radius: 0 0 10px 10px; + -webkit-border-top-left-radius: 0; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-left-radius: 10px; + -webkit-border-bottom-right-radius: 10px; + border-radius: 0 0 10px 10px; +} +#skip-link a:hover, +#skip-link a:active, +#skip-link a:focus { + height: auto; + outline: 0; + overflow: visible; + padding: 1px 10px 2px 10px; + position: static; + width: auto; } #header, #preview #preview-header { @@ -207,13 +251,6 @@ ul.tips { #preview #preview-header a { color: #fff; } -#header a:hover, -#header a:focus { - color: #b5b7b9; -} -#header a:active { - color: #adb0bf; -} #header #logo, #preview #preview-header #preview-logo { float: left; /* LTR */ @@ -338,24 +375,24 @@ ul.tips { display: none; } -/* --------------- Main Navigation ------------ */ +/* --------------- Main Menu ------------ */ -#navigation { +#main-menu { padding: 0 15px; clear: both; } -#navigation a { +#main-menu a { color: #d9d9d9; padding: 0.6em 1em 0.4em; } -#navigation ul { +#main-menu ul { padding: 2px 0; } -#navigation ul.links { +#main-menu ul.links { font-size: 105%; padding: 0.6em 0.6em 4px; } -#navigation ul.links li a { +#main-menu ul.links li a { color: #333; background: #ccc; background: rgba(255, 255, 255, 0.7); @@ -369,31 +406,42 @@ ul.tips { -webkit-border-top-right-radius: 8px; border-top-right-radius: 8px; } -#navigation ul.links li a:hover, -#navigation ul.links li a:focus { +#main-menu ul.links li a:hover, +#main-menu ul.links li a:focus { background: #fff; background: rgba(255, 255, 255, 0.95); } -#navigation ul.links li a:active { +#main-menu ul.links li a:active { background: #b3b3b3; background: rgba(255, 255, 255, 1); } -#navigation ul.links li.active-trail a { +#main-menu ul.links li.active-trail a { border-bottom: none; } -.featured #navigation ul.links li.active-trail a { +.featured #main-menu ul.links li.active-trail a { background: #f0f0f0; background: rgba(240, 240, 240, 1.0); } -#navigation ul.links li { +#main-menu ul.links li { display: inline; list-style-type: none; padding: 0.6em 0 0.4em; } -#navigation ul.links li.first { +#main-menu ul.links li.first { padding-left: 2px; /* LTR */ } +/* --------------- Secondary Menu ------------ */ + +#secondary-menu-links { + float: right; /* LTR */ + margin: 10px; +} +#secondary-menu-links a:hover, +#secondary-menu-links a:focus { + text-decoration: underline; +} + /* ------------------- Main ------------------- */ #main, @@ -520,6 +568,7 @@ h1#page-title, } #comments .comment { margin-bottom: 20px; + position: relative; } #comments .attribution { float: left; /* LTR */ @@ -557,6 +606,12 @@ h1#page-title, #comments .indented { margin-left: 40px; /* LTR */ } +#comments ul.links { + padding: 0 0 0.25em 0; +} +#comments ul.links li { + padding: 0 0.5em 0 0; /* LTR */ +} /* ------------------ Sidebar ----------------- */ @@ -672,9 +727,6 @@ h1#page-title, #footer .block h2 { margin: 0; } -#footer-wrapper ul#secondary-menu { - margin: 1em 0; -} #footer-columns h2 { border-bottom: 1px solid #555; border-color: rgba(255,255,255,0.15); @@ -773,8 +825,8 @@ h1#page-title, vertical-align: bottom; margin: 0 5px 0 0; /* LTR */ } -.tabs ul.primary li a.active { - border-bottom: 1px solid #fff; +.tabs ul.primary li.active a { + border-bottom: 1px solid #ffffff; } .tabs ul.primary li a { color: #000; @@ -795,6 +847,9 @@ h1#page-title, -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; } +.tabs ul.primary li.active a { + background-color: #ffffff; +} .tabs ul.secondary { border-bottom: none; padding: 0.5em 0; @@ -842,21 +897,8 @@ ul.action-links li a { margin: 0 auto; } div.messages { - padding: 1.2em 2em 1em; margin: 8px 0; } -div.status, tr.status { - background-color: #c7ffc0; - border: 1px solid #89d47f; -} -div.warning, tr.warning { - background-color: #fcfca7; - border: 1px solid #e1c46b; -} -div.error, tr.error { - background-color: #ffcccc; - border: 1px solid #fb6b6b; -} /* -------------- Breadcrumbs -------------- */ @@ -907,6 +949,7 @@ a.button { font-weight: normal; text-align: center; margin-bottom: 1em; + margin-right: 1em; /* LTR */ padding: 4px 17px; -khtml-border-radius: 15px; -moz-border-radius: 20px; @@ -941,6 +984,8 @@ fieldset { margin-top: 25px; } .filter-wrapper { + top: 0; + padding-bottom: 10px; -khtml-border-radius-topright: 0; -khtml-border-radius-topleft: 0; -moz-border-radius-topright: 0; @@ -1081,10 +1126,6 @@ html.js input.throbbing { border-top-left-radius: 4px; border-top-right-radius: 4px; } -#comment-form fieldset.filter-wrapper { - top: 0; - padding-bottom: 10px; -} #comment-form fieldset.filter-wrapper .fieldset-wrapper, #comment-form .text-format-wrapper .form-item { margin-top: 0; @@ -1112,35 +1153,30 @@ div.vertical-tabs .vertical-tabs-panes fieldset.vertical-tabs-pane { /* --------------- Search Form ---------------- */ +#block-search-form { + padding-bottom: 7px; +} #block-search-form .content { margin-top: 0; } +#search-form input#edit-keys, #block-search-form .form-item-search-block-form input { - width: 70%; -} -.region-content #block-search-form .form-item-search-block-form input, -.region-footer #block-search-form .form-item-search-block-form input { - width: auto; -} -#block-search-form #edit-actions { - float: right; -} -.region-content #block-search-form #edit-actions, -.region-footer #block-search-form #edit-actions { - float: none; -} -#block-search-form .form-actions { - padding-top: 0; + float: left; /* LTR */ } #search-block-form input.form-submit, #search-form input.form-submit { - height: 24px; - width: 14px; - overflow: hidden; + margin-left: 5px; /* LTR */ + margin-right: 0; /* LTR */ + height: 25px; + width: 34px; + padding: 0; cursor: pointer; text-indent: -9999px; - border: none; - background: url(../images/search-button.png) no-repeat left center; + border: 1px solid #ccc; + background: url(../images/search-button.png) no-repeat center center; +} +#search-form .form-item-keys label { + display: block; } /* -------------- Shortcut Links -------------- */ diff --git a/themes/bartik/template.php b/themes/bartik/template.php index 3ddeedf8d3bcbcf44f4806ca008f06c3ea14e38b..f9bdaf70e2218e406a939970d979def5210a43f8 100644 --- a/themes/bartik/template.php +++ b/themes/bartik/template.php @@ -1,5 +1,5 @@ <?php -// $Id: template.php,v 1.1 2010/07/06 05:25:51 webchick Exp $ +// $Id: template.php,v 1.5 2010/09/01 02:13:58 dries Exp $ /** * Add body classes if certain regions have content. @@ -23,8 +23,8 @@ function bartik_preprocess_html(&$variables) { } // Add conditional stylesheets for IE - drupal_add_css(path_to_theme() . '/css/ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); - drupal_add_css(path_to_theme() . '/css/ie6.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'IE 6', '!IE' => FALSE), 'preprocess' => FALSE)); + drupal_add_css(path_to_theme() . '/css/ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE))); + drupal_add_css(path_to_theme() . '/css/ie6.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'IE 6', '!IE' => FALSE))); } /** @@ -74,6 +74,13 @@ function bartik_process_page(&$variables) { } } +/** + * Implements hook_preprocess_maintenance_page(). + */ +function bartik_preprocess_maintenance_page(&$variables) { + drupal_add_css(drupal_get_path('theme', 'bartik') . '/css/maintenance-page.css'); +} + /** * Override or insert variables into the maintenance page template. */ @@ -98,49 +105,7 @@ function bartik_process_maintenance_page(&$variables) { function bartik_preprocess_block(&$variables) { // In the header region, visually hide the title of any menu block or of the // user login block, but leave it accessible. - if ($variables['block']->region == 'header' && ($variables['block']->module == 'menu' || $variables['block']->module == 'user' && $variables['block']->delta == 'login')) { + if ($variables['block']->region == 'header' && ($variables['block']->module == 'user' && $variables['block']->delta == 'login' || in_array('block-menu', $variables['classes_array']))) { $variables['title_attributes_array']['class'][] = 'element-invisible'; } - // System menu blocks should get the same class as menu module blocks. - if (in_array($variables['block']->delta, array_keys(menu_list_system_menus()))) { - $variables['classes_array'][] = 'block-menu'; - // Also, hide the title if its in the header region. - if ($variables['block']->region == 'header') { - $variables['title_attributes_array']['class'][] = 'element-invisible'; - } - } - // Set "first" and "last" classes. - if ($variables['block']->position_first){ - $variables['classes_array'][] = 'first'; - } - if ($variables['block']->position_last){ - $variables['classes_array'][] = 'last'; - } - // Set "odd" & "even" classes. - $variables['classes_array'][] = $variables['block']->position % 2 == 0 ? 'odd' : 'even'; -} - -/** - * Implements hook_page_alter(). - */ -function bartik_page_alter(&$page) { - // Determine the position and count of blocks within regions. - foreach ($page as &$region) { - // Make sure this is a "region" element. - if (is_array($region) && isset($region['#region'])) { - $i = 0; - foreach ($region as &$block) { - // Make sure this is a "block" element. - if (is_array($block) && isset($block['#block'])) { - $block['#block']->position = $i++; - // Set a flag for "first" and "last" blocks. - $block['#block']->position_first = ($block['#block']->position == 0); - $block['#block']->position_last = FALSE; - $last_block =& $block; - } - } - $last_block['#block']->position_last = TRUE; - $region['#block_count'] = $i; - } - } } diff --git a/themes/bartik/templates/maintenance-page.tpl.php b/themes/bartik/templates/maintenance-page.tpl.php index 403f7740c976ca52441cb321207833f40a038337..3fa2c1627e46abb401bfcb7465b6c2f2344b2516 100644 --- a/themes/bartik/templates/maintenance-page.tpl.php +++ b/themes/bartik/templates/maintenance-page.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: maintenance-page.tpl.php,v 1.1 2010/07/06 05:25:51 webchick Exp $ +// $Id: maintenance-page.tpl.php,v 1.2 2010/07/28 01:40:39 dries Exp $ /** * @file @@ -66,7 +66,7 @@ <div id="main-wrapper"><div id="main" class="clearfix"> <div id="content" class="column"><div class="section"> - <?php if ($highlight): ?><div id="highlight"><?php print $highlight; ?></div><?php endif; ?> + <?php if ($highlighted): ?><div id="highlighted"><?php print $highlighted; ?></div><?php endif; ?> <a id="main-content"></a> <?php if ($title): ?> <h1 class="title" id="page-title"> diff --git a/themes/bartik/templates/page.tpl.php b/themes/bartik/templates/page.tpl.php index 5ac4491cea3574176c1ce866da7fe482710bd679..83aa3026cb420cc64246d693df89e4e9725c0958 100644 --- a/themes/bartik/templates/page.tpl.php +++ b/themes/bartik/templates/page.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: page.tpl.php,v 1.1 2010/07/06 05:25:51 webchick Exp $ +// $Id: page.tpl.php,v 1.5 2010/09/09 15:27:08 webchick Exp $ /** * @file @@ -15,7 +15,7 @@ * - $base_path: The base URL path of the Drupal installation. At the very * least, this will always default to /. * - $directory: The directory the template is located in, e.g. modules/system - * or themes/garland. + * or themes/bartik. * - $is_front: TRUE if the current page is the front page. * - $logged_in: TRUE if the user is registered and signed in. * - $is_admin: TRUE if the user has permission to access administration pages. @@ -66,7 +66,7 @@ * Regions: * - $page['header']: Items for the header region. * - $page['featured']: Items for the featured region. - * - $page['highlight']: Items for the highlighted content region. + * - $page['highlighted']: Items for the highlighted content region. * - $page['help']: Dynamic help text, mostly for admin pages. * - $page['content']: The main content of the current page. * - $page['sidebar_first']: Items for the first sidebar. @@ -124,11 +124,11 @@ <?php print render($page['header']); ?> <?php if ($main_menu): ?> - <div id="navigation"><div class="section clearfix"> + <div id="main-menu" class="navigation"> <?php print theme('links__system_main_menu', array( 'links' => $main_menu, 'attributes' => array( - 'id' => 'main-menu', + 'id' => 'main-menu-links', 'class' => array('links', 'clearfix'), ), 'heading' => array( @@ -137,7 +137,24 @@ 'class' => array('element-invisible'), ), )); ?> - </div></div> <!-- /.section, /#navigation --> + </div> <!-- /#main-menu --> + <?php endif; ?> + + <?php if ($secondary_menu): ?> + <div id="secondary-menu" class="navigation"> + <?php print theme('links__system_secondary_menu', array( + 'links' => $secondary_menu, + 'attributes' => array( + 'id' => 'secondary-menu-links', + 'class' => array('links', 'clearfix'), + ), + 'heading' => array( + 'text' => t('Secondary menu'), + 'level' => 'h2', + 'class' => array('element-invisible'), + ), + )); ?> + </div> <!-- /#secondary-menu --> <?php endif; ?> </div></div> <!-- /.section, /#header --> @@ -149,9 +166,9 @@ <?php endif; ?> <?php if ($page['featured']): ?> - <div id="featured" class="region"><div class="section clearfix"> + <div id="featured" class="section clearfix"> <?php print render($page['featured']); ?> - </div></div> <!-- /.section, /#featured --> + </div> <!-- /#featured --> <?php endif; ?> <div id="main-wrapper" class="clearfix"><div id="main" class="clearfix"> @@ -166,9 +183,8 @@ </div></div> <!-- /.section, /#sidebar-first --> <?php endif; ?> - <div id="content" class="column"><div class="section"> - <?php if ($page['highlight']): ?><div id="highlight"><?php print render($page['highlight']); ?></div><?php endif; ?> + <?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?> <a id="main-content"></a> <?php print render($title_prefix); ?> <?php if ($title): ?> @@ -203,25 +219,9 @@ <?php if ($page['triptych_first'] || $page['triptych_middle'] || $page['triptych_last']): ?> <div id="triptych-wrapper"><div id="triptych" class="clearfix"> - - <?php if ($page['triptych_first']): ?> - <div id="triptych-first" class="region triptych"><div class="section"> - <?php print render($page['triptych_first']); ?> - </div></div> <!-- /.section, /#triptych-first --> - <?php endif; ?> - - <?php if ($page['triptych_middle']): ?> - <div id="triptych-middle" class="region triptych"><div class="section"> - <?php print render($page['triptych_middle']); ?> - </div></div> <!-- /.section, /#triptych-middle --> - <?php endif; ?> - - <?php if ($page['triptych_last']): ?> - <div id="triptych-last" class="region triptych"><div class="section"> - <?php print render($page['triptych_last']); ?> - </div></div> <!-- /.section, /#triptych-last --> - <?php endif; ?> - + <?php print render($page['triptych_first']); ?> + <?php print render($page['triptych_middle']); ?> + <?php print render($page['triptych_last']); ?> </div></div> <!-- /#triptych, /#triptych-wrapper --> <?php endif; ?> @@ -229,50 +229,17 @@ <?php if ($page['footer_firstcolumn'] || $page['footer_secondcolumn'] || $page['footer_thirdcolumn'] || $page['footer_fourthcolumn']): ?> <div id="footer-columns" class="clearfix"> - - <?php if ($page['footer_firstcolumn']): ?> - <div id="footer-firstcolumn" class="region sitemap"><div class="section"> - <?php print render($page['footer_firstcolumn']); ?> - </div></div> <!-- /.section, /#footer-firstcolumn --> - <?php endif; ?> - - <?php if ($page['footer_secondcolumn']): ?> - <div id="footer-secondcolumn" class="region sitemap"><div class="section"> - <?php print render($page['footer_secondcolumn']); ?> - </div></div> <!-- /.section, /#footer-secondcolumn --> - <?php endif; ?> - - <?php if ($page['footer_thirdcolumn']): ?> - <div id="footer-thirdcolumn" class="region sitemap"><div class="section"> - <?php print render($page['footer_thirdcolumn']); ?> - </div></div> <!-- /.section, /#footer-thirdcolumn --> - <?php endif; ?> - - <?php if ($page['footer_fourthcolumn']): ?> - <div id="footer-fourthcolumn" class="region sitemap"><div class="section"> - <?php print render($page['footer_fourthcolumn']); ?> - </div></div> <!-- /.section, /#footer-fourthcolumn --> - <?php endif; ?> - - </div><!-- /#footer-columns --> + <?php print render($page['footer_firstcolumn']); ?> + <?php print render($page['footer_secondcolumn']); ?> + <?php print render($page['footer_thirdcolumn']); ?> + <?php print render($page['footer_fourthcolumn']); ?> + </div> <!-- /#footer-columns --> <?php endif; ?> - <?php if ($page['footer'] || $secondary_menu): ?> + <?php if ($page['footer']): ?> <div id="footer" class="clearfix"> - <?php print theme('links__system_secondary_menu', array( - 'links' => $secondary_menu, - 'attributes' => array( - 'id' => 'secondary-menu', - 'class' => array('links', 'clearfix'), - ), - 'heading' => array( - 'text' => t('Secondary menu'), - 'level' => 'h2', - 'class' => array('element-invisible'), - ), - )); ?> <?php print render($page['footer']); ?> - </div><!-- /#footer --> + </div> <!-- /#footer --> <?php endif; ?> </div></div> <!-- /.section, /#footer-wrapper --> diff --git a/themes/garland/block.tpl.php b/themes/garland/block.tpl.php deleted file mode 100644 index 5997594a109e4a04a46f7249b93646c6227c6480..0000000000000000000000000000000000000000 --- a/themes/garland/block.tpl.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -// $Id: block.tpl.php,v 1.14 2010/04/26 14:10:40 dries Exp $ -?> -<div id="<?php print $block_html_id; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>> - - <?php print render($title_prefix); ?> -<?php if (!empty($block->subject)): ?> - <h2 class="title"<?php print $title_attributes; ?>><?php print $block->subject ?></h2> -<?php endif;?> - <?php print render($title_suffix); ?> - - <div class="content"<?php print $content_attributes; ?>><?php print $content ?></div> -</div> diff --git a/themes/garland/garland.info b/themes/garland/garland.info index f5a5ee6d02e801fc81933a1d0c00689ae9e54727..f84794ee2e20f72ad6b92b638460fa1ad8210e3b 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/garland/page.tpl.php b/themes/garland/page.tpl.php index 0eba988973df6b790e6a4af0dc7c1c9c87a10a9e..275b1364fd7dd80c3b30eb834283db97bfe1e77d 100644 --- a/themes/garland/page.tpl.php +++ b/themes/garland/page.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: page.tpl.php,v 1.42 2010/01/30 07:59:26 dries Exp $ +// $Id: page.tpl.php,v 1.46 2010/09/11 05:01:19 webchick Exp $ ?> <?php print render($page['header']); ?> @@ -10,16 +10,16 @@ <div id="logo-floater"> <?php if ($logo || $site_title): ?> <?php if ($title): ?> - <div id="branding"><strong><a href="<?php print $front_page ?>" title="<?php print $site_name_and_slogan ?>"> + <div id="branding"><strong><a href="<?php print $front_page ?>"> <?php if ($logo): ?> - <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" id="logo" /> + <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" /> <?php endif; ?> <?php print $site_html ?> </a></strong></div> <?php else: /* Use h1 when the content title is empty */ ?> - <h1 id="branding"><a href="<?php print $front_page ?>" title="<?php print $site_name_and_slogan ?>"> + <h1 id="branding"><a href="<?php print $front_page ?>"> <?php if ($logo): ?> - <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" id="logo" /> + <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" /> <?php endif; ?> <?php print $site_html ?> </a></h1> @@ -39,7 +39,7 @@ <div id="center"><div id="squeeze"><div class="right-corner"><div class="left-corner"> <?php print $breadcrumb; ?> - <?php if ($page['highlight']): ?><div id="highlight"><?php render($page['highlight']); ?></div><?php endif; ?> + <?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?> <a id="main-content"></a> <?php if ($tabs): ?><div id="tabs-wrapper" class="clearfix"><?php endif; ?> <?php print render($title_prefix); ?> @@ -56,7 +56,7 @@ <?php print render($page['content']); ?> </div> <?php print $feed_icons ?> - <?php print render($page['footer']) ?> + <?php print render($page['footer']); ?> </div></div></div></div> <!-- /.left-corner, /.right-corner, /#squeeze, /#center --> <?php if ($page['sidebar_second']): ?> diff --git a/themes/garland/style.css b/themes/garland/style.css index 80f4b4ea13dee0364e8b98d30fbd1b5e3feb128d..2355337b30408d9a821cef569dd3ec19606c5736 100644 --- a/themes/garland/style.css +++ b/themes/garland/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.83 2010/06/08 05:16:29 webchick Exp $ */ +/* $Id: style.css,v 1.85 2010/09/09 15:47:03 webchick Exp $ */ /** * Generic elements @@ -290,20 +290,6 @@ span.form-required { padding: .5em 1em; } -div.messages { - margin: .75em 0 .75em; - padding: .1em .5em .15em; -} - -.messages ul { - margin: 0; -} - -.messages li { - margin: 0; - padding: 0 0 0 1.3em; /* LTR */ -} - .form-checkboxes, .form-radios, .form-checkboxes .form-item, @@ -622,7 +608,7 @@ div#branding strong { color: #529ad6; } -#highlight { +#highlighted { padding: 1em; background-color: #fff; border: 1px solid #e0e5fb; @@ -1196,38 +1182,11 @@ tr.taxonomy-term-divider-bottom { /** * Generic elements. */ -div.messages { - background-color: #fff; - border: 1px solid #b8d3e5; -} - .preview { background-color: #fcfce8; border: 1px solid #e5e58f; } -div.status { - background-color: #fff; - border-color: #c7f2c8; - color: #33a333; -} - -div.error { - border: 1px solid #d77; -} - -div.error, -tr.error { - color: #a30000; - background-color: #FFCCCC; -} - -div.warning { - background-color: #ffd; - border: 1px solid #f0c020; - color: #220; -} - .form-item input.error, .form-item textarea.error { border: 1px solid #c52020; diff --git a/themes/garland/template.php b/themes/garland/template.php index 64f1929e03e8df961104a688361c0da6781cb299..37d76b65abab6bffaf7f9b57e57d86aa7ea00a06 100644 --- a/themes/garland/template.php +++ b/themes/garland/template.php @@ -1,5 +1,5 @@ <?php -// $Id: template.php,v 1.39 2010/03/04 09:03:08 dries Exp $ +// $Id: template.php,v 1.41 2010/07/30 02:47:28 dries Exp $ /** * Return a themed breadcrumb trail. @@ -42,7 +42,7 @@ function garland_preprocess_html(&$vars) { $vars['classes_array'][] = 'fluid-width'; } // Add conditional CSS for IE6. - drupal_add_css(path_to_theme() . '/fix-ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); + drupal_add_css(path_to_theme() . '/fix-ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE))); } /** @@ -113,6 +113,14 @@ function garland_preprocess_page(&$vars) { $vars['site_name_and_slogan'] = $site_name_text . ' ' . $slogan_text; } +/** + * Override or insert variables into the block template. + */ +function garland_preprocess_block(&$vars) { + $vars['title_attributes_array']['class'][] = 'title'; + $vars['classes_array'][] = 'clearfix'; +} + /** * Override or insert variables into the page template. */ diff --git a/themes/garland/theme-settings.php b/themes/garland/theme-settings.php index 5520b8ac4b75071e2bcd0d1a6ffcf36016d08ec7..dc2ea33609b59b09b52e5f20f3a902c3fdb8d2ec 100644 --- a/themes/garland/theme-settings.php +++ b/themes/garland/theme-settings.php @@ -1,15 +1,21 @@ <?php -// $Id: theme-settings.php,v 1.2 2009/12/04 16:49:48 dries Exp $ +// $Id: theme-settings.php,v 1.3 2010/09/04 15:21:09 dries Exp $ /** - * Implements hook_form_system_theme_settings_alter(). + * @file + * Theme setting callbacks for the garland theme. + */ + +/** + * Implements hook_form_FORM_ID_alter(). * * @param $form * The form. * @param $form_state * The form state. */ -function garland_form_system_theme_settings_alter(&$form, $form_state) { +function garland_form_system_theme_settings_alter(&$form, &$form_state) { + $form['garland_width'] = array( '#type' => 'radios', '#title' => t('Content width'), diff --git a/themes/seven/jquery.ui.theme.css b/themes/seven/jquery.ui.theme.css new file mode 100644 index 0000000000000000000000000000000000000000..88e6dd1f5b297d7c5ccba90117415c32f5c5f601 --- /dev/null +++ b/themes/seven/jquery.ui.theme.css @@ -0,0 +1,433 @@ +/** + * @file + * Seven styles for jQuery UI. + * Overrides /misc/ui/ui.theme.css. + */ + +/** + * Component containers + */ +.ui-widget { + background: #fff; +} +.ui-widget-content { + border: solid 1px #ccc; +} + +/** + * Interaction states + */ +.ui-state-default, +.ui-state-hover, +.ui-state-focus, +.ui-state-active { + outline: 0; +} +.ui-state-active { + font-weight: bold; +} + +/** + * Interaction cues + */ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight { + color: #840; + background: #fe6; + border: solid 1px #ed5; +} +.ui-state-error, +.ui-widget-content .ui-state-error { + color: #fff; + background: #e63; + border-color: #d52; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled { + opacity: .35; + filter: Alpha(Opacity=35); +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary { + opacity: .7; + filter: Alpha(Opacity=70); +} + +/** + * Icons + */ +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + width: 16px; + height: 16px; + overflow: hidden; + background-repeat: no-repeat; + background-image: url(images/ui-icons-222222-256x240.png); +} +.ui-widget-content .ui-icon, +.ui-widget-header .ui-icon { + background-image: url(images/ui-icons-222222-256x240.png); +} +.ui-state-default .ui-icon { + background-image: url(images/ui-icons-888888-256x240.png); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon, +.ui-state-active .ui-icon { + background-image: url(images/ui-icons-454545-256x240.png); +} +.ui-state-highlight .ui-icon { + background-image: url(images/ui-icons-800000-256x240.png); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url(images/ui-icons-ffffff-256x240.png); +} +.ui-widget p .ui-icon { + margin: 2px 3px 0 0; +} + +/* positioning */ +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } +.ui-icon-carat-1-n { background-position: 0 0; } + +/** + * Accordion + */ +.ui-accordion { + border: none; +} +.ui-accordion .ui-accordion-header { + border: solid 1px #ccc; + text-transform: uppercase; +} +.ui-accordion h3.ui-accordion-header, +#block-system-main h3.ui-accordion-header { + font-size: 1.1em; + margin: 10px 0; +} +#block-system-main .ui-accordion h3.ui-state-active, +.ui-accordion h3.ui-state-active { + margin-bottom: 0; +} +.ui-accordion .ui-accordion-header a { + display: block; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border: solid 1px #ccc; + border-top: 0; +} + +/** + * Tabs + */ +.ui-tabs { + padding: 0; +} +.ui-tabs .ui-tabs-nav { + padding: 5px 10px 4px; + margin: 0; + line-height: 20px; + border-bottom: solid 1px #ccc; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; +} +.ui-tabs .ui-tabs-nav li { + padding: 0 1em 0 10px; + margin: 0; + list-style: none; +} +.ui-tabs .ui-tabs-nav li a { + float: none; + padding: 0 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 7px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a { + color: #fff; + background: #666; + font-weight: normal; +} + +/** + * Overlays + */ +.ui-widget-overlay { + background: #000; + opacity: .70; + filter: Alpha(Opacity=70); +} + +/** + * Dialogs + */ +.ui-dialog { + background: #fff; + border: solid 1px #ccc; + padding: 0; +} +.ui-dialog .ui-dialog-titlebar { + font-weight: bold; + background: #e1e2dc; +} +.ui-dialog .ui-dialog-buttonpane { + border-width: 0; +} +.ui-dialog .ui-dialog-buttonpane button { + cursor: pointer; + padding: 4px 17px; + color: #5a5a5a; + text-align: center; + font-family: "Lucida Grande", Verdana, sans-serif; + font-weight: normal; + font-size: 1em; + border: 1px solid #e4e4e4; + border-bottom: 1px solid #b4b4b4; + border-left-color: #D2D2D2; + border-right-color: #D2D2D2; + background: url(images/buttons.png) 0 0 repeat-x; + border-radius: 20px; + -moz-border-radius: 20px; + -webkit-border-radius: 15px; +} +.ui-dialog .ui-dialog-buttonpane button:active { + background: #666; + color: #fff; + border-color: #555; + text-shadow: #222 0px -1px 0px; +} +.overlay { + padding-right: 26px; +} +.overlay .ui-dialog-titlebar { + background: transparent; +} + +/** + * Slider + */ +.ui-slider { + border: solid 1px #ccc; +} +.ui-slider .ui-slider-range { + background: #e4e4e4; +} +.ui-slider .ui-slider-handle { + border: 1px solid #e4e4e4; + border-bottom: 1px solid #b4b4b4; + border-left-color: #D2D2D2; + border-right-color: #D2D2D2; + background: url(images/buttons.png) 0 0 repeat-x; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; +} +.ui-slider a.ui-state-active, +.ui-slider .ui-slider-handle:active { + background: #666; + color: #fff; + border: solid 1px #555; +} + +/** + * Progress Bar + */ +.ui-progressbar { + background: #e4e4e4; + height: 1.4em; +} +.ui-progressbar .ui-progressbar-value { + background: #0072b9 url(../../misc/progress.gif); + height: 1.5em; +} + +/** + * Date Picker + */ +.ui-datepicker { + border: none; +} +.ui-datepicker td span, .ui-datepicker td a { + text-align: center; +} +.ui-datepicker .ui-state-highlight { + background: #E4E4E4; + border-color: #D2D2D2; + color: #000; +} diff --git a/themes/seven/reset.css b/themes/seven/reset.css index aa9c8044abc0cd5f68f67d207b938703ef461673..02be479f3eadd758db59727d27f6dfd3cb04ea1d 100644 --- a/themes/seven/reset.css +++ b/themes/seven/reset.css @@ -1,4 +1,4 @@ -/* $Id: reset.css,v 1.9 2010/04/28 20:08:39 dries Exp $ */ +/* $Id: reset.css,v 1.12 2010/08/14 03:01:11 dries Exp $ */ /** * Reset CSS styles. @@ -129,9 +129,7 @@ ul.secondary a.active, margin: 0; padding: 0; border: 0; - font-size: 100%; vertical-align: baseline; - line-height: inherit; } /* Drupal: system-menus.css */ ul.links, @@ -178,17 +176,19 @@ table { */ input, select, -textarea, -body { - font: 13px/20px "Helvetica Neue", Helvetica, Arial, sans-serif; +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +textarea { + font-size: 0.923em; + line-height: 1.538em; } - /** * Markup free clearing. * * Consider adding your own selectors to this instead of finding ways * to sneak the clearfix class into Drupal's markup. - * From http://www.positioniseverything.net/easyclearing.html + * From http://perishablepress.com/press/2009/12/06/new-clearfix-hack */ ul.links:after, div.admin-panel .body:after, @@ -206,24 +206,17 @@ ul.inline:after { display: none; clear: none; } -.form-item, -ul.links, -div.admin-panel .body, -.clearfix { - display: inline-block; -} - -/* Hides from IE-mac \*/ +/* IE6 */ * html .form-item, * html ul.links, * html div.admin-panel .body, * html .clearfix { height: 1%; } -.form-item, -ul.links, -div.admin-panel .body, -.clearfix { - display: block; +/* IE7 */ +*:first-child + html .form-item, +*:first-child + html ul.links, +*:first-child + html div.admin-panel .body, +*:first-child + html .clearfix { + min-height: 1%; } -/* End hide from IE-mac */ diff --git a/themes/seven/seven.info b/themes/seven/seven.info index 9c7c9399e1f00b48c745b41c328674ad933b1310..f0407743b12a92aad99bec5768c9cb404370bb7f 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/seven/style.css b/themes/seven/style.css index fcf02f78831e2c09ce51834030034295c0da4653..d6d89aab1a27182fd7f49d578fb68cdadb476053 100644 --- a/themes/seven/style.css +++ b/themes/seven/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.57 2010/05/05 16:41:57 dries Exp $ */ +/* $Id: style.css,v 1.69 2010/09/13 00:59:47 dries Exp $ */ /** * Generic elements. @@ -6,10 +6,7 @@ body { color: #000; background: #fff; - font-style: normal; - line-height: 20px; - font-size: 0.8em; - font-family: Lucida Grande, Lucida Sans Unicode, sans-serif; + font: 81.3%/1.538em normal Lucida Grande, Lucida Sans Unicode, sans-serif; } a { color: #0074BD; @@ -38,22 +35,20 @@ h6 { margin: 10px 0; } h1 { - font-size: 150%; + font-size: 1.538em; } h2 { - font-size: 140%; + font-size: 1.385em; } h3 { - font-size: 130%; + font-size: 1.231em; } h4 { - font-size: 120%; -} -h5 { - font-size: 110%; + font-size: 1.154em; } +h5, h6 { - font-size: 105%; + font-size: 1.077em; } p { margin: 1em 0; @@ -142,7 +137,7 @@ code { code, pre, kbd { - font-size: 1.25em; + font-size: 1.231em; } /** @@ -175,7 +170,6 @@ kbd { -webkit-border-top-right-radius: 0; -webkit-border-bottom-left-radius: 10px; -webkit-border-bottom-right-radius: 10px; - } #skip-link a:hover, #skip-link a:active, @@ -197,7 +191,7 @@ kbd { background-color: #e0e0d8; } #branding div.breadcrumb { - font-size: 11px; + font-size: 0.846em; padding-bottom: 5px; } #branding div.block { @@ -229,46 +223,16 @@ kbd { * Help. */ #help { - font-size: 0.9em; + font-size: 0.923em; margin-top: 1em; } #help p { margin: 0 0 10px; } - #help div.more-help-link { text-align: right; } -/** - * Messages. - */ -div.messages { - padding: 9px; - margin: 0.5em 0 0; - color: #036; - background: #bdf; - border: 1px solid #ace; -} -div.warning { - color: #840; - background: #fe6; - border-color: #ed5; -} -div.error { - color: #fff; - background: #e63; - border-color: #d52; -} -div.error p.error { - color: #333; -} -div.status { - color: #360; - background: #cf8; - border-color: #be7; -} - /** * Page title. */ @@ -280,7 +244,7 @@ div.status { color: #000; margin: 0; padding-bottom: 10px; - font-size: 18px; + font-size: 1.385em; font-weight: normal; float: left; } @@ -298,11 +262,9 @@ div.status { ul.primary { float: right; border-bottom: none; - margin: 0 0 -10px 0; - padding: 3px 0 0 8px; - line-height: 30px; + padding: 0.769em 0 5px 8px; text-transform: uppercase; - font-size: 12px; + font-size: 0.923em; } ul.primary li { display: inline; @@ -317,7 +279,7 @@ ul.primary li.active a { background-color: #a6a7a2; color: #000; font-weight: bold; - padding: 7px 20px 5px 20px; + padding: 6px 20px; border-width: 1px 1px 0 1px; border-style: solid; border-color: #a6a7a2; @@ -342,10 +304,10 @@ ul.primary li.active a:hover { ul.secondary { float: none; clear: both; - font-size: 12px; + font-size: 0.923em; text-align: right; padding: 4px 10px 10px; - line-height: 18px; + line-height: 1.385em; overflow: hidden; background-color: #fff; } @@ -387,8 +349,7 @@ ul.secondary li.active a.active { padding: 0 10px 10px 0; } #secondary-links ul.links li a { - font-size: 9px; - line-height: 10px; + font-size: 0.923em; background: #777; color: #fff; text-align: center; @@ -446,9 +407,8 @@ ul.admin-list.compact li { ul.admin-list li:last-child { border-bottom: none; } - ul.node-type-list .label { - font-size: 15px; + font-size: 1.154em; } ul.node-type-list li a, ul.admin-list li a { @@ -472,7 +432,7 @@ ul.admin-list li div.description a { */ table { width: 100%; - font-size: 12px; + font-size: 0.923em; margin: 0 0 10px; border: 1px solid #bebfb9; } @@ -500,7 +460,6 @@ tr.drag-previous { background: #ffb; } table th { - font-size: 12px; text-transform: uppercase; background: #e1e2dc; font-weight: normal; @@ -533,20 +492,26 @@ table tr.selected td { border-color: #eeb; } table.system-status-report tr { - border-bottom: 1px solid #bebfb9; + border-bottom: 1px solid #ccc; } -table.system-status-report tr.ok td { - background-color: #dfd; +table.system-status-report tr.ok { + color: #255b1e; + background-color: #e5ffe2; } -table.system-status-report tr.info td { +table.system-status-report tr.info { + color: #040f37; background-color: #bdf; } -table.system-status-report tr.warning td { - background-color: #ffd; +table.system-status-report tr.warning { + color: #840; + background-color: #fffce5; } -table.system-status-report tr.error td { - background-color: #fdd; +table.system-status-report tr.error { + color: #8c2e0b; + background-color: #fef5f1; } + + /** * Fieldsets. * @@ -622,33 +587,40 @@ div.teaser-checkbox .form-item, text-transform: none; } .form-item label.option { - font-size: 12px; + font-size: 0.923em; } .form-item label.option input { vertical-align: middle; } +.form-disabled input.form-autocomplete, +.form-disabled input.form-text, +.form-disabled input.form-file, +.form-disabled textarea.form-textarea, +.form-disabled select.form-select { + background-color: #eee; + color: #777; +} /* Filter */ .filter-wrapper { border-top: 0; padding: 10px 2px; } -.filter-wrapper > div { - padding-right: 6px; - padding-left: 6px; +.filter-wrapper .fieldset-wrapper { + padding: 0 6px; } .filter-wrapper .form-item, .filter-wrapper .filter-guidelines, .filter-wrapper .filter-help { - font-size: 12px; - padding: 0; + font-size: 0.923em; + padding: 2px 0 0 0; } ul.tips, div.description, .form-item div.description { margin: 5px 0; - line-height: 15px; - font-size: 12px; + line-height: 1.231em; + font-size: 0.923em; color: #666; } ul.tips li { @@ -663,10 +635,11 @@ a.button { cursor: pointer; padding: 4px 17px; margin-bottom: 1em; + margin-right: 1em; color: #5a5a5a; text-align: center; font-weight: normal; - font-size: 1.1em; + font-size: 1.077em; font-family: "Lucida Grande", Verdana, sans-serif; border: 1px solid #e4e4e4; border-bottom: 1px solid #b4b4b4; @@ -699,6 +672,13 @@ input.form-submit:active { border-color: #555; text-shadow: #222 0px -1px 0px; } +input.form-button-disabled, +input.form-button-disabled:active { + background: #eee none; + border-color: #eee; + text-shadow: none; + color: #999; +} form input#edit-delete { background: #eee; border-color: #fff #ddd #ccc; @@ -707,6 +687,7 @@ form input#edit-delete { } input.form-autocomplete, input.form-text, +input.form-file, textarea.form-textarea, select.form-select { padding: 2px; @@ -716,6 +697,7 @@ select.form-select { color: #333; } input.form-text:focus, +input.form-file:focus, textarea.form-textarea:focus, select.form-select:focus { color: #000; @@ -769,7 +751,7 @@ div.admin-panel { border: 1px solid #ccc; } div.admin-panel h3 { - font-size: 12px; + font-size: 0.923em; text-transform: uppercase; margin: 0; padding-bottom: 9px; @@ -798,7 +780,7 @@ dl.multiselect dd { } dl.multiselect select, dl.multiselect dd select { - font-size: 12px; + font-size: 0.923em; background: #fff; border: 1px solid #ccc; } @@ -814,7 +796,7 @@ div.admin-options { } div.admin-options label { text-transform: uppercase; - font: 12px/30px Lucida Grande, Lucida Sans Unicode, sans-serif; + font: 0.846em/1.875em Lucida Grande, Lucida Sans Unicode, sans-serif; } div.admin-options label, div.admin-options div.form-item { @@ -906,6 +888,15 @@ ol.task-list li.done { position: relative; z-index: 10; } +#overlay-tabs { + bottom: -2px; + font-size: 1.54em; + line-height: 1.54em; + margin: 0; +} +#overlay-tabs li { + margin: 0 -2px; +} .overlay ul.secondary { background: transparent none; margin: -2.4em 0 0.3em 0; @@ -940,6 +931,28 @@ div.add-or-remove-shortcuts { border: #ccc 1px solid; } +/* Field UI */ + +#field-display-overview input.field-formatter-settings-edit { + margin: 0; + padding: 1px 8px; +} +#field-display-overview tr.field-formatter-settings-changed { + background: #FFFFBB; +} +#field-display-overview tr.drag { + background: #FFEE77; +} +#field-display-overview tr.field-formatter-settings-editing { + background: #D5E9F2; +} +#field-display-overview .field-formatter-settings-edit-form .form-item { + margin: 10px 0; +} +#field-display-overview .field-formatter-settings-edit-form .form-submit { + margin-bottom: 0; +} + /* Recent content block */ #dashboard div#block-node-recent div.content { padding: 0; @@ -959,3 +972,47 @@ 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; +} + +/* Vertical Tabs */ +div.vertical-tabs .vertical-tabs-list { + font-size: 1em; +} +div.vertical-tabs ul li.vertical-tab-button strong { + font-size: 0.923em; +} +div.vertical-tabs ul li.vertical-tab-button .summary { + font-size: 0.846em; +} diff --git a/themes/seven/template.php b/themes/seven/template.php index 09773e2aee8fec61b08cf84019e2586ed0e8c725..40862ff609e4e22afe46d6fb76c429edf2a2cf78 100644 --- a/themes/seven/template.php +++ b/themes/seven/template.php @@ -1,5 +1,5 @@ <?php -// $Id: template.php,v 1.17 2010/05/12 09:22:24 dries Exp $ +// $Id: template.php,v 1.20 2010/08/14 00:43:24 dries Exp $ /** * Override or insert variables into the maintenance page template. @@ -18,9 +18,9 @@ function seven_preprocess_maintenance_page(&$vars) { */ function seven_preprocess_html(&$vars) { // Add conditional CSS for IE8 and below. - drupal_add_css(path_to_theme() . '/ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE), 'preprocess' => FALSE)); + drupal_add_css(path_to_theme() . '/ie.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE))); // Add conditional CSS for IE6. - drupal_add_css(path_to_theme() . '/ie6.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); + drupal_add_css(path_to_theme() . '/ie6.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE))); } /** @@ -53,7 +53,7 @@ function seven_node_add_list($variables) { /** * Override of theme_admin_block_content(). * - * Use unordered list markup in both compact and extended move. + * Use unordered list markup in both compact and extended mode. */ function seven_admin_block_content($variables) { $content = $variables['content']; @@ -97,4 +97,8 @@ function seven_css_alter(&$css) { if (isset($css['misc/vertical-tabs.css'])) { $css['misc/vertical-tabs.css']['data'] = drupal_get_path('theme', 'seven') . '/vertical-tabs.css'; } + // Use Seven's jQuery UI theme style instead of the default one. + if (isset($css['misc/ui/jquery.ui.theme.css'])) { + $css['misc/ui/jquery.ui.theme.css']['data'] = drupal_get_path('theme', 'seven') . '/jquery.ui.theme.css'; + } } diff --git a/themes/seven/vertical-tabs.css b/themes/seven/vertical-tabs.css index 5896f775d4bdfe4346d0099173f96a4434616f61..8e2f5b83dc16609770b8edb69a6d268c491b2d17 100644 --- a/themes/seven/vertical-tabs.css +++ b/themes/seven/vertical-tabs.css @@ -1,4 +1,4 @@ -/* $Id: vertical-tabs.css,v 1.5 2010/03/20 14:45:05 dries Exp $ */ +/* $Id: vertical-tabs.css,v 1.6 2010/07/18 01:36:06 dries Exp $ */ /** * Override of misc/vertical-tabs.css. @@ -17,7 +17,7 @@ div.vertical-tabs fieldset { div.vertical-tabs .vertical-tabs-list { line-height: 10px; - font-size: 11px; + font-size: 0.875em; width: 25%; float: left; list-style-type: none; @@ -43,7 +43,7 @@ div.vertical-tabs ul li.vertical-tab-button a { div.vertical-tabs ul li.vertical-tab-button .summary { display: block; - font-size: 9px; + font-size: 0.75em; color: #666; padding-top: 0.4em; } diff --git a/themes/stark/stark.info b/themes/stark/stark.info index b642df2c12b239ab1ee4984dd1ce3cf08c9218ec..3266e5265991226aecd5385bed1ffba7e4b7e1a9 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/tests/test_theme/template.php b/themes/tests/test_theme/template.php index 85a5e5c5b8e12b4cbbd1c347f84b207330697573..5681d5be79f364840484a56c540a084ceb415e39 100644 --- a/themes/tests/test_theme/template.php +++ b/themes/tests/test_theme/template.php @@ -1,5 +1,5 @@ <?php -// $Id: template.php,v 1.1 2010/03/21 04:05:24 webchick Exp $ +// $Id: template.php,v 1.2 2010/08/22 12:46:21 dries Exp $ /** * Tests a theme overriding a suggestion of a base theme hook. @@ -9,3 +9,14 @@ function test_theme_breadcrumb__suggestion($variables) { // when the suggestion has an implementation. return 'test_theme_breadcrumb__suggestion: ' . $variables['theme_test_preprocess_breadcrumb']; } + +/** + * Tests a theme implementing an alter hook. + * + * The confusing function name here is due to this being an implementation of + * the alter hook invoked when the 'theme_test' module calls + * drupal_alter('theme_test_alter'). + */ +function test_theme_theme_test_alter_alter(&$data) { + $data = 'test_theme_theme_test_alter_alter was invoked'; +} diff --git a/themes/tests/test_theme/test_theme.info b/themes/tests/test_theme/test_theme.info index 0214fefd3d92ea26eb5b2c108330675a03b018ef..406be1035ca8f31fb79adf86828e1bcbd8bb0622 100644 --- a/themes/tests/test_theme/test_theme.info +++ b/themes/tests/test_theme/test_theme.info @@ -4,8 +4,8 @@ description = Theme for testing the theme system core = 7.x engine = phptemplate hidden = TRUE -; Information added by drupal.org packaging script on 2010-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/tests/update_test_basetheme/update_test_basetheme.info b/themes/tests/update_test_basetheme/update_test_basetheme.info index df00aa326cace33a4940bd411ef362cb0e0823fd..b176671f7215a3bda55fe5c0c861d1eb2bec8bd2 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/themes/tests/update_test_subtheme/update_test_subtheme.info b/themes/tests/update_test_subtheme/update_test_subtheme.info index f4430a575f8a9e294092a8faac45c6249c077c64..6e3a7e3be05d814f62a7776ffe8d405c200acea7 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-07-09 -version = "7.0-alpha6" +; Information added by drupal.org packaging script on 2010-09-16 +version = "7.0-alpha7" project = "drupal" -datestamp = "1278634806" +datestamp = "1284599761" diff --git a/web.config b/web.config index d97e8953106eeab048c85696d172a91ddfe91664..34afd0a469be4414105e7cabe3d58fb38e39710b 100644 --- a/web.config +++ b/web.config @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> - <!-- Don't show directory listings for URLs which map to a directory. --> + <!-- Don't show directory listings for URLs which map to a directory. --> <directoryBrowse enabled="false" /> <rewrite> <rules> <rule name="Protect files and directories from prying eyes" stopProcessing="true"> - <match url="\.(engine|inc|info|install|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl|svn-base)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$" /> + <match url="\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$" /> <action type="CustomResponse" statusCode="403" subStatusCode="0" statusReason="Forbidden" statusDescription="Access is forbidden." /> </rule> <rule name="Force simple error message for requests for non-existent favicon.ico" stopProcessing="true"> @@ -14,7 +14,7 @@ <action type="CustomResponse" statusCode="404" subStatusCode="1" statusReason="File Not Found" statusDescription="The requested file favicon.ico was not found" /> </rule> <!-- Rewrite URLs of the form 'x' to the form 'index.php?q=x'. --> - <rule name="Short URLS" stopProcessing="true"> + <rule name="Short URLs" stopProcessing="true"> <match url="^(.*)$" ignoreCase="false" /> <conditions> <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" /> @@ -32,7 +32,7 @@ </httpErrors> <defaultDocument> - <!-- Set the default document --> + <!-- Set the default document --> <files> <remove value="index.php" /> <add value="index.php" />