Index: modules/node/content_types.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v
retrieving revision 1.93
diff -u -r1.93 content_types.inc
--- modules/node/content_types.inc 10 Sep 2009 22:10:10 -0000 1.93
+++ modules/node/content_types.inc 17 Sep 2009 16:54:11 -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' => array('message')));
}
-
+
$build['node_table'] = array(
'#theme' => 'table',
'#header' => $header,
@@ -196,12 +196,22 @@
'#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' => 'checkboxes',
+ '#title' => t('Read more link'),
+ '#default_value' => variable_get('teaser_read_more_location_' . $type->type, array('inline')),
+ '#options' => array(
+ 'inline' => t('Append to summary'),
+ 'link' => t('Display in links section'),
+ ),
+ '#description' => t('Placement of Read more link(s) on the trimmed post, for linking to the full post.'),
+ );
$form['old_type'] = array(
'#type' => 'value',
'#value' => $type->type,
@@ -320,8 +330,6 @@
return;
}
- $status = node_type_save($type);
-
$variables = $form_state['values'];
// Remove everything that's been saved already - whatever's left is assumed
@@ -349,6 +357,8 @@
}
}
+ $status = node_type_save($type);
+
node_types_rebuild();
menu_rebuild();
$t_args = array('%name' => $type->name);
@@ -439,6 +449,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.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1124
diff -u -r1.1124 node.module
--- modules/node/node.module 17 Sep 2009 03:12:40 -0000 1.1124
+++ modules/node/node.module 17 Sep 2009 16:54:12 -0000
@@ -541,13 +541,16 @@
);
$field = field_create_field($field);
}
+ $summary_link = in_array('inline', variable_get('teaser_read_more_location_' . $type->type, array('inline')));
+
if (empty($instance)) {
$instance = array(
'field_name' => 'body',
'bundle' => $type->type,
'label' => $type->body_label,
'widget_type' => 'text_textarea_with_summary',
- 'settings' => array('display_summary' => TRUE),
+ 'settings' => array('display_summary' => TRUE,
+ 'read_more' => ($summary_link ? t('Read more') : '')),
// With no UI in core, we have to define default
// formatters for the teaser and full view.
@@ -564,11 +567,13 @@
),
),
);
+
field_create_instance($instance);
}
else {
$instance['label'] = $type->body_label;
$instance['settings']['display_summary'] = TRUE;
+ $instance['settings']['read_more'] = ($summary_link ? t('Read more') : '');
field_update_instance($instance);
}
}
@@ -1085,11 +1090,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 (in_array('link', variable_get('teaser_read_more_location_' . $node->type, array('inline')))) {
+ $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/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.43
diff -u -r1.43 node.test
--- modules/node/node.test 29 Aug 2009 04:16:15 -0000 1.43
+++ modules/node/node.test 17 Sep 2009 16:54:13 -0000
@@ -870,6 +870,111 @@
}
}
+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() {
+ $langcode = FIELD_LANGUAGE_NONE;
+ // Create a regular article.
+ $edit = array(
+ 'title' => $this->randomName(8),
+ "body[$langcode][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[$langcode][0][value]"] = '
';
+ for ($index = 0; $index < 10; $index++) {
+ $edit["body[$langcode][0][value]"] .= '- ' . $this->randomString(10) . '
';
+ }
+ $edit["body[$langcode][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(
+ 'teaser_read_more_location[inline]' => TRUE,
+ 'teaser_read_more_location[link]' => FALSE,
+ );
+ $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(
+ 'teaser_read_more_location[inline]' => FALSE,
+ 'teaser_read_more_location[link]' => TRUE,
+ );
+ $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 both "links"and "inline".
+ $this->drupalGet('admin/structure/node-type/article');
+ $edit = array(
+ 'teaser_read_more_location[inline]' => TRUE,
+ 'teaser_read_more_location[link]' => TRUE
+ );
+ $this->drupalPost('admin/structure/node-type/article', $edit, t('Save content type'));
+
+ // Verify that the read more link is included in the links section, and
+ // 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');
+ $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.'));
+ $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.'));
+
+ // Remove the read more links entirely
+ $this->drupalGet('admin/structure/node-type/article');
+ $edit = array(
+ 'teaser_read_more_location[inline]' => FALSE,
+ 'teaser_read_more_location[link]' => FALSE
+ );
+ $this->drupalPost(NULL, $edit, t('Save content type'));
+
+ // Verify that the read more links is not displayed.
+ $this->drupalGet('node');
+ $this->assertNoLink(t('Read more'));
+ }
+}
/**
* Test node administration page functionality.
*/
Index: modules/field/modules/text/text.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v
retrieving revision 1.28
diff -u -r1.28 text.module
--- modules/field/modules/text/text.module 11 Sep 2009 13:30:49 -0000 1.28
+++ modules/field/modules/text/text.module 17 Sep 2009 16:54:10 -0000
@@ -17,6 +17,9 @@
'text_textfield' => array(
'arguments' => array('element' => NULL),
),
+ 'read_more' => array(
+ 'arguments' => array('link' => NULL, 'inline' => FALSE),
+ ),
);
}
@@ -53,7 +56,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',
),
@@ -327,12 +330,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;
}
/**
@@ -767,6 +822,14 @@
}
/**
+ * Theme function for 'read more' link.
+ */
+function theme_read_more($link, $inline = FALSE) {
+ $element = ($inline ? 'span' : 'div');
+ return ' <'. $element .' class="read-more">' . $link . ''. $element .'>';
+}
+
+/**
* FAPI theme for an individual text elements.
*
* The textfield or textarea is already rendered by the