Index: modules/node/content_types.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v
retrieving revision 1.85
diff -u -r1.85 content_types.inc
--- modules/node/content_types.inc 9 Aug 2009 01:15:33 -0000 1.85
+++ modules/node/content_types.inc 17 Aug 2009 09:24:03 -0000
@@ -16,7 +16,7 @@
$rows = array();
foreach ($names as $key => $name) {
- $type = $types[$key];
+ $type = $types[$key];
if (node_hook($type->type, 'form')) {
$type_url_str = str_replace('_', '-', $type->type);
$row = array(theme('node_admin_overview', $name, $type));
@@ -37,7 +37,7 @@
if (empty($rows)) {
$rows[] = array(array('data' => t('No content types available. Add content type.', array('@link' => url('admin/structure/types/add'))), 'colspan' => '5', 'class' => 'message'));
}
-
+
$build['node_table'] = array(
'#theme' => 'table',
'#header' => $header,
@@ -180,12 +180,23 @@
'#description' => t('Enable the submitted by Username on date text.'),
);
$form['display']['teaser_length'] = array(
- '#type' => 'select',
+ '#type' => 'select',
'#title' => t('Length of trimmed posts'),
'#default_value' => variable_get('teaser_length_' . $type->type, 600),
'#options' => drupal_map_assoc(array(0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000), '_node_characters'),
'#description' => t("The maximum number of characters used in the trimmed version of content.")
);
+ $form['display']['teaser_read_more_location'] = array(
+ '#type' => 'radios',
+ '#title' => t('Read more link'),
+ '#default_value' => variable_get('teaser_read_more_location_' . $type->type, 'inline'),
+ '#options' => array(
+ 'hidden' => t('Disabled'),
+ 'inline' => t('Append to summary'),
+ 'link' => t('Display in links section'),
+ ),
+ '#description' => t('Determine the location of "read more" links which allow a user to click through to the full post from the trimmed post.'),
+ );
$form['old_type'] = array(
'#type' => 'value',
'#value' => $type->type,
@@ -304,8 +315,6 @@
return;
}
- $status = node_type_save($type);
-
$variables = $form_state['values'];
// Remove everything that's been saved already - whatever's left is assumed
@@ -333,6 +342,8 @@
}
}
+ $status = node_type_save($type);
+
node_types_rebuild();
$t_args = array('%name' => $type->name);
@@ -409,6 +420,7 @@
node_type_delete($form_state['values']['type']);
variable_del('teaser_length_' . $form_state['values']['type']);
+ variable_del('teaser_read_more_location_' . $form_state['values']['type']);
variable_del('node_preview_' . $form_state['values']['type']);
$t_args = array('%name' => $form_state['values']['name']);
drupal_set_message(t('The content type %name has been deleted.', $t_args));
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.38
diff -u -r1.38 node.test
--- modules/node/node.test 28 Jul 2009 19:18:06 -0000 1.38
+++ modules/node/node.test 17 Aug 2009 09:24:05 -0000
@@ -840,3 +840,80 @@
$this->assertText(t('Content permissions have been rebuilt.'));
}
}
+
+class SummaryReadMoreTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Summary read more',
+ 'description' => 'Check the presence and placement of read more links in summaries',
+ 'group' => 'Node',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $web_user = $this->drupalCreateUser(array('create article content','administer content types'));
+ $this->drupalLogin($web_user);
+ $this->web_user = $web_user;
+ }
+
+ function testSummaryReadMore() {
+ // Create a regular article.
+ $edit = array();
+ $edit['title'] = $this->randomName(8);
+ $edit['body[0][value]'] = $this->randomName(16);
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ // Create an article containing just an unordered list. Since
+ // the read more link will only be added inline to paragraphs, this
+ // allows us to test how the read more link is inserted when the
+ // summary does not end with a paragraph tag.
+ $edit = array();
+ $edit['body[0][value]'] = '
';
+ for ($index = 0; $index < 10; $index++) {
+ $edit['body[0][value]'] .= '- ' . $this->randomString(10) . '
';
+ }
+ $edit['body[0][value]'] .= '
';
+
+ $edit['title'] = $this->randomName(8);
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ // Change the read more link placement to "inline".
+ $this->drupalGet('admin/structure/node-type/article');
+ $edit = array();
+ $edit['teaser_read_more_location'] = 'inline';
+ $this->drupalPost(NULL, $edit, t('Save content type'));
+
+ // Verify that the link was added at the end of the summary, either as a span,
+ // in case the summary ends with a paragraph, or as a div, in case the summary
+ // ends with any other element.
+ $this->drupalGet('node');
+ $read_more_node = $this->xpath('//div[@id="node-1"]//p[last()]/span[@class="read-more"]');
+ $this->assertTrue(is_array($read_more_node) && count($read_more_node) == 1, t('Read more link was added inline as a span to the summary.'));
+ $read_more_node = $this->xpath('//div[@id="node-2"]//div[@class="read-more"]');
+ $this->assertTrue(is_array($read_more_node) && count($read_more_node) == 1, t('Read more link was added inline as a div to the summary.'));
+
+ // Change the read more link placement to "links" instead of "inline".
+ $this->drupalGet('admin/structure/node-type/article');
+ $edit = array();
+ $edit['teaser_read_more_location'] = 'link';
+ $this->drupalPost('admin/structure/node-type/article', $edit, t('Save content type'));
+
+ // Verify that the read more link is included in the links section.
+ $this->drupalGet('node');
+ $this->assertLink(t('Read more'));
+ $read_more_node = $this->xpath('//div[@id="node-1"]//div[@class="links"]//ul/li/a[text()="'. t('Read more').'"]');
+ $this->assertTrue(is_array($read_more_node) && count($read_more_node) == 1, t('Read more link was added to the links section.'));
+
+ // Change the read more link placement to "hidden".
+ $this->drupalGet('admin/structure/node-type/article');
+ $edit = array();
+ $edit['teaser_read_more_location'] = 'hidden';
+ $this->drupalPost(NULL, $edit, t('Save content type'));
+
+ // Verify that the read more link is not displayed.
+ $this->drupalGet('node');
+ $this->assertNoLink(t('Read more'));
+ }
+}
\ No newline at end of file
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1095
diff -u -r1.1095 node.module
--- modules/node/node.module 8 Aug 2009 22:52:59 -0000 1.1095
+++ modules/node/node.module 17 Aug 2009 09:24:05 -0000
@@ -469,6 +469,7 @@
);
$field = field_create_field($field);
}
+
if (empty($instance)) {
$instance = array(
'field_name' => 'body',
@@ -492,11 +493,17 @@
),
),
);
+
+ $summary_link = variable_get('teaser_read_more_location_' . $type->type, 'inline') == 'inline';
+ $instance['settings']['read_more'] = ($summary_link ? t('Read more') : '');
+
field_create_instance($instance);
}
else {
$instance['label'] = $type->body_label;
$instance['settings']['display_summary'] = TRUE;
+ $summary_link = variable_get('teaser_read_more_location_' . $type->type, 'inline') == 'inline';
+ $instance['settings']['read_more'] = ($summary_link ? t('Read more') : '');
field_update_instance($instance);
}
}
@@ -1145,11 +1152,13 @@
// to know when a teaser view is different than a full view.
$links = array();
if ($build_mode == 'teaser') {
- $links['node_readmore'] = array(
- 'title' => t('Read more'),
- 'href' => 'node/' . $node->nid,
- 'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title))
- );
+ if (variable_get('teaser_read_more_location_' . $node->type, 'inline') == 'link') {
+ $links['node_readmore'] = array(
+ 'title' => t('Read more'),
+ 'href' => 'node/' . $node->nid,
+ 'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title))
+ );
+ }
}
$node->content['links']['node'] = array(
'#theme' => 'links',
Index: modules/field/modules/text/text.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v
retrieving revision 1.17
diff -u -r1.17 text.module
--- modules/field/modules/text/text.module 4 Aug 2009 06:38:56 -0000 1.17
+++ modules/field/modules/text/text.module 17 Aug 2009 09:24:02 -0000
@@ -32,6 +32,9 @@
'field_formatter_text_summary_or_trimmed' => array(
'arguments' => array('element' => NULL),
),
+ 'read_more' => array(
+ 'arguments' => array('link' => NULL, 'inline' => FALSE),
+ ),
);
}
@@ -68,7 +71,7 @@
'label' => t('Long text and summary'),
'description' => t('This field stores long text in the database along with optional summary text.'),
'settings' => array('max_length' => ''),
- 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
+ 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0, 'read_more' => ''),
'default_widget' => 'text_textarea_with_summary',
'default_formatter' => 'text_summary_or_trimmed',
),
@@ -302,12 +305,64 @@
$instance = field_info_instance($element['#field_name'], $element['#bundle']);
if (!empty($element['#item']['safe_summary'])) {
- return $element['#item']['safe_summary'];
+ $summary = $element['#item']['safe_summary'];
}
else {
$size = variable_get('teaser_length_' . $element['#bundle'], 600);
- return text_summary($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL, $size);
+ $summary = text_summary($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL, $size);
}
+
+ // If enabled, add a "Read more" link to the end of the summary.
+ if (!empty($instance['settings']['read_more'])) {
+ list($id, $vid, $bundle) = field_attach_extract_ids($element['#object_type'], $element['#object']);
+
+ if ($summary) {
+ // Define the tags that allow the read more link to be inserted right before their
+ // closing tag. If the summary ends with any other tag, the read more link will be
+ // appended at the end of the summary instead.
+ $inline_tags = array('p');
+
+ $htmlDom = DOMDocument::loadHTML($summary);
+
+ // The result of DOMDocument::saveXML($bodyNode) is a partial (X)HTML document.
+ // We only need what is inside the body tag.
+ $bodyNode = $htmlDom->getElementsByTagName('body')->item(0);
+
+ // Fetch the last element in the summary.
+ $lastNode = $bodyNode->lastChild;
+
+ // Determine how to insert the read more link into the summary. Inline means the
+ // the link will be inserted before the ending tag of the last element in the summary,
+ // otherwise the link will be appended to the summary as a stand-alone element.
+ $inline = in_array($lastNode->tagName, $inline_tags);
+
+ // Create and theme the read more link.
+ // @todo: Once #525622 lands, use the new API to build the link to the object.
+ $link = $htmlDom->createDocumentFragment();
+ $link->appendXml(theme('read_more', l($instance['settings']['read_more'], "{$element['#object_type']}/$id"), $inline));
+
+ // Add the read more link to the summary.
+ if ($inline && $lastNode) {
+ $lastNode->appendChild($link);
+ }
+ else {
+ $bodyNode->appendChild($link);
+ }
+
+ // DOM manipulation treats our fragment as a full document. Extract the contents of
+ // the element, as we are only interested in that.
+ if (preg_match("|^]*>(.*)$|s", $htmlDom->saveXML($bodyNode), $matches)) {
+ $summary = $matches[1];
+ }
+ }
+ else {
+ // The summary is empty, return the read more link.
+ // @todo: Once #525622 lands, use the new API to build the link to the object.
+ return theme('read_more', l($instance['settings']['read_more'], "{$element['#object_type']}/$id"), FALSE);
+ }
+ }
+
+ return $summary;
}
/**
@@ -744,3 +799,7 @@
}
}
+function theme_read_more($link, $inline = FALSE) {
+ $element = ($inline ? 'span' : 'div');
+ return ' <'. $element .' class="read-more">' . $link . ''. $element .'>';
+}
\ No newline at end of file