From 6cc19bff0e06dd4cb3db5c92c267b779bce42392 Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Thu, 19 May 2011 12:04:39 -0400
Subject: [PATCH 1/2] Issue #823380 by AlexisWilke, NancyDru, greg.harvey, pillarsdotnet, AES2, vito_swat, scito, joachim, intoxination: Better read_more handling.

---
 modules/book/book.test                 |    7 +-
 modules/field/field.module             |   35 ++++++++
 modules/field/modules/text/text.module |   25 ++++--
 modules/node/node.module               |    4 +-
 modules/node/node.test                 |  150 ++++++++++++++++++++++++++++++--
 5 files changed, 204 insertions(+), 17 deletions(-)

diff --git a/modules/book/book.test b/modules/book/book.test
index cc61778b9d321f1e4f0b27d14752d4ab7e57dc99..aa1620e0cd5131b28284c5ea31629b11dee11e91 100644
--- a/modules/book/book.test
+++ b/modules/book/book.test
@@ -164,8 +164,9 @@ class BookTestCase extends DrupalWebTestCase {
 
     // Check printer friendly version.
     $this->drupalGet('book/export/html/' . $node->nid);
-    $this->assertText($node->title, t('Printer friendly title found.'));
-    $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Printer friendly body found.'));
+    $this->assertText($node->title, t('Printer friendly title
+ found.'));
+    $this->assertRaw(trim(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format'])), t('Printer friendly body found.'));
 
     $number++;
   }
@@ -234,7 +235,7 @@ class BookTestCase extends DrupalWebTestCase {
     // Make sure each part of the book is there.
     foreach ($nodes as $node) {
       $this->assertText($node->title, t('Node title found in printer friendly version.'));
-      $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Node body found in printer friendly version.'));
+      $this->assertRaw(trim(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format'])), t('Node body found in printer friendly version.'));
     }
 
     // Make sure we can't export an unsupported format.
diff --git a/modules/field/field.module b/modules/field/field.module
index 9e03c8d911dc3dc6ef42eee599752bbc789399ed..367d1046bcf617d7df1004eb651f444bb6a9a0b0 100644
--- a/modules/field/field.module
+++ b/modules/field/field.module
@@ -1010,6 +1010,41 @@ function field_extract_bundle($entity_type, $bundle) {
 }
 
 /**
+ * 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 field_has_read_more($elements) {
+  // Early-return if the user does not have access.
+  if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
+    return FALSE;
+  }
+
+  // Return if #read_more is set on this element.
+  if (!empty($elements['#read_more']) && $elements['#read_more']) {
+    return TRUE;
+  }
+
+  // Iterate through children.
+  foreach (element_children($elements) as $key) {
+    if (field_has_read_more($elements[$key])) {
+      // A child element has #read_more set to TRUE.
+      return TRUE;
+    }
+  }
+
+  // Neither this element, nor any child elements had #read_more set.
+  return FALSE;
+}
+
+/**
  * Theme preprocess function for theme_field() and field.tpl.php.
  *
  * @see theme_field()
diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module
index 89c605cf2c046eb48d448e20d27a63ef88a8583f..2cd9383b1214a2316557e1ba5c628bdb17dd0cc3 100644
--- a/modules/field/modules/text/text.module
+++ b/modules/field/modules/text/text.module
@@ -261,24 +261,35 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la
     case 'text_default':
     case 'text_trimmed':
       foreach ($items as $delta => $item) {
-        $output = _text_sanitize($instance, $langcode, $item, 'value');
+        $output = trim(_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, '#read_more' => $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');
+          $readmore = TRUE;
+          $output = trim(_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']);
+          $output = trim(_text_sanitize($instance, $langcode, $item, 'value'));
+          $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, '#read_more' => $readmore);
       }
       break;
 
diff --git a/modules/node/node.module b/modules/node/node.module
index 524a57fa7e744d116e9306af60d7a2420f99fb3a..46c023e5e023d40d576e2aa06f8a55e02e6af2ed 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -1369,7 +1369,9 @@ 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 read more in teaser view_mode and if a field has #read_more
+  // set to TRUE.
+  if ($view_mode == 'teaser' && field_has_read_more($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
index 8a871c0c731fcf61fd5c52d0bb8e962c4e916f46..3eb6ca517066f8c3ccf107ecc4a529aa79744977 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -475,7 +475,7 @@ class NodeCreationTestCase extends DrupalWebTestCase {
   }
 
   function setUp() {
-    // Enable dummy module that implements hook_node_insert for exceptions.
+    // Enable dummy module that implements hook_node_insert() for exceptions.
     parent::setUp('node_test_exception');
 
     $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content'));
@@ -770,7 +770,7 @@ class NodeRSSContentTestCase extends DrupalWebTestCase {
   }
 
   function setUp() {
-    // Enable dummy module that implements hook_node_view.
+    // Enable dummy module that implements hook_node_view().
     parent::setUp('node_test');
 
     // Use bypass node access permission here, because the test class uses
@@ -886,7 +886,7 @@ class NodeAccessUnitTest extends DrupalWebTestCase {
 }
 
 /**
- * Test case to verify hook_node_access_records functionality.
+ * Test case to verify hook_node_access_records() functionality.
  */
 class NodeAccessRecordsUnitTest extends DrupalWebTestCase {
   public static function getInfo() {
@@ -1447,7 +1447,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
    * Tests content overview with different user permissions.
    *
    * Taxonomy filters are tested separately.
-   * @see TaxonomyNodeFilterTestCase
+   * @see TaxonomyNodeFilterTestCase()
    */
   function testContentAdminPages() {
     $this->drupalLogin($this->admin_user);
@@ -1578,7 +1578,7 @@ class NodeTitleTestCase extends DrupalWebTestCase {
     // Test <title> tag.
     $this->drupalGet("node/$node->nid");
     $xpath = '//title';
-    $this->assertEqual(current($this->xpath($xpath)), $node->title .' | Drupal', 'Page title is equal to node title.', 'Node');
+    $this->assertEqual(current($this->xpath($xpath)), $node->title . ' | Drupal', 'Page title is equal to node title.', 'Node');
 
     // Test breadcrumb in comment preview.
     $this->drupalGet("comment/reply/$node->nid");
@@ -1603,7 +1603,7 @@ class NodeFeedTestCase extends DrupalWebTestCase {
       'name' => 'Node feed',
       'description' => 'Ensures that node_feed() functions correctly.',
       'group' => 'Node',
-   );
+    );
   }
 
   /**
@@ -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.'));
+  }
+
+}
\ No newline at end of file
-- 
1.7.4.1


From bfc44aa3ed574dd209eedc090096d95e3cdabcd1 Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Thu, 19 May 2011 13:11:58 -0400
Subject: [PATCH 2/2] Issue #93854 by moonray, becw, Dave Reid, pounard, das-peter, Steven Jones, c960657: Fix autocompletion of taxonomy terms with slashes.

---
 includes/form.inc                |    2 +-
 misc/autocomplete.js             |    2 +-
 modules/taxonomy/taxonomy.module |    5 +++--
 modules/taxonomy/taxonomy.test   |   28 ++++++++++++++++++++++++++++
 4 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/includes/form.inc b/includes/form.inc
index c0e2ec7c1556098585935ecb579fd0dc203d6d73..acd26c204291c1e5aee6683349dbe1a2e40a4796 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -3636,7 +3636,7 @@ function theme_textfield($variables) {
   _form_set_class($element, array('form-text'));
 
   $extra = '';
-  if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
+  if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'] . '/dummy')) {
     drupal_add_library('system', 'drupal.autocomplete');
     $element['#attributes']['class'][] = 'form-autocomplete';
 
diff --git a/misc/autocomplete.js b/misc/autocomplete.js
index 5e85be44fc5f372e1d4a6ccc4cef2454e3be7376..da3b82074dcf399dc3fa1cd798b36bfb09e91176 100644
--- a/misc/autocomplete.js
+++ b/misc/autocomplete.js
@@ -290,7 +290,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
     // Ajax GET request for autocompletion.
     $.ajax({
       type: 'GET',
-      url: db.uri + '/' + encodeURIComponent(searchString),
+      url: db.uri + '/' + Drupal.encodePath(searchString),
       dataType: 'json',
       success: function (matches) {
         if (typeof matches.status == 'undefined' || matches.status != 0) {
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index 50d2fd6083407883c271f9567bb5b14a28b79c72..385e5e3fb0e7089047d57b1d55817fcafdb0254b 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -312,14 +312,15 @@ function taxonomy_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'taxonomy.pages.inc',
   );
-  $items['taxonomy/autocomplete'] = array(
+  $items['taxonomy/autocomplete/%/%menu_tail'] = array(
     'title' => 'Autocomplete taxonomy',
     'page callback' => 'taxonomy_autocomplete',
+    'page arguments' => array(2, 3),
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
     'file' => 'taxonomy.pages.inc',
+    'load arguments' => array('%map', '%index'),
   );
-
   $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name'] = array(
     'title callback' => 'taxonomy_admin_vocabulary_title_callback',
     'title arguments' => array(3),
diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test
index 1fd47f5ea990ddd8811b8fca0c8053f8a72de04c..6bb12a7763b2df354b670cb7136351d5df3dfde6 100644
--- a/modules/taxonomy/taxonomy.test
+++ b/modules/taxonomy/taxonomy.test
@@ -600,6 +600,34 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase {
   }
 
   /**
+   * Test term autocompletion edge cases.
+   */
+  function testTermAutocompletion() {
+    $base = $this->randomName(2);
+    // Add a term with a slash in the name.
+    $first_term = $this->createTerm($this->vocabulary);
+    $first_term->name = $base . '/' . $this->randomName();
+    taxonomy_term_save($first_term);
+    // Add another term that matches the first through the slash character.
+    $search_term = $this->createTerm($this->vocabulary);
+    $search_term->name = $base . '/' . $this->randomName();
+    taxonomy_term_save($search_term);
+
+    // Try to autocomplete a term name that contains a slash.
+    // We should only get a single term returned.
+    $input = substr($search_term->name, 0, 5);
+    $url = 'taxonomy/autocomplete/taxonomy_';
+    $url .= $this->vocabulary->machine_name . '/' . $input;
+    $this->drupalGet($url);
+    $target = array($search_term->name => check_plain($search_term->name));
+    $message = t(
+      'Autocomplete returns term %term_name after typing the first 4 letters, including a slash in the name.',
+      array('%term_name' => $search_term->name)
+    );
+    $this->assertRaw(drupal_json_encode($target), $message);
+  }
+
+  /**
    * Save, edit and delete a term using the user interface.
    */
   function testTermInterface() {
-- 
1.7.4.1

