Commit 3d64cb5e authored by Dries's avatar Dries

- Patch #372743 by bjaspan, yched, KarenS, catch et al: node body and teasers as fields. Oh, my.

parent bfdea953
......@@ -110,6 +110,8 @@ Drupal 7.0, xxxx-xx-xx (development version)
for RDFa support.
- Field API:
* Custom data fields may be attached to nodes and users in Drupal.
* Node bodies and teasers are now Field API fields instead of
being a hard-coded property of node objects.
* In addition, any other object type may register with Field API
and allow custom data fields to be attached to itself.
* Provides a subset of the features of the Content Construction
......
......@@ -378,11 +378,11 @@ function theme_aggregator_page_rss($feeds, $category = NULL) {
foreach ($feeds as $feed) {
switch ($feed_length) {
case 'teaser':
$teaser = node_teaser($feed->description, NULL, variable_get('aggregator_teaser_length', 600));
if ($teaser != $feed->description) {
$teaser .= '<p><a href="' . check_url($feed->link) . '">' . t('read more') . "</a></p>\n";
$summary = text_summary($feed->description, NULL, variable_get('aggregator_teaser_length', 600));
if ($summary != $feed->description) {
$summary .= '<p><a href="' . check_url($feed->link) . '">' . t('read more') . "</a></p>\n";
}
$feed->description = $teaser;
$feed->description = $summary;
break;
case 'title':
$feed->description = '';
......
......@@ -252,7 +252,7 @@ EOF;
for ($i = 0; $i < 5; $i++) {
$edit = array();
$edit['title'] = $this->randomName();
$edit['body'] = $this->randomName();
$edit['body[0][value]'] = $this->randomName();
$this->drupalPost('node/add/article', $edit, t('Save'));
}
}
......
......@@ -73,12 +73,7 @@ function blog_help($path, $arg) {
* Implement hook_form().
*/
function blog_form($node, $form_state) {
global $nid;
$type = node_type_get_type($node);
$form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => !empty($node->title) ? $node->title : NULL, '#weight' => -5);
$form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
return $form;
return node_content_form($node, $form_state);
}
/**
......@@ -89,8 +84,7 @@ function blog_view($node, $teaser) {
// Breadcrumb navigation.
drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => $node->name)), 'blog/' . $node->uid)));
}
return node_prepare($node, $teaser);
return $node;
}
/**
......
......@@ -154,7 +154,7 @@ class BlogTestCase extends DrupalWebTestCase {
// Edit blog node.
$edit = array();
$edit['title'] = 'node/' . $node->nid;
$edit['body'] = $this->randomName(256);
$edit['body[0][value]'] = $this->randomName(256);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit['title'])), t('Blog node was edited'));
......
......@@ -213,7 +213,7 @@ function blogapi_blogger_new_post($appkey, $blogid, $username, $password, $conte
}
else {
$edit['title'] = blogapi_blogger_title($content);
$edit['body'] = $content;
$edit['body'][0]['value'] = $content;
}
if (!node_access('create', $edit['type'])) {
......@@ -274,12 +274,12 @@ function blogapi_blogger_edit_post($appkey, $postid, $username, $password, $cont
// Check for bloggerAPI vs. metaWeblogAPI.
if (is_array($content)) {
$node->title = $content['title'];
$node->body = $content['description'];
$node->body[0]['value'] = $content['description'];
_blogapi_mt_extra($node, $content);
}
else {
$node->title = blogapi_blogger_title($content);
$node->body = $content;
$node->body[0]['value'] = $content;
}
module_invoke_all('node_blogapi_edit', $node);
......@@ -379,7 +379,7 @@ function blogapi_blogger_get_recent_posts($appkey, $blogid, $username, $password
}
if ($bodies) {
$result = db_query_range("SELECT n.nid, n.title, r.body, r.format, n.comment, n.created, u.name FROM {node} n, {node_revision} r, {users} u WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = :type AND n.uid = :uid ORDER BY n.created DESC", array(
$result = db_query_range("SELECT n.nid, n.title, n.comment, n.created, u.name FROM {node} n, {node_revision} r, {users} u WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = :type AND n.uid = :uid ORDER BY n.created DESC", array(
':type' => $blogid,
':uid' => $user->uid
), 0, $number_of_posts);
......@@ -892,14 +892,14 @@ function _blogapi_mt_extra($node, $struct) {
// Merge the 3 body sections (description, mt_excerpt, mt_text_more) into one body.
if ($struct['mt_excerpt']) {
$node->body = $struct['mt_excerpt'] . '<!--break-->' . $node->body;
$node->body[0]['value'] = $struct['mt_excerpt'] . '<!--break-->' . $node->body[0]['value'];
}
if ($struct['mt_text_more']) {
$node->body = $node->body . '<!--extended-->' . $struct['mt_text_more'];
$node->body[0]['value'] = $node->body[0]['value'] . '<!--extended-->' . $struct['mt_text_more'];
}
if ($struct['mt_convert_breaks']) {
$node->format = $struct['mt_convert_breaks'];
$node->body[0]['format'] = $struct['mt_convert_breaks'];
}
if ($struct['dateCreated']) {
......@@ -922,17 +922,19 @@ function _blogapi_get_post($node, $bodies = TRUE) {
);
if ($bodies) {
$body = $node->body[0]['value'];
$format = $node->body[0]['format'];
if ($node->comment == 1) {
$comment = 2;
}
elseif ($node->comment == 2) {
$comment = 1;
}
$xmlrpcval['content'] = "<title>$node->title</title>$node->body";
$xmlrpcval['description'] = $node->body;
$xmlrpcval['content'] = "<title>$node->title</title>$body";
$xmlrpcval['description'] = $body;
// Add MT specific fields
$xmlrpcval['mt_allow_comments'] = (int) $comment;
$xmlrpcval['mt_convert_breaks'] = $node->format;
$xmlrpcval['mt_convert_breaks'] = $format;
}
return $xmlrpcval;
......
......@@ -1037,7 +1037,7 @@ function book_export_traverse($tree, $visit_func) {
function book_node_export($node, $children = '') {
$node->build_mode = NODE_BUILD_PRINT;
$node = node_build_content($node, FALSE, FALSE);
$node->body = drupal_render($node->content);
$node->rendered = drupal_render($node->content);
return theme('book_node_export_html', $node, $children);
}
......@@ -1054,7 +1054,7 @@ function book_node_export($node, $children = '') {
function template_preprocess_book_node_export_html(&$variables) {
$variables['depth'] = $variables['node']->book['depth'];
$variables['title'] = check_plain($variables['node']->title);
$variables['content'] = $variables['node']->body;
$variables['content'] = $variables['node']->rendered;
}
/**
......
......@@ -141,8 +141,7 @@ class BookTestCase extends DrupalWebTestCase {
// Check printer friendly version.
$this->drupalGet('book/export/html/' . $node->nid);
$this->assertText($node->title, t('Printer friendly title found.'));
$node->body = str_replace('<!--break-->', '', $node->body);
$this->assertRaw(check_markup($node->body, $node->format), t('Printer friendly body found.'));
$this->assertRaw(check_markup($node->body[0]['value'], $node->body[0]['format']), t('Printer friendly body found.'));
$number++;
}
......@@ -174,7 +173,7 @@ class BookTestCase extends DrupalWebTestCase {
$edit = array();
$edit['title'] = $number . ' - SimpleTest test node ' . $this->randomName(10);
$edit['body'] = 'SimpleTest test body ' . $this->randomName(32) . ' ' . $this->randomName(32);
$edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomName(32) . ' ' . $this->randomName(32);
$edit['book[bid]'] = $book_nid;
if ($parent !== NULL) {
......
......@@ -327,7 +327,7 @@ class DBLogTestCase extends DrupalWebTestCase {
default:
$content = array(
'title' => $this->randomName(8),
'body' => $this->randomName(32),
'body[0][value]' => $this->randomName(32),
);
break;
}
......@@ -351,7 +351,7 @@ class DBLogTestCase extends DrupalWebTestCase {
default:
$content = array(
'body' => $this->randomName(32),
'body[0][value]' => $this->randomName(32),
);
break;
}
......
......@@ -425,7 +425,9 @@ function field_create_instance($instance) {
// Set the field id.
$instance['field_id'] = $field['id'];
// TODO: Check that the specifed bundle exists.
// Note that we do *not* prevent creating a field on non-existing bundles,
// because that would break the 'Body as field' upgrade for contrib
// node types.
// TODO: Check that the widget type is known and can handle the field type ?
// TODO: Check that the formatters are known and can handle the field type ?
......
......@@ -930,10 +930,9 @@ class FieldInfoTestCase extends DrupalWebTestCase {
$this->assertEqual($info[$w_key]['module'], 'field_test', t("Widget type field_test module appears"));
}
// Verify that no fields or instances exist
$fields = field_info_fields();
// Verify that no unexpected instances exist.
$core_fields = field_info_fields();
$instances = field_info_instances(FIELD_TEST_BUNDLE);
$this->assertTrue(empty($fields), t('With no fields, info fields is empty.'));
$this->assertTrue(empty($instances), t('With no instances, info bundles is empty.'));
// Create a field, verify it shows up.
......@@ -943,7 +942,7 @@ class FieldInfoTestCase extends DrupalWebTestCase {
);
field_create_field($field);
$fields = field_info_fields();
$this->assertEqual(count($fields), 1, t('One field exists'));
$this->assertEqual(count($fields), count($core_fields) + 1, t('One new field exists'));
$this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], t('info fields contains field name'));
$this->assertEqual($fields[$field['field_name']]['type'], $field['type'], t('info fields contains field type'));
$this->assertEqual($fields[$field['field_name']]['module'], 'field_test', t('info fields contains field module'));
......
......@@ -5,3 +5,4 @@ package = Core - fields
version = VERSION
core = 7.x
files[]=list.module
required = TRUE
......@@ -5,3 +5,4 @@ package = Core - fields
version = VERSION
core = 7.x
files[]=number.module
required = TRUE
......@@ -5,3 +5,4 @@ package = Core - fields
version = VERSION
core = 7.x
files[]=options.module
required = TRUE
......@@ -6,3 +6,4 @@ version = VERSION
core = 7.x
files[] = text.module
files[] = text.test
required = TRUE
\ No newline at end of file
This diff is collapsed.
......@@ -13,7 +13,7 @@ class TextFieldTestCase extends DrupalWebTestCase {
}
function setUp() {
parent::setUp('field', 'text', 'field_test');
parent::setUp('field_test');
$web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
$this->drupalLogin($web_user);
......@@ -149,7 +149,7 @@ class TextFieldTestCase extends DrupalWebTestCase {
// selector is displayed
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName($this->field_name . '[0][value]', '', t('Widget is displayed'));
$this->assertNoFieldByName($this->field_name . '[0][format]', '1', t('Format selector is not displayed'));
$this->assertNoFieldByName($this->field_name . '[0][value_format]', '1', t('Format selector is not displayed'));
// Submit with data that should be filtered.
$value = $this->randomName() . '<br />' . $this->randomName();
......@@ -175,11 +175,11 @@ class TextFieldTestCase extends DrupalWebTestCase {
// We should now have a 'text format' selector.
$this->drupalGet('test-entity/' . $id . '/edit');
$this->assertFieldByName($this->field_name . '[0][value]', '', t('Widget is displayed'));
$this->assertFieldByName($this->field_name . '[0][format]', '1', t('Format selector is displayed'));
$this->assertFieldByName($this->field_name . '[0][value_format]', '1', t('Format selector is displayed'));
// Edit and change the format to 'Full HTML'.
$edit = array(
$this->field_name . '[0][format]' => 2,
$this->field_name . '[0][value_format]' => 2,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), t('Entity was updated'));
......@@ -196,3 +196,148 @@ class TextFieldTestCase extends DrupalWebTestCase {
*
*/
}
class TextSummaryTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('Text summary'),
'description' => t('Test text_summary() with different strings and lengths.'),
'group' => t('Field'),
);
}
/**
* Tests an edge case where the first sentence is a question and
* subsequent sentences are not. This edge case is documented at
* http://drupal.org/node/180425.
*/
function testFirstSentenceQuestion() {
$text = 'A question? A sentence. Another sentence.';
$expected = 'A question? A sentence.';
$this->callTextSummary($text, $expected, NULL, 30);
}
/**
* Test summary with long example.
*/
function testLongSentence() {
$text = '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->callTextSummary($text, $expected, NULL, 340);
}
/**
* Test various summary length edge cases.
*/
function testLength() {
// This string tests a number of edge cases.
$text = "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>";
// The summaries we expect text_summary() to return when $size is the index
// of each array item.
// Using no text format:
$expected = 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 a text format WITH the line-break and htmlcorrector filters.
$expected_lb = array(
"<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
"<",
"<p",
"<p></p>",
"<p></p>",
"<p></p>",
"<p></p>",
"<p>\nHi</p>",
"<p>\nHi</p>",
"<p>\nHi</p>",
"<p>\nHi</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>",
);
// Test text_summary() for different sizes.
for ($i = 0; $i <= 37; $i++) {
$this->callTextSummary($text, $expected[$i], NULL, $i);
$this->callTextSummary($text, $expected_lb[$i], 1, $i);
$this->callTextSummary($text, $expected_lb[$i], 2, $i);
}
}
/**
* Calls text_summary() and asserts that the expected teaser is returned.
*/
function callTextSummary($text, $expected, $format = NULL, $size = NULL) {
$summary = text_summary($text, $format, $size);
$this->assertIdentical($summary, $expected, t('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
}
}
......@@ -32,7 +32,11 @@ form .field-add-more-submit {
margin: .5em 0 0;
}
.form-item .number {
form .form-item .text {
display: inline;
width: auto;
}
\ No newline at end of file
}
form .form-item .number {
display: inline;
width: auto;
}
......@@ -311,10 +311,6 @@ function filter_admin_delete_submit($form, &$form_state) {
$default = variable_get('filter_default_format', 1);
// Replace existing instances of the deleted format with the default format.
db_update('node_revision')
->fields(array('format' => $default))
->condition('format', $form_state['values']['format'])
->execute();
if (db_table_exists('comment')) {
db_update('comment')
->fields(array('format' => $default))
......
......@@ -109,8 +109,8 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$edit = array();
$edit['title'] = $this->randomName();
$edit['body'] = $body . '<random>' . $extra_text . '</random>';
$edit['body_format'] = $filtered;
$edit['body[0][value]'] = $body . '<random>' . $extra_text . '</random>';
$edit['body[0][value_format]'] = $filtered;
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Page %title has been created.', array('%title' => $edit['title'])), t('Filtered node created.'));
......
......@@ -549,8 +549,6 @@ function forum_form($node, $form_state) {
$form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
}
$form['body_field'] = node_body_field($node, $type->body_label, 1);
$form['#submit'][] = 'forum_submit';
// Assign the forum topic submit handler.
......
......@@ -233,7 +233,7 @@ class ForumTestCase extends DrupalWebTestCase {
$edit = array(
'title' => $title,
'body' => $body,
'body[0][value]' => $body,
'taxonomy[1]' => $tid
);
......@@ -322,7 +322,7 @@ class ForumTestCase extends DrupalWebTestCase {
// Edit forum node (including moving it to another forum).
$edit = array();
$edit['title'] = 'node/' . $node->nid;
$edit['body'] = $this->randomName(256);
$edit['body[0][value]'] = $this->randomName(256);
$edit['taxonomy[1]'] = $this->root_forum['tid']; // Assumes the topic is initially associated with $forum.
$edit['shadow'] = TRUE;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
......
......@@ -54,14 +54,19 @@ class HelpTestCase extends DrupalWebTestCase {
// View module help node.
$this->drupalGet('admin/help/' . $module);
$this->assertResponse($response);
if ($response == 200) {
// NOTE: The asserts fail on blog and poll because the get returns the 'admin/help' node instead of the indicated node???
// if ($module == 'blog' || $module == 'poll') {
// continue;
// }
$this->assertTitle($name . ' | Drupal', t('[' . $module . '] Title was displayed'));
$this->assertRaw('<h2>' . t($name) . '</h2>', t('[' . $module . '] Heading was displayed'));
$this->assertText(t('Home ' . $crumb . ' Administer ' . $crumb . ' Help'), t('[' . $module . '] Breadcrumbs were displayed'));
if (drupal_function_exists($module . '_help')) {
// View module help node.
$this->drupalGet('admin/help/' . $module);
$this->assertResponse($response);
if ($response == 200) {
// NOTE: The asserts fail on blog and poll because the get returns the 'admin/help' node instead of the indicated node???
// if ($module == 'blog' || $module == 'poll') {
// continue;
// }
$this->assertTitle($name . ' | Drupal', t('[' . $module . '] Title was displayed'));
$this->assertRaw('<h2>' . t($name) . '</h2>', t('[' . $module . '] Heading was displayed'));
$this->assertText(t('Home ' . $crumb . ' Administer ' . $crumb . ' Help'), t('[' . $module . '] Breadcrumbs were displayed'));
}
}
}
}
......
......@@ -1395,7 +1395,7 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase {
$edit = array(
'type' => 'page',
'title' => $node_title,
'body' => $node_body,
'body' => array(array('value' => $node_body)),
'language' => $langcode,
);
$node = $this->drupalCreateNode($edit);
......
......@@ -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,
......
......@@ -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.
......@@ -429,6 +412,119 @@ function node_update_7004() {
return array();
}
/**
* 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;
}