|
@@ -391,7 +391,7 @@ function drupal_add_feed($url = NULL, $title = '') {
|
|
|
*/
|
|
|
function drupal_get_feeds($delimiter = "\n") {
|
|
|
$feeds = drupal_add_feed();
|
|
|
- return implode($feeds, $delimiter);
|
|
|
+ return implode($delimiter, $feeds);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -684,7 +684,10 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
|
|
|
// We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector.
|
|
|
if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
|
|
|
$destination = drupal_parse_url($_GET['destination']);
|
|
|
- $path = $destination['path'];
|
|
|
+ // Double check the path derived by drupal_parse_url() is not external.
|
|
|
+ if (!url_is_external($destination['path'])) {
|
|
|
+ $path = $destination['path'];
|
|
|
+ }
|
|
|
$options['query'] = $destination['query'];
|
|
|
$options['fragment'] = $destination['fragment'];
|
|
|
}
|
|
@@ -760,9 +763,10 @@ function drupal_access_denied() {
|
|
|
* (optional) An array that can have one or more of the following elements:
|
|
|
* - headers: An array containing request headers to send as name/value pairs.
|
|
|
* - method: A string containing the request method. Defaults to 'GET'.
|
|
|
- * - data: A string containing the request body, formatted as
|
|
|
- * 'param=value¶m=value&...'; to generate this, use http_build_query().
|
|
|
- * Defaults to NULL.
|
|
|
+ * - data: An array containing the values for the request body or a string
|
|
|
+ * containing the request body, formatted as
|
|
|
+ * 'param=value¶m=value&...'; to generate this, use
|
|
|
+ * drupal_http_build_query(). Defaults to NULL.
|
|
|
* - max_redirects: An integer representing how many times a redirect
|
|
|
* may be followed. Defaults to 3.
|
|
|
* - timeout: A float representing the maximum number of seconds the function
|
|
@@ -788,7 +792,7 @@ function drupal_access_denied() {
|
|
|
* easy access the array keys are returned in lower case.
|
|
|
* - data: A string containing the response body that was received.
|
|
|
*
|
|
|
- * @see http_build_query()
|
|
|
+ * @see drupal_http_build_query()
|
|
|
*/
|
|
|
function drupal_http_request($url, array $options = array()) {
|
|
|
// Allow an alternate HTTP client library to replace Drupal's default
|
|
@@ -930,6 +934,11 @@ function drupal_http_request($url, array $options = array()) {
|
|
|
$path .= '?' . $uri['query'];
|
|
|
}
|
|
|
|
|
|
+ // Convert array $options['data'] to query string.
|
|
|
+ if (is_array($options['data'])) {
|
|
|
+ $options['data'] = drupal_http_build_query($options['data']);
|
|
|
+ }
|
|
|
+
|
|
|
// Only add Content-Length if we actually have any content or if it is a POST
|
|
|
// or PUT request. Some non-standard servers get confused by Content-Length in
|
|
|
// at least HEAD/GET requests, and Squid always requires Content-Length in
|
|
@@ -1550,7 +1559,7 @@ function _filter_xss_split($m, $store = FALSE) {
|
|
|
return '<';
|
|
|
}
|
|
|
|
|
|
- if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
|
|
|
+ if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)\s*([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
|
|
|
// Seriously malformed.
|
|
|
return '';
|
|
|
}
|
|
@@ -1609,7 +1618,13 @@ function _filter_xss_attributes($attr) {
|
|
|
// Attribute name, href for instance.
|
|
|
if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
|
|
|
$attrname = strtolower($match[1]);
|
|
|
- $skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
|
|
|
+ $skip = (
|
|
|
+ $attrname == 'style' ||
|
|
|
+ substr($attrname, 0, 2) == 'on' ||
|
|
|
+ substr($attrname, 0, 1) == '-' ||
|
|
|
+ // Ignore long attributes to avoid unnecessary processing overhead.
|
|
|
+ strlen($attrname) > 96
|
|
|
+ );
|
|
|
$working = $mode = 1;
|
|
|
$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
|
|
|
}
|
|
@@ -2320,6 +2335,7 @@ function url($path = NULL, array $options = array()) {
|
|
|
}
|
|
|
elseif (!empty($path) && !$options['alias']) {
|
|
|
$language = isset($options['language']) && isset($options['language']->language) ? $options['language']->language : '';
|
|
|
+ require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
|
|
|
$alias = drupal_get_path_alias($original_path, $language);
|
|
|
if ($alias != $original_path) {
|
|
|
// Strip leading slashes from internal path aliases to prevent them
|
|
@@ -3734,7 +3750,7 @@ function _drupal_build_css_path($matches, $base = NULL) {
|
|
|
}
|
|
|
|
|
|
// Prefix with base and remove '../' segments where possible.
|
|
|
- $path = $_base . $matches[1];
|
|
|
+ $path = $_base . (isset($matches[1]) ? $matches[1] : '');
|
|
|
$last = '';
|
|
|
while ($path != $last) {
|
|
|
$last = $path;
|
|
@@ -4441,12 +4457,54 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- $output = '';
|
|
|
- // The index counter is used to keep aggregated and non-aggregated files in
|
|
|
- // order by weight.
|
|
|
- $index = 1;
|
|
|
- $processed = array();
|
|
|
- $files = array();
|
|
|
+ // Sort the JavaScript so that it appears in the correct order.
|
|
|
+ uasort($items, 'drupal_sort_css_js');
|
|
|
+
|
|
|
+ // Provide the page with information about the individual JavaScript files
|
|
|
+ // used, information not otherwise available when aggregation is enabled.
|
|
|
+ $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1);
|
|
|
+ unset($setting['ajaxPageState']['js']['settings']);
|
|
|
+ drupal_add_js($setting, 'setting');
|
|
|
+
|
|
|
+ // If we're outputting the header scope, then this might be the final time
|
|
|
+ // that drupal_get_js() is running, so add the setting to this output as well
|
|
|
+ // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's
|
|
|
+ // because drupal_get_js() was intentionally passed a $javascript argument
|
|
|
+ // stripped off settings, potentially in order to override how settings get
|
|
|
+ // output, so in this case, do not add the setting to this output.
|
|
|
+ if ($scope == 'header' && isset($items['settings'])) {
|
|
|
+ $items['settings']['data'][] = $setting;
|
|
|
+ }
|
|
|
+
|
|
|
+ $elements = array(
|
|
|
+ '#type' => 'scripts',
|
|
|
+ '#items' => $items,
|
|
|
+ );
|
|
|
+
|
|
|
+ return drupal_render($elements);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * The #pre_render callback for the "scripts" element.
|
|
|
+ *
|
|
|
+ * This callback adds elements needed for <script> tags to be rendered.
|
|
|
+ *
|
|
|
+ * @param array $elements
|
|
|
+ * A render array containing:
|
|
|
+ * - '#items': The JS items as returned by drupal_add_js() and altered by
|
|
|
+ * drupal_get_js().
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ * The $elements variable passed as argument with two more children keys:
|
|
|
+ * - "scripts": contains the Javascript items
|
|
|
+ * - "settings": contains the Javascript settings items.
|
|
|
+ * If those keys are already existing, then the items will be appended and
|
|
|
+ * their keys will be preserved.
|
|
|
+ *
|
|
|
+ * @see drupal_get_js()
|
|
|
+ * @see drupal_add_js()
|
|
|
+ */
|
|
|
+function drupal_pre_render_scripts(array $elements) {
|
|
|
$preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
|
|
|
|
|
|
// A dummy query-string is added to filenames, to gain control over
|
|
@@ -4467,34 +4525,29 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
// third-party code might require the use of a different query string.
|
|
|
$js_version_string = variable_get('drupal_js_version_query_string', 'v=');
|
|
|
|
|
|
- // Sort the JavaScript so that it appears in the correct order.
|
|
|
- uasort($items, 'drupal_sort_css_js');
|
|
|
+ $files = array();
|
|
|
|
|
|
- // Provide the page with information about the individual JavaScript files
|
|
|
- // used, information not otherwise available when aggregation is enabled.
|
|
|
- $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1);
|
|
|
- unset($setting['ajaxPageState']['js']['settings']);
|
|
|
- drupal_add_js($setting, 'setting');
|
|
|
+ $scripts = isset($elements['scripts']) ? $elements['scripts'] : array();
|
|
|
+ $scripts += array('#weight' => 0);
|
|
|
|
|
|
- // If we're outputting the header scope, then this might be the final time
|
|
|
- // that drupal_get_js() is running, so add the setting to this output as well
|
|
|
- // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's
|
|
|
- // because drupal_get_js() was intentionally passed a $javascript argument
|
|
|
- // stripped off settings, potentially in order to override how settings get
|
|
|
- // output, so in this case, do not add the setting to this output.
|
|
|
- if ($scope == 'header' && isset($items['settings'])) {
|
|
|
- $items['settings']['data'][] = $setting;
|
|
|
- }
|
|
|
+ $settings = isset($elements['settings']) ? $elements['settings'] : array();
|
|
|
+ $settings += array('#weight' => $scripts['#weight'] + 10);
|
|
|
+
|
|
|
+ // The index counter is used to keep aggregated and non-aggregated files in
|
|
|
+ // order by weight. Use existing scripts count as a starting point.
|
|
|
+ $index = count(element_children($scripts)) + 1;
|
|
|
|
|
|
// Loop through the JavaScript to construct the rendered output.
|
|
|
$element = array(
|
|
|
+ '#type' => 'html_tag',
|
|
|
'#tag' => 'script',
|
|
|
'#value' => '',
|
|
|
'#attributes' => array(
|
|
|
'type' => 'text/javascript',
|
|
|
),
|
|
|
);
|
|
|
- foreach ($items as $item) {
|
|
|
+
|
|
|
+ foreach ($elements['#items'] as $item) {
|
|
|
$query_string = empty($item['version']) ? $default_query_string : $js_version_string . $item['version'];
|
|
|
|
|
|
switch ($item['type']) {
|
|
@@ -4503,7 +4556,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
$js_element['#value_prefix'] = $embed_prefix;
|
|
|
$js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(drupal_array_merge_deep_array($item['data'])) . ");";
|
|
|
$js_element['#value_suffix'] = $embed_suffix;
|
|
|
- $output .= theme('html_tag', array('element' => $js_element));
|
|
|
+ $settings[] = $js_element;
|
|
|
break;
|
|
|
|
|
|
case 'inline':
|
|
@@ -4514,7 +4567,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
$js_element['#value_prefix'] = $embed_prefix;
|
|
|
$js_element['#value'] = $item['data'];
|
|
|
$js_element['#value_suffix'] = $embed_suffix;
|
|
|
- $processed[$index++] = theme('html_tag', array('element' => $js_element));
|
|
|
+ $scripts[$index++] = $js_element;
|
|
|
break;
|
|
|
|
|
|
case 'file':
|
|
@@ -4525,7 +4578,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
}
|
|
|
$query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
|
|
|
$js_element['#attributes']['src'] = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME);
|
|
|
- $processed[$index++] = theme('html_tag', array('element' => $js_element));
|
|
|
+ $scripts[$index++] = $js_element;
|
|
|
}
|
|
|
else {
|
|
|
// By increasing the index for each aggregated file, we maintain
|
|
@@ -4536,7 +4589,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
// leading to better front-end performance of a website as a whole.
|
|
|
// See drupal_add_js() for details.
|
|
|
$key = 'aggregate_' . $item['group'] . '_' . $item['every_page'] . '_' . $index;
|
|
|
- $processed[$key] = '';
|
|
|
+ $scripts[$key] = '';
|
|
|
$files[$key][$item['data']] = $item;
|
|
|
}
|
|
|
break;
|
|
@@ -4548,7 +4601,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
$js_element['#attributes']['defer'] = 'defer';
|
|
|
}
|
|
|
$js_element['#attributes']['src'] = $item['data'];
|
|
|
- $processed[$index++] = theme('html_tag', array('element' => $js_element));
|
|
|
+ $scripts[$index++] = $js_element;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -4563,14 +4616,18 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|
|
$preprocess_file = file_create_url($uri);
|
|
|
$js_element = $element;
|
|
|
$js_element['#attributes']['src'] = $preprocess_file;
|
|
|
- $processed[$key] = theme('html_tag', array('element' => $js_element));
|
|
|
+ $scripts[$key] = $js_element;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Keep the order of JS files consistent as some are preprocessed and others are not.
|
|
|
- // Make sure any inline or JS setting variables appear last after libraries have loaded.
|
|
|
- return implode('', $processed) . $output;
|
|
|
+ // Keep the order of JS files consistent as some are preprocessed and others
|
|
|
+ // are not. Make sure any inline or JS setting variables appear last after
|
|
|
+ // libraries have loaded.
|
|
|
+ $element['scripts'] = $scripts;
|
|
|
+ $element['settings'] = $settings;
|
|
|
+
|
|
|
+ return $element;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -5116,6 +5173,8 @@ function drupal_build_js_cache($files) {
|
|
|
$contents .= file_get_contents($path) . ";\n";
|
|
|
}
|
|
|
}
|
|
|
+ // Remove JS source and source mapping urls or these may cause 404 errors.
|
|
|
+ $contents = preg_replace('/\/\/(#|@)\s(sourceURL|sourceMappingURL)=\s*(\S*?)\s*$/m', '', $contents);
|
|
|
// Prefix filename to prevent blocking by firewalls which reject files
|
|
|
// starting with "ad*".
|
|
|
$filename = 'js_' . drupal_hash_base64($contents) . '.js';
|
|
@@ -6603,30 +6662,41 @@ function element_children(&$elements, $sort = FALSE) {
|
|
|
$sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
|
|
|
|
|
|
// Filter out properties from the element, leaving only children.
|
|
|
- $children = array();
|
|
|
+ $count = count($elements);
|
|
|
+ $child_weights = array();
|
|
|
+ $i = 0;
|
|
|
$sortable = FALSE;
|
|
|
foreach ($elements as $key => $value) {
|
|
|
- if ($key === '' || $key[0] !== '#') {
|
|
|
- $children[$key] = $value;
|
|
|
+ if (is_int($key) || $key === '' || $key[0] !== '#') {
|
|
|
if (is_array($value) && isset($value['#weight'])) {
|
|
|
+ $weight = $value['#weight'];
|
|
|
$sortable = TRUE;
|
|
|
}
|
|
|
+ else {
|
|
|
+ $weight = 0;
|
|
|
+ }
|
|
|
+ // Support weights with up to three digit precision and conserve the
|
|
|
+ // insertion order.
|
|
|
+ $child_weights[$key] = floor($weight * 1000) + $i / $count;
|
|
|
}
|
|
|
+ $i++;
|
|
|
}
|
|
|
+
|
|
|
// Sort the children if necessary.
|
|
|
if ($sort && $sortable) {
|
|
|
- uasort($children, 'element_sort');
|
|
|
+ asort($child_weights);
|
|
|
// Put the sorted children back into $elements in the correct order, to
|
|
|
// preserve sorting if the same element is passed through
|
|
|
// element_children() twice.
|
|
|
- foreach ($children as $key => $child) {
|
|
|
+ foreach ($child_weights as $key => $weight) {
|
|
|
+ $value = $elements[$key];
|
|
|
unset($elements[$key]);
|
|
|
- $elements[$key] = $child;
|
|
|
+ $elements[$key] = $value;
|
|
|
}
|
|
|
$elements['#sorted'] = TRUE;
|
|
|
}
|
|
|
|
|
|
- return array_keys($children);
|
|
|
+ return array_keys($child_weights);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -6952,7 +7022,16 @@ function drupal_common_theme() {
|
|
|
'variables' => array(),
|
|
|
),
|
|
|
'table' => array(
|
|
|
- 'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
|
|
|
+ 'variables' => array(
|
|
|
+ 'header' => NULL,
|
|
|
+ 'footer' => NULL,
|
|
|
+ 'rows' => NULL,
|
|
|
+ 'attributes' => array(),
|
|
|
+ 'caption' => NULL,
|
|
|
+ 'colgroups' => array(),
|
|
|
+ 'sticky' => TRUE,
|
|
|
+ 'empty' => '',
|
|
|
+ ),
|
|
|
),
|
|
|
'tablesort_indicator' => array(
|
|
|
'variables' => array('style' => NULL),
|