|
@@ -281,7 +281,7 @@ function drupal_get_rdf_namespaces() {
|
|
/**
|
|
/**
|
|
* Adds output to the HEAD tag of the HTML page.
|
|
* Adds output to the HEAD tag of the HTML page.
|
|
*
|
|
*
|
|
- * This function can be called as long the headers aren't sent. Pass no
|
|
|
|
|
|
+ * This function can be called as long as the headers aren't sent. Pass no
|
|
* arguments (or NULL for both) to retrieve the currently stored elements.
|
|
* arguments (or NULL for both) to retrieve the currently stored elements.
|
|
*
|
|
*
|
|
* @param $data
|
|
* @param $data
|
|
@@ -458,7 +458,7 @@ function drupal_get_query_array($query) {
|
|
$result = array();
|
|
$result = array();
|
|
if (!empty($query)) {
|
|
if (!empty($query)) {
|
|
foreach (explode('&', $query) as $param) {
|
|
foreach (explode('&', $query) as $param) {
|
|
- $param = explode('=', $param);
|
|
|
|
|
|
+ $param = explode('=', $param, 2);
|
|
$result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
|
|
$result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -544,37 +544,32 @@ function drupal_get_destination() {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Parses a system URL string into an associative array suitable for url().
|
|
|
|
|
|
+ * Parses a URL string into its path, query, and fragment components.
|
|
*
|
|
*
|
|
- * This function should only be used for URLs that have been generated by the
|
|
|
|
- * system, such as via url(). It should not be used for URLs that come from
|
|
|
|
- * external sources, or URLs that link to external resources.
|
|
|
|
|
|
+ * This function splits both internal paths like @code node?b=c#d @endcode and
|
|
|
|
+ * external URLs like @code https://example.com/a?b=c#d @endcode into their
|
|
|
|
+ * component parts. See
|
|
|
|
+ * @link http://tools.ietf.org/html/rfc3986#section-3 RFC 3986 @endlink for an
|
|
|
|
+ * explanation of what the component parts are.
|
|
*
|
|
*
|
|
- * The returned array contains a 'path' that may be passed separately to url().
|
|
|
|
- * For example:
|
|
|
|
- * @code
|
|
|
|
- * $options = drupal_parse_url($_GET['destination']);
|
|
|
|
- * $my_url = url($options['path'], $options);
|
|
|
|
- * $my_link = l('Example link', $options['path'], $options);
|
|
|
|
- * @endcode
|
|
|
|
- *
|
|
|
|
- * This is required, because url() does not support relative URLs containing a
|
|
|
|
- * query string or fragment in its $path argument. Instead, any query string
|
|
|
|
- * needs to be parsed into an associative query parameter array in
|
|
|
|
- * $options['query'] and the fragment into $options['fragment'].
|
|
|
|
|
|
+ * Note that, unlike the RFC, when passed an external URL, this function
|
|
|
|
+ * groups the scheme, authority, and path together into the path component.
|
|
*
|
|
*
|
|
- * @param $url
|
|
|
|
- * The URL string to parse, f.e. $_GET['destination'].
|
|
|
|
|
|
+ * @param string $url
|
|
|
|
+ * The internal path or external URL string to parse.
|
|
*
|
|
*
|
|
- * @return
|
|
|
|
- * An associative array containing the keys:
|
|
|
|
- * - 'path': The path of the URL. If the given $url is external, this includes
|
|
|
|
- * the scheme and host.
|
|
|
|
- * - 'query': An array of query parameters of $url, if existent.
|
|
|
|
- * - 'fragment': The fragment of $url, if existent.
|
|
|
|
|
|
+ * @return array
|
|
|
|
+ * An associative array containing:
|
|
|
|
+ * - path: The path component of $url. If $url is an external URL, this
|
|
|
|
+ * includes the scheme, authority, and path.
|
|
|
|
+ * - query: An array of query parameters from $url, if they exist.
|
|
|
|
+ * - fragment: The fragment component from $url, if it exists.
|
|
*
|
|
*
|
|
- * @see url()
|
|
|
|
* @see drupal_goto()
|
|
* @see drupal_goto()
|
|
|
|
+ * @see l()
|
|
|
|
+ * @see url()
|
|
|
|
+ * @see http://tools.ietf.org/html/rfc3986
|
|
|
|
+ *
|
|
* @ingroup php_wrappers
|
|
* @ingroup php_wrappers
|
|
*/
|
|
*/
|
|
function drupal_parse_url($url) {
|
|
function drupal_parse_url($url) {
|
|
@@ -641,7 +636,7 @@ function drupal_encode_path($path) {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Sends the user to a different Drupal page.
|
|
|
|
|
|
+ * Sends the user to a different page.
|
|
*
|
|
*
|
|
* This issues an on-site HTTP redirect. The function makes sure the redirected
|
|
* This issues an on-site HTTP redirect. The function makes sure the redirected
|
|
* URL is formatted correctly.
|
|
* URL is formatted correctly.
|
|
@@ -785,6 +780,13 @@ function drupal_access_denied() {
|
|
* - data: A string containing the response body that was received.
|
|
* - data: A string containing the response body that was received.
|
|
*/
|
|
*/
|
|
function drupal_http_request($url, array $options = array()) {
|
|
function drupal_http_request($url, array $options = array()) {
|
|
|
|
+ // Allow an alternate HTTP client library to replace Drupal's default
|
|
|
|
+ // implementation.
|
|
|
|
+ $override_function = variable_get('drupal_http_request_function', FALSE);
|
|
|
|
+ if (!empty($override_function) && function_exists($override_function)) {
|
|
|
|
+ return $override_function($url, $options);
|
|
|
|
+ }
|
|
|
|
+
|
|
$result = new stdClass();
|
|
$result = new stdClass();
|
|
|
|
|
|
// Parse the URL and make sure we can handle the schema.
|
|
// Parse the URL and make sure we can handle the schema.
|
|
@@ -922,7 +924,7 @@ function drupal_http_request($url, array $options = array()) {
|
|
|
|
|
|
// If the server URL has a user then attempt to use basic authentication.
|
|
// If the server URL has a user then attempt to use basic authentication.
|
|
if (isset($uri['user'])) {
|
|
if (isset($uri['user'])) {
|
|
- $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
|
|
|
|
|
|
+ $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ':'));
|
|
}
|
|
}
|
|
|
|
|
|
// If the database prefix is being used by SimpleTest to run the tests in a copied
|
|
// If the database prefix is being used by SimpleTest to run the tests in a copied
|
|
@@ -983,9 +985,10 @@ function drupal_http_request($url, array $options = array()) {
|
|
$response = preg_split("/\r\n|\n|\r/", $response);
|
|
$response = preg_split("/\r\n|\n|\r/", $response);
|
|
|
|
|
|
// Parse the response status line.
|
|
// Parse the response status line.
|
|
- list($protocol, $code, $status_message) = explode(' ', trim(array_shift($response)), 3);
|
|
|
|
- $result->protocol = $protocol;
|
|
|
|
- $result->status_message = $status_message;
|
|
|
|
|
|
+ $response_status_array = _drupal_parse_response_status(trim(array_shift($response)));
|
|
|
|
+ $result->protocol = $response_status_array['http_version'];
|
|
|
|
+ $result->status_message = $response_status_array['reason_phrase'];
|
|
|
|
+ $code = $response_status_array['response_code'];
|
|
|
|
|
|
$result->headers = array();
|
|
$result->headers = array();
|
|
|
|
|
|
@@ -1076,12 +1079,43 @@ function drupal_http_request($url, array $options = array()) {
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
- $result->error = $status_message;
|
|
|
|
|
|
+ $result->error = $result->status_message;
|
|
}
|
|
}
|
|
|
|
|
|
return $result;
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Splits an HTTP response status line into components.
|
|
|
|
+ *
|
|
|
|
+ * See the @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html status line definition @endlink
|
|
|
|
+ * in RFC 2616.
|
|
|
|
+ *
|
|
|
|
+ * @param string $respone
|
|
|
|
+ * The response status line, for example 'HTTP/1.1 500 Internal Server Error'.
|
|
|
|
+ *
|
|
|
|
+ * @return array
|
|
|
|
+ * Keyed array containing the component parts. If the response is malformed,
|
|
|
|
+ * all possible parts will be extracted. 'reason_phrase' could be empty.
|
|
|
|
+ * Possible keys:
|
|
|
|
+ * - 'http_version'
|
|
|
|
+ * - 'response_code'
|
|
|
|
+ * - 'reason_phrase'
|
|
|
|
+ */
|
|
|
|
+function _drupal_parse_response_status($response) {
|
|
|
|
+ $response_array = explode(' ', trim($response), 3);
|
|
|
|
+ // Set up empty values.
|
|
|
|
+ $result = array(
|
|
|
|
+ 'reason_phrase' => '',
|
|
|
|
+ );
|
|
|
|
+ $result['http_version'] = $response_array[0];
|
|
|
|
+ $result['response_code'] = $response_array[1];
|
|
|
|
+ if (isset($response_array[2])) {
|
|
|
|
+ $result['reason_phrase'] = $response_array[2];
|
|
|
|
+ }
|
|
|
|
+ return $result;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Helper function for determining hosts excluded from needing a proxy.
|
|
* Helper function for determining hosts excluded from needing a proxy.
|
|
*
|
|
*
|
|
@@ -1127,7 +1161,7 @@ function _fix_gpc_magic(&$item) {
|
|
* @param $key
|
|
* @param $key
|
|
* The key for the item within $_FILES.
|
|
* The key for the item within $_FILES.
|
|
*
|
|
*
|
|
- * @see http://php.net/manual/en/features.file-upload.php#42280
|
|
|
|
|
|
+ * @see http://php.net/manual/features.file-upload.php#42280
|
|
*/
|
|
*/
|
|
function _fix_gpc_magic_files(&$item, $key) {
|
|
function _fix_gpc_magic_files(&$item, $key) {
|
|
if ($key != 'tmp_name') {
|
|
if ($key != 'tmp_name') {
|
|
@@ -1167,7 +1201,8 @@ function fix_gpc_magic() {
|
|
/**
|
|
/**
|
|
* Verifies the syntax of the given e-mail address.
|
|
* Verifies the syntax of the given e-mail address.
|
|
*
|
|
*
|
|
- * See @link http://tools.ietf.org/html/rfc5321 RFC 5321 @endlink for details.
|
|
|
|
|
|
+ * This uses the
|
|
|
|
+ * @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
|
|
*
|
|
*
|
|
* @param $mail
|
|
* @param $mail
|
|
* A string containing an e-mail address.
|
|
* A string containing an e-mail address.
|
|
@@ -1418,7 +1453,6 @@ function filter_xss_admin($string) {
|
|
* valid UTF-8.
|
|
* valid UTF-8.
|
|
*
|
|
*
|
|
* @see drupal_validate_utf8()
|
|
* @see drupal_validate_utf8()
|
|
- * @ingroup sanitization
|
|
|
|
*/
|
|
*/
|
|
function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
|
|
function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
|
|
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
|
|
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
|
|
@@ -1942,7 +1976,7 @@ function format_interval($interval, $granularity = 2, $langcode = NULL) {
|
|
* get interpreted as date format characters.
|
|
* get interpreted as date format characters.
|
|
* @param $timezone
|
|
* @param $timezone
|
|
* (optional) Time zone identifier, as described at
|
|
* (optional) Time zone identifier, as described at
|
|
- * http://php.net/manual/en/timezones.php Defaults to the time zone used to
|
|
|
|
|
|
+ * http://php.net/manual/timezones.php Defaults to the time zone used to
|
|
* display the page.
|
|
* display the page.
|
|
* @param $langcode
|
|
* @param $langcode
|
|
* (optional) Language code to translate to. Defaults to the language used to
|
|
* (optional) Language code to translate to. Defaults to the language used to
|
|
@@ -2078,6 +2112,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
|
|
/**
|
|
/**
|
|
* Format a username.
|
|
* Format a username.
|
|
*
|
|
*
|
|
|
|
+ * This is also the label callback implementation of
|
|
|
|
+ * callback_entity_info_label() for user_entity_info().
|
|
|
|
+ *
|
|
* By default, the passed-in object's 'name' property is used if it exists, or
|
|
* By default, the passed-in object's 'name' property is used if it exists, or
|
|
* else, the site-defined value for the 'anonymous' variable. However, a module
|
|
* else, the site-defined value for the 'anonymous' variable. However, a module
|
|
* may override this by implementing hook_username_alter(&$name, $account).
|
|
* may override this by implementing hook_username_alter(&$name, $account).
|
|
@@ -2177,14 +2214,20 @@ function url($path = NULL, array $options = array()) {
|
|
'prefix' => ''
|
|
'prefix' => ''
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ // A duplicate of the code from url_is_external() to avoid needing another
|
|
|
|
+ // function call, since performance inside url() is critical.
|
|
if (!isset($options['external'])) {
|
|
if (!isset($options['external'])) {
|
|
- // Return an external link if $path contains an allowed absolute URL. Only
|
|
|
|
- // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
|
|
|
|
- // before any / ? or #. Note: we could use url_is_external($path) here, but
|
|
|
|
- // that would require another function call, and performance inside url() is
|
|
|
|
- // critical.
|
|
|
|
|
|
+ // Return an external link if $path contains an allowed absolute URL. Avoid
|
|
|
|
+ // calling drupal_strip_dangerous_protocols() if there is any slash (/),
|
|
|
|
+ // hash (#) or question_mark (?) before the colon (:) occurrence - if any -
|
|
|
|
+ // as this would clearly mean it is not a URL. If the path starts with 2
|
|
|
|
+ // slashes then it is always considered an external URL without an explicit
|
|
|
|
+ // protocol part.
|
|
$colonpos = strpos($path, ':');
|
|
$colonpos = strpos($path, ':');
|
|
- $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
|
|
|
|
|
|
+ $options['external'] = (strpos($path, '//') === 0)
|
|
|
|
+ || ($colonpos !== FALSE
|
|
|
|
+ && !preg_match('![/?#]!', substr($path, 0, $colonpos))
|
|
|
|
+ && drupal_strip_dangerous_protocols($path) == $path);
|
|
}
|
|
}
|
|
|
|
|
|
// Preserve the original path before altering or aliasing.
|
|
// Preserve the original path before altering or aliasing.
|
|
@@ -2222,6 +2265,11 @@ function url($path = NULL, array $options = array()) {
|
|
return $path . $options['fragment'];
|
|
return $path . $options['fragment'];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Strip leading slashes from internal paths to prevent them becoming external
|
|
|
|
+ // URLs without protocol. /example.com should not be turned into
|
|
|
|
+ // //example.com.
|
|
|
|
+ $path = ltrim($path, '/');
|
|
|
|
+
|
|
global $base_url, $base_secure_url, $base_insecure_url;
|
|
global $base_url, $base_secure_url, $base_insecure_url;
|
|
|
|
|
|
// The base_url might be rewritten from the language rewrite in domain mode.
|
|
// The base_url might be rewritten from the language rewrite in domain mode.
|
|
@@ -2299,10 +2347,15 @@ function url($path = NULL, array $options = array()) {
|
|
*/
|
|
*/
|
|
function url_is_external($path) {
|
|
function url_is_external($path) {
|
|
$colonpos = strpos($path, ':');
|
|
$colonpos = strpos($path, ':');
|
|
- // Avoid calling drupal_strip_dangerous_protocols() if there is any
|
|
|
|
- // slash (/), hash (#) or question_mark (?) before the colon (:)
|
|
|
|
- // occurrence - if any - as this would clearly mean it is not a URL.
|
|
|
|
- return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
|
|
|
|
|
|
+ // Avoid calling drupal_strip_dangerous_protocols() if there is any slash (/),
|
|
|
|
+ // hash (#) or question_mark (?) before the colon (:) occurrence - if any - as
|
|
|
|
+ // this would clearly mean it is not a URL. If the path starts with 2 slashes
|
|
|
|
+ // then it is always considered an external URL without an explicit protocol
|
|
|
|
+ // part.
|
|
|
|
+ return (strpos($path, '//') === 0)
|
|
|
|
+ || ($colonpos !== FALSE
|
|
|
|
+ && !preg_match('![/?#]!', substr($path, 0, $colonpos))
|
|
|
|
+ && drupal_strip_dangerous_protocols($path) == $path);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -2379,6 +2432,14 @@ function drupal_attributes(array $attributes = array()) {
|
|
* internal links output by modules should be generated by this function if
|
|
* internal links output by modules should be generated by this function if
|
|
* possible.
|
|
* possible.
|
|
*
|
|
*
|
|
|
|
+ * However, for links enclosed in translatable text you should use t() and
|
|
|
|
+ * embed the HTML anchor tag directly in the translated string. For example:
|
|
|
|
+ * @code
|
|
|
|
+ * t('Visit the <a href="@url">settings</a> page', array('@url' => url('admin')));
|
|
|
|
+ * @endcode
|
|
|
|
+ * This keeps the context of the link title ('settings' in the example) for
|
|
|
|
+ * translators.
|
|
|
|
+ *
|
|
* @param string $text
|
|
* @param string $text
|
|
* The translated link text for the anchor tag.
|
|
* The translated link text for the anchor tag.
|
|
* @param string $path
|
|
* @param string $path
|
|
@@ -2591,7 +2652,10 @@ function drupal_deliver_html_page($page_callback_result) {
|
|
|
|
|
|
// Keep old path for reference, and to allow forms to redirect to it.
|
|
// Keep old path for reference, and to allow forms to redirect to it.
|
|
if (!isset($_GET['destination'])) {
|
|
if (!isset($_GET['destination'])) {
|
|
- $_GET['destination'] = $_GET['q'];
|
|
|
|
|
|
+ // Make sure that the current path is not interpreted as external URL.
|
|
|
|
+ if (!url_is_external($_GET['q'])) {
|
|
|
|
+ $_GET['destination'] = $_GET['q'];
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
$path = drupal_get_normal_path(variable_get('site_404', ''));
|
|
$path = drupal_get_normal_path(variable_get('site_404', ''));
|
|
@@ -2620,7 +2684,10 @@ function drupal_deliver_html_page($page_callback_result) {
|
|
|
|
|
|
// Keep old path for reference, and to allow forms to redirect to it.
|
|
// Keep old path for reference, and to allow forms to redirect to it.
|
|
if (!isset($_GET['destination'])) {
|
|
if (!isset($_GET['destination'])) {
|
|
- $_GET['destination'] = $_GET['q'];
|
|
|
|
|
|
+ // Make sure that the current path is not interpreted as external URL.
|
|
|
|
+ if (!url_is_external($_GET['q'])) {
|
|
|
|
+ $_GET['destination'] = $_GET['q'];
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
$path = drupal_get_normal_path(variable_get('site_403', ''));
|
|
$path = drupal_get_normal_path(variable_get('site_403', ''));
|
|
@@ -2779,7 +2846,7 @@ function drupal_set_time_limit($time_limit) {
|
|
* The name of the item for which the path is requested.
|
|
* The name of the item for which the path is requested.
|
|
*
|
|
*
|
|
* @return
|
|
* @return
|
|
- * The path to the requested item.
|
|
|
|
|
|
+ * The path to the requested item or an empty string if the item is not found.
|
|
*/
|
|
*/
|
|
function drupal_get_path($type, $name) {
|
|
function drupal_get_path($type, $name) {
|
|
return dirname(drupal_get_filename($type, $name));
|
|
return dirname(drupal_get_filename($type, $name));
|
|
@@ -3429,7 +3496,11 @@ function drupal_pre_render_styles($elements) {
|
|
$import_batch = array_slice($import, 0, 31);
|
|
$import_batch = array_slice($import, 0, 31);
|
|
$import = array_slice($import, 31);
|
|
$import = array_slice($import, 31);
|
|
$element = $style_element_defaults;
|
|
$element = $style_element_defaults;
|
|
- $element['#value'] = implode("\n", $import_batch);
|
|
|
|
|
|
+ // This simplifies the JavaScript regex, allowing each line
|
|
|
|
+ // (separated by \n) to be treated as a completely different string.
|
|
|
|
+ // This means that we can use ^ and $ on one line at a time, and not
|
|
|
|
+ // worry about style tags since they'll never match the regex.
|
|
|
|
+ $element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
|
|
$element['#attributes']['media'] = $group['media'];
|
|
$element['#attributes']['media'] = $group['media'];
|
|
$element['#browsers'] = $group['browsers'];
|
|
$element['#browsers'] = $group['browsers'];
|
|
$elements[] = $element;
|
|
$elements[] = $element;
|
|
@@ -3654,17 +3725,23 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
|
|
if ($basepath && !file_uri_scheme($file)) {
|
|
if ($basepath && !file_uri_scheme($file)) {
|
|
$file = $basepath . '/' . $file;
|
|
$file = $basepath . '/' . $file;
|
|
}
|
|
}
|
|
|
|
+ // Store the parent base path to restore it later.
|
|
|
|
+ $parent_base_path = $basepath;
|
|
|
|
+ // Set the current base path to process possible child imports.
|
|
$basepath = dirname($file);
|
|
$basepath = dirname($file);
|
|
|
|
|
|
// Load the CSS stylesheet. We suppress errors because themes may specify
|
|
// Load the CSS stylesheet. We suppress errors because themes may specify
|
|
// stylesheets in their .info file that don't exist in the theme's path,
|
|
// stylesheets in their .info file that don't exist in the theme's path,
|
|
// but are merely there to disable certain module CSS files.
|
|
// but are merely there to disable certain module CSS files.
|
|
|
|
+ $content = '';
|
|
if ($contents = @file_get_contents($file)) {
|
|
if ($contents = @file_get_contents($file)) {
|
|
// Return the processed stylesheet.
|
|
// Return the processed stylesheet.
|
|
- return drupal_load_stylesheet_content($contents, $_optimize);
|
|
|
|
|
|
+ $content = drupal_load_stylesheet_content($contents, $_optimize);
|
|
}
|
|
}
|
|
|
|
|
|
- return '';
|
|
|
|
|
|
+ // Restore the parent base path as the file and its childen are processed.
|
|
|
|
+ $basepath = $parent_base_path;
|
|
|
|
+ return $content;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -3681,7 +3758,7 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
|
|
*/
|
|
*/
|
|
function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
|
|
function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
|
|
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
|
|
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
|
|
- $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
|
|
|
|
|
|
+ $contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
|
|
|
|
|
|
if ($optimize) {
|
|
if ($optimize) {
|
|
// Perform some safe CSS optimizations.
|
|
// Perform some safe CSS optimizations.
|
|
@@ -3700,7 +3777,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
|
|
// Remove certain whitespace.
|
|
// Remove certain whitespace.
|
|
// There are different conditions for removing leading and trailing
|
|
// There are different conditions for removing leading and trailing
|
|
// whitespace.
|
|
// whitespace.
|
|
- // @see http://php.net/manual/en/regexp.reference.subpatterns.php
|
|
|
|
|
|
+ // @see http://php.net/manual/regexp.reference.subpatterns.php
|
|
$contents = preg_replace('<
|
|
$contents = preg_replace('<
|
|
# Strip leading and trailing whitespace.
|
|
# Strip leading and trailing whitespace.
|
|
\s*([@{};,])\s*
|
|
\s*([@{};,])\s*
|
|
@@ -3749,7 +3826,7 @@ function _drupal_load_stylesheet($matches) {
|
|
// Alter all internal url() paths. Leave external paths alone. We don't need
|
|
// Alter all internal url() paths. Leave external paths alone. We don't need
|
|
// to normalize absolute paths here (i.e. remove folder/... segments) because
|
|
// to normalize absolute paths here (i.e. remove folder/... segments) because
|
|
// that will be done later.
|
|
// that will be done later.
|
|
- return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
|
|
|
|
|
|
+ return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -3814,7 +3891,14 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
|
|
* The cleaned class name.
|
|
* The cleaned class name.
|
|
*/
|
|
*/
|
|
function drupal_html_class($class) {
|
|
function drupal_html_class($class) {
|
|
- return drupal_clean_css_identifier(drupal_strtolower($class));
|
|
|
|
|
|
+ // The output of this function will never change, so this uses a normal
|
|
|
|
+ // static instead of drupal_static().
|
|
|
|
+ static $classes = array();
|
|
|
|
+
|
|
|
|
+ if (!isset($classes[$class])) {
|
|
|
|
+ $classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
|
|
|
|
+ }
|
|
|
|
+ return $classes[$class];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -3869,7 +3953,16 @@ function drupal_html_id($id) {
|
|
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
|
|
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
|
|
// returned by this function, potentially with the appended counter, so
|
|
// returned by this function, potentially with the appended counter, so
|
|
// we parse that to reconstruct the $seen_ids array.
|
|
// we parse that to reconstruct the $seen_ids array.
|
|
- foreach ($_POST['ajax_html_ids'] as $seen_id) {
|
|
|
|
|
|
+ if (isset($_POST['ajax_html_ids'][0]) && strpos($_POST['ajax_html_ids'][0], ',') === FALSE) {
|
|
|
|
+ $ajax_html_ids = $_POST['ajax_html_ids'];
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ // jquery.form.js may send the server a comma-separated string as the
|
|
|
|
+ // first element of an array (see http://drupal.org/node/1575060), so
|
|
|
|
+ // we need to convert it to an array in that case.
|
|
|
|
+ $ajax_html_ids = explode(',', $_POST['ajax_html_ids'][0]);
|
|
|
|
+ }
|
|
|
|
+ foreach ($ajax_html_ids as $seen_id) {
|
|
// We rely on '--' being used solely for separating a base id from the
|
|
// We rely on '--' being used solely for separating a base id from the
|
|
// counter, which this function ensures when returning an id.
|
|
// counter, which this function ensures when returning an id.
|
|
$parts = explode('--', $seen_id, 2);
|
|
$parts = explode('--', $seen_id, 2);
|
|
@@ -4069,7 +4162,14 @@ function drupal_region_class($region) {
|
|
* else being the same, JavaScript added by a call to drupal_add_js() that
|
|
* else being the same, JavaScript added by a call to drupal_add_js() that
|
|
* happened later in the page request gets added to the page after one for
|
|
* happened later in the page request gets added to the page after one for
|
|
* which drupal_add_js() happened earlier in the page request.
|
|
* which drupal_add_js() happened earlier in the page request.
|
|
- * - defer: If set to TRUE, the defer attribute is set on the <script>
|
|
|
|
|
|
+ * - requires_jquery: Set this to FALSE if the JavaScript you are adding does
|
|
|
|
+ * not have a dependency on jQuery. Defaults to TRUE, except for JavaScript
|
|
|
|
+ * settings where it defaults to FALSE. This is used on sites that have the
|
|
|
|
+ * 'javascript_always_use_jquery' variable set to FALSE; on those sites, if
|
|
|
|
+ * all the JavaScript added to the page by drupal_add_js() does not have a
|
|
|
|
+ * dependency on jQuery, then for improved front-end performance Drupal
|
|
|
|
+ * will not add jQuery and related libraries and settings to the page.
|
|
|
|
+ * - defer: If set to TRUE, the defer attribute is set on the <script>
|
|
* tag. Defaults to FALSE.
|
|
* tag. Defaults to FALSE.
|
|
* - cache: If set to FALSE, the JavaScript file is loaded anew on every page
|
|
* - 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
|
|
* call; in other words, it is not cached. Used only when 'type' references
|
|
@@ -4086,6 +4186,14 @@ function drupal_region_class($region) {
|
|
*/
|
|
*/
|
|
function drupal_add_js($data = NULL, $options = NULL) {
|
|
function drupal_add_js($data = NULL, $options = NULL) {
|
|
$javascript = &drupal_static(__FUNCTION__, array());
|
|
$javascript = &drupal_static(__FUNCTION__, array());
|
|
|
|
+ $jquery_added = &drupal_static(__FUNCTION__ . ':jquery_added', FALSE);
|
|
|
|
+
|
|
|
|
+ // If the $javascript variable has been reset with drupal_static_reset(),
|
|
|
|
+ // jQuery and related files will have been removed from the list, so set the
|
|
|
|
+ // variable back to FALSE to indicate they have not yet been added.
|
|
|
|
+ if (empty($javascript)) {
|
|
|
|
+ $jquery_added = FALSE;
|
|
|
|
+ }
|
|
|
|
|
|
// Construct the options, taking the defaults into consideration.
|
|
// Construct the options, taking the defaults into consideration.
|
|
if (isset($options)) {
|
|
if (isset($options)) {
|
|
@@ -4096,6 +4204,9 @@ function drupal_add_js($data = NULL, $options = NULL) {
|
|
else {
|
|
else {
|
|
$options = array();
|
|
$options = array();
|
|
}
|
|
}
|
|
|
|
+ if (isset($options['type']) && $options['type'] == 'setting') {
|
|
|
|
+ $options += array('requires_jquery' => FALSE);
|
|
|
|
+ }
|
|
$options += drupal_js_defaults($data);
|
|
$options += drupal_js_defaults($data);
|
|
|
|
|
|
// Preprocess can only be set if caching is enabled.
|
|
// Preprocess can only be set if caching is enabled.
|
|
@@ -4106,14 +4217,18 @@ function drupal_add_js($data = NULL, $options = NULL) {
|
|
$options['weight'] += count($javascript) / 1000;
|
|
$options['weight'] += count($javascript) / 1000;
|
|
|
|
|
|
if (isset($data)) {
|
|
if (isset($data)) {
|
|
- // Add jquery.js and drupal.js, as well as the basePath setting, the
|
|
|
|
- // first time a JavaScript file is added.
|
|
|
|
- if (empty($javascript)) {
|
|
|
|
|
|
+ // Add jquery.js, drupal.js, and related files and settings if they have
|
|
|
|
+ // not been added yet. However, if the 'javascript_always_use_jquery'
|
|
|
|
+ // variable is set to FALSE (indicating that the site does not want jQuery
|
|
|
|
+ // automatically added on all pages) then only add it if a file or setting
|
|
|
|
+ // that requires jQuery is being added also.
|
|
|
|
+ if (!$jquery_added && (variable_get('javascript_always_use_jquery', TRUE) || $options['requires_jquery'])) {
|
|
|
|
+ $jquery_added = TRUE;
|
|
// url() generates the prefix using hook_url_outbound_alter(). Instead of
|
|
// url() generates the prefix using hook_url_outbound_alter(). Instead of
|
|
// running the hook_url_outbound_alter() again here, extract the prefix
|
|
// running the hook_url_outbound_alter() again here, extract the prefix
|
|
// from url().
|
|
// from url().
|
|
url('', array('prefix' => &$prefix));
|
|
url('', array('prefix' => &$prefix));
|
|
- $javascript = array(
|
|
|
|
|
|
+ $default_javascript = array(
|
|
'settings' => array(
|
|
'settings' => array(
|
|
'data' => array(
|
|
'data' => array(
|
|
array('basePath' => base_path()),
|
|
array('basePath' => base_path()),
|
|
@@ -4132,11 +4247,13 @@ function drupal_add_js($data = NULL, $options = NULL) {
|
|
'group' => JS_LIBRARY,
|
|
'group' => JS_LIBRARY,
|
|
'every_page' => TRUE,
|
|
'every_page' => TRUE,
|
|
'weight' => -1,
|
|
'weight' => -1,
|
|
|
|
+ 'requires_jquery' => TRUE,
|
|
'preprocess' => TRUE,
|
|
'preprocess' => TRUE,
|
|
'cache' => TRUE,
|
|
'cache' => TRUE,
|
|
'defer' => FALSE,
|
|
'defer' => FALSE,
|
|
),
|
|
),
|
|
);
|
|
);
|
|
|
|
+ $javascript = drupal_array_merge_deep($javascript, $default_javascript);
|
|
// Register all required libraries.
|
|
// Register all required libraries.
|
|
drupal_add_library('system', 'jquery', TRUE);
|
|
drupal_add_library('system', 'jquery', TRUE);
|
|
drupal_add_library('system', 'jquery.once', TRUE);
|
|
drupal_add_library('system', 'jquery.once', TRUE);
|
|
@@ -4177,6 +4294,7 @@ function drupal_js_defaults($data = NULL) {
|
|
'group' => JS_DEFAULT,
|
|
'group' => JS_DEFAULT,
|
|
'every_page' => FALSE,
|
|
'every_page' => FALSE,
|
|
'weight' => 0,
|
|
'weight' => 0,
|
|
|
|
+ 'requires_jquery' => TRUE,
|
|
'scope' => 'header',
|
|
'scope' => 'header',
|
|
'cache' => TRUE,
|
|
'cache' => TRUE,
|
|
'defer' => FALSE,
|
|
'defer' => FALSE,
|
|
@@ -4223,7 +4341,12 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
if (!isset($javascript)) {
|
|
if (!isset($javascript)) {
|
|
$javascript = drupal_add_js();
|
|
$javascript = drupal_add_js();
|
|
}
|
|
}
|
|
- if (empty($javascript)) {
|
|
|
|
|
|
+
|
|
|
|
+ // If no JavaScript items have been added, or if the only JavaScript items
|
|
|
|
+ // that have been added are JavaScript settings (which don't do anything
|
|
|
|
+ // without any JavaScript code to use them), then no JavaScript code should
|
|
|
|
+ // be added to the page.
|
|
|
|
+ if (empty($javascript) || (isset($javascript['settings']) && count($javascript) == 1)) {
|
|
return '';
|
|
return '';
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4377,8 +4500,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
*
|
|
*
|
|
* Libraries, JavaScript, CSS and other types of custom structures are attached
|
|
* Libraries, JavaScript, CSS and other types of custom structures are attached
|
|
* to elements using the #attached property. The #attached property is an
|
|
* to elements using the #attached property. The #attached property is an
|
|
- * associative array, where the keys are the the attachment types and the values
|
|
|
|
- * are the attached data. For example:
|
|
|
|
|
|
+ * associative array, where the keys are the attachment types and the values are
|
|
|
|
+ * the attached data. For example:
|
|
* @code
|
|
* @code
|
|
* $build['#attached'] = array(
|
|
* $build['#attached'] = array(
|
|
* 'js' => array(drupal_get_path('module', 'taxonomy') . '/taxonomy.js'),
|
|
* 'js' => array(drupal_get_path('module', 'taxonomy') . '/taxonomy.js'),
|
|
@@ -5006,19 +5129,6 @@ function drupal_json_output($var = NULL) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * Gets a salt useful for hardening against SQL injection.
|
|
|
|
- *
|
|
|
|
- * @return
|
|
|
|
- * A salt based on information in settings.php, not in the database.
|
|
|
|
- */
|
|
|
|
-function drupal_get_hash_salt() {
|
|
|
|
- global $drupal_hash_salt, $databases;
|
|
|
|
- // If the $drupal_hash_salt variable is empty, a hash of the serialized
|
|
|
|
- // database credentials is used as a fallback salt.
|
|
|
|
- return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Ensures the private key variable used to generate tokens is set.
|
|
* Ensures the private key variable used to generate tokens is set.
|
|
*
|
|
*
|
|
@@ -5027,7 +5137,7 @@ function drupal_get_hash_salt() {
|
|
*/
|
|
*/
|
|
function drupal_get_private_key() {
|
|
function drupal_get_private_key() {
|
|
if (!($key = variable_get('drupal_private_key', 0))) {
|
|
if (!($key = variable_get('drupal_private_key', 0))) {
|
|
- $key = drupal_hash_base64(drupal_random_bytes(55));
|
|
|
|
|
|
+ $key = drupal_random_key();
|
|
variable_set('drupal_private_key', $key);
|
|
variable_set('drupal_private_key', $key);
|
|
}
|
|
}
|
|
return $key;
|
|
return $key;
|
|
@@ -5038,6 +5148,18 @@ function drupal_get_private_key() {
|
|
*
|
|
*
|
|
* @param $value
|
|
* @param $value
|
|
* An additional value to base the token on.
|
|
* An additional value to base the token on.
|
|
|
|
+ *
|
|
|
|
+ * The generated token is based on the session ID of the current user. Normally,
|
|
|
|
+ * anonymous users do not have a session, so the generated token will be
|
|
|
|
+ * different on every page request. To generate a token for users without a
|
|
|
|
+ * session, manually start a session prior to calling this function.
|
|
|
|
+ *
|
|
|
|
+ * @return string
|
|
|
|
+ * A 43-character URL-safe token for validation, based on the user session ID,
|
|
|
|
+ * the hash salt provided from drupal_get_hash_salt(), and the
|
|
|
|
+ * 'drupal_private_key' configuration variable.
|
|
|
|
+ *
|
|
|
|
+ * @see drupal_get_hash_salt()
|
|
*/
|
|
*/
|
|
function drupal_get_token($value = '') {
|
|
function drupal_get_token($value = '') {
|
|
return drupal_hmac_base64($value, session_id() . drupal_get_private_key() . drupal_get_hash_salt());
|
|
return drupal_hmac_base64($value, session_id() . drupal_get_private_key() . drupal_get_hash_salt());
|
|
@@ -5059,7 +5181,7 @@ function drupal_get_token($value = '') {
|
|
*/
|
|
*/
|
|
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
|
|
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
|
|
global $user;
|
|
global $user;
|
|
- return (($skip_anonymous && $user->uid == 0) || ($token == drupal_get_token($value)));
|
|
|
|
|
|
+ return (($skip_anonymous && $user->uid == 0) || ($token === drupal_get_token($value)));
|
|
}
|
|
}
|
|
|
|
|
|
function _drupal_bootstrap_full() {
|
|
function _drupal_bootstrap_full() {
|
|
@@ -5092,6 +5214,10 @@ function _drupal_bootstrap_full() {
|
|
module_load_all();
|
|
module_load_all();
|
|
// Make sure all stream wrappers are registered.
|
|
// Make sure all stream wrappers are registered.
|
|
file_get_stream_wrappers();
|
|
file_get_stream_wrappers();
|
|
|
|
+ // Ensure mt_rand is reseeded, to prevent random values from one page load
|
|
|
|
+ // being exploited to predict random values in subsequent page loads.
|
|
|
|
+ $seed = unpack("L", drupal_random_bytes(4));
|
|
|
|
+ mt_srand($seed[1]);
|
|
|
|
|
|
$test_info = &$GLOBALS['drupal_test_info'];
|
|
$test_info = &$GLOBALS['drupal_test_info'];
|
|
if (!empty($test_info['in_child_site'])) {
|
|
if (!empty($test_info['in_child_site'])) {
|
|
@@ -5129,7 +5255,7 @@ function _drupal_bootstrap_full() {
|
|
* client without gzip support.
|
|
* client without gzip support.
|
|
*
|
|
*
|
|
* Page compression requires the PHP zlib extension
|
|
* Page compression requires the PHP zlib extension
|
|
- * (http://php.net/manual/en/ref.zlib.php).
|
|
|
|
|
|
+ * (http://php.net/manual/ref.zlib.php).
|
|
*
|
|
*
|
|
* @see drupal_page_header()
|
|
* @see drupal_page_header()
|
|
*/
|
|
*/
|
|
@@ -5137,6 +5263,10 @@ function drupal_page_set_cache() {
|
|
global $base_root;
|
|
global $base_root;
|
|
|
|
|
|
if (drupal_page_is_cacheable()) {
|
|
if (drupal_page_is_cacheable()) {
|
|
|
|
+
|
|
|
|
+ // Check whether the current page might be compressed.
|
|
|
|
+ $page_compressed = variable_get('page_compression', TRUE) && extension_loaded('zlib');
|
|
|
|
+
|
|
$cache = (object) array(
|
|
$cache = (object) array(
|
|
'cid' => $base_root . request_uri(),
|
|
'cid' => $base_root . request_uri(),
|
|
'data' => array(
|
|
'data' => array(
|
|
@@ -5144,6 +5274,9 @@ function drupal_page_set_cache() {
|
|
'body' => ob_get_clean(),
|
|
'body' => ob_get_clean(),
|
|
'title' => drupal_get_title(),
|
|
'title' => drupal_get_title(),
|
|
'headers' => array(),
|
|
'headers' => array(),
|
|
|
|
+ // We need to store whether page was compressed or not,
|
|
|
|
+ // because by the time it is read, the configuration might change.
|
|
|
|
+ 'page_compressed' => $page_compressed,
|
|
),
|
|
),
|
|
'expire' => CACHE_TEMPORARY,
|
|
'expire' => CACHE_TEMPORARY,
|
|
'created' => REQUEST_TIME,
|
|
'created' => REQUEST_TIME,
|
|
@@ -5161,7 +5294,7 @@ function drupal_page_set_cache() {
|
|
}
|
|
}
|
|
|
|
|
|
if ($cache->data['body']) {
|
|
if ($cache->data['body']) {
|
|
- if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
|
|
|
|
|
|
+ if ($page_compressed) {
|
|
$cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
|
|
$cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
|
|
}
|
|
}
|
|
cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire);
|
|
cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire);
|
|
@@ -5210,8 +5343,6 @@ function drupal_cron_run() {
|
|
foreach ($queues as $queue_name => $info) {
|
|
foreach ($queues as $queue_name => $info) {
|
|
DrupalQueue::get($queue_name)->createQueue();
|
|
DrupalQueue::get($queue_name)->createQueue();
|
|
}
|
|
}
|
|
- // Register shutdown callback.
|
|
|
|
- drupal_register_shutdown_function('drupal_cron_cleanup');
|
|
|
|
|
|
|
|
// Iterate through the modules calling their cron handlers (if any):
|
|
// Iterate through the modules calling their cron handlers (if any):
|
|
foreach (module_implements('cron') as $module) {
|
|
foreach (module_implements('cron') as $module) {
|
|
@@ -5236,12 +5367,23 @@ function drupal_cron_run() {
|
|
}
|
|
}
|
|
|
|
|
|
foreach ($queues as $queue_name => $info) {
|
|
foreach ($queues as $queue_name => $info) {
|
|
|
|
+ if (!empty($info['skip on cron'])) {
|
|
|
|
+ // Do not run if queue wants to skip.
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
$function = $info['worker callback'];
|
|
$function = $info['worker callback'];
|
|
$end = time() + (isset($info['time']) ? $info['time'] : 15);
|
|
$end = time() + (isset($info['time']) ? $info['time'] : 15);
|
|
$queue = DrupalQueue::get($queue_name);
|
|
$queue = DrupalQueue::get($queue_name);
|
|
while (time() < $end && ($item = $queue->claimItem())) {
|
|
while (time() < $end && ($item = $queue->claimItem())) {
|
|
- $function($item->data);
|
|
|
|
- $queue->deleteItem($item);
|
|
|
|
|
|
+ try {
|
|
|
|
+ $function($item->data);
|
|
|
|
+ $queue->deleteItem($item);
|
|
|
|
+ }
|
|
|
|
+ catch (Exception $e) {
|
|
|
|
+ // In case of exception log it and leave the item in the queue
|
|
|
|
+ // to be processed again later.
|
|
|
|
+ watchdog_exception('cron', $e);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Restore the user.
|
|
// Restore the user.
|
|
@@ -5252,10 +5394,13 @@ function drupal_cron_run() {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Shutdown function: Performs cron cleanup.
|
|
|
|
|
|
+ * DEPRECATED: Shutdown function: Performs cron cleanup.
|
|
*
|
|
*
|
|
- * @see drupal_cron_run()
|
|
|
|
- * @see drupal_register_shutdown_function()
|
|
|
|
|
|
+ * This function is deprecated because the 'cron_semaphore' variable it
|
|
|
|
+ * references no longer exists. It is therefore no longer used as a shutdown
|
|
|
|
+ * function by Drupal core.
|
|
|
|
+ *
|
|
|
|
+ * @deprecated
|
|
*/
|
|
*/
|
|
function drupal_cron_cleanup() {
|
|
function drupal_cron_cleanup() {
|
|
// See if the semaphore is still locked.
|
|
// See if the semaphore is still locked.
|
|
@@ -5568,7 +5713,7 @@ function drupal_pre_render_link($element) {
|
|
* @code
|
|
* @code
|
|
* $node->content['links'] = array(
|
|
* $node->content['links'] = array(
|
|
* '#theme' => 'links__node',
|
|
* '#theme' => 'links__node',
|
|
- * '#pre_render' = array('drupal_pre_render_links'),
|
|
|
|
|
|
+ * '#pre_render' => array('drupal_pre_render_links'),
|
|
* 'comment' => array(
|
|
* 'comment' => array(
|
|
* '#theme' => 'links__node__comment',
|
|
* '#theme' => 'links__node__comment',
|
|
* '#links' => array(
|
|
* '#links' => array(
|
|
@@ -5773,23 +5918,23 @@ function drupal_render_page($page) {
|
|
* array to be rendered independently and prevents them from being rendered
|
|
* array to be rendered independently and prevents them from being rendered
|
|
* more than once on subsequent calls to drupal_render() (e.g., as part of a
|
|
* more than once on subsequent calls to drupal_render() (e.g., as part of a
|
|
* larger array). If the same array or array element is passed more than once
|
|
* larger array). If the same array or array element is passed more than once
|
|
- * to drupal_render(), it simply returns a NULL value.
|
|
|
|
|
|
+ * to drupal_render(), it simply returns an empty string.
|
|
*
|
|
*
|
|
- * @param $elements
|
|
|
|
|
|
+ * @param array $elements
|
|
* The structured array describing the data to be rendered.
|
|
* The structured array describing the data to be rendered.
|
|
*
|
|
*
|
|
- * @return
|
|
|
|
|
|
+ * @return string
|
|
* The rendered HTML.
|
|
* The rendered HTML.
|
|
*/
|
|
*/
|
|
function drupal_render(&$elements) {
|
|
function drupal_render(&$elements) {
|
|
// Early-return nothing if user does not have access.
|
|
// Early-return nothing if user does not have access.
|
|
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
|
|
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
|
|
- return;
|
|
|
|
|
|
+ return '';
|
|
}
|
|
}
|
|
|
|
|
|
// Do not print elements twice.
|
|
// Do not print elements twice.
|
|
if (!empty($elements['#printed'])) {
|
|
if (!empty($elements['#printed'])) {
|
|
- return;
|
|
|
|
|
|
+ return '';
|
|
}
|
|
}
|
|
|
|
|
|
// Try to fetch the element's markup from cache and return.
|
|
// Try to fetch the element's markup from cache and return.
|
|
@@ -5825,7 +5970,7 @@ function drupal_render(&$elements) {
|
|
|
|
|
|
// Allow #pre_render to abort rendering.
|
|
// Allow #pre_render to abort rendering.
|
|
if (!empty($elements['#printed'])) {
|
|
if (!empty($elements['#printed'])) {
|
|
- return;
|
|
|
|
|
|
+ return '';
|
|
}
|
|
}
|
|
|
|
|
|
// Get the children of the element, sorted by weight.
|
|
// Get the children of the element, sorted by weight.
|
|
@@ -5896,14 +6041,16 @@ function drupal_render(&$elements) {
|
|
/**
|
|
/**
|
|
* Renders children of an element and concatenates them.
|
|
* Renders children of an element and concatenates them.
|
|
*
|
|
*
|
|
- * This renders all children of an element using drupal_render() and then
|
|
|
|
- * joins them together into a single string.
|
|
|
|
- *
|
|
|
|
- * @param $element
|
|
|
|
|
|
+ * @param array $element
|
|
* The structured array whose children shall be rendered.
|
|
* The structured array whose children shall be rendered.
|
|
- * @param $children_keys
|
|
|
|
- * If the keys of the element's children are already known, they can be passed
|
|
|
|
- * in to save another run of element_children().
|
|
|
|
|
|
+ * @param array $children_keys
|
|
|
|
+ * (optional) If the keys of the element's children are already known, they
|
|
|
|
+ * can be passed in to save another run of element_children().
|
|
|
|
+ *
|
|
|
|
+ * @return string
|
|
|
|
+ * The rendered HTML of all children of the element.
|
|
|
|
+
|
|
|
|
+ * @see drupal_render()
|
|
*/
|
|
*/
|
|
function drupal_render_children(&$element, $children_keys = NULL) {
|
|
function drupal_render_children(&$element, $children_keys = NULL) {
|
|
if ($children_keys === NULL) {
|
|
if ($children_keys === NULL) {
|
|
@@ -6447,6 +6594,44 @@ function element_set_attributes(array &$element, array $map) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Recursively computes the difference of arrays with additional index check.
|
|
|
|
+ *
|
|
|
|
+ * This is a version of array_diff_assoc() that supports multidimensional
|
|
|
|
+ * arrays.
|
|
|
|
+ *
|
|
|
|
+ * @param array $array1
|
|
|
|
+ * The array to compare from.
|
|
|
|
+ * @param array $array2
|
|
|
|
+ * The array to compare to.
|
|
|
|
+ *
|
|
|
|
+ * @return array
|
|
|
|
+ * Returns an array containing all the values from array1 that are not present
|
|
|
|
+ * in array2.
|
|
|
|
+ */
|
|
|
|
+function drupal_array_diff_assoc_recursive($array1, $array2) {
|
|
|
|
+ $difference = array();
|
|
|
|
+
|
|
|
|
+ foreach ($array1 as $key => $value) {
|
|
|
|
+ if (is_array($value)) {
|
|
|
|
+ if (!array_key_exists($key, $array2) || !is_array($array2[$key])) {
|
|
|
|
+ $difference[$key] = $value;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ $new_diff = drupal_array_diff_assoc_recursive($value, $array2[$key]);
|
|
|
|
+ if (!empty($new_diff)) {
|
|
|
|
+ $difference[$key] = $new_diff;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ elseif (!array_key_exists($key, $array2) || $array2[$key] !== $value) {
|
|
|
|
+ $difference[$key] = $value;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return $difference;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Sets a value in a nested array with variable depth.
|
|
* Sets a value in a nested array with variable depth.
|
|
*
|
|
*
|
|
@@ -6540,10 +6725,10 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f
|
|
* $value = drupal_array_get_nested_value($form, $parents);
|
|
* $value = drupal_array_get_nested_value($form, $parents);
|
|
* @endcode
|
|
* @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:
|
|
|
|
|
|
+ * A return value of NULL is ambiguous, and can mean either that the requested
|
|
|
|
+ * key does not exist, or that the actual value is NULL. If it is required to
|
|
|
|
+ * know whether the nested array key actually exists, pass a third argument that
|
|
|
|
+ * is altered by reference:
|
|
* @code
|
|
* @code
|
|
* $key_exists = NULL;
|
|
* $key_exists = NULL;
|
|
* $value = drupal_array_get_nested_value($form, $parents, $key_exists);
|
|
* $value = drupal_array_get_nested_value($form, $parents, $key_exists);
|
|
@@ -7739,7 +7924,10 @@ function entity_load_unchanged($entity_type, $id) {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Get the entity controller class for an entity type.
|
|
|
|
|
|
+ * Gets the entity controller for an entity type.
|
|
|
|
+ *
|
|
|
|
+ * @return DrupalEntityControllerInterface
|
|
|
|
+ * The entity controller object for the specified entity type.
|
|
*/
|
|
*/
|
|
function entity_get_controller($entity_type) {
|
|
function entity_get_controller($entity_type) {
|
|
$controllers = &drupal_static(__FUNCTION__, array());
|
|
$controllers = &drupal_static(__FUNCTION__, array());
|
|
@@ -7799,6 +7987,56 @@ function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Invoke hook_entity_view_mode_alter().
|
|
|
|
+ *
|
|
|
|
+ * If adding a new entity similar to nodes, comments or users, you should invoke
|
|
|
|
+ * this function during the ENTITY_build_content() or ENTITY_view_multiple()
|
|
|
|
+ * phases of rendering to allow other modules to alter the view mode during this
|
|
|
|
+ * phase. This function needs to be called before field_attach_prepare_view() to
|
|
|
|
+ * ensure that the correct content is loaded by field API.
|
|
|
|
+ *
|
|
|
|
+ * @param $entity_type
|
|
|
|
+ * The type of entity, i.e. 'node', 'user'.
|
|
|
|
+ * @param $entities
|
|
|
|
+ * The entity objects which are being prepared for view, keyed by object ID.
|
|
|
|
+ * @param $view_mode
|
|
|
|
+ * The original view mode e.g. 'full', 'teaser'...
|
|
|
|
+ * @param $langcode
|
|
|
|
+ * (optional) A language code to be used for rendering. Defaults to the global
|
|
|
|
+ * content language of the current request.
|
|
|
|
+ * @return
|
|
|
|
+ * An associative array with arrays of entities keyed by view mode.
|
|
|
|
+ *
|
|
|
|
+ * @see hook_entity_view_mode_alter()
|
|
|
|
+ */
|
|
|
|
+function entity_view_mode_prepare($entity_type, $entities, $view_mode, $langcode = NULL) {
|
|
|
|
+ if (!isset($langcode)) {
|
|
|
|
+ $langcode = $GLOBALS['language_content']->language;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // To ensure hooks are never run after field_attach_prepare_view() only
|
|
|
|
+ // process items without the entity_view_prepared flag.
|
|
|
|
+ $entities_by_view_mode = array();
|
|
|
|
+ foreach ($entities as $id => $entity) {
|
|
|
|
+ $entity_view_mode = $view_mode;
|
|
|
|
+ if (empty($entity->entity_view_prepared)) {
|
|
|
|
+
|
|
|
|
+ // Allow modules to change the view mode.
|
|
|
|
+ $context = array(
|
|
|
|
+ 'entity_type' => $entity_type,
|
|
|
|
+ 'entity' => $entity,
|
|
|
|
+ 'langcode' => $langcode,
|
|
|
|
+ );
|
|
|
|
+ drupal_alter('entity_view_mode', $entity_view_mode, $context);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $entities_by_view_mode[$entity_view_mode][$id] = $entity;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return $entities_by_view_mode;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Returns the URI elements of an entity.
|
|
* Returns the URI elements of an entity.
|
|
*
|
|
*
|