diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-06-12 08:39:40 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-06-12 08:39:40 +0000 |
commit | 3d64cb5ecae7c0d093e1343f87901769dc7d819e (patch) | |
tree | 765b3104ae2bbdf96ac677f8deab9b5457ffa4bf /modules/node | |
parent | bfdea95337376b00e60049b640c076e8ab32293f (diff) | |
download | brdo-3d64cb5ecae7c0d093e1343f87901769dc7d819e.tar.gz brdo-3d64cb5ecae7c0d093e1343f87901769dc7d819e.tar.bz2 |
- Patch #372743 by bjaspan, yched, KarenS, catch et al: node body and teasers as fields. Oh, my.
Diffstat (limited to 'modules/node')
-rw-r--r-- | modules/node/node.api.php | 3 | ||||
-rw-r--r-- | modules/node/node.install | 134 | ||||
-rw-r--r-- | modules/node/node.module | 355 | ||||
-rw-r--r-- | modules/node/node.pages.inc | 82 | ||||
-rw-r--r-- | modules/node/node.test | 187 |
5 files changed, 243 insertions, 518 deletions
diff --git a/modules/node/node.api.php b/modules/node/node.api.php index d3058a57d..66630b7d0 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -204,7 +204,7 @@ function hook_node_grants_alter(&$grants, $account, $op) { // Get our list of banned roles. $restricted = variable_get('example_restricted_roles', array()); - + if ($op != 'view' && !empty($restricted)) { // Now check the roles for this account against the restrictions. foreach ($restricted as $role_id) { @@ -904,7 +904,6 @@ function hook_view($node, $teaser = FALSE) { menu_set_location($breadcrumb); } - $node = node_prepare($node, $teaser); $node->content['myfield'] = array( '#value' => theme('mymodule_myfield', $node->myfield), '#weight' => 1, diff --git a/modules/node/node.install b/modules/node/node.install index 9ec782dea..72c82481a 100644 --- a/modules/node/node.install +++ b/modules/node/node.install @@ -213,18 +213,6 @@ function node_schema() { 'not null' => TRUE, 'default' => '', ), - 'body' => array( - 'description' => 'The body of this version.', - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - ), - 'teaser' => array( - 'description' => 'The teaser of this version.', - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - ), 'log' => array( 'description' => 'The log entry explaining the changes in this version.', 'type' => 'text', @@ -237,12 +225,6 @@ function node_schema() { 'not null' => TRUE, 'default' => 0, ), - 'format' => array( - 'description' => "The text format used by this version's body.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), ), 'indexes' => array( 'nid' => array('nid'), @@ -304,7 +286,7 @@ function node_schema() { 'default' => '', ), 'has_body' => array( - 'description' => 'Boolean indicating whether this type uses the {node_revision}.body field.', + 'description' => 'Boolean indicating whether this type has the body field attached.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, @@ -416,6 +398,7 @@ function node_update_7004() { // Map old preview setting to new values order. $original_preview ? $original_preview = 2 : $original_preview = 1; + $node_types_clear(); $type_list = node_type_get_types(); // Apply original settings to all types. @@ -430,6 +413,119 @@ function node_update_7004() { } /** + * Convert body and teaser from node properties to fields. + */ +function node_update_7005(&$context) { + $ret = array('#finished' => 0); + + // Get node type info for every invocation. + node_type_clear(); + $node_types = node_type_get_types(); + $body_types = array(); + foreach ($node_types as $type => $info) { + if ($info->has_body) { + $body_types[] = $type; + } + } + + if (!isset($context['total'])) { + // Initial invocation. + + // Re-save node types to create body field instances. + foreach ($node_types as $type => $info) { + if ($info->has_body) { + node_type_save($info); + } + } + + // Initialize state for future calls. + $context['last'] = 0; + $context['count'] = 0; + + $query = db_select('node', 'n'); + $query->join('node_revision', 'nr', 'n.vid = nr.vid'); + $query->condition('n.type', $body_types, 'IN'); + $context['total'] = $query->countQuery()->execute()->fetchField(); + } + else { + // Subsequent invocations. + + $found = FALSE; + if ($context['total']) { + // Operate on every revision of every node (whee!), in batches. + $batch_size = 50; + $query = db_select('node', 'n'); + $nr = $query->innerJoin('node_revision', 'nr', 'n.vid = nr.vid'); + $revisions = $query + ->fields('n', array('type')) + ->fields($nr) + ->condition('nr.vid', $context['last'], '>') + ->condition('n.type', $body_types, 'IN') + ->orderBy('nr.vid', 'ASC') + ->execute(); + + // Load each reversion of each node, set up 'body' + // appropriately, and save the node's field data. Note that + // node_load() will not return the body or teaser values from + // {node_revision} because those columns have been removed from the + // schema structure in memory (but not yet from the database), + // so we get the values from the explicit query of the table + // instead. + foreach ($revisions as $revision) { + $found = TRUE; + + if ($node_types[$revision->type]->has_body) { + $node = (object) array( + 'nid' => $revision->nid, + 'vid' => $revision->vid, + 'type' => $revision->type, + ); + if (!empty($revision->teaser) && $revision->teaser != text_summary($revision->body)) { + $node->body[0]['summary'] = $revision->teaser; + } + // Do this after text_summary() above. + $break = '<!--break-->'; + if (substr($revision->body, 0, strlen($break)) == $break) { + $revision->body = substr($revision->body, strlen($break)); + } + $node->body[0]['value'] = $revision->body; + $node->body[0]['format'] = $revision->format; + // This is a core update and no contrib modules are enabled yet, so + // we can assume default field storage for a faster update. + field_sql_storage_field_storage_write('node', $node, FIELD_STORAGE_INSERT, array()); + } + + $context['last'] = $revision->vid; + $context['count'] += 1; + + if (--$batch_size == 0) { + break; + } + } + + $ret['#finished'] = min(0.99, $context['count'] / $context['total']); + } + + if (!$found) { + // All nodes are processed. + $ret[] = array('success' => TRUE, 'query' => "{$context['total']} node body and teaser properties migrated to the 'body' field."); + + // Remove the now-obsolete body info from node_revision. + db_drop_field($ret, 'node_revision', 'body'); + db_drop_field($ret, 'node_revision', 'teaser'); + db_drop_field($ret, 'node_revision', 'format'); + + // We're done. + $ret['#finished'] = 1; + } + } + + return $ret; +} + + + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ diff --git a/modules/node/node.module b/modules/node/node.module index d853da33f..85bcceb36 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -291,186 +291,6 @@ function node_mark($nid, $timestamp) { } /** - * See if the user used JS to submit a teaser. - */ -function node_teaser_js(&$form, &$form_state) { - if (isset($form_state['input']['teaser_js'])) { - // Glue the teaser to the body. - if (trim($form_state['values']['teaser_js'])) { - // Space the teaser from the body - $body = trim($form_state['values']['teaser_js']) . "\r\n<!--break-->\r\n" . trim($form_state['values']['body']); - } - else { - // Empty teaser, no spaces. - $body = '<!--break-->' . $form_state['values']['body']; - } - // Pass updated body value on to preview/submit form processing. - form_set_value($form['body'], $body, $form_state); - // Pass updated body value back onto form for those cases - // in which the form is redisplayed. - $form['body']['#value'] = $body; - } - return $form; -} - -/** - * Ensure value of "teaser_include" checkbox is consistent with other form data. - * - * This handles two situations in which an unchecked checkbox is rejected: - * - * 1. The user defines a teaser (summary) but it is empty; - * 2. The user does not define a teaser (summary) (in this case an - * unchecked checkbox would cause the body to be empty, or missing - * the auto-generated teaser). - * - * If JavaScript is active then it is used to force the checkbox to be - * checked when hidden, and so the second case will not arise. - * - * In either case a warning message is output. - */ -function node_teaser_include_verify(&$form, &$form_state) { - $message = ''; - - // $form_state['input'] is set only when the form is built for preview/submit. - if (isset($form_state['input']['body']) && isset($form_state['values']['teaser_include']) && !$form_state['values']['teaser_include']) { - // "teaser_include" checkbox is present and unchecked. - if (strpos($form_state['values']['body'], '<!--break-->') === 0) { - // Teaser is empty string. - $message = t('You specified that the summary should not be shown when this post is displayed in full view. This setting is ignored when the summary is empty.'); - } - elseif (strpos($form_state['values']['body'], '<!--break-->') === FALSE) { - // Teaser delimiter is not present in the body. - $message = t('You specified that the summary should not be shown when this post is displayed in full view. This setting has been ignored since you have not defined a summary for the post. (To define a summary, insert the delimiter "<!--break-->" (without the quotes) in the Body of the post to indicate the end of the summary and the start of the main content.)'); - } - - if (!empty($message)) { - drupal_set_message($message, 'warning'); - // Pass new checkbox value on to preview/submit form processing. - form_set_value($form['teaser_include'], 1, $form_state); - // Pass new checkbox value back onto form for those cases - // in which form is redisplayed. - $form['teaser_include']['#value'] = 1; - } - } - - return $form; -} - -/** - * Generate a teaser for a node body. - * - * If the end of the teaser is not indicated using the <!--break--> delimiter - * then we generate the teaser automatically, trying to end it at a sensible - * place such as the end of a paragraph, a line break, or the end of a - * sentence (in that order of preference). - * - * @param $body - * The content for which a teaser will be generated. - * @param $format - * The format of the content. If the content contains PHP code, we do not - * split it up to prevent parse errors. If the line break filter is present - * then we treat newlines embedded in $body as line breaks. - * @param $size - * The desired character length of the teaser. If omitted, the default - * value will be used. Ignored if the special delimiter is present - * in $body. - * @return - * The generated teaser. - */ -function node_teaser($body, $format = NULL, $size = NULL) { - - if (!isset($size)) { - $size = variable_get('teaser_length', 600); - } - - // Find where the delimiter is in the body - $delimiter = strpos($body, '<!--break-->'); - - // If the size is zero, and there is no delimiter, the entire body is the teaser. - if ($size == 0 && $delimiter === FALSE) { - return $body; - } - - // If a valid delimiter has been specified, use it to chop off the teaser. - if ($delimiter !== FALSE) { - return substr($body, 0, $delimiter); - } - - // We check for the presence of the PHP evaluator filter in the current - // format. If the body contains PHP code, we do not split it up to prevent - // parse errors. - if (isset($format)) { - $filters = filter_list_format($format); - if (isset($filters['php/0']) && strpos($body, '<?') !== FALSE) { - return $body; - } - } - - // If we have a short body, the entire body is the teaser. - if (drupal_strlen($body) <= $size) { - return $body; - } - - // If the delimiter has not been specified, try to split at paragraph or - // sentence boundaries. - - // The teaser may not be longer than maximum length specified. Initial slice. - $teaser = truncate_utf8($body, $size); - - // Store the actual length of the UTF8 string -- which might not be the same - // as $size. - $max_rpos = strlen($teaser); - - // How much to cut off the end of the teaser so that it doesn't end in the - // middle of a paragraph, sentence, or word. - // Initialize it to maximum in order to find the minimum. - $min_rpos = $max_rpos; - - // Store the reverse of the teaser. We use strpos on the reversed needle and - // haystack for speed and convenience. - $reversed = strrev($teaser); - - // Build an array of arrays of break points grouped by preference. - $break_points = array(); - - // A paragraph near the end of sliced teaser is most preferable. - $break_points[] = array('</p>' => 0); - - // If no complete paragraph then treat line breaks as paragraphs. - $line_breaks = array('<br />' => 6, '<br>' => 4); - // Newline only indicates a line break if line break converter - // filter is present. - if (isset($filters['filter/1'])) { - $line_breaks["\n"] = 1; - } - $break_points[] = $line_breaks; - - // If the first paragraph is too long, split at the end of a sentence. - $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1); - - // Iterate over the groups of break points until a break point is found. - foreach ($break_points as $points) { - // Look for each break point, starting at the end of the teaser. - foreach ($points as $point => $offset) { - // The teaser is already reversed, but the break point isn't. - $rpos = strpos($reversed, strrev($point)); - if ($rpos !== FALSE) { - $min_rpos = min($rpos + $offset, $min_rpos); - } - } - - // If a break point was found in this group, slice and return the teaser. - if ($min_rpos !== $max_rpos) { - // Don't slice with length 0. Length must be <0 to slice from RHS. - return ($min_rpos === 0) ? $teaser : substr($teaser, 0, 0 - $min_rpos); - } - } - - // If a break point was not found, still return a teaser. - return $teaser; -} - -/** * Extract the type name. * * @param $node @@ -632,6 +452,7 @@ function node_type_save($info) { if (!empty($type->old_type) && $type->old_type != $type->type) { field_attach_rename_bundle($type->old_type, $type->type); } + node_configure_fields($type); module_invoke_all('node_type', 'update', $type); return SAVED_UPDATED; } @@ -642,13 +463,74 @@ function node_type_save($info) { ->execute(); field_attach_create_bundle($type->type); - + node_configure_fields($type); module_invoke_all('node_type', 'insert', $type); return SAVED_NEW; } } /** + * Manage the field(s) for a node type. + * + * Currently, the node module manages a single Field API field, + * 'body'. If $type->has_body is true, this function ensures the + * 'body' field exists and creates an instance of it for the bundle + * $type->type (e.g. 'page', 'story', ...). If $type->has_body is + * false, this function removes the instance (if it exists) for the + * 'body' field on $type->type. + */ +function node_configure_fields($type) { + // Add or remove the body field, as needed. + $field = field_info_field('body'); + $instance = field_info_instance('body', $type->type); + if ($type->has_body) { + if (empty($field)) { + $field = array( + 'field_name' => 'body', + 'type' => 'text_with_summary', + ); + $field = field_create_field($field); + } + 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), + + // With no UI in core, we have to define default + // formatters for the teaser and full view. + // This may change if the method of handling displays + // is changed or if a UI gets into core. + 'display' => array( + 'full' => array( + 'label' => 'hidden', + 'type' => 'text_default', + 'exclude' => 0, + ), + 'teaser' => array( + 'label' => 'hidden', + 'type' => 'text_summary_or_trimmed', + 'exclude' => 0, + ), + ), + ); + field_create_instance($instance); + } + else { + $instance['label'] = $type->body_label; + $instance['settings']['display_summary'] = TRUE; + field_update_instance($instance); + } + } + elseif (!empty($instance)) { + field_delete_instance($instance); + } + +} + +/** * Deletes a node type from the database. * * @param $type @@ -1004,7 +886,8 @@ function node_validate($node, $form = array()) { // Make sure the body has the minimum number of words. // TODO : use a better word counting algorithm that will work in other languages - if (!empty($type->min_word_count) && isset($node->body) && count(explode(' ', $node->body)) < $type->min_word_count) { + if (!empty($type->min_word_count) && isset($node->body[0]['value']) && count(explode(' ', $node->body[0]['value'])) < $type->min_word_count) { + // TODO: Use Field API to set this error. form_set_error('body', t('The body of your @type is too short. You need at least %words words.', array('%words' => $type->min_word_count, '@type' => $type->name))); } @@ -1041,25 +924,6 @@ function node_submit($node) { // Convert the node to an object, if necessary. $node = (object)$node; - // Generate the teaser, but only if it hasn't been set (e.g. by a - // module-provided 'teaser' form item). - if (!isset($node->teaser)) { - if (isset($node->body)) { - $node->format = (!empty($node->body_format) ? $node->body_format : FILTER_FORMAT_DEFAULT); - $node->teaser = node_teaser($node->body, isset($node->format) ? $node->format : NULL, variable_get('teaser_length_' . $node->type, 600)); - // Chop off the teaser from the body if needed. The teaser_include - // property might not be set (eg. in Blog API postings), so only act on - // it, if it was set with a given value. - if (isset($node->teaser_include) && !$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) { - $node->body = substr($node->body, strlen($node->teaser)); - } - } - else { - $node->teaser = ''; - $node->format = 0; - } - } - if (user_access('administer nodes')) { // Populate the "authored by" field. if ($account = user_load_by_name($node->name)) { @@ -1105,16 +969,6 @@ function node_save($node) { if (!isset($node->log)) { $node->log = ''; } - - // For the same reasons, make sure we have $node->teaser and - // $node->body. We should consider making these fields nullable - // in a future version since node types are not required to use them. - if (!isset($node->teaser)) { - $node->teaser = ''; - } - if (!isset($node->body)) { - $node->body = ''; - } } elseif (!empty($node->revision)) { $node->old_vid = $node->vid; @@ -1274,30 +1128,6 @@ function node_build($node, $teaser = FALSE) { } /** - * Apply filters and build the node's standard elements. - */ -function node_prepare($node, $teaser = FALSE) { - // First we'll overwrite the existing node teaser and body with - // the filtered copies! Then, we'll stick those into the content - // array and set the read more flag if appropriate. - $node->readmore = (strlen($node->teaser) < strlen($node->body)); - - if ($teaser == FALSE) { - $node->body = check_markup($node->body, $node->format, $node->language, FALSE); - } - else { - $node->teaser = check_markup($node->teaser, $node->format, $node->language, FALSE); - } - - $node->content['body'] = array( - '#markup' => $teaser ? $node->teaser : $node->body, - '#weight' => 0, - ); - - return $node; -} - -/** * Builds a structured array representing the node's content. * * The content built for the node will vary depending on the $node->build_mode @@ -1322,30 +1152,41 @@ function node_prepare($node, $teaser = FALSE) { * * @return * An structured array containing the individual elements - * of the node's body. + * of the node's content. */ function node_build_content($node, $teaser = FALSE) { - // The build mode identifies the target for which the node is built. if (!isset($node->build_mode)) { $node->build_mode = NODE_BUILD_NORMAL; } - // Remove the delimiter (if any) that separates the teaser from the body. - $node->body = isset($node->body) ? str_replace('<!--break-->', '', $node->body) : ''; - // The 'view' hook can be implemented to overwrite the default function // to display nodes. if (node_hook($node, 'view')) { $node = node_invoke($node, 'view', $teaser); } - else { - $node = node_prepare($node, $teaser); - } // Build fields content. + if (empty($node->content)) { + $node->content = array(); + }; $node->content += field_attach_view('node', $node, $teaser); + // Always display a read more link on teasers because we have no way + // to know when a teaser view is different than a full view. + $links = array(); + if ($teaser) { + $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( + '#type' => 'node_links', + '#value' => $links + ); + // Allow modules to make their own additions to the node. module_invoke_all('node_view', $node, $teaser); @@ -1649,16 +1490,16 @@ function node_search($op = 'search', $keys = NULL) { // Load results. $results = array(); foreach ($find as $item) { - // Build the node body. + // Render the node. $node = node_load($item->sid); $node->build_mode = NODE_BUILD_SEARCH_RESULT; $node = node_build_content($node, FALSE, FALSE); - $node->body = drupal_render($node->content); + $node->rendered = drupal_render($node->content); // Fetch comments for snippet. - $node->body .= module_invoke('comment', 'node_update_index', $node); + $node->rendered .= module_invoke('comment', 'node_update_index', $node); // Fetch terms for snippet. - $node->body .= module_invoke('taxonomy', 'node_update_index', $node); + $node->rendered .= module_invoke('taxonomy', 'node_update_index', $node); $extra = module_invoke_all('node_search_result', $node); @@ -1671,7 +1512,7 @@ function node_search($op = 'search', $keys = NULL) { 'node' => $node, 'extra' => $extra, 'score' => $total ? ($item->calculated_score / $total) : 0, - 'snippet' => search_excerpt($keys, $node->body), + 'snippet' => search_excerpt($keys, $node->rendered), ); } return $results; @@ -1801,7 +1642,7 @@ function node_link($type, $node = NULL, $teaser = FALSE) { $links = array(); if ($type == 'node') { - if ($teaser == 1 && $node->teaser && !empty($node->readmore)) { + if ($teaser == 1) { $links['node_read_more'] = array( 'title' => t('Read more'), 'href' => "node/$node->nid", @@ -2254,12 +2095,12 @@ function _node_index_node($node) { // results half-life calculation. variable_set('node_cron_last', $node->changed); - // Build the node body. + // Render the node. $node->build_mode = NODE_BUILD_SEARCH_INDEX; $node = node_build_content($node, FALSE, FALSE); - $node->body = drupal_render($node->content); + $node->rendered = drupal_render($node->content); - $text = '<h1>' . check_plain($node->title) . '</h1>' . $node->body; + $text = '<h1>' . check_plain($node->title) . '</h1>' . $node->rendered; // Fetch extra data normally not visible $extra = module_invoke_all('node_update_index', $node); @@ -2464,10 +2305,6 @@ function node_access($op, $node, $account = NULL) { if (empty($account)) { $account = $user; } - // If the node is in a restricted format, disallow editing. - if ($op == 'update' && !filter_access($node->format)) { - return FALSE; - } if (user_access('bypass node access', $account)) { return TRUE; @@ -2977,9 +2814,9 @@ function node_content_access($op, $node, $account) { * Implement hook_form(). */ function node_content_form($node, $form_state) { - + $type = node_type_get_type($node); - + $form = array(); if ($type->has_title) { @@ -2993,8 +2830,6 @@ function node_content_form($node, $form_state) { ); } - $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); - return $form; } diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index 729a991db..ee362cbf6 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -114,7 +114,7 @@ function node_form(&$form_state, $node) { $form['#prefix'] = $form_state['node_preview']; } $node = (object)$node; - foreach (array('body', 'title', 'format') as $key) { + foreach (array('title') as $key) { if (!isset($node->$key)) { $node->$key = NULL; } @@ -287,52 +287,6 @@ function node_form(&$form_state, $node) { } /** - * Return a node body field, with format and teaser. - */ -function node_body_field($node, $label, $word_count) { - // Do not generate a body field if the type does not specify one. - if (!node_type_get_type($node->type)->has_body) { - return array(); - } - - // Check if we need to restore the teaser at the beginning of the body. - $include = !isset($node->teaser) || ($node->teaser == substr($node->body, 0, strlen($node->teaser))); - - $form = array( - '#after_build' => array('node_teaser_js', 'node_teaser_include_verify')); - - $form['#prefix'] = '<div class="body-field-wrapper clearfix">'; - $form['#suffix'] = '</div>'; - - $form['teaser_js'] = array( - '#type' => 'textarea', - '#rows' => 10, - '#teaser' => 'edit-body', - '#teaser_checkbox' => 'edit-teaser-include', - '#disabled' => TRUE, - ); - - $form['teaser_include'] = array( - '#type' => 'checkbox', - '#title' => t('Show summary in full view'), - '#default_value' => $include, - '#prefix' => '<div class="teaser-checkbox">', - '#suffix' => '</div>', - ); - - $form['body'] = array( - '#type' => 'textarea', - '#title' => check_plain($label), - '#default_value' => $include ? $node->body : ($node->teaser . $node->body), - '#rows' => 20, - '#required' => ($word_count > 0), - '#text_format' => isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT, - ); - - return $form; -} - -/** * Button submit function: handle the 'Delete' button on the node form. */ function node_form_delete_submit($form, &$form_state) { @@ -393,16 +347,6 @@ function node_preview($node) { $node->changed = REQUEST_TIME; - // Extract a teaser, if it hasn't been set (e.g. by a module-provided - // 'teaser' form item). - if (!isset($node->teaser)) { - $node->teaser = empty($node->body) ? '' : node_teaser($node->body, $node->format, variable_get('teaser_length_' . $type, 600)); - // Chop off the teaser from the body if needed. - if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) { - $node->body = substr($node->body, strlen($node->teaser)); - } - } - // Display a preview of the node. // Previewing alters $node so it needs to be cloned. if (!form_get_errors()) { @@ -428,28 +372,20 @@ function theme_node_preview($node) { $output = '<div class="preview">'; $preview_trimmed_version = FALSE; - // Do we need to preview trimmed version of post as well as full version? - if (isset($node->teaser) && isset($node->body)) { - $teaser = trim($node->teaser); - $body = trim(str_replace('<!--break-->', '', $node->body)); - - // Preview trimmed version if teaser and body will appear different; - // also (edge case) if both teaser and body have been specified by the user - // and are actually the same. - if ($teaser != $body || ($body && strpos($node->body, '<!--break-->') === 0)) { - $preview_trimmed_version = TRUE; - } - } - if ($preview_trimmed_version) { + $trimmed = drupal_render(node_build(clone $node, TRUE)); + $full = drupal_render(node_build($node, FALSE)); + + // Do we need to preview trimmed version of post as well as full version? + if ($trimmed != $full) { drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.</span>')); $output .= '<h3>' . t('Preview trimmed version') . '</h3>'; - $output .= drupal_render(node_build(clone $node, TRUE)); + $output .= $trimmed; $output .= '<h3>' . t('Preview full version') . '</h3>'; - $output .= drupal_render(node_build($node, FALSE)); + $output .= $full; } else { - $output .= drupal_render(node_build($node, FALSE)); + $output .= $full; } $output .= "</div>\n"; diff --git a/modules/node/node.test b/modules/node/node.test index 252ebbd3c..152a1353b 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -141,7 +141,7 @@ class NodeRevisionsTestCase extends DrupalWebTestCase { // Confirm the correct revision text appears on "view revisions" page. $this->drupalGet("node/$node->nid/revisions/$node->vid/view"); - $this->assertText($node->body, t('Correct text displays for version.')); + $this->assertText($node->body[0]['value'], t('Correct text displays for version.')); // Confirm the correct log message appears on "revisions overview" page. $this->drupalGet("node/$node->nid/revisions"); @@ -155,7 +155,7 @@ class NodeRevisionsTestCase extends DrupalWebTestCase { array('@type' => 'Page', '%title' => $nodes[1]->title, '%revision-date' => format_date($nodes[1]->revision_timestamp))), t('Revision reverted.')); $reverted_node = node_load($node->nid); - $this->assertTrue(($nodes[1]->body == $reverted_node->body), t('Node reverted correctly.')); + $this->assertTrue(($nodes[1]->body[0]['value'] == $reverted_node->body[0]['value']), t('Node reverted correctly.')); // Confirm revisions delete properly. $this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete')); @@ -166,151 +166,6 @@ class NodeRevisionsTestCase extends DrupalWebTestCase { } } -class NodeTeaserTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => t('Node teaser'), - 'description' => t('Test node_teaser() with different strings and lengths.'), - 'group' => t('Node'), - ); - } - - /** - * Tests an edge case where if the first sentence is a question and - * subsequent sentences are not. This is edge case is documented at - * http://drupal.org/node/180425. - */ - function testFirstSentenceQuestion() { - $body = 'A question? A sentence. Another sentence.'; - $expected = 'A question? A sentence.'; - $this->callNodeTeaser($body, $expected, NULL, 30); - } - - /** - * Test teaser with long example. - */ - function testLongSentence() { - $body = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . // 125 - 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' . // 108 - 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' . // 103 - 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; // 110 - $expected = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . - 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' . - 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.'; - // First three sentences add up to: 336, so add one for space and then 3 to get half-way into next word. - $this->callNodeTeaser($body, $expected, NULL, 340); - } - - /** - * Test various teaser length edge cases. - */ - function testLength() { - // This body string tests a number of edge cases. - $body = "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>"; - - // The teasers we expect node_teaser() to return when $size is the index - // of each array item. - // Using an text format with no line-break filter: - $teasers = array( - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<", - "<p", - "<p>", - "<p>\n", - "<p>\nH", - "<p>\nHi", - "<p>\nHi\n", - "<p>\nHi\n<", - "<p>\nHi\n</", - "<p>\nHi\n</p", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - ); - - // And Using an text format WITH the line-break filter. - $teasers_lb = array( - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<", - "<p", - "<p>", - "<p>", - "<p>", - "<p>", - "<p>\nHi", - "<p>\nHi", - "<p>\nHi", - "<p>\nHi", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", - ); - - // Test node_teaser() for different sizes. - for ($i = 0; $i <= 37; $i++) { - $this->callNodeTeaser($body, $teasers[$i], NULL, $i); - $this->callNodeTeaser($body, $teasers_lb[$i], 1, $i); - $this->callNodeTeaser($body, $teasers_lb[$i], 2, $i); - } - } - - /** - * Calls node_teaser() and asserts that the expected teaser is returned. - */ - function callNodeTeaser($body, $expected, $format = NULL, $size = NULL) { - $teaser = node_teaser($body, $format, $size); - $this->assertIdentical($teaser, $expected, t('Generated teaser "@teaser" matches expected teaser.', array('@teaser' => $teaser))); - } -} - class PageEditTestCase extends DrupalWebTestCase { public static function getInfo() { return array( @@ -331,10 +186,11 @@ class PageEditTestCase extends DrupalWebTestCase { * Check node edit functionality. */ function testPageEdit() { + $body_key = 'body[0][value]'; // Create node to edit. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit[$body_key] = $this->randomName(16); $this->drupalPost('node/add/page', $edit, t('Save')); // Check that the node exists in the database. @@ -350,18 +206,18 @@ class PageEditTestCase extends DrupalWebTestCase { // Check that the title and body fields are displayed with the correct values. $this->assertLink(t('Edit'), 0, t('Edit tab found.')); $this->assertFieldByName('title', $edit['title'], t('Title field displayed.')); - $this->assertFieldByName('body', '<!--break-->' . $edit['body'], t('Body field displayed.')); + $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.')); // Edit the content of the node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit[$body_key] = $this->randomName(16); // Stay on the current page, without reloading. $this->drupalPost(NULL, $edit, t('Save')); // Check that the title and body fields are displayed with the updated values. $this->assertText($edit['title'], t('Title displayed.')); - $this->assertText($edit['body'], t('Body displayed.')); + $this->assertText($edit[$body_key], t('Body displayed.')); } } @@ -385,44 +241,47 @@ class PagePreviewTestCase extends DrupalWebTestCase { * Check the node preview functionality. */ function testPagePreview() { + $body_key = 'body[0][value]'; + // Fill in node creation form and preview node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit[$body_key] = $this->randomName(16); $this->drupalPost('node/add/page', $edit, t('Preview')); // Check that the preview is displaying the title and body. $this->assertTitle(t('Preview | Drupal'), t('Page title is preview.')); $this->assertText($edit['title'], t('Title displayed.')); - $this->assertText($edit['body'], t('Body displayed.')); + $this->assertText($edit[$body_key], t('Body displayed.')); // Check that the title and body fields are displayed with the correct values. $this->assertFieldByName('title', $edit['title'], t('Title field displayed.')); - $this->assertFieldByName('body', '<!--break-->' . $edit['body'], t('Body field displayed.')); + $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.')); } /** * Check the node preview functionality, when using revisions. */ function testPagePreviewWithRevisions() { + $body_key = 'body[0][value]'; // Force revision on page content. variable_set('node_options_page', array('status', 'revision')); // Fill in node creation form and preview node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit[$body_key] = $this->randomName(16); $edit['log'] = $this->randomName(32); $this->drupalPost('node/add/page', $edit, t('Preview')); // Check that the preview is displaying the title and body. $this->assertTitle(t('Preview | Drupal'), t('Page title is preview.')); $this->assertText($edit['title'], t('Title displayed.')); - $this->assertText($edit['body'], t('Body displayed.')); + $this->assertText($edit[$body_key], t('Body displayed.')); // Check that the title and body fields are displayed with the correct values. $this->assertFieldByName('title', $edit['title'], t('Title field displayed.')); - $this->assertFieldByName('body', '<!--break-->' . $edit['body'], t('Body field displayed.')); + $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.')); // Check that the log field has the correct value. $this->assertFieldByName('log', $edit['log'], t('Log field displayed.')); @@ -452,7 +311,7 @@ class PageCreationTestCase extends DrupalWebTestCase { // Create a node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit['body[0][value]'] = $this->randomName(16); $this->drupalPost('node/add/page', $edit, t('Save')); // Check that the page has been created. @@ -599,7 +458,7 @@ class NodePostSettingsTestCase extends DrupalWebTestCase { // Create a node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit['body[0][value]'] = $this->randomName(16); $this->drupalPost('node/add/page', $edit, t('Save')); // Check that the post information is displayed. @@ -620,7 +479,7 @@ class NodePostSettingsTestCase extends DrupalWebTestCase { // Create a node. $edit = array(); $edit['title'] = $this->randomName(8); - $edit['body'] = $this->randomName(16); + $edit['body[0][value]'] = $this->randomName(16); $this->drupalPost('node/add/page', $edit, t('Save')); // Check that the post information is displayed. @@ -726,7 +585,7 @@ class NodeAccessRecordsAlterUnitTest extends DrupalWebTestCase { $this->assertEqual(count($records), 1, t('Returned the correct number of rows.')); $this->assertEqual($records[0]->realm, 'test_page_realm', t('Grant with page_realm acquired for node without alteration.')); $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.')); - + // Create a promoted page node. $node3 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1)); $this->assertTrue(node_load($node3->nid), t('Promoted page node created.')); @@ -785,7 +644,7 @@ class NodeSaveTestCase extends DrupalWebTestCase { $title = $this->randomName(8); $node = array( 'title' => $title, - 'body' => $this->randomName(32), + 'body' => array(array('value' => $this->randomName(32))), 'uid' => $this->web_user->uid, 'type' => 'article', 'nid' => $test_nid, @@ -845,7 +704,7 @@ class NodeAccessRebuildTestCase extends DrupalWebTestCase { 'group' => t('Node'), ); } - + function setUp() { parent::setUp(); @@ -853,7 +712,7 @@ class NodeAccessRebuildTestCase extends DrupalWebTestCase { $this->drupalLogin($web_user); $this->web_user = $web_user; } - + function testNodeAccessRebuild() { $this->drupalGet('admin/reports/status'); $this->clickLink(t('Rebuild permissions')); |