summaryrefslogtreecommitdiff
path: root/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'modules/system')
-rw-r--r--modules/system/language.api.php170
-rw-r--r--modules/system/page.tpl.php4
-rw-r--r--modules/system/system.admin.inc64
-rw-r--r--modules/system/system.api.php64
-rw-r--r--modules/system/system.base-rtl.css8
-rw-r--r--modules/system/system.base.css11
-rw-r--r--modules/system/system.install31
-rw-r--r--modules/system/system.js27
-rw-r--r--modules/system/system.module2
-rw-r--r--modules/system/system.test146
10 files changed, 446 insertions, 81 deletions
diff --git a/modules/system/language.api.php b/modules/system/language.api.php
new file mode 100644
index 000000000..671479309
--- /dev/null
+++ b/modules/system/language.api.php
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the base system for language support.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Allows modules to act after language initialization has been performed.
+ *
+ * This is primarily needed to provide translation for configuration variables
+ * in the proper bootstrap phase. Variables are user-defined strings and
+ * therefore should not be translated via t(), since the source string can
+ * change without notice and any previous translation would be lost. Moreover,
+ * since variables can be used in the bootstrap phase, we need a bootstrap hook
+ * to provide a translation early enough to avoid misalignments between code
+ * using the original values and code using the translated values. However
+ * modules implementing hook_boot() should be aware that language initialization
+ * did not happen yet and thus they cannot rely on translated variables.
+ */
+function hook_language_init() {
+ global $language, $conf;
+
+ switch ($language->language) {
+ case 'it':
+ $conf['site_name'] = 'Il mio sito Drupal';
+ break;
+
+ case 'fr':
+ $conf['site_name'] = 'Mon site Drupal';
+ break;
+ }
+}
+
+/**
+ * Perform alterations on language switcher links.
+ *
+ * A language switcher link may need to point to a different path or use a
+ * translated link text before going through l(), which will just handle the
+ * path aliases.
+ *
+ * @param $links
+ * Nested array of links keyed by language code.
+ * @param $type
+ * The language type the links will switch.
+ * @param $path
+ * The current path.
+ */
+function hook_language_switch_links_alter(array &$links, $type, $path) {
+ global $language;
+
+ if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language->language])) {
+ foreach ($links[$language->language] as $link) {
+ $link['attributes']['class'][] = 'active-language';
+ }
+ }
+}
+
+/**
+ * Allow modules to define their own language types.
+ *
+ * @return
+ * An array of language type definitions. Each language type has an identifier
+ * key. The language type definition is an associative array that may contain
+ * the following key-value pairs:
+ * - "name": The human-readable language type identifier.
+ * - "description": A description of the language type.
+ * - "fixed": An array of language provider identifiers. Defining this key
+ * makes the language type non-configurable.
+ */
+function hook_language_types_info() {
+ return array(
+ 'custom_language_type' => array(
+ 'name' => t('Custom language'),
+ 'description' => t('A custom language type.'),
+ ),
+ 'fixed_custom_language_type' => array(
+ 'fixed' => array('custom_language_provider'),
+ ),
+ );
+}
+
+/**
+ * Perform alterations on language types.
+ *
+ * @param $language_types
+ * Array of language type definitions.
+ */
+function hook_language_types_info_alter(array &$language_types) {
+ if (isset($language_types['custom_language_type'])) {
+ $language_types['custom_language_type_custom']['description'] = t('A far better description.');
+ }
+}
+
+/**
+ * Allow modules to define their own language providers.
+ *
+ * @return
+ * An array of language provider definitions. Each language provider has an
+ * identifier key. The language provider definition is an associative array
+ * that may contain the following key-value pairs:
+ * - "types": An array of allowed language types. If a language provider does
+ * not specify which language types it should be used with, it will be
+ * available for all the configurable language types.
+ * - "callbacks": An array of functions that will be called to perform various
+ * tasks. Possible key-value pairs are:
+ * - "language": Required. The callback that will determine the language
+ * value.
+ * - "switcher": The callback that will determine the language switch links
+ * associated to the current language provider.
+ * - "url_rewrite": The callback that will provide URL rewriting.
+ * - "file": A file that will be included before the callback is invoked; this
+ * allows callback functions to be in separate files.
+ * - "weight": The default weight the language provider has.
+ * - "name": A human-readable identifier.
+ * - "description": A description of the language provider.
+ * - "config": An internal path pointing to the language provider
+ * configuration page.
+ * - "cache": The value Drupal's page cache should be set to for the current
+ * language provider to be invoked.
+ */
+function hook_language_negotiation_info() {
+ return array(
+ 'custom_language_provider' => array(
+ 'callbacks' => array(
+ 'language' => 'custom_language_provider_callback',
+ 'switcher' => 'custom_language_switcher_callback',
+ 'url_rewrite' => 'custom_language_url_rewrite_callback',
+ ),
+ 'file' => drupal_get_path('module', 'custom') . '/custom.module',
+ 'weight' => -4,
+ 'types' => array('custom_language_type'),
+ 'name' => t('Custom language provider'),
+ 'description' => t('This is a custom language provider.'),
+ 'cache' => 0,
+ ),
+ );
+}
+
+/**
+ * Perform alterations on language providers.
+ *
+ * @param $language_providers
+ * Array of language provider definitions.
+ */
+function hook_language_negotiation_info_alter(array &$language_providers) {
+ if (isset($language_providers['custom_language_provider'])) {
+ $language_providers['custom_language_provider']['config'] = 'admin/config/regional/language/configure/custom-language-provider';
+ }
+}
+
+/**
+ * Perform alterations on the language fallback candidates.
+ *
+ * @param $fallback_candidates
+ * An array of language codes whose order will determine the language fallback
+ * order.
+ */
+function hook_language_fallback_candidates_alter(array &$fallback_candidates) {
+ $fallback_candidates = array_reverse($fallback_candidates);
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/modules/system/page.tpl.php b/modules/system/page.tpl.php
index 8ffe82aa6..bd440afb2 100644
--- a/modules/system/page.tpl.php
+++ b/modules/system/page.tpl.php
@@ -4,6 +4,9 @@
* @file
* Default theme implementation to display a single Drupal page.
*
+ * The doctype, html, head and body tags are not in this template. Instead they
+ * can be found in the html.tpl.php template in this directory.
+ *
* Available variables:
*
* General utility variables:
@@ -64,6 +67,7 @@
* @see template_preprocess()
* @see template_preprocess_page()
* @see template_process()
+ * @see html.tpl.php
*/
?>
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 7ed232a54..e9682e7dd 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -106,7 +106,15 @@ function system_admin_index() {
}
/**
- * Menu callback; displays a module's settings page.
+ * Displays the configuration overview page.
+ *
+ * This menu callback implementation is a legacy function that used to display
+ * the configuration overview page at admin/config. It is currently unused and
+ * will be removed in Drupal 8. The page at admin/config is now generated by
+ * system_admin_config_page().
+ *
+ * @deprecated
+ * @see system_admin_config_page()
*/
function system_settings_overview() {
// Check database setup if necessary
@@ -470,17 +478,11 @@ function system_theme_settings($form, &$form_state, $key = '') {
),
),
);
- $logo_path = theme_get_setting('logo_path', $key);
- // If $logo_path is a public:// URI, display the path relative to the files
- // directory; stream wrappers are not end-user friendly.
- if (file_uri_scheme($logo_path) == 'public') {
- $logo_path = file_uri_target($logo_path);
- }
$form['logo']['settings']['logo_path'] = array(
'#type' => 'textfield',
'#title' => t('Path to custom logo'),
- '#default_value' => $logo_path,
'#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'),
+ '#default_value' => theme_get_setting('logo_path', $key),
);
$form['logo']['settings']['logo_upload'] = array(
'#type' => 'file',
@@ -511,17 +513,11 @@ function system_theme_settings($form, &$form_state, $key = '') {
),
),
);
- $favicon_path = theme_get_setting('favicon_path', $key);
- // If $favicon_path is a public:// URI, display the path relative to the
- // files directory; stream wrappers are not end-user friendly.
- if (file_uri_scheme($favicon_path) == 'public') {
- $favicon_path = file_uri_target($favicon_path);
- }
$form['favicon']['settings']['favicon_path'] = array(
'#type' => 'textfield',
'#title' => t('Path to custom icon'),
- '#default_value' => $favicon_path,
- '#description' => t('The path to the image file you would like to use as your custom shortcut icon.')
+ '#description' => t('The path to the image file you would like to use as your custom shortcut icon.'),
+ '#default_value' => theme_get_setting('favicon_path', $key),
);
$form['favicon']['settings']['favicon_upload'] = array(
'#type' => 'file',
@@ -530,6 +526,22 @@ function system_theme_settings($form, &$form_state, $key = '') {
);
}
+ // Inject human-friendly values for logo and favicon.
+ foreach (array('logo' => 'logo.png', 'favicon' => 'favicon.ico') as $type => $default) {
+ if (isset($form[$type]['settings'][$type . '_path'])) {
+ $element = &$form[$type]['settings'][$type . '_path'];
+
+ // If path is a public:// URI, display the path relative to the files
+ // directory; stream wrappers are not end-user friendly.
+ $original_path = $element['#default_value'];
+ $friendly_path = NULL;
+ if (file_uri_scheme($original_path) == 'public') {
+ $friendly_path = file_uri_target($original_path);
+ $element['#default_value'] = $friendly_path;
+ }
+ }
+ }
+
if ($key) {
// Call engine-specific settings.
$function = $themes[$key]->prefix . '_engine_settings';
@@ -657,13 +669,20 @@ function system_theme_settings_validate($form, &$form_state) {
* the path could not be validated.
*/
function _system_theme_settings_validate_path($path) {
- if (drupal_realpath($path)) {
- // The path is relative to the Drupal root, or is a valid URI.
+ // Absolute local file paths are invalid.
+ if (drupal_realpath($path) == $path) {
+ return FALSE;
+ }
+ // A path relative to the Drupal root or a fully qualified URI is valid.
+ if (is_file($path)) {
return $path;
}
- $uri = 'public://' . $path;
- if (file_exists($uri)) {
- return $uri;
+ // Prepend 'public://' for relative file paths within public filesystem.
+ if (file_uri_scheme($path) === FALSE) {
+ $path = 'public://' . $path;
+ }
+ if (is_file($path)) {
+ return $path;
}
return FALSE;
}
@@ -1370,6 +1389,7 @@ function system_ip_blocking($default_ip = '') {
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
+ '#empty' => t('No blocked IP addresses available.'),
);
return $build;
@@ -1766,7 +1786,7 @@ function system_file_system_settings() {
'#title' => t('Private file system path'),
'#default_value' => variable_get('file_private_path', ''),
'#maxlength' => 255,
- '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/handbook/modules/file')),
+ '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
'#after_build' => array('system_check_directory'),
);
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index cb67268ca..e28386997 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -1200,22 +1200,23 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
* "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.
+ * from this menu item. Note that the "options" parameter has no effect on
+ * MENU_LOCAL_TASK, MENU_DEFAULT_LOCAL_TASK, and MENU_LOCAL_ACTION items.
*
* For a detailed usage example, see page_example.module.
* For comprehensive documentation on the menu system, see
* http://drupal.org/node/102338.
*/
function hook_menu() {
- $items['blog'] = array(
- 'title' => 'blogs',
- 'page callback' => 'blog_page',
+ $items['example'] = array(
+ 'title' => 'Example Page',
+ 'page callback' => 'example_page',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
- $items['blog/feed'] = array(
- 'title' => 'RSS feed',
- 'page callback' => 'blog_feed',
+ $items['example/feed'] = array(
+ 'title' => 'Example RSS feed',
+ 'page callback' => 'example_feed',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
@@ -2233,6 +2234,10 @@ function hook_theme_registry_alter(&$theme_registry) {
* theme set via a theme callback function in hook_menu(); the themes on those
* pages can only be overridden using hook_menu_alter().
*
+ * Note that returning different themes for the same path may not work with page
+ * caching. This is most likely to be a problem if an anonymous user on a given
+ * path could have different themes returned under different conditions.
+ *
* Since only one theme can be used at a time, the last (i.e., highest
* weighted) module which returns a valid theme name from this hook will
* prevail.
@@ -2328,31 +2333,40 @@ function hook_xmlrpc_alter(&$methods) {
}
/**
- * Log an event message
+ * Log an event message.
*
* This hook allows modules to route log events to custom destinations, such as
* SMS, Email, pager, syslog, ...etc.
*
* @param $log_entry
* An associative array containing the following keys:
- * - type: The type of message for this entry. For contributed modules, this is
- * normally the module name. Do not use 'debug', use severity WATCHDOG_DEBUG instead.
- * - user: The user object for the user who was logged in when the event happened.
- * - request_uri: The Request URI for the page the event happened in.
- * - referer: The page that referred the use to the page where the event occurred.
+ * - type: The type of message for this entry.
+ * - user: The user object for the user who was logged in when the event
+ * happened.
+ * - request_uri: The request URI for the page the event happened in.
+ * - referer: The page that referred the user to the page where the event
+ * occurred.
* - ip: The IP address where the request for the page came from.
- * - timestamp: The UNIX timestamp of the date/time the event occurred
- * - severity: One of the following values as defined in RFC 3164 http://www.faqs.org/rfcs/rfc3164.html
- * WATCHDOG_EMERGENCY Emergency: system is unusable
- * WATCHDOG_ALERT Alert: action must be taken immediately
- * WATCHDOG_CRITICAL Critical: critical conditions
- * WATCHDOG_ERROR Error: error conditions
- * WATCHDOG_WARNING Warning: warning conditions
- * WATCHDOG_NOTICE Notice: normal but significant condition
- * WATCHDOG_INFO Informational: informational messages
- * WATCHDOG_DEBUG Debug: debug-level messages
- * - link: an optional link provided by the module that called the watchdog() function.
- * - message: The text of the message to be logged.
+ * - timestamp: The UNIX timestamp of the date/time the event occurred.
+ * - severity: The severity of the message; one of the following values as
+ * defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ * - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ * - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ * - WATCHDOG_CRITICAL: Critical conditions.
+ * - WATCHDOG_ERROR: Error conditions.
+ * - WATCHDOG_WARNING: Warning conditions.
+ * - WATCHDOG_NOTICE: Normal but significant conditions.
+ * - WATCHDOG_INFO: Informational messages.
+ * - WATCHDOG_DEBUG: Debug-level messages.
+ * - link: An optional link provided by the module that called the watchdog()
+ * function.
+ * - message: The text of the message to be logged. Variables in the message
+ * are indicated by using placeholder strings alongside the variables
+ * argument to declare the value of the placeholders. See t() for
+ * documentation on how the message and variable parameters interact.
+ * - variables: An array of variables to be inserted into the message on
+ * display. Will be NULL or missing if a message is already translated or if
+ * the message is not possible to translate.
*/
function hook_watchdog(array $log_entry) {
global $base_url, $language;
diff --git a/modules/system/system.base-rtl.css b/modules/system/system.base-rtl.css
index 9099c9d72..075cafec2 100644
--- a/modules/system/system.base-rtl.css
+++ b/modules/system/system.base-rtl.css
@@ -36,8 +36,12 @@ html.js input.throbbing {
*/
.draggable a.tabledrag-handle {
float: right;
- margin: -0.4em -0.5em -0.4em 0;
- padding: 0.42em 0.5em 0.42em 1.5em;
+ margin-right: -1em;
+ margin-left: 0;
+}
+a.tabledrag-handle .handle {
+ margin: -0.4em 0.5em;
+ padding: 0.42em 0.5em;
}
div.indentation {
float: right;
diff --git a/modules/system/system.base.css b/modules/system/system.base.css
index addbf1130..bf6dd9f71 100644
--- a/modules/system/system.base.css
+++ b/modules/system/system.base.css
@@ -93,21 +93,22 @@ body.drag {
cursor: move;
float: left; /* LTR */
height: 1.7em;
- margin: -0.4em 0 -0.4em -0.5em; /* LTR */
- padding: 0.42em 1.5em 0.42em 0.5em; /* LTR */
+ margin-left: -1em; /* LTR */
+ overflow: hidden;
text-decoration: none;
}
a.tabledrag-handle:hover {
text-decoration: none;
}
a.tabledrag-handle .handle {
- background: url(../../misc/draggable.png) no-repeat 0 0;
+ background: url(../../misc/draggable.png) no-repeat 6px 9px;
height: 13px;
- margin-top: 4px;
+ margin: -0.4em 0.5em; /* LTR */
+ padding: 0.42em 0.5em; /* LTR */
width: 13px;
}
a.tabledrag-handle-hover .handle {
- background-position: 0 -20px;
+ background-position: 6px -11px;
}
div.indentation {
float: left; /* LTR */
diff --git a/modules/system/system.install b/modules/system/system.install
index aed7cc489..c0300000e 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 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'))),
+ '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/documentation/modules/update', '@module' => url('admin/modules'))),
);
}
else {
@@ -810,6 +810,7 @@ function system_schema() {
'length' => 255,
'not null' => TRUE,
'default' => '',
+ 'binary' => TRUE,
),
'uri' => array(
'description' => 'The URI to access the file (either local or remote).',
@@ -817,6 +818,7 @@ function system_schema() {
'length' => 255,
'not null' => TRUE,
'default' => '',
+ 'binary' => TRUE,
),
'filemime' => array(
'description' => "The file's MIME type.",
@@ -2173,6 +2175,7 @@ function system_update_7034() {
'length' => 255,
'not null' => TRUE,
'default' => '',
+ 'binary' => TRUE,
),
'uri' => array(
'description' => 'URI of file.',
@@ -2180,6 +2183,7 @@ function system_update_7034() {
'length' => 255,
'not null' => TRUE,
'default' => '',
+ 'binary' => TRUE,
),
'filemime' => array(
'description' => "The file's MIME type.",
@@ -2791,7 +2795,7 @@ function system_update_7061(&$sandbox) {
foreach ($revision['file'][LANGUAGE_NONE] as $delta => $file) {
// We will convert filepaths to uri using the default scheme
// and stripping off the existing file directory path.
- $file['uri'] = $scheme . str_replace($basename, '', $file['filepath']);
+ $file['uri'] = $scheme . preg_replace('!^' . preg_quote($basename) . '!', '', $file['filepath']);
$file['uri'] = file_stream_wrapper_uri_normalize($file['uri']);
unset($file['filepath']);
// Insert into the file_managed table.
@@ -2989,6 +2993,29 @@ function system_update_7072() {
*/
/**
+ * Add binary to {file_managed}, in case system_update_7034() was run without
+ * it.
+ */
+function system_update_7073() {
+ db_change_field('file_managed', 'filename', 'filename', array(
+ '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' => '',
+ 'binary' => TRUE,
+ ));
+ db_change_field('file_managed', 'uri', 'uri', array(
+ 'description' => 'The URI to access the file (either local or remote).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ));
+}
+
+/**
* @} End of "defgroup updates-7.x-extra"
* The next series of updates should start at 8000.
*/
diff --git a/modules/system/system.js b/modules/system/system.js
index 5446d28a3..910fb5d3d 100644
--- a/modules/system/system.js
+++ b/modules/system/system.js
@@ -96,19 +96,22 @@ Drupal.behaviors.copyFieldValue = {
*/
Drupal.behaviors.dateTime = {
attach: function (context, settings) {
- for (var value in settings.dateTime) {
- var settings = settings.dateTime[value];
- var source = '#edit-' + value;
- var suffix = source + '-suffix';
+ for (var fieldName in settings.dateTime) {
+ if (settings.dateTime.hasOwnProperty(fieldName)) {
+ (function (fieldSettings, fieldName) {
+ var source = '#edit-' + fieldName;
+ var suffix = source + '-suffix';
- // Attach keyup handler to custom format inputs.
- $('input' + source, context).once('date-time').keyup(function () {
- var input = $(this);
- var url = settings.lookup + (settings.lookup.match(/\?q=/) ? '&format=' : '?format=') + encodeURIComponent(input.val());
- $.getJSON(url, function (data) {
- $(suffix).empty().append(' ' + settings.text + ': <em>' + data + '</em>');
- });
- });
+ // Attach keyup handler to custom format inputs.
+ $('input' + source, context).once('date-time').keyup(function () {
+ var input = $(this);
+ var url = fieldSettings.lookup + (/\?q=/.test(fieldSettings.lookup) ? '&format=' : '?format=') + encodeURIComponent(input.val());
+ $.getJSON(url, function (data) {
+ $(suffix).empty().append(' ' + fieldSettings.text + ': <em>' + data + '</em>');
+ });
+ });
+ })(settings.dateTime[fieldName], fieldName);
+ }
}
}
};
diff --git a/modules/system/system.module b/modules/system/system.module
index 086a298e9..072850e01 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -76,7 +76,7 @@ function system_help($path, $arg) {
case 'admin/help#system':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t('The System module is integral to the site, and provides basic but extensible functionality for use by other modules and themes. Some integral elements of Drupal are contained in and managed by the System module, including caching, enabling and disabling modules and themes, preparing and displaying the administrative page, and configuring fundamental site settings. A number of key system maintenance operations are also part of the System module. For more information, see the online handbook entry for <a href="@system">System module</a>.', array('@system' => 'http://drupal.org/handbook/modules/system')) . '</p>';
+ $output .= '<p>' . t('The System module is integral to the site, and provides basic but extensible functionality for use by other modules and themes. Some integral elements of Drupal are contained in and managed by the System module, including caching, enabling and disabling modules and themes, preparing and displaying the administrative page, and configuring fundamental site settings. A number of key system maintenance operations are also part of the System module. For more information, see the online handbook entry for <a href="@system">System module</a>.', array('@system' => 'http://drupal.org/documentation/modules/system')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Managing modules') . '</dt>';
diff --git a/modules/system/system.test b/modules/system/system.test
index f40bd686a..abd21aae6 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -1638,26 +1638,98 @@ 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' => $fullpath,
+ $file_relative = strtr($file->uri, array('public:/' => variable_get('file_public_path', conf_path() . '/files')));
+ $default_theme_path = 'themes/stark';
+
+ $supported_paths = array(
+ // Raw stream wrapper URI.
+ $file->uri => array(
+ 'form' => file_uri_target($file->uri),
+ 'src' => file_create_url($file->uri),
+ ),
+ // Relative path within the public filesystem.
+ file_uri_target($file->uri) => array(
+ 'form' => file_uri_target($file->uri),
+ 'src' => file_create_url($file->uri),
+ ),
+ // Relative path to a public file.
+ $file_relative => array(
+ 'form' => $file_relative,
+ 'src' => file_create_url($file->uri),
+ ),
+ // Relative path to an arbitrary file.
+ 'misc/druplicon.png' => array(
+ 'form' => 'misc/druplicon.png',
+ 'src' => $GLOBALS['base_url'] . '/' . 'misc/druplicon.png',
+ ),
+ // Relative path to a file in a theme.
+ $default_theme_path . '/logo.png' => array(
+ 'form' => $default_theme_path . '/logo.png',
+ 'src' => $GLOBALS['base_url'] . '/' . $default_theme_path . '/logo.png',
+ ),
);
- $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
- $this->drupalGet('node');
- $this->assertRaw($fullpath, t('Logo path successfully changed.'));
+ foreach ($supported_paths as $input => $expected) {
+ $edit = array(
+ 'default_logo' => FALSE,
+ 'logo_path' => $input,
+ );
+ $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
+ $this->assertNoText('The custom logo path is invalid.');
+ $this->assertFieldByName('logo_path', $expected['form']);
+
+ // Verify the actual 'src' attribute of the logo being output.
+ $this->drupalGet('');
+ $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo'));
+ $this->assertEqual((string) $elements[0]['src'], $expected['src']);
+ }
+
+ $unsupported_paths = array(
+ // Stream wrapper URI to non-existing file.
+ 'public://whatever.png',
+ 'private://whatever.png',
+ 'temporary://whatever.png',
+ // Bogus stream wrapper URIs.
+ 'public:/whatever.png',
+ '://whatever.png',
+ ':whatever.png',
+ 'public://',
+ // Relative path within the public filesystem to non-existing file.
+ 'whatever.png',
+ // Relative path to non-existing file in public filesystem.
+ variable_get('file_public_path', conf_path() . '/files') . '/whatever.png',
+ // Semi-absolute path to non-existing file in public filesystem.
+ '/' . variable_get('file_public_path', conf_path() . '/files') . '/whatever.png',
+ // Relative path to arbitrary non-existing file.
+ 'misc/whatever.png',
+ // Semi-absolute path to arbitrary non-existing file.
+ '/misc/whatever.png',
+ // Absolute paths to any local file (even if it exists).
+ drupal_realpath($file->uri),
+ );
+ $this->drupalGet('admin/appearance/settings');
+ foreach ($unsupported_paths as $path) {
+ $edit = array(
+ 'default_logo' => FALSE,
+ 'logo_path' => $path,
+ );
+ $this->drupalPost(NULL, $edit, t('Save configuration'));
+ $this->assertText('The custom logo path is invalid.');
+ }
// Upload a file to use for the logo.
- $file = current($this->drupalGetTestFiles('image'));
$edit = array(
'default_logo' => FALSE,
'logo_path' => '',
'files[logo_upload]' => drupal_realpath($file->uri),
);
- $options = array();
- $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'), $options);
- $this->drupalGet('node');
- $this->assertRaw($file->name, t('Logo file successfully uploaded.'));
+ $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
+
+ $fields = $this->xpath($this->constructFieldXpath('name', 'logo_path'));
+ $uploaded_filename = 'public://' . $fields[0]['value'];
+
+ $this->drupalGet('');
+ $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo'));
+ $this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename));
}
/**
@@ -2239,6 +2311,56 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase {
$final_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll();
$this->assertEqual($original_theme_data, $final_theme_data, t('Visiting update.php does not alter the information about themes stored in the database.'));
}
+
+ /**
+ * Tests update.php when there are no updates to apply.
+ */
+ function testNoUpdateFunctionality() {
+ // Click through update.php with 'administer software updates' permission.
+ $this->drupalLogin($this->update_user);
+ $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
+ $this->assertText(t('No pending updates.'));
+ $this->assertNoLink('Administration pages');
+ $this->clickLink('Front page');
+ $this->assertResponse(200);
+
+ // Click through update.php with 'access administration pages' permission.
+ $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages'));
+ $this->drupalLogin($admin_user);
+ $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
+ $this->assertText(t('No pending updates.'));
+ $this->clickLink('Administration pages');
+ $this->assertResponse(200);
+ }
+
+ /**
+ * Tests update.php after performing a successful update.
+ */
+ function testSuccessfulUpdateFunctionality() {
+ drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
+ // Click through update.php with 'administer software updates' permission.
+ $this->drupalLogin($this->update_user);
+ $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
+ $this->drupalPost(NULL, array(), t('Apply pending updates'));
+ $this->assertText('Updates were attempted.');
+ $this->assertLink('site');
+ $this->assertNoLink('Administration pages');
+ $this->assertNoLink('logged');
+ $this->clickLink('Front page');
+ $this->assertResponse(200);
+
+ drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
+ // Click through update.php with 'access administration pages' and
+ // 'access site reports' permissions.
+ $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages', 'access site reports'));
+ $this->drupalLogin($admin_user);
+ $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
+ $this->drupalPost(NULL, array(), t('Apply pending updates'));
+ $this->assertText('Updates were attempted.');
+ $this->assertLink('logged');
+ $this->clickLink('Administration pages');
+ $this->assertResponse(200);
+ }
}
/**