Index: CHANGELOG.txt =================================================================== RCS file: /cvs/drupal/drupal/CHANGELOG.txt,v retrieving revision 1.348 diff -u -p -r1.348 CHANGELOG.txt --- CHANGELOG.txt 21 Oct 2009 19:34:53 -0000 1.348 +++ CHANGELOG.txt 27 Oct 2009 23:11:22 -0000 @@ -149,6 +149,12 @@ Drupal 7.0, xxxx-xx-xx (development vers * Modules can specify how their data structure maps to RDF. * Added support for RDFa export of nodes, comments, terms, users, etc. and their fields. +- Search engine optimization and web linking: + * Added a rel="canonical" link on node and comment pages to prevent + duplicate content indexing by search engines. + * Added a default rel="shortlink" link on node and comment pages that + advertises a short link as an alternative URL to third-party services. + * Meta information is now alterable by all modules before rendering. - Field API: * Custom data fields may be attached to nodes, users, comments and taxonomy terms. Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.43 diff -u -p -r1.43 batch.inc --- includes/batch.inc 27 Oct 2009 04:12:39 -0000 1.43 +++ includes/batch.inc 27 Oct 2009 23:11:22 -0000 @@ -199,7 +199,14 @@ function _batch_progress_page_nojs() { $batch['url_options']['query']['op'] = $new_op; $url = url($batch['url'], $batch['url_options']); - drupal_add_html_head(''); + $element = array( + '#tag' => 'meta', + '#attributes' => array( + 'http-equiv' => 'Refresh', + 'content' => '0; URL=' . $url, + ), + ); + drupal_add_html_head($element, 'batch_progress_meta_refresh'); return theme('progress_bar', array('percent' => $percentage, 'message' => $message)); } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1031 diff -u -p -r1.1031 common.inc --- includes/common.inc 27 Oct 2009 19:29:12 -0000 1.1031 +++ includes/common.inc 27 Oct 2009 23:11:22 -0000 @@ -280,23 +280,78 @@ function drupal_get_rdf_namespaces() { /** * Add output to the head tag of the HTML page. * - * This function can be called as long the headers aren't sent. + * This function can be called as long the headers aren't sent. Pass no + * arguments (or NULL for both) to retrieve the currently stored elements. + * + * @param $data + * A renderable array. If the '#type' key is not set then 'html_tag' will be + * added as the default '#type'. + * @param $key + * A unique string key to allow implementations of hook_html_head_alter() to + * identify the element in $data. Required if $data is not NULL. + * + * @return + * An array of all stored HEAD elements. + * + * @see theme_html_tag() */ -function drupal_add_html_head($data = NULL) { - $stored_head = &drupal_static(__FUNCTION__, ''); +function drupal_add_html_head($data = NULL, $key = NULL) { + $stored_head = &drupal_static(__FUNCTION__); - if (!is_null($data)) { - $stored_head .= $data . "\n"; + if (!isset($stored_head)) { + // Make sure the defaults, including Content-Type, come first. + $stored_head = _drupal_default_html_head(); + } + + if (isset($data) && isset($key)) { + if (!isset($data['#type'])) { + $data['#type'] = 'html_tag'; + } + $stored_head[$key] = $data; } return $stored_head; } /** - * Retrieve output to be displayed in the head tag of the HTML page. + * Returns elements that are always displayed in the HEAD tag of the HTML page. + */ +function _drupal_default_html_head() { + // Add default elements. Make sure the Content-Type comes first because the + // IE browser may be vulnerable to XSS via encoding attacks from any content + // that comes before this META tag, such as a TITLE tag. + $elements['system_meta_content_type'] = array( + '#type' => 'html_tag', + '#tag' => 'meta', + '#attributes' => array( + 'http-equiv' => 'Content-Type', + 'content' => 'text/html; charset=utf-8', + ), + // Security: This always has to be output first. + '#weight' => -1000, + ); + // Show Drupal and the major version number in the META GENERATOR tag. + // Get the major version. + list($version, ) = explode('.', VERSION); + $elements['system_meta_generator'] = array( + '#type' => 'html_tag', + '#tag' => 'meta', + '#attributes' => array( + 'name' => 'Generator', + 'content' => 'Drupal ' . $version . ' (http://drupal.org)', + ), + ); + // Also send the generator in the HTTP header. + $elements['system_meta_generator']['#attached']['drupal_add_http_header'][] = array('X-Generator', $elements['system_meta_generator']['#attributes']['content']); + return $elements; +} + +/** + * Retrieve output to be displayed in the HEAD tag of the HTML page. */ function drupal_get_html_head() { - $output = "\n"; - return $output . drupal_add_html_head(); + $elements = drupal_add_html_head(); + drupal_alter('html_head', $elements); + return drupal_render($elements); } /** @@ -319,10 +374,10 @@ function drupal_clear_path_cache() { function drupal_add_feed($url = NULL, $title = '') { $stored_feed_links = &drupal_static(__FUNCTION__, array()); - if (!is_null($url) && !isset($stored_feed_links[$url])) { + if (isset($url)) { $stored_feed_links[$url] = theme('feed_icon', array('url' => $url, 'title' => $title)); - drupal_add_link(array('rel' => 'alternate', + drupal_add_html_head_link(array('rel' => 'alternate', 'type' => 'application/rss+xml', 'title' => $title, 'href' => $url)); @@ -2489,6 +2544,28 @@ function url($path = NULL, array $option } /** + * Format an attribute string for a HTTP header. + * + * @param $attributes + * An associative array of attributes such as 'rel'. + * + * @return + * A ; separated string ready for insertion in a HTTP header. No escaping is + * performed for HTML entities, so this string is not safe to be printed. + * + * @see drupal_add_http_header() + */ +function drupal_http_header_attributes(array $attributes = array()) { + foreach ($attributes as $attribute => &$data) { + if (is_array($data)) { + $data = implode(' ', $data); + } + $data = $attribute . '="' . $data . '"'; + } + return $attributes ? ' ' . implode('; ', $attributes) : ''; +} + +/** * Format an attribute string to insert in a tag. * * Each array key and its value will be formatted into an HTML attribute string. @@ -2898,12 +2975,33 @@ function base_path() { } /** - * Add a tag to the page's HEAD. + * Add a LINK tag with a distinct 'rel' attribute to the page's HEAD. * - * This function can be called as long the HTML header hasn't been sent. - */ -function drupal_add_link($attributes) { - drupal_add_html_head('\n"); + * This function can be called as long the HTML header hasn't been sent, + * which on normal pages is up through the preprocess step of theme('html'). + * Adding a link will overwrite a prior link with the exact same 'rel' and + * 'href' attributes. + * + * @param $attributes + * Associative array of element attributes including 'href' and 'rel'. + * @param $header + * Optional flag to determine if a HTTP 'Link:' header should be sent. + */ +function drupal_add_html_head_link($attributes, $header = FALSE) { + $element = array( + '#tag' => 'link', + '#attributes' => $attributes, + ); + $href = $attributes['href']; + + if ($header) { + // Also add a HTTP header "Link:". + $href = '<' . check_plain($attributes['href']) . '>;'; + unset($attributes['href']); + $element['#attached']['drupal_add_http_header'][] = array('Link', $href . drupal_http_header_attributes($attributes), TRUE); + } + + drupal_add_html_head($element, 'drupal_add_html_head_link:' . $attributes['rel'] . ':' . $href); } /** @@ -3089,7 +3187,15 @@ function drupal_get_css($css = NULL) { } // If CSS preprocessing is off, we still need to output the styles. - // Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones. + // Additionally, go through any remaining styles if CSS preprocessing is on + // and output the non-cached ones. + $css_element = array( + '#tag' => 'link', + '#attributes' => array( + 'type' => 'text/css', + 'rel' => 'stylesheet', + ), + ); $rendered_css = array(); $inline_css = ''; $external_css = ''; @@ -3101,7 +3207,10 @@ function drupal_get_css($css = NULL) { case 'file': // Depending on whether aggregation is desired, include the file. if (!$item['preprocess'] || !($is_writable && $preprocess_css)) { - $rendered_css[] = ''; + $element = $css_element; + $element['#attributes']['media'] = $item['media']; + $element['#attributes']['href'] = file_create_url($item['data']) . $query_string; + $rendered_css[] = theme('html_tag', array('element' => $element)); } else { $preprocess_items[$item['media']][] = $item; @@ -3116,7 +3225,10 @@ function drupal_get_css($css = NULL) { break; case 'external': // Preprocessing for external CSS files is ignored. - $external_css .= '' . "\n"; + $element = $css_element; + $element['#attributes']['media'] = $item['media']; + $element['#attributes']['href'] = $item['data']; + $external_css .= theme('html_tag', array('element' => $element)); break; } } @@ -3125,18 +3237,24 @@ function drupal_get_css($css = NULL) { foreach ($preprocess_items as $media => $items) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". + $element = $css_element; + $element['#attributes']['media'] = $media; $filename = 'css_' . md5(serialize($items) . $query_string) . '.css'; - $preprocess_file = file_create_url(drupal_build_css_cache($items, $filename)); - $rendered_css['preprocess'] .= '' . "\n"; + $element['#attributes']['href'] = file_create_url(drupal_build_css_cache($items, $filename)); + $rendered_css['preprocess'] .= theme('html_tag', array('element' => $element)); } } // Enclose the inline CSS with the style tag if required. if (!empty($inline_css)) { - $inline_css = "\n" . ''; + $element = $css_element; + $element['#tag'] = 'style'; + $element['#value'] = $inline_css; + unset($element['#attributes']['rel']); + $inline_css = "\n" . theme('html_tag', array('element' => $element)); } // Output all the CSS files with the inline stylesheets showing up last. - return implode("\n", $rendered_css) . $external_css . $inline_css; + return implode($rendered_css) . $external_css . $inline_css; } /** @@ -3617,7 +3735,7 @@ function drupal_get_js($scope = 'header' $output = ''; $preprocessed = ''; - $no_preprocess = ''; + $no_preprocess = "\n"; $files = array(); $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); $directory = file_directory_path('public'); @@ -3641,19 +3759,42 @@ function drupal_get_js($scope = 'header' uasort($items, 'drupal_sort_weight'); // Loop through the JavaScript to construct the rendered output. + $element = array( + '#tag' => 'script', + '#value' => '', + '#attributes' => array( + 'type' => 'text/javascript', + ), + ); foreach ($items as $item) { switch ($item['type']) { case 'setting': - $output .= '\n"; + $js_element = $element; + $js_element['#value_prefix'] = $embed_prefix; + $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(call_user_func_array('array_merge_recursive', $item['data'])) . ");"; + $js_element['#value_suffix'] = $embed_suffix; + $output .= theme('html_tag', array('element' => $js_element)); break; case 'inline': - $output .= '\n"; + $js_element = $element; + if ($item['defer']) { + $js_element['#attributes']['defer'] = 'defer'; + } + $js_element['#value_prefix'] = $embed_prefix; + $js_element['#value'] = $item['data']; + $js_element['#value_suffix'] = $embed_suffix; + $output .= theme('html_tag', array('element' => $js_element)); break; case 'file': + $js_element = $element; if (!$item['preprocess'] || !$is_writable || !$preprocess_js) { - $no_preprocess .= '\n"; + if ($item['defer']) { + $js_element['#attributes']['defer'] = 'defer'; + } + $js_element['#attributes']['src'] = file_create_url($item['data']) . ($item['cache'] ? $query_string : '?' . REQUEST_TIME); + $no_preprocess .= theme('html_tag', array('element' => $js_element)); } else { $files[$item['data']] = $item; @@ -3661,8 +3802,13 @@ function drupal_get_js($scope = 'header' break; case 'external': + $js_element = $element; // Preprocessing for external JavaScript files is ignored. - $output .= '\n"; + if ($item['defer']) { + $js_element['#attributes']['defer'] = 'defer'; + } + $js_element['#attributes']['src'] = $item['data']; + $output .= theme('html_tag', array('element' => $js_element)); break; } } @@ -3673,7 +3819,9 @@ function drupal_get_js($scope = 'header' // starting with "ad*". $filename = 'js_' . md5(serialize($files) . $query_string) . '.js'; $preprocess_file = file_create_url(drupal_build_js_cache($files, $filename)); - $preprocessed .= '' . "\n"; + $js_element = $element; + $js_element['#attributes']['src'] = $preprocess_file; + $preprocessed .= theme('html_tag', array('element' => $js_element)) . "\n"; } // Keep the order of JS files consistent as some are preprocessed and others are not. @@ -5156,6 +5304,9 @@ function drupal_common_theme() { 'indentation' => array( 'variables' => array('size' => 1), ), + 'html_tag' => array( + 'render element' => 'element', + ), // from theme.maintenance.inc 'maintenance_page' => array( 'variables' => array('content' => NULL, 'show_messages' => TRUE), Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.542 diff -u -p -r1.542 theme.inc --- includes/theme.inc 23 Oct 2009 22:24:11 -0000 1.542 +++ includes/theme.inc 27 Oct 2009 23:11:22 -0000 @@ -1879,6 +1879,48 @@ function theme_feed_icon($variables) { } /** + * Generate the output for a generic HTML tag with attributes. + * + * This theme function should be used for tags appearing in the HTML HEAD of a + * document (specified via the #tag property in the passed $element): + * + * @param $variables + * An associative array containing: + * - element: An associative array describing the tag: + * - #tag: The tag name to output. Typical tags added to the HTML HEAD: + * - meta: To provide meta information, such as a page refresh. + * - link: To refer to stylesheets and other contextual information. + * - script: To load JavaScript. + * - #attributes: (optional) An array of HTML attributes to apply to the + * tag. + * - #value: (optional) A string containing tag content, such as inline CSS. + * - #value_prefix: (optional) A string to prepend to #value, e.g. a CDATA + * wrapper prefix. + * - #value_suffix: (optional) A string to append to #value, e.g. a CDATA + * wrapper suffix. + * + * @ingroup themeable + */ +function theme_html_tag($variables) { + $element = $variables['element']; + if (!isset($element['#value'])) { + return '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . " />\n"; + } + else { + $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>'; + if (isset($element['#value_prefix'])) { + $output .= $element['#value_prefix']; + } + $output .= $element['#value']; + if (isset($element['#value_suffix'])) { + $output .= $element['#value_suffix']; + } + $output .= '\n"; + return $output; + } +} + +/** * Returns code that emits the 'more' link used on blocks. * * @param $variables @@ -2178,7 +2220,7 @@ function template_preprocess_html(&$vari if (theme_get_setting('toggle_favicon')) { $favicon = theme_get_setting('favicon'); $type = theme_get_setting('favicon_mimetype'); - drupal_add_html_head(''); + drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => check_url($favicon), 'type' => $type)); } // Construct page title. @@ -2347,7 +2389,7 @@ function template_preprocess_maintenance if (theme_get_setting('toggle_favicon')) { $favicon = theme_get_setting('favicon'); $type = theme_get_setting('favicon_mimetype'); - drupal_add_html_head(''); + drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => check_url($favicon), 'type' => $type)); } global $theme; Index: modules/book/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book/book.module,v retrieving revision 1.520 diff -u -p -r1.520 book.module --- modules/book/book.module 23 Oct 2009 22:24:12 -0000 1.520 +++ modules/book/book.module 27 Oct 2009 23:11:23 -0000 @@ -940,21 +940,21 @@ function template_preprocess_book_naviga if ($prev = book_prev($book_link)) { $prev_href = url($prev['href']); - drupal_add_link(array('rel' => 'prev', 'href' => $prev_href)); + drupal_add_html_head_link(array('rel' => 'prev', 'href' => $prev_href)); $variables['prev_url'] = $prev_href; $variables['prev_title'] = check_plain($prev['title']); } if ($book_link['plid'] && $parent = book_link_load($book_link['plid'])) { $parent_href = url($parent['href']); - drupal_add_link(array('rel' => 'up', 'href' => $parent_href)); + drupal_add_html_head_link(array('rel' => 'up', 'href' => $parent_href)); $variables['parent_url'] = $parent_href; $variables['parent_title'] = check_plain($parent['title']); } if ($next = book_next($book_link)) { $next_href = url($next['href']); - drupal_add_link(array('rel' => 'next', 'href' => $next_href)); + drupal_add_html_head_link(array('rel' => 'next', 'href' => $next_href)); $variables['next_url'] = $next_href; $variables['next_title'] = check_plain($next['title']); } Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.796 diff -u -p -r1.796 comment.module --- modules/comment/comment.module 23 Oct 2009 22:24:12 -0000 1.796 +++ modules/comment/comment.module 27 Oct 2009 23:11:23 -0000 @@ -362,9 +362,6 @@ function comment_permalink($comment) { $_GET['q'] = 'node/' . $node->nid; $_GET['page'] = $page; - // Set the node path as the canonical URL to prevent duplicate content. - drupal_add_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid))); - // Return the node view, this will show the correct comment in context. return menu_execute_active_handler('node/' . $node->nid, FALSE); } Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1154 diff -u -p -r1.1154 node.module --- modules/node/node.module 23 Oct 2009 22:24:16 -0000 1.1154 +++ modules/node/node.module 27 Oct 2009 23:11:23 -0000 @@ -2149,6 +2149,10 @@ function node_page_view($node) { if (isset($return['nodes'][$node->nid]['title'])) { drupal_set_title($return['nodes'][$node->nid]['title']['items'][0]['#item']['value']); } + // Set the node path as the canonical URL to prevent duplicate content. + drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)), TRUE); + // Set the non-aliased path as a default shortlink. + drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url('node/' . $node->nid, array('alias' => TRUE))), TRUE); return $return; } Index: modules/openid/tests/openid_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/openid/tests/openid_test.module,v retrieving revision 1.5 diff -u -p -r1.5 openid_test.module --- modules/openid/tests/openid_test.module 30 Sep 2009 18:36:02 -0000 1.5 +++ modules/openid/tests/openid_test.module 27 Oct 2009 23:11:23 -0000 @@ -97,7 +97,14 @@ function openid_test_yadis_x_xrds_locati * Menu callback; regular HTML page with element. */ function openid_test_yadis_http_equiv() { - drupal_add_html_head(''); + $element = array( + '#tag' => 'meta', + '#attributes' => array( + 'http-equiv' => 'X-XRDS-Location', + 'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)), + ), + ); + drupal_add_html_head($element, 'openid_test_yadis_http_equiv'); return t('This page includes a <meta equiv=...> element containing the URL of an XRDS document.'); } @@ -105,7 +112,7 @@ function openid_test_yadis_http_equiv() * Menu callback; regular HTML page with OpenID 1.0 element. */ function openid_test_html_openid1() { - drupal_add_html_head(''); + drupal_add_html_head_link(array('rel' => 'openid.server', 'href' => url('openid-test/endpoint', array('absolute' => TRUE)))); return t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.'); } @@ -113,7 +120,7 @@ function openid_test_html_openid1() { * Menu callback; regular HTML page with OpenID 2.0 element. */ function openid_test_html_openid2() { - drupal_add_html_head(''); + drupal_add_html_head_link(array('rel' => 'openid2.provider', 'href' => url('openid-test/endpoint', array('absolute' => TRUE)))); return t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.'); } Index: modules/rdf/rdf.module =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v retrieving revision 1.5 diff -u -p -r1.5 rdf.module --- modules/rdf/rdf.module 23 Oct 2009 22:24:17 -0000 1.5 +++ modules/rdf/rdf.module 27 Oct 2009 23:11:23 -0000 @@ -384,10 +384,17 @@ function rdf_preprocess_node(&$variables // In full node mode, the title is not displayed by node.tpl.php so it is // added in the head tag of the HTML page. if ($variables['page']) { - $title_attributes['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates']; - $title_attributes['content'] = $variables['node_title']; - $title_attributes['about'] = $variables['node_url']; - drupal_add_html_head(''); + $element = array( + '#tag' => 'meta', + '#attributes' => array( + 'content' => $variables['node_title'], + 'about' => $variables['node_url'], + ), + ); + if (!empty($variables['node']->rdf_mapping['title']['predicates'])) { + $element['#attributes']['property'] = $variables['node']->rdf_mapping['title']['predicates']; + } + drupal_add_html_head($element, 'rdf_node'); } // Adds RDFa markup for the date. Index: modules/simpletest/tests/browser_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/browser_test.module,v retrieving revision 1.4 diff -u -p -r1.4 browser_test.module --- modules/simpletest/tests/browser_test.module 30 Sep 2009 18:36:02 -0000 1.4 +++ modules/simpletest/tests/browser_test.module 27 Oct 2009 23:11:23 -0000 @@ -57,7 +57,14 @@ function browser_test_print_post_form_su function browser_test_refresh_meta() { if (!isset($_GET['refresh'])) { $url = url('browser_test/refresh/meta', array('absolute' => TRUE, 'query' => array('refresh' => 'true'))); - drupal_add_html_head(''); + $element = array( + '#tag' => 'meta', + '#attributes' => array( + 'http-equiv' => 'Refresh', + 'content' => '0; URL=' . $url, + ), + ); + drupal_add_html_head($element, 'browser_test_refresh_meta'); return ''; } echo 'Refresh successful'; Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.85 diff -u -p -r1.85 common.test --- modules/simpletest/tests/common.test 27 Oct 2009 19:29:12 -0000 1.85 +++ modules/simpletest/tests/common.test 27 Oct 2009 23:11:23 -0000 @@ -530,7 +530,7 @@ class CascadingStylesheetsTestCase exten $css_preprocessed = ''; drupal_add_css($css, 'inline'); $styles = drupal_get_css(); - $this->assertEqual($styles, "\n" . $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.')); + $this->assertEqual($styles, "\n" . $css_preprocessed . "\n", t('Rendering preprocessed inline CSS adds it to the page.')); } /** Index: modules/system/system.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v retrieving revision 1.97 diff -u -p -r1.97 system.api.php --- modules/system/system.api.php 24 Oct 2009 05:28:16 -0000 1.97 +++ modules/system/system.api.php 27 Oct 2009 23:11:24 -0000 @@ -2282,6 +2282,27 @@ function hook_drupal_goto_alter(&$path, } /** + * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head(). + * + * Elements available to be altered are only those added using + * drupal_add_html_head_link() or drupal_add_html_head(). CSS and JS files + * are handled using drupal_add_css() and drupal_add_js(), so the head links + * for those files will not appear in the $head_elements array. + * + * @param $head_elements + * An array of renderable elements. Generally the values of the #attributes + * array will be the most likely target for changes. + */ +function hook_html_head_alter(&$head_elements) { + foreach($head_elements as $key => $element) { + if (isset($element['#attributes']['rel']) && $element['#attributes']['rel'] == 'canonical') { + // I want a custom canonical url. + $head_elements[$key]['#attributes']['href'] = mymodule_canonical_url(); + } + } +} + +/** * Alter the full list of installation tasks. * * @param $tasks Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.827 diff -u -p -r1.827 system.module --- modules/system/system.module 27 Oct 2009 04:16:39 -0000 1.827 +++ modules/system/system.module 27 Oct 2009 23:11:24 -0000 @@ -211,12 +211,6 @@ function system_theme() { 'system_powered_by' => array( 'variables' => array(), ), - 'meta_generator_html' => array( - 'variables' => array('version' => NULL), - ), - 'meta_generator_header' => array( - 'variables' => array('version' => NULL), - ), 'system_compact_link' => array( 'variables' => array(), ), @@ -350,6 +344,12 @@ function system_element_info() { '#ajax_commands' => array(), ); + $types['html_tag'] = array( + '#theme' => 'html_tag', + '#attributes' => array(), + '#value' => NULL, + ); + // Input elements. $types['submit'] = array( '#input' => TRUE, @@ -1651,22 +1651,6 @@ function system_init() { } /** - * Implement MODULE_preprocess_HOOK(). - */ -function system_preprocess_page(&$variables) { - // Get the major version - list($version, ) = explode('.', VERSION); - - // Emit the META tag in the HTML HEAD section - theme('meta_generator_html', array('version' => $version)); - - // Emit the HTTP Header too - theme('meta_generator_header', array('version' => $version)); - - $variables['head'] = drupal_get_html_head(); -} - -/** * Implement hook_form_FORM_ID_alter(). */ function system_form_user_profile_form_alter(&$form, &$form_state) { @@ -2901,24 +2885,6 @@ function theme_system_compact_link() { } /** - * Send Drupal and the major version number in the META GENERATOR HTML. - * - * @ingroup themeable - */ -function theme_meta_generator_html($variables) { - drupal_add_html_head(''); -} - -/** - * Send Drupal and the major version number in the HTTP headers. - * - * @ingroup themeable - */ -function theme_meta_generator_header($variables) { - drupal_add_http_header('X-Generator', 'Drupal ' . $variables['version'] . ' (http://drupal.org)'); -} - -/** * Implement hook_image_toolkits(). */ function system_image_toolkits() {