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 10 Aug 2009 07:15:28 -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 node 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.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 10 Aug 2009 07:15:30 -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 10 Aug 2009 07:15:28 -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,61 @@ $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'])) { + 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. + $node = $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($node->tagName, $inline_tags); + + // Create and theme the read more link. + // TODO: find a type-agnostic way to build the link. + $link = $htmlDom->createDocumentFragment(); + $link->appendXml(theme('read_more', l($instance['settings']['read_more'], "{$element['#object_type']}/{$element['#object']->nid}"), $inline)); + + // Add the read more link to the summary. + if ($inline && $node) { + $node->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. + return theme('read_more', l($instance['settings']['read_more'], "{$element['#object_type']}/{$element['#object']->nid}"), FALSE); + } } + + return $summary; } /** @@ -744,3 +796,7 @@ } } +function theme_read_more($link, $inline = FALSE) { + $element = ($inline ? 'span' : 'div'); + return ' <'. $element .' class="read-more">' . $link . ''; +} \ No newline at end of file