summaryrefslogtreecommitdiff
path: root/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'modules/system')
-rw-r--r--modules/system/system.admin.inc11
-rw-r--r--modules/system/system.api.php139
-rw-r--r--modules/system/system.base.css8
-rw-r--r--modules/system/system.install15
-rw-r--r--modules/system/system.mail.inc65
-rw-r--r--modules/system/system.module51
-rw-r--r--modules/system/system.queue.inc14
-rw-r--r--modules/system/system.test123
8 files changed, 341 insertions, 85 deletions
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index e250bf171..7ed232a54 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -622,7 +622,7 @@ function system_theme_settings_validate($form, &$form_state) {
}
else {
// File upload failed.
- form_set_error('logo_upload', t('The favicon could not be uploaded.'));
+ form_set_error('favicon_upload', t('The favicon could not be uploaded.'));
}
}
@@ -811,6 +811,7 @@ function system_modules($form, $form_state = array()) {
// Only display visible modules.
elseif (isset($visible_files[$requires])) {
$requires_name = $files[$requires]->info['name'];
+ // Disable this module if it is incompatible with the dependency's version.
if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $requires_name . $incompatible_version,
@@ -818,6 +819,14 @@ function system_modules($form, $form_state = array()) {
));
$extra['disabled'] = TRUE;
}
+ // Disable this module if the dependency is incompatible with this
+ // version of Drupal core.
+ elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
+ $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
+ '@module' => $requires_name,
+ ));
+ $extra['disabled'] = TRUE;
+ }
elseif ($files[$requires]->status) {
$extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
}
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index ae76ab44b..cb67268ca 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -991,7 +991,8 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
* );
* @endcode
* When path 'my-module/foo/edit' is requested, integer 1 will be replaced
- * with 'foo' and passed to the callback function.
+ * with 'foo' and passed to the callback function. Note that wildcards may not
+ * be used as the first component.
*
* Registered paths may also contain special "auto-loader" wildcard components
* in the form of '%mymodule_abc', where the '%' part means that this path
@@ -1018,7 +1019,11 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
* @endcode
* This 'abc' object will then be passed into the callback functions defined
* for the menu item, such as the page callback function mymodule_abc_edit()
- * to replace the integer 1 in the argument array.
+ * to replace the integer 1 in the argument array. Note that a load function
+ * should return FALSE when it is unable to provide a loadable object. For
+ * example, the node_load() function for the 'node/%node/edit' menu item will
+ * return FALSE for the path 'node/999/edit' if a node with a node ID of 999
+ * does not exist. The menu routing system will return a 404 error in this case.
*
* You can also define a %wildcard_to_arg() function (for the example menu
* entry above this would be 'mymodule_abc_to_arg()'). The _to_arg() function
@@ -1578,12 +1583,21 @@ function hook_page_alter(&$page) {
* One popular use of this hook is to add form elements to the node form. When
* altering a node form, the node object can be accessed at $form['#node'].
*
- * Note that instead of hook_form_alter(), which is called for all forms, you
- * can also use hook_form_FORM_ID_alter() to alter a specific form. For each
- * module (in system weight order) the general form alter hook implementation
- * is invoked first, then the form ID specific alter implementation is called.
- * After all module hook implementations are invoked, the hook_form_alter()
- * implementations from themes are invoked in the same manner.
+ * In addition to hook_form_alter(), which is called for all forms, there are
+ * two more specific form hooks available. The first,
+ * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base
+ * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to
+ * target a specific form directly.
+ *
+ * The call order is as follows: all existing form alter functions are called
+ * for module A, then all for module B, etc., followed by all for any base
+ * theme(s), and finally for the theme itself. The module order is determined
+ * by system weight, then by module name.
+ *
+ * Within each module, form alter hooks are called in the following order:
+ * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third,
+ * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are
+ * called first followed by the more specific.
*
* @param $form
* Nested array of form elements that comprise the form.
@@ -1595,6 +1609,7 @@ function hook_page_alter(&$page) {
* String representing the name of the form itself. Typically this is the
* name of the function that generated the form.
*
+ * @see hook_form_BASE_FORM_ID_alter()
* @see hook_form_FORM_ID_alter()
*/
function hook_form_alter(&$form, &$form_state, $form_id) {
@@ -1615,6 +1630,10 @@ function hook_form_alter(&$form, &$form_state, $form_id) {
* rather than implementing hook_form_alter() and checking the form ID, or
* using long switch statements to alter multiple forms.
*
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
* @param $form
* Nested array of form elements that comprise the form.
* @param $form_state
@@ -1626,6 +1645,7 @@ function hook_form_alter(&$form, &$form_state, $form_id) {
* name of the function that generated the form.
*
* @see hook_form_alter()
+ * @see hook_form_BASE_FORM_ID_alter()
* @see drupal_prepare_form()
*/
function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
@@ -1642,17 +1662,27 @@ function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
}
/**
- * Provide a form-specific alteration for shared forms.
+ * Provide a form-specific alteration for shared ('base') forms.
+ *
+ * By default, when drupal_get_form() is called, Drupal looks for a function
+ * with the same name as the form ID, and uses that function to build the form.
+ * In contrast, base forms allow multiple form IDs to be mapped to a single base
+ * (also called 'factory') form function.
*
* Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
- * form belonging to multiple form_ids, rather than implementing
- * hook_form_alter() and checking for conditions that would identify the
- * shared form constructor.
+ * base form, rather than implementing hook_form_alter() and checking for
+ * conditions that would identify the shared form constructor.
+ *
+ * To identify the base form ID for a particular form (or to determine whether
+ * one exists) check the $form_state. The base form ID is stored under
+ * $form_state['build_info']['base_form_id'].
*
- * Examples for such forms are node_form() or comment_form().
+ * See hook_forms() for more information on how to implement base forms in
+ * Drupal.
*
- * Note that this hook fires after hook_form_FORM_ID_alter() and before
- * hook_form_alter().
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
*
* @param $form
* Nested array of form elements that comprise the form.
@@ -1662,8 +1692,10 @@ function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
* String representing the name of the form itself. Typically this is the
* name of the function that generated the form.
*
+ * @see hook_form_alter()
* @see hook_form_FORM_ID_alter()
* @see drupal_prepare_form()
+ * @see hook_forms()
*/
function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
// Modification for the form with the given BASE_FORM_ID goes here. For
@@ -1683,13 +1715,25 @@ function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
*
* By default, when drupal_get_form() is called, the system will look for a
* function with the same name as the form ID, and use that function to build
- * the form. This hook allows you to override that behavior in two ways.
+ * the form. If no such function is found, Drupal calls this hook. Modules
+ * implementing this hook can then provide their own instructions for mapping
+ * form IDs to constructor functions. As a result, you can easily map multiple
+ * form IDs to a single form constructor (referred to as a 'base' form).
+ *
+ * Using a base form can help to avoid code duplication, by allowing many
+ * similar forms to use the same code base. Another benefit is that it becomes
+ * much easier for other modules to apply a general change to the group of
+ * forms; hook_form_BASE_FORM_ID_alter() can be used to easily alter multiple
+ * forms at once by directly targeting the shared base form.
+ *
+ * Two example use cases where base forms may be useful are given below.
*
* First, you can use this hook to tell the form system to use a different
* function to build certain forms in your module; this is often used to define
* a form "factory" function that is used to build several similar forms. In
* this case, your hook implementation will likely ignore all of the input
- * arguments. See node_forms() for an example of this.
+ * arguments. See node_forms() for an example of this. Note, node_forms() is the
+ * hook_forms() implementation; the base form itself is defined in node_form().
*
* Second, you could use this hook to define how to build a form with a
* dynamically-generated form ID. In this case, you would need to verify that
@@ -1706,7 +1750,9 @@ function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
* @return
* An associative array whose keys define form_ids and whose values are an
* associative array defining the following keys:
- * - callback: The name of the form builder function to invoke.
+ * - callback: The name of the form builder function to invoke. This will be
+ * used for the base form ID, for example, to target a base form using
+ * hook_form_BASE_FORM_ID_alter().
* - callback arguments: (optional) Additional arguments to pass to the
* function defined in 'callback', which are prepended to $args.
* - wrapper_callback: (optional) The name of a form builder function to
@@ -1861,12 +1907,20 @@ function hook_image_toolkits() {
* - 'language':
* The language object used to build the message before hook_mail_alter()
* is invoked.
+ * - 'send':
+ * Set to FALSE to abort sending this email message.
*
* @see drupal_mail()
*/
function hook_mail_alter(&$message) {
if ($message['id'] == 'modulename_messagekey') {
- $message['body'][] = "--\nMail sent out from " . variable_get('sitename', t('Drupal'));
+ if (!example_notifications_optin($message['to'], $message['id'])) {
+ // If the recipient has opted to not receive such messages, cancel
+ // sending.
+ $message['send'] = FALSE;
+ return;
+ }
+ $message['body'][] = "--\nMail sent out from " . variable_get('site_name', t('Drupal'));
}
}
@@ -1877,6 +1931,15 @@ function hook_mail_alter(&$message) {
* hook in order to reorder the implementing modules, which are otherwise
* ordered by the module's system weight.
*
+ * Note that hooks invoked using drupal_alter() can have multiple variations
+ * (such as hook_form_alter() and hook_form_FORM_ID_alter()). drupal_alter()
+ * will call all such variants defined by a single module in turn. For the
+ * purposes of hook_module_implements_alter(), these variants are treated as
+ * a single hook. Thus, to ensure that your implementation of
+ * hook_form_FORM_ID_alter() is called at the right time, you will have to
+ * have to change the order of hook_form_alter() implementation in
+ * hook_module_implements_alter().
+ *
* @param $implementations
* An array keyed by the module's name. The value of each item corresponds
* to a $group, which is usually FALSE, unless the implementation is in a
@@ -1897,6 +1960,25 @@ function hook_module_implements_alter(&$implementations, $hook) {
}
/**
+ * Return additional themes provided by modules.
+ *
+ * Only use this hook for testing purposes. Use a hidden MYMODULE_test.module
+ * to implement this hook. Testing themes should be hidden, too.
+ *
+ * This hook is invoked from _system_rebuild_theme_data() and allows modules to
+ * register additional themes outside of the regular 'themes' directories of a
+ * Drupal installation.
+ *
+ * @return
+ * An associative array. Each key is the system name of a theme and each value
+ * is the corresponding path to the theme's .info file.
+ */
+function hook_system_theme_info() {
+ $themes['mymodule_test_theme'] = drupal_get_path('module', 'mymodule') . '/mymodule_test_theme/mymodule_test_theme.info';
+ return $themes;
+}
+
+/**
* Alter the information parsed from module and theme .info files
*
* This hook is invoked in _system_rebuild_module_data() and in
@@ -2583,7 +2665,7 @@ function hook_stream_wrappers_alter(&$wrappers) {
* An array of file objects, indexed by fid.
*
* @see file_load_multiple()
- * @see upload_file_load()
+ * @see file_load()
*/
function hook_file_load($files) {
// Add the upload specific data into the file object.
@@ -2708,7 +2790,6 @@ function hook_file_move($file, $source) {
* The file that has just been deleted.
*
* @see file_delete()
- * @see upload_file_delete()
*/
function hook_file_delete($file) {
// Delete all information associated with the file.
@@ -2881,7 +2962,7 @@ function hook_requirements($phase) {
// Test PHP version
$requirements['php'] = array(
'title' => $t('PHP'),
- 'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/logs/status/php') : phpversion(),
+ 'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/reports/status/php') : phpversion(),
);
if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) {
$requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP %version.', array('%version' => DRUPAL_MINIMUM_PHP));
@@ -2903,7 +2984,7 @@ function hook_requirements($phase) {
);
}
- $requirements['cron']['description'] .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/logs/status/run-cron')));
+ $requirements['cron']['description'] .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
$requirements['cron']['title'] = $t('Cron maintenance tasks');
}
@@ -2930,7 +3011,8 @@ function hook_requirements($phase) {
* By declaring the tables used by your module via an implementation of
* hook_schema(), these tables will be available on all supported database
* engines. You don't have to deal with the different SQL dialects for table
- * creation and alteration of the supported database engines *
+ * creation and alteration of the supported database engines.
+ *
* See the Schema API Handbook at http://drupal.org/node/146843 for
* details on schema definition structures.
*
@@ -3160,14 +3242,11 @@ function hook_install() {
*
* A good rule of thumb is to remove updates older than two major releases of
* Drupal. See hook_update_last_removed() to notify Drupal about the removals.
+ * For further information about releases and release numbers see:
+ * @link http://drupal.org/node/711070 Maintaining a drupal.org project with Git @endlink
*
* Never renumber update functions.
*
- * Further information about releases and release numbers:
- * - @link http://drupal.org/handbook/version-info http://drupal.org/handbook/version-info @endlink
- * - @link http://drupal.org/node/93999 http://drupal.org/node/93999 @endlink (Overview of contributions branches and tags)
- * - @link http://drupal.org/handbook/cvs/releases http://drupal.org/handbook/cvs/releases @endlink
- *
* Implementations of this hook should be placed in a mymodule.install file in
* the same directory as mymodule.module. Drupal core's updates are implemented
* using the system module as a name and stored in database/updates.inc.
@@ -4033,7 +4112,7 @@ function hook_url_inbound_alter(&$path, $original_path, $path_language) {
* @param $path
* The outbound path to alter, not adjusted for path aliases yet. It won't be
* adjusted for path aliases until all modules are finished altering it, thus
- * being consistent with hook_url_alter_inbound(), which adjusts for all path
+ * being consistent with hook_url_inbound_alter(), which adjusts for all path
* aliases before allowing modules to alter it. This may have been altered by
* other modules before this one.
* @param $options
diff --git a/modules/system/system.base.css b/modules/system/system.base.css
index a6748de42..addbf1130 100644
--- a/modules/system/system.base.css
+++ b/modules/system/system.base.css
@@ -157,12 +157,9 @@ table.sticky-header {
.progress .percentage {
float: right; /* LTR */
}
-.progress-disabled {
- float: left; /* LTR */
-}
/* Throbber */
.ajax-progress {
- float: left; /* LTR */
+ display: inline-block;
}
.ajax-progress .throbber {
background: transparent url(../../misc/throbber.gif) no-repeat 0px -18px;
@@ -171,6 +168,9 @@ table.sticky-header {
margin: 2px;
width: 15px;
}
+.ajax-progress .message {
+ padding-left: 20px;
+}
tr .ajax-progress .throbber {
margin: 0 2px;
}
diff --git a/modules/system/system.install b/modules/system/system.install
index 219f067d1..aed7cc489 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -465,7 +465,7 @@ function system_requirements($phase) {
$requirements['update status'] = array(
'value' => $t('Not enabled'),
'severity' => REQUIREMENT_WARNING,
- 'description' => $t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you enable the update status module from the <a href="@module">module administration page</a> in order to stay up-to-date on new releases. For more information, <a href="@update">Update status handbook page</a>.', array('@update' => 'http://drupal.org/handbook/modules/update', '@module' => url('admin/modules'))),
+ 'description' => $t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you enable the update manager module from the <a href="@module">module administration page</a> in order to stay up-to-date on new releases. For more information, <a href="@update">Update status handbook page</a>.', array('@update' => 'http://drupal.org/handbook/modules/update', '@module' => url('admin/modules'))),
);
}
else {
@@ -2242,10 +2242,6 @@ function system_update_7036() {
));
}
$insert->execute();
-
- // Remove obsolete variable 'site_offline_message'. See
- // update_fix_d7_requirements().
- variable_del('site_offline_message');
}
/**
@@ -2974,6 +2970,15 @@ function system_update_7071() {
}
/**
+ * Remove the obsolete 'site_offline_message' variable.
+ *
+ * @see update_fix_d7_requirements()
+ */
+function system_update_7072() {
+ variable_del('site_offline_message');
+}
+
+/**
* @} End of "defgroup updates-6.x-to-7.x"
* The next series of updates should start at 8000.
*/
diff --git a/modules/system/system.mail.inc b/modules/system/system.mail.inc
index ef50642c5..4e7544006 100644
--- a/modules/system/system.mail.inc
+++ b/modules/system/system.mail.inc
@@ -65,28 +65,49 @@ class DefaultMailSystem implements MailSystemInterface {
// For headers, PHP's API suggests that we use CRLF normally,
// but some MTAs incorrectly replace LF with CRLF. See #234403.
$mail_headers = join("\n", $mimeheaders);
- if (isset($message['Return-Path']) && !ini_get('safe_mode')) {
- $mail_result = mail(
- $message['to'],
- $mail_subject,
- $mail_body,
- $mail_headers,
- // Pass the Return-Path via sendmail's -f command.
- '-f ' . $message['Return-Path']
- );
- }
- else {
- // The optional $additional_parameters argument to mail() is not allowed
- // if safe_mode is enabled. Passing any value throws a PHP warning and
- // makes mail() return FALSE.
- $mail_result = mail(
- $message['to'],
- $mail_subject,
- $mail_body,
- $mail_headers
- );
- }
- return $mail_result;
+
+ // We suppress warnings and notices from mail() because of issues on some
+ // hosts. The return value of this method will still indicate whether mail
+ // was sent successfully.
+ if (!isset($_SERVER['WINDIR']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') === FALSE) {
+ if (isset($message['Return-Path']) && !ini_get('safe_mode')) {
+ // On most non-Windows systems, the "-f" option to the sendmail command
+ // is used to set the Return-Path. There is no space between -f and
+ // the value of the return path.
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers,
+ '-f' . $message['Return-Path']
+ );
+ }
+ else {
+ // The optional $additional_parameters argument to mail() is not
+ // allowed if safe_mode is enabled. Passing any value throws a PHP
+ // warning and makes mail() return FALSE.
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers
+ );
+ }
+ }
+ else {
+ // On Windows, PHP will use the value of sendmail_from for the
+ // Return-Path header.
+ $old_from = ini_get('sendmail_from');
+ ini_set('sendmail_from', $message['Return-Path']);
+ $mail_result = @mail(
+ $message['to'],
+ $mail_subject,
+ $mail_body,
+ $mail_headers
+ );
+ ini_set('sendmail_from', $old_from);
+ }
+ return $mail_result;
}
}
diff --git a/modules/system/system.module b/modules/system/system.module
index d0a542efb..086a298e9 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -46,6 +46,13 @@ define('DRUPAL_OPTIONAL', 1);
define('DRUPAL_REQUIRED', 2);
/**
+ * Maximum number of values in a weight select element.
+ *
+ * If the number of values is over the maximum, a text field is used instead.
+ */
+define('DRUPAL_WEIGHT_SELECT_MAX', 100);
+
+/**
* Return only visible regions.
*
* @see system_region_list()
@@ -969,7 +976,7 @@ function system_menu() {
);
$items['admin/config/system/site-information'] = array(
'title' => 'Site information',
- 'description' => t('Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.'),
+ 'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.',
'page callback' => 'drupal_get_form',
'page arguments' => array('system_site_information_settings'),
'access arguments' => array('administer site configuration'),
@@ -977,8 +984,8 @@ function system_menu() {
'weight' => -20,
);
$items['admin/config/system/cron'] = array(
- 'title' => t('Cron'),
- 'description' => t('Manage automatic site maintenance tasks.'),
+ 'title' => 'Cron',
+ 'description' => 'Manage automatic site maintenance tasks.',
'page callback' => 'drupal_get_form',
'page arguments' => array('system_cron_settings'),
'access arguments' => array('administer site configuration'),
@@ -1108,7 +1115,7 @@ function system_library() {
'title' => 'Drupal progress indicator',
'version' => VERSION,
'js' => array(
- 'misc/progress.js' => array('group' => JS_DEFAULT, 'cache' => FALSE),
+ 'misc/progress.js' => array('group' => JS_DEFAULT),
),
);
@@ -2465,6 +2472,18 @@ function _system_update_bootstrap_status() {
function _system_rebuild_theme_data() {
// Find themes
$themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes');
+ // Allow modules to add further themes.
+ if ($module_themes = module_invoke_all('system_theme_info')) {
+ foreach ($module_themes as $name => $uri) {
+ // @see file_scan_directory()
+ $themes[$name] = (object) array(
+ 'uri' => $uri,
+ 'filename' => pathinfo($uri, PATHINFO_FILENAME),
+ 'name' => $name,
+ );
+ }
+ }
+
// Find theme engines
$engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
@@ -2700,8 +2719,8 @@ function system_region_list($theme_key, $show = REGIONS_ALL) {
* Implements hook_system_info_alter().
*/
function system_system_info_alter(&$info, $file, $type) {
- // Remove page-top from the blocks UI since it is reserved for modules to
- // populate from outside the blocks system.
+ // Remove page-top and page-bottom from the blocks UI since they are reserved for
+ // modules to populate from outside the blocks system.
if ($type == 'theme') {
$info['regions_hidden'][] = 'page_top';
$info['regions_hidden'][] = 'page_bottom';
@@ -2864,7 +2883,18 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL,
}
/**
- * Determines if the current user is in compact mode.
+ * Determines whether the current user is in compact mode.
+ *
+ * Compact mode shows certain administration pages with less description text,
+ * such as the configuration page and the permissions page.
+ *
+ * Whether the user is in compact mode is determined by a cookie, which is set
+ * for the user by system_admin_compact_page().
+ *
+ * If the user does not have the cookie, the default value is given by the
+ * system variable 'admin_compact_mode', which itself defaults to FALSE. This
+ * does not have a user interface to set it: it is a hidden variable which can
+ * be set in the settings.php file.
*
* @return
* TRUE when in compact mode, FALSE when in expanded mode.
@@ -3420,12 +3450,12 @@ function system_image_toolkits() {
function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FILE_EXISTS_RENAME) {
$parsed_url = parse_url($url);
if (!isset($destination)) {
- $path = file_build_uri(basename($parsed_url['path']));
+ $path = file_build_uri(drupal_basename($parsed_url['path']));
}
else {
if (is_dir(drupal_realpath($destination))) {
// Prevent URIs with triple slashes when glueing parts together.
- $path = str_replace('///', '//', "$destination/") . basename($parsed_url['path']);
+ $path = str_replace('///', '//', "$destination/") . drupal_basename($parsed_url['path']);
}
else {
$path = $destination;
@@ -3860,7 +3890,10 @@ function system_date_format_save($date_format, $dfid = 0) {
drupal_write_record('date_formats', $info, $keys);
}
+ // Retrieve an array of language objects for enabled languages.
$languages = language_list('enabled');
+ // This list is keyed off the value of $language->enabled; we want the ones
+ // that are enabled (value of 1).
$languages = $languages[1];
$locale_format = array();
diff --git a/modules/system/system.queue.inc b/modules/system/system.queue.inc
index 806015c24..47d8e7cc3 100644
--- a/modules/system/system.queue.inc
+++ b/modules/system/system.queue.inc
@@ -45,13 +45,13 @@
* would be an in-memory queue backend which might lose items if it crashes.
* However, such a backend would be able to deal with significantly more writes
* than a reliable queue and for many tasks this is more important. See
- * aggregator_cron() for an example of how can this not be a problem. Another
- * example is doing Twitter statistics -- the small possibility of losing a few
- * items is insignificant next to power of the queue being able to keep up with
- * writes. As described in the processing section, regardless of the queue
- * being reliable or not, the processing code should be aware that an item
- * might be handed over for processing more than once (because the processing
- * code might time out before it finishes).
+ * aggregator_cron() for an example of how to effectively utilize a
+ * non-reliable queue. Another example is doing Twitter statistics -- the small
+ * possibility of losing a few items is insignificant next to power of the
+ * queue being able to keep up with writes. As described in the processing
+ * section, regardless of the queue being reliable or not, the processing code
+ * should be aware that an item might be handed over for processing more than
+ * once (because the processing code might time out before it finishes).
*/
/**
diff --git a/modules/system/system.test b/modules/system/system.test
index a75153f6a..f40bd686a 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -272,12 +272,32 @@ class EnableDisableTestCase extends ModuleTestCase {
}
/**
- * Tests entity cache after enabling a module with a dependency on an enitity
- * providing module.
+ * Ensures entity info cache is updated after changes.
+ */
+ function testEntityInfoChanges() {
+ module_enable(array('entity_cache_test'));
+ $entity_info = entity_get_info();
+ $this->assertTrue(isset($entity_info['entity_cache_test']), 'Test entity type found.');
+
+ // Change the label of the test entity type and make sure changes appear
+ // after flushing caches.
+ variable_set('entity_cache_test_label', 'New label.');
+ drupal_flush_all_caches();
+ $info = entity_get_info('entity_cache_test');
+ $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.');
+
+ // Disable the providing module and make sure the entity type is gone.
+ module_disable(array('entity_cache_test', 'entity_cache_test_dependency'));
+ $entity_info = entity_get_info();
+ $this->assertFalse(isset($entity_info['entity_cache_test']), 'Entity type of the providing module is gone.');
+ }
+
+ /**
+ * Tests entity info cache after enabling a module with a dependency on an entity providing module.
*
* @see entity_cache_test_watchdog()
*/
- function testEntityCache() {
+ function testEntityInfoCacheWatchdog() {
module_enable(array('entity_cache_test'));
$info = variable_get('entity_cache_test');
$this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.');
@@ -422,6 +442,35 @@ class ModuleDependencyTestCase extends ModuleTestCase {
}
/**
+ * Tests enabling a module that depends on an incompatible version of a module.
+ */
+ function testIncompatibleModuleVersionDependency() {
+ // Test that the system_incompatible_module_version_dependencies_test is
+ // marked as having an incompatible dependency.
+ $this->drupalGet('admin/modules');
+ $this->assertRaw(t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
+ '@module' => 'System incompatible module version test (>2.0)',
+ '@version' => '1.0',
+ )), 'A module that depends on an incompatible version of a module is marked as such.');
+ $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_incompatible_module_version_dependencies_test][enable]"]');
+ $this->assert(count($checkbox) == 1, t('Checkbox for the module is disabled.'));
+ }
+
+ /**
+ * Tests enabling a module that depends on a module with an incompatible core version.
+ */
+ function testIncompatibleCoreVersionDependency() {
+ // Test that the system_incompatible_core_version_dependencies_test is
+ // marked as having an incompatible dependency.
+ $this->drupalGet('admin/modules');
+ $this->assertRaw(t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
+ '@module' => 'System incompatible core version test',
+ )), 'A module that depends on a module with an incompatible core version is marked as such.');
+ $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_incompatible_core_version_dependencies_test][enable]"]');
+ $this->assert(count($checkbox) == 1, t('Checkbox for the module is disabled.'));
+ }
+
+ /**
* Tests enabling a module that depends on a module which fails hook_requirements().
*/
function testEnableRequirementsFailureDependency() {
@@ -1832,6 +1881,9 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
$generated = token_generate('node', $raw_tokens, array('node' => $node), array('sanitize' => FALSE));
$this->assertEqual($generated['[node:title]'], $node->title, t('Unsanitized token generated properly.'));
+
+ // Test token replacement when the string contains no tokens.
+ $this->assertEqual(token_replace('No tokens here.'), 'No tokens here.');
}
/**
@@ -2086,7 +2138,7 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase {
}
function setUp() {
- parent::setUp();
+ parent::setUp('update_script_test');
$this->update_url = $GLOBALS['base_url'] . '/update.php';
$this->update_user = $this->drupalCreateUser(array('administer software updates'));
}
@@ -2123,6 +2175,58 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase {
}
/**
+ * Tests that requirements warnings and errors are correctly displayed.
+ */
+ function testRequirements() {
+ $this->drupalLogin($this->update_user);
+
+ // If there are no requirements warnings or errors, we expect to be able to
+ // go through the update process uninterrupted.
+ $this->drupalGet($this->update_url, array('external' => TRUE));
+ $this->drupalPost(NULL, array(), t('Continue'));
+ $this->assertText(t('No pending updates.'), t('End of update process was reached.'));
+ // Confirm that all caches were cleared.
+ $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared when there were no requirements warnings or errors.');
+
+ // If there is a requirements warning, we expect it to be initially
+ // displayed, but clicking the link to proceed should allow us to go
+ // through the rest of the update process uninterrupted.
+
+ // First, run this test with pending updates to make sure they can be run
+ // successfully.
+ variable_set('update_script_test_requirement_type', REQUIREMENT_WARNING);
+ drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
+ $this->drupalGet($this->update_url, array('external' => TRUE));
+ $this->assertText('This is a requirements warning provided by the update_script_test module.');
+ $this->clickLink('try again');
+ $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
+ $this->drupalPost(NULL, array(), t('Continue'));
+ $this->drupalPost(NULL, array(), t('Apply pending updates'));
+ $this->assertText(t('The update_script_test_update_7000() update was executed successfully.'), t('End of update process was reached.'));
+ // Confirm that all caches were cleared.
+ $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.');
+
+ // Now try again without pending updates to make sure that works too.
+ $this->drupalGet($this->update_url, array('external' => TRUE));
+ $this->assertText('This is a requirements warning provided by the update_script_test module.');
+ $this->clickLink('try again');
+ $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
+ $this->drupalPost(NULL, array(), t('Continue'));
+ $this->assertText(t('No pending updates.'), t('End of update process was reached.'));
+ // Confirm that all caches were cleared.
+ $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after applying updates and re-running the script.');
+
+ // If there is a requirements error, it should be displayed even after
+ // clicking the link to proceed (since the problem that triggered the error
+ // has not been fixed).
+ variable_set('update_script_test_requirement_type', REQUIREMENT_ERROR);
+ $this->drupalGet($this->update_url, array('external' => TRUE));
+ $this->assertText('This is a requirements error provided by the update_script_test module.');
+ $this->clickLink('try again');
+ $this->assertText('This is a requirements error provided by the update_script_test module.');
+ }
+
+ /**
* Tests the effect of using the update script on the theme system.
*/
function testThemeSystem() {
@@ -2193,7 +2297,7 @@ class RetrieveFileTestCase extends DrupalWebTestCase {
function testFileRetrieving() {
// Test 404 handling by trying to fetch a randomly named file.
drupal_mkdir($sourcedir = 'public://' . $this->randomName());
- $filename = $this->randomName();
+ $filename = 'Файл для тестирования ' . $this->randomName();
$url = file_create_url($sourcedir . '/' . $filename);
$retrieved_file = system_retrieve_file($url);
$this->assertFalse($retrieved_file, t('Non-existent file not fetched.'));
@@ -2201,7 +2305,12 @@ class RetrieveFileTestCase extends DrupalWebTestCase {
// Actually create that file, download it via HTTP and test the returned path.
file_put_contents($sourcedir . '/' . $filename, 'testing');
$retrieved_file = system_retrieve_file($url);
- $this->assertEqual($retrieved_file, 'public://' . $filename, t('Sane path for downloaded file returned (public:// scheme).'));
+
+ // URLs could not contains characters outside the ASCII set so $filename
+ // has to be encoded.
+ $encoded_filename = rawurlencode($filename);
+
+ $this->assertEqual($retrieved_file, 'public://' . $encoded_filename, t('Sane path for downloaded file returned (public:// scheme).'));
$this->assertTrue(is_file($retrieved_file), t('Downloaded file does exist (public:// scheme).'));
$this->assertEqual(filesize($retrieved_file), 7, t('File size of downloaded file is correct (public:// scheme).'));
file_unmanaged_delete($retrieved_file);
@@ -2209,7 +2318,7 @@ class RetrieveFileTestCase extends DrupalWebTestCase {
// Test downloading file to a different location.
drupal_mkdir($targetdir = 'temporary://' . $this->randomName());
$retrieved_file = system_retrieve_file($url, $targetdir);
- $this->assertEqual($retrieved_file, "$targetdir/$filename", t('Sane path for downloaded file returned (temporary:// scheme).'));
+ $this->assertEqual($retrieved_file, "$targetdir/$encoded_filename", t('Sane path for downloaded file returned (temporary:// scheme).'));
$this->assertTrue(is_file($retrieved_file), t('Downloaded file does exist (temporary:// scheme).'));
$this->assertEqual(filesize($retrieved_file), 7, t('File size of downloaded file is correct (temporary:// scheme).'));
file_unmanaged_delete($retrieved_file);