Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal/drupal/CHANGELOG.txt,v
retrieving revision 1.340
diff -u -r1.340 CHANGELOG.txt
--- CHANGELOG.txt 5 Sep 2009 06:03:29 -0000 1.340
+++ CHANGELOG.txt 6 Sep 2009 19:11:04 -0000
@@ -140,6 +140,14 @@
- Added RDF support:
* Modules can declare RDF namespaces which are serialized in the tag
for RDFa support.
+- 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 default a rel="shortlink" link that advertises a short link as an
+ alternative to third-party services.
+ * drupal_set_html_head now stores structured data for link, meta, and
+ and other elements, allowing it to be altered by modules before
+ rendering.
- Field API:
* Custom data fields may be attached to nodes, users, comments and taxonomy
terms.
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.519
diff -u -r1.519 theme.inc
--- includes/theme.inc 31 Aug 2009 19:50:17 -0000 1.519
+++ includes/theme.inc 6 Sep 2009 19:11:51 -0000
@@ -1941,6 +1941,20 @@
}
/**
+ * Return a tag to be placed in the HEAD section of a page.
+ *
+ * @param $element
+ * An associative array containing the properties of the element:
+ * #tag: The tag name, for example 'title, 'meta', 'link'.
+ * #attributes: an array of atttributes.
+ * @return
+ * A string containing the tag generated.
+ */
+function theme_head_tag(array $element) {
+ return '<' . $element['#tag'] . drupal_attributes($element['#attributes']) ." />\n";
+}
+
+/**
* @} End of "defgroup themeable".
*/
@@ -2040,7 +2054,11 @@
if (theme_get_setting('toggle_favicon')) {
$favicon = theme_get_setting('favicon');
$type = theme_get_setting('favicon_mimetype');
- drupal_add_html_head('');
+ drupal_add_link(array(
+ 'rel' => 'shortcut icon',
+ 'href' => check_url($favicon),
+ 'type' => $type,
+ ));
}
// Set up layout variable.
@@ -2214,7 +2232,11 @@
if (theme_get_setting('toggle_favicon')) {
$favicon = theme_get_setting('favicon');
$type = theme_get_setting('favicon_mimetype');
- drupal_add_html_head('');
+ drupal_add_link(array(
+ 'rel' => 'shortcut icon',
+ 'href' => check_url($favicon),
+ 'type' => $type,
+ ));
}
global $theme;
Index: includes/batch.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/batch.inc,v
retrieving revision 1.37
diff -u -r1.37 batch.inc
--- includes/batch.inc 26 Aug 2009 15:00:17 -0000 1.37
+++ includes/batch.inc 6 Sep 2009 19:11:07 -0000
@@ -190,7 +190,11 @@
}
$url = url($batch['url'], array('query' => array('id' => $batch['id'], 'op' => $new_op)));
- drupal_add_html_head('');
+ drupal_add_html_head(array(
+ 'tag' => 'meta',
+ 'http-equiv' => 'Refresh',
+ 'content' => '0; URL=' . $url,
+ ));
return theme('progress_bar', $percentage, $message);
}
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.984
diff -u -r1.984 common.inc
--- includes/common.inc 5 Sep 2009 15:05:01 -0000 1.984
+++ includes/common.inc 6 Sep 2009 19:11:39 -0000
@@ -247,15 +247,24 @@
}
/**
- * Add output to the head tag of the HTML page.
+ * Add data to the head tag of the HTML page.
*
* This function can be called as long the headers aren't sent.
+ *
+ * @param $data
+ * An associative array of data with the following keys.
+ * - 'key': Optional key for identifying the data in hook_html_head_alter().
+ * - 'tag': The tag to be added to the HEAD section of the page.
+ * - 'attributes': An array of tag attributes.
+ *
+ * @return
+ * An array of all the stored head data.
*/
function drupal_add_html_head($data = NULL) {
- $stored_head = &drupal_static(__FUNCTION__, '');
+ $stored_head = &drupal_static(__FUNCTION__, array());
- if (!is_null($data)) {
- $stored_head .= $data . "\n";
+ if (isset($data)) {
+ $stored_head[] = $data;
}
return $stored_head;
}
@@ -264,8 +273,68 @@
* 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();
+ $head_data = drupal_add_html_head();
+ $head_elements = array();
+ foreach ($head_data as $data) {
+ $element = array(
+ '#type' => 'head_tag',
+ '#tag' => isset($data['tag']) ? $data['tag'] : 'meta',
+ '#attributes' => isset($data['attributes']) ? $data['attributes'] : array(),
+ );
+ // Attach HTTP headers.
+ if (isset($data['headers'])) {
+ $element['#attached']['drupal_set_header'] = $data['headers'];
+ }
+ // If a key is provided, use it for identifying the element
+ // in hook_html_head_alter().
+ if (isset($data['key'])) {
+ $head_elements[$data['key']] = $element;
+ }
+ $head_elements[][] = $element;
+ }
+
+ // Add default head elements.
+ $head_elements += drupal_html_head_defaults();
+
+ // Allow modules to alter the head elements.
+ drupal_alter('html_head', $head_elements);
+
+ // Return the rendered data for later inclusion in the page.
+ return drupal_render($head_elements);
+}
+
+/**
+ * Return an array of head tag elements added by default to all pages generated
+ * by Drupal.
+ */
+function drupal_html_head_defaults() {
+ // Set the default content type.
+ $defaults['content_type'] = array(
+ '#type' => 'head_tag',
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'http-equiv' => 'Content-Type',
+ 'content' => 'text/html; charset=utf-8',
+ ),
+ );
+
+ // Send Drupal and the major version number in the META GENERATOR HTML
+ // and HTTP headers.
+ list($version, ) = explode('.', VERSION);
+ $defaults['generator'] = array(
+ '#type' => 'head_tag',
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'name' => 'Generator',
+ 'content' => 'Drupal ' . $version . ' (http://drupal.org)',
+ ),
+ '#attached' => array(
+ 'drupal_set_header' => array(
+ array('X-Generator', 'Drupal ' . $version . ' (http://drupal.org)'),
+ ),
+ ),
+ );
+ return $defaults;
}
/**
@@ -2503,9 +2572,44 @@
* Add a tag to the page's HEAD.
*
* This function can be called as long the HTML header hasn't been sent.
+ *
+ * @param $attributes
+ * Associative array of attributes.
+ *
+ * @see drupal_add_html_head()
+ * @see drupal_attributes()
*/
function drupal_add_link($attributes) {
- drupal_add_html_head('\n");
+ $data['tag'] = 'link';
+ $data['attributes'] = $attributes;
+ if (!empty($attributes['header'])) {
+ $data['headers'] = array(
+ drupal_set_link_header($attributes)
+ );
+ }
+ // Add tag under HTML HEAD.
+ drupal_add_html_head($data);
+}
+
+/**
+ * Generate a HTTP Link: header.
+ *
+ * @param $attributes
+ * Associative array of element attributes.
+ */
+function drupal_set_link_header($attributes) {
+ // From drupal_attributes function
+ foreach ($attributes as $attribute => &$data) {
+ if ($attribute != 'href') {
+ if (is_array($data)) {
+ $data = implode(' ', $data);
+ }
+ $data = $attribute . '="' . check_plain($data) . '"';
+ }
+ }
+ $href = '<' . $attributes['href'] . '> ';
+ unset($attributes['href']);
+ return array('Link', $href . implode('; ', $attributes));
}
/**
@@ -4715,6 +4819,9 @@
'vertical_tabs' => array(
'arguments' => array('element' => NULL),
),
+ 'head_tag' => array(
+ 'arguments' => array('element' => NULL),
+ ),
);
}
Index: modules/openid/tests/openid_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/tests/openid_test.module,v
retrieving revision 1.3
diff -u -r1.3 openid_test.module
--- modules/openid/tests/openid_test.module 10 Jun 2009 20:13:20 -0000 1.3
+++ modules/openid/tests/openid_test.module 6 Sep 2009 19:12:25 -0000
@@ -97,24 +97,54 @@
* Menu callback; regular HTML page with element.
*/
function openid_test_yadis_http_equiv() {
- drupal_add_html_head('');
- return t('This page includes a <meta equiv=...> element containing the URL of an XRDS document.');
+ $build[] = array(
+ '#type' => 'markup',
+ '#markup' => t('This page includes a <meta equiv=...> element containing the URL of an XRDS document.'),
+ '#attached' => array(
+ 'drupal_add_html_head' => array(
+ 'tag' => 'meta',
+ 'attributes' => array(
+ 'http-equiv' => 'X-XRDS-Location',
+ 'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)),
+ ),
+ ),
+ ),
+ );
+ return $build;
}
/**
* Menu callback; regular HTML page with OpenID 1.0 element.
*/
function openid_test_html_openid1() {
- drupal_add_html_head('');
- return t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.');
+ $build[] = array(
+ '#type' => 'markup',
+ '#markup' => t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.'),
+ '#attached' => array(
+ 'drupal_add_link' => array(
+ 'rel' => 'openid.server',
+ 'href' => url('openid-test/endpoint', array('absolute' => TRUE)),
+ ),
+ ),
+ );
+ return $build;
}
/**
* Menu callback; regular HTML page with OpenID 2.0 element.
*/
function openid_test_html_openid2() {
- drupal_add_html_head('');
- return t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.');
+ $build[] = array(
+ '#type' => 'markup',
+ '#markup' => t('This page includes a <link rel=...> element containing the URL of an OpenID Provider Endpoint.'),
+ '#attached' => array(
+ 'drupal_add_link' => array(
+ 'rel' => 'openid2.provider',
+ 'href' => url('openid-test/endpoint', array('absolute' => TRUE)),
+ ),
+ ),
+ );
+ return $build;
}
/**
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1119
diff -u -r1.1119 node.module
--- modules/node/node.module 5 Sep 2009 06:53:01 -0000 1.1119
+++ modules/node/node.module 6 Sep 2009 19:12:22 -0000
@@ -1955,6 +1955,18 @@
*/
function node_page_view($node) {
drupal_set_title($node->title);
+
+ // Set the node path as the canonical URL to prevent duplicate content.
+ drupal_add_link(array(
+ 'rel' => 'canonical',
+ 'href' => url('node/' . $node->nid),
+ 'header' => TRUE,
+ ));
+ drupal_add_link(array(
+ 'rel' => 'shortlink',
+ 'href' => url('node/' . $node->nid, array('alias' => TRUE)),
+ 'header' => TRUE,
+ ));
return node_show($node);
}
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.785
diff -u -r1.785 system.module
--- modules/system/system.module 5 Sep 2009 15:05:04 -0000 1.785
+++ modules/system/system.module 6 Sep 2009 19:12:42 -0000
@@ -194,12 +194,6 @@
'system_powered_by' => array(
'arguments' => array('image_path' => NULL),
),
- 'meta_generator_html' => array(
- 'arguments' => array('version' => NULL),
- ),
- 'meta_generator_header' => array(
- 'arguments' => array('version' => NULL),
- ),
'system_compact_link' => array(),
'system_run_cron_image' => array(
'arguments' => array('image_path' => NULL),
@@ -486,6 +480,10 @@
'#theme' => array('hidden'),
);
+ $type['head_tag'] = array(
+ '#theme' => 'head_tag',
+ );
+
return $type;
}
@@ -3022,25 +3020,6 @@
return $output;
}
-
-/**
- * Send Drupal and the major version number in the META GENERATOR HTML.
- *
- * @ingroup themeable
- */
-function theme_meta_generator_html($version = VERSION) {
- drupal_add_html_head('');
-}
-
-/**
- * Send Drupal and the major version number in the HTTP headers.
- *
- * @ingroup themeable
- */
-function theme_meta_generator_header($version = VERSION) {
- drupal_set_header('X-Generator', 'Drupal ' . $version . ' (http://drupal.org)');
-}
-
/**
* Implement hook_image_toolkits().
*/
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.767
diff -u -r1.767 comment.module
--- modules/comment/comment.module 5 Sep 2009 15:05:02 -0000 1.767
+++ modules/comment/comment.module 6 Sep 2009 19:12:03 -0000
@@ -349,9 +349,6 @@
$_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);
}
Index: modules/simpletest/tests/browser_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/browser_test.module,v
retrieving revision 1.1
diff -u -r1.1 browser_test.module
--- modules/simpletest/tests/browser_test.module 17 Aug 2009 06:08:47 -0000 1.1
+++ modules/simpletest/tests/browser_test.module 6 Sep 2009 19:12:25 -0000
@@ -61,7 +61,13 @@
function browser_test_refresh_meta() {
if (!isset($_GET['refresh'])) {
$url = url('browser_test/refresh/meta', array('absolute' => TRUE, 'query' => 'refresh=true'));
- drupal_add_html_head('');
+ drupal_add_html_head(array(
+ 'tag' => 'meta',
+ 'attributes' => array(
+ 'http-equiv' => 'Refresh',
+ 'content' => '0; URL=' . $url,
+ ),
+ ));
return '';
}
echo 'Refresh successful';