From 686243c671cd7e2c4b47f432f4f4b704d94e7255 Mon Sep 17 00:00:00 2001
From: HollyIT <jamie@hollyit.net>
Date: Tue, 15 Mar 2011 10:37:31 -0400
Subject: [PATCH] Issue #823380 by AE52, joachim, Intoxination: Only show readmore link on nodes with more text

---
 fix-readmore-link-823380-78.patch      |  321 ++++++++++++++++++++++++++++++++
 modules/field/modules/text/text.module |   49 +++++-
 modules/node/node.module               |    5 +-
 modules/node/node.test                 |  138 ++++++++++++++
 4 files changed, 508 insertions(+), 5 deletions(-)
 create mode 100644 fix-readmore-link-823380-78.patch
 mode change 100644 => 100755 modules/field/modules/text/text.module
 mode change 100644 => 100755 modules/node/node.module
 mode change 100644 => 100755 modules/node/node.test

diff --git a/fix-readmore-link-823380-78.patch b/fix-readmore-link-823380-78.patch
new file mode 100644
index 0000000..199c6d0
--- /dev/null
+++ b/fix-readmore-link-823380-78.patch
@@ -0,0 +1,321 @@
+From 68fa98e12c959554b4aeab4a51d1de20f9623c8c Mon Sep 17 00:00:00 2001
+From: HollyIT <jamie@hollyit.net>
+Date: Tue, 15 Mar 2011 10:27:50 -0400
+Subject: [PATCH] Issue #823380 by AE52, joachim, Intoxination: Only show readmore link on nodes with more text
+
+---
+ modules/field/modules/text/text.module |   49 ++++++++-
+ modules/node/node.module               |    5 +-
+ modules/node/node.test                 |  194 ++++++++++++++++++++++++++++++++
+ 3 files changed, 243 insertions(+), 5 deletions(-)
+ mode change 100644 => 100755 modules/field/modules/text/text.module
+ mode change 100644 => 100755 modules/node/node.module
+ mode change 100644 => 100755 modules/node/node.test
+
+diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module
+old mode 100644
+new mode 100755
+index 89c605c..1af2f01
+--- a/modules/field/modules/text/text.module
++++ b/modules/field/modules/text/text.module
+@@ -262,23 +262,33 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la
+     case 'text_trimmed':
+       foreach ($items as $delta => $item) {
+         $output = _text_sanitize($instance, $langcode, $item, 'value');
++        $readmore = FALSE;
+         if ($display['type'] == 'text_trimmed') {
+-          $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
++          $trimmed_output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
++          if ($trimmed_output != $output){
++            $readmore = TRUE;
++          }
++          $output = $trimmed_output;
+         }
+-        $element[$delta] = array('#markup' => $output);
++        $element[$delta] = array('#markup' => $output, '#readmore' => $readmore);
+       }
+       break;
+ 
+     case 'text_summary_or_trimmed':
+       foreach ($items as $delta => $item) {
++        $readmore = FALSE;
+         if (!empty($item['summary'])) {
+           $output = _text_sanitize($instance, $langcode, $item, 'summary');
+         }
+         else {
+           $output = _text_sanitize($instance, $langcode, $item, 'value');
+-          $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
++          $trimmed_output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
++          if ($trimmed_output != $output){
++            $readmore = TRUE;
++          }
++          $output = $trimmed_output;
+         }
+-        $element[$delta] = array('#markup' => $output);
++        $element[$delta] = array('#markup' => $output, '#readmore' => $readmore);
+       }
+       break;
+ 
+@@ -445,6 +455,37 @@ function text_summary($text, $format = NULL, $size = NULL) {
+ }
+ 
+ /**
++ * Check if any fields should trigger a readmore.
++ *
++ * This will parse through all renderable fields in the renederable array
++ * and check for #readmore property. If #readmore is TRUE, then we will
++ * return TRUE to signify that the caller should include a readmore link.
++ *
++ * @param $elements
++ *   An array of renderable fields
++ * @return
++ *   True if a single element has #readmore set to TRUE, otherwise FALSE.
++ */
++
++function text_has_readmore($elements){
++  foreach ($elements as $element){
++    if (isset($element['#access']) && $element['#access'] == 1){
++      foreach ($element as $key=>$value){
++        // check to see if the key is a property or child. We want children only here
++        if ($key === '' || $key[0] !== '#') {
++          if (isset($value['#readmore']) && $value['#readmore'] == TRUE){
++            // We have found a #readmore set to true. Since this is only needed once
++            // we can go ahead and return TRUE and stop looping through the elements.
++            return TRUE;
++          }
++        }
++      }
++    }
++  }
++  return FALSE;
++}
++
++/**
+  * Implements hook_field_widget_info().
+  */
+ function text_field_widget_info() {
+diff --git a/modules/node/node.module b/modules/node/node.module
+old mode 100644
+new mode 100755
+index c052f4b..3991a5f
+--- a/modules/node/node.module
++++ b/modules/node/node.module
+@@ -1370,7 +1370,10 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
+     '#pre_render' => array('drupal_pre_render_links'),
+     '#attributes' => array('class' => array('links', 'inline')),
+   );
+-  if ($view_mode == 'teaser') {
++
++  // Only show readmore in teaser view_mode and if a field has #readmore
++  // set to TRUE.
++  if ($view_mode == 'teaser' && text_has_readmore($node->content)) {
+     $node_title_stripped = strip_tags($node->title);
+     $links['node-readmore'] = array(
+       'title' => t('Read more<span class="element-invisible"> about @title</span>', array('@title' => $node_title_stripped)),
+diff --git a/modules/node/node.test b/modules/node/node.test
+old mode 100644
+new mode 100755
+index 8a871c0..34515bb
+--- a/modules/node/node.test
++++ b/modules/node/node.test
+@@ -2146,3 +2146,197 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
+     }
+   }
+ }
++
++/**
++ * Test the "Read more" link for teasers.
++ */
++class NodeTeaserReadMoreTest extends DrupalWebTestCase {
++
++  public static function getInfo() {
++    return array(
++      'name' => 'Teaser read more',
++      'description' => 'Test the "Read more" link for teasers.',
++      'group' => 'Node',
++    );
++  }
++
++  function setUp() {
++    parent::setUp();
++
++    $web_user = $this->drupalCreateUser(array(
++      'create article content',
++      'create page content',
++      'administer filters',
++      filter_permission_name(filter_format_load('filtered_html')),
++      filter_permission_name(filter_format_load('full_html')),
++      'administer content types',
++      'access administration pages',
++      'bypass node access',
++      'administer taxonomy',
++      'administer nodes',
++    ));
++    $this->drupalLogin($web_user);
++  }
++
++  /**
++   * Create a node where teaser and full view are equal. The "Read more" link
++   * must not appear.
++   */
++  function testTeaserIsComplete() {
++    $node1 = $this->drupalCreateNode(array(
++      'type' => 'article',
++      'promote' => 1,
++      'body' => array(
++        LANGUAGE_NONE => array(
++          '0' => array(
++            'value' => 'body_' . $this->randomName(32),
++          ),
++        ),
++      ),
++    ) );
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
++    // Confirm that promoted node appears without the read more link in the
++    // default node listing.
++    $this->assertNoText('Read more', t('"Read more" does not appear in the default listing.'));
++  }
++
++  /**
++   * Create a node with a tag where teaser and full view are equal. The
++   * "Read more" link must not appear.
++   */
++  function testTeaserIsCompleteWithTag() {
++    // Post an article with a taxonomy term.
++    $langcode = LANGUAGE_NONE;
++    $tag = 'tag_' . $this->randomName(8);
++    $edit = array();
++    $edit['title'] = 'title_' . $this->randomName(8);
++    $edit["body[$langcode][0][value]"] = 'body_' . $this->randomName(32);
++    $edit["field_tags[$langcode]"] = $tag;
++    $edit['promote'] = 1;
++    $this->drupalPost('node/add/article', $edit, t('Save'));
++    $node1 = $this->drupalGetNodeByTitle($edit['title']);
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
++    $this->assertText($tag, t('Tag appears on the default listing.'));
++    // Confirm that promoted node appears without the read more link in the
++    // default node listing.
++    $this->assertNoText('Read more', t('"Read more" does not appear in the default listing.'));
++  }
++
++  /**
++   * Hide tags field in teaser and create a node without tags. The "Read more"
++   * link must not appear.
++   */
++  function testTeaserHiddenFieldEmpty() {
++    // Hide the tags in teaser.
++    $edit = array('fields[field_tags][type]' => 'hidden');
++    $this->drupalPost('admin/structure/types/manage/article/display/teaser', $edit, t('Save'));
++
++    // Post an article without a taxonomy term.
++    $langcode = LANGUAGE_NONE;
++    $tag = 'tag_' . $this->randomName(8);
++    $edit = array();
++    $edit['title'] = 'title_' . $this->randomName(8);
++    $edit["body[$langcode][0][value]"] = 'body_' . $this->randomName(32);
++    $edit['promote'] = 1;
++    $this->drupalPost('node/add/article', $edit, t('Save'));
++    $node1 = $this->drupalGetNodeByTitle($edit['title']);
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
++    // Confirm that promoted node does not appear with the read more link in the
++    // default node listing.
++    $this->assertNoText('Read more', t('"Read more" does not appear in the default listing.'));
++  }
++
++  /**
++   * Hide tags field in teaser and create a node with tags. The "Read more"
++   * link must be set.
++   */
++  function testTeaserHiddenFieldFilled() {
++    // Hide the tags in teaser.
++    $edit = array('fields[field_tags][type]' => 'hidden');
++    $this->drupalPost('admin/structure/types/manage/article/display/teaser', $edit, t('Save'));
++
++    // Post an article with a taxonomy term.
++    $langcode = LANGUAGE_NONE;
++    $tag = 'tag_' . $this->randomName(8);
++    $edit = array();
++    $edit['title'] = 'title_' . $this->randomName(8);
++    $edit["body[$langcode][0][value]"] = 'body_' . $this->randomName(32);
++    $edit["field_tags[$langcode]"] = $tag;
++    $edit['promote'] = 1;
++    $this->drupalPost('node/add/article', $edit, t('Save'));
++    $node1 = $this->drupalGetNodeByTitle($edit['title']);
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
++    $this->assertNoText($tag, t('Tag does not appear on the default listing.'));
++    // Confirm that promoted node appears with the read more link in the
++    // default node listing.
++    $this->assertText('Read more', t('"Read more" appears in the default listing.'));
++  }
++
++  /**
++   * Create a node with a summary. The "Read more" link must be set.
++   */
++  function testTeaserSummary() {
++    $body = 'body_' . $this->randomName(32);
++    $summary = 'summary_' . $this->randomName(32);
++    $node1 = $this->drupalCreateNode(array(
++      'type' => 'article',
++      'promote' => 1,
++      'body' => array(
++        LANGUAGE_NONE => array(
++          '0' => array(
++            'value' => $body,
++            'summary' => $summary,
++          ),
++        ),
++      ),
++    ) );
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText($summary, t('The summary text appears in the default listing.'));
++    $this->assertNoText($body, t('The body text does not appear in the default listing.'));
++    // Confirm that promoted node appears with the read more link in the
++    // default node listing.
++    $this->assertText('Read more', t('"Read more" appears in the default listing.'));
++  }
++
++  /**
++   * Create a node with trimmed body. The "Read more" link must be set.
++   */
++  function testTeaserTrimmed() {
++    $node1 = $this->drupalCreateNode(array(
++      'type' => 'article',
++      'promote' => 1,
++      'body' => array(
++        LANGUAGE_NONE => array(
++          '0' => array(
++            'value' => 'teaser<!--break-->body',
++            // Set text format to Full HTML due to bug http://drupal.org/node/881006
++            'format' => 'full_html',
++          ),
++        ),
++      ),
++    ) );
++
++    $this->drupalGet('node');
++    $this->assertText($node1->title, t('Node title appears on the default listing.'));
++    $this->assertText('teaser', t('The teaser text appears in the default listing.'));
++    $this->assertNoText('body', t('The body text does not appear in the default listing.'));
++    // Confirm that promoted node appears with the read more link in the
++    // default node listing.
++    $this->assertText('Read more', t('"Read more" appears in the default listing.'));
++  }
++
++}
+-- 
+1.7.3
+
diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module
old mode 100644
new mode 100755
index 89c605c..1af2f01
--- a/modules/field/modules/text/text.module
+++ b/modules/field/modules/text/text.module
@@ -262,23 +262,33 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la
     case 'text_trimmed':
       foreach ($items as $delta => $item) {
         $output = _text_sanitize($instance, $langcode, $item, 'value');
+        $readmore = FALSE;
         if ($display['type'] == 'text_trimmed') {
-          $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+          $trimmed_output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+          if ($trimmed_output != $output){
+            $readmore = TRUE;
+          }
+          $output = $trimmed_output;
         }
-        $element[$delta] = array('#markup' => $output);
+        $element[$delta] = array('#markup' => $output, '#readmore' => $readmore);
       }
       break;
 
     case 'text_summary_or_trimmed':
       foreach ($items as $delta => $item) {
+        $readmore = FALSE;
         if (!empty($item['summary'])) {
           $output = _text_sanitize($instance, $langcode, $item, 'summary');
         }
         else {
           $output = _text_sanitize($instance, $langcode, $item, 'value');
-          $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+          $trimmed_output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+          if ($trimmed_output != $output){
+            $readmore = TRUE;
+          }
+          $output = $trimmed_output;
         }
-        $element[$delta] = array('#markup' => $output);
+        $element[$delta] = array('#markup' => $output, '#readmore' => $readmore);
       }
       break;
 
@@ -445,6 +455,37 @@ function text_summary($text, $format = NULL, $size = NULL) {
 }
 
 /**
+ * Check if any fields should trigger a readmore.
+ *
+ * This will parse through all renderable fields in the renederable array
+ * and check for #readmore property. If #readmore is TRUE, then we will
+ * return TRUE to signify that the caller should include a readmore link.
+ *
+ * @param $elements
+ *   An array of renderable fields
+ * @return
+ *   True if a single element has #readmore set to TRUE, otherwise FALSE.
+ */
+
+function text_has_readmore($elements){
+  foreach ($elements as $element){
+    if (isset($element['#access']) && $element['#access'] == 1){
+      foreach ($element as $key=>$value){
+        // check to see if the key is a property or child. We want children only here
+        if ($key === '' || $key[0] !== '#') {
+          if (isset($value['#readmore']) && $value['#readmore'] == TRUE){
+            // We have found a #readmore set to true. Since this is only needed once
+            // we can go ahead and return TRUE and stop looping through the elements.
+            return TRUE;
+          }
+        }
+      }
+    }
+  }
+  return FALSE;
+}
+
+/**
  * Implements hook_field_widget_info().
  */
 function text_field_widget_info() {
diff --git a/modules/node/node.module b/modules/node/node.module
old mode 100644
new mode 100755
index c052f4b..3991a5f
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -1370,7 +1370,10 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
     '#pre_render' => array('drupal_pre_render_links'),
     '#attributes' => array('class' => array('links', 'inline')),
   );
-  if ($view_mode == 'teaser') {
+
+  // Only show readmore in teaser view_mode and if a field has #readmore
+  // set to TRUE.
+  if ($view_mode == 'teaser' && text_has_readmore($node->content)) {
     $node_title_stripped = strip_tags($node->title);
     $links['node-readmore'] = array(
       'title' => t('Read more<span class="element-invisible"> about @title</span>', array('@title' => $node_title_stripped)),
diff --git a/modules/node/node.test b/modules/node/node.test
old mode 100644
new mode 100755
index 8a871c0..5282f66
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -2146,3 +2146,141 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
     }
   }
 }
+
+/**
+ * Test the "Read more" link for teasers.
+ */
+class NodeTeaserReadMoreTest extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Teaser read more',
+      'description' => 'Test the "Read more" link for teasers.',
+      'group' => 'Node',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $web_user = $this->drupalCreateUser(array(
+      'create article content',
+      'create page content',
+      'administer filters',
+      filter_permission_name(filter_format_load('filtered_html')),
+      filter_permission_name(filter_format_load('full_html')),
+      'administer content types',
+      'access administration pages',
+      'bypass node access',
+      'administer taxonomy',
+      'administer nodes',
+    ));
+    $this->drupalLogin($web_user);
+  }
+
+  /**
+   * Create a node where teaser and full view are equal. The "Read more" link
+   * must not appear.
+   */
+  function testTeaserIsComplete() {
+    $node1 = $this->drupalCreateNode(array(
+      'type' => 'article',
+      'promote' => 1,
+      'body' => array(
+        LANGUAGE_NONE => array(
+          '0' => array(
+            'value' => 'body_' . $this->randomName(32),
+          ),
+        ),
+      ),
+    ) );
+
+    $this->drupalGet('node');
+    $this->assertText($node1->title, t('Node title appears on the default listing.'));
+    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
+    // Confirm that promoted node appears without the read more link in the
+    // default node listing.
+    $this->assertNoText('Read more', t('"Read more" does not appear in the default listing.'));
+  }
+
+  /**
+   * Create a node with a tag where teaser and full view are equal. The
+   * "Read more" link must not appear.
+   */
+  function testTeaserIsCompleteWithTag() {
+    // Post an article with a taxonomy term.
+    $langcode = LANGUAGE_NONE;
+    $tag = 'tag_' . $this->randomName(8);
+    $edit = array();
+    $edit['title'] = 'title_' . $this->randomName(8);
+    $edit["body[$langcode][0][value]"] = 'body_' . $this->randomName(32);
+    $edit["field_tags[$langcode]"] = $tag;
+    $edit['promote'] = 1;
+    $this->drupalPost('node/add/article', $edit, t('Save'));
+    $node1 = $this->drupalGetNodeByTitle($edit['title']);
+
+    $this->drupalGet('node');
+    $this->assertText($node1->title, t('Node title appears on the default listing.'));
+    $this->assertText($node1->body['und'][0]['value'], t('Node body appears on the default listing.'));
+    $this->assertText($tag, t('Tag appears on the default listing.'));
+    // Confirm that promoted node appears without the read more link in the
+    // default node listing.
+    $this->assertNoText('Read more', t('"Read more" does not appear in the default listing.'));
+  }
+
+  /**
+   * Create a node with a summary. The "Read more" link must be set.
+   */
+  function testTeaserSummary() {
+    $body = 'body_' . $this->randomName(32);
+    $summary = 'summary_' . $this->randomName(32);
+    $node1 = $this->drupalCreateNode(array(
+      'type' => 'article',
+      'promote' => 1,
+      'body' => array(
+        LANGUAGE_NONE => array(
+          '0' => array(
+            'value' => $body,
+            'summary' => $summary,
+          ),
+        ),
+      ),
+    ) );
+
+    $this->drupalGet('node');
+    $this->assertText($node1->title, t('Node title appears on the default listing.'));
+    $this->assertText($summary, t('The summary text appears in the default listing.'));
+    $this->assertNoText($body, t('The body text does not appear in the default listing.'));
+    // Confirm that promoted node appears with the read more link in the
+    // default node listing.
+    $this->assertText('Read more', t('"Read more" appears in the default listing.'));
+  }
+
+  /**
+   * Create a node with trimmed body. The "Read more" link must be set.
+   */
+  function testTeaserTrimmed() {
+    $node1 = $this->drupalCreateNode(array(
+      'type' => 'article',
+      'promote' => 1,
+      'body' => array(
+        LANGUAGE_NONE => array(
+          '0' => array(
+            'value' => 'teaser<!--break-->body',
+            // Set text format to Full HTML due to bug http://drupal.org/node/881006
+            'format' => 'full_html',
+          ),
+        ),
+      ),
+    ) );
+
+    $this->drupalGet('node');
+    $this->assertText($node1->title, t('Node title appears on the default listing.'));
+    $this->assertText('teaser', t('The teaser text appears in the default listing.'));
+    $this->assertNoText('body', t('The body text does not appear in the default listing.'));
+    // Confirm that promoted node appears with the read more link in the
+    // default node listing.
+    $this->assertText('Read more', t('"Read more" appears in the default listing.'));
+  }
+
+}
-- 
1.7.3

