Commit c4e1242e authored by webchick's avatar webchick

#412518 by catch, bangpound, and yched: Convert taxonomy_node_* to field API...

#412518 by catch, bangpound, and yched: Convert taxonomy_node_* to field API (with upgrade path). Say buh-bye to old, crusty code.
parent f5b02199
......@@ -20,7 +20,7 @@
* - $topic->message: If the topic has been moved, this contains an
* explanation and a link.
* - $topic->zebra: 'even' or 'odd' string used for row class.
* - $topic->num_comments: The number of replies on this topic.
* - $topic->comment_count: The number of replies on this topic.
* - $topic->new_replies: A flag to indicate whether there are unread comments.
* - $topic->new_url: If there are unread replies, this is a link to them.
* - $topic->new_text: Text containing the translated, properly pluralized count.
......@@ -53,7 +53,7 @@
<td colspan="3"><?php print $topic->message; ?></td>
<?php else: ?>
<td class="replies">
<?php print $topic->num_comments; ?>
<?php print $topic->comment_count; ?>
<?php if ($topic->new_replies): ?>
<br />
<a href="<?php print $topic->new_url; ?>"><?php print $topic->new_text; ?></a>
......
......@@ -5,8 +5,8 @@
* @file
* Administrative page callbacks for the forum module.
*/
function forum_form_main($type, $edit = array()) {
$edit = (array) $edit;
if ((isset($_POST['op']) && $_POST['op'] == t('Delete')) || !empty($_POST['confirm'])) {
return drupal_get_form('forum_confirm_delete', $edit['tid']);
}
......
......@@ -22,9 +22,7 @@ function forum_install() {
function forum_enable() {
if ($vocabulary = taxonomy_vocabulary_load(variable_get('forum_nav_vocabulary', 0))) {
// Existing install. Add back forum node type, if the forums
// vocabulary still exists. Keep all other node types intact there.
$vocabulary->nodes['forum'] = 1;
// Save the vocabulary to create the default field instance.
taxonomy_vocabulary_save($vocabulary);
}
else {
......@@ -33,17 +31,26 @@ function forum_enable() {
// forms.
$edit = array(
'name' => t('Forums'),
'multiple' => 0,
'required' => 0,
'machine_name' => 'forums',
'description' => t('Forum navigation vocabulary'),
'hierarchy' => 1,
'relations' => 0,
'module' => 'forum',
'weight' => -10,
'nodes' => array('forum' => 1),
);
$vocabulary = (object) $edit;
taxonomy_vocabulary_save($vocabulary);
$instance = array(
'field_name' => 'taxonomy_' . $vocabulary->machine_name,
'label' => $vocabulary->name,
'bundle' => 'forum',
'widget' => array(
'type' => 'options_select',
),
);
field_create_instance($instance);
variable_set('forum_nav_vocabulary', $vocabulary->vid);
}
}
......@@ -109,6 +116,68 @@ function forum_schema() {
),
);
$schema['forum_index'] = array(
'description' => 'Maintains denormalized information about node/term relationships.',
'fields' => array(
'nid' => array(
'description' => 'The {node}.nid this record tracks.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'title' => array(
'description' => 'The title of this node, always treated as non-markup plain text.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'tid' => array(
'description' => 'The term ID.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(
'description' => 'Boolean indicating whether the node is sticky.',
'type' => 'int',
'not null' => FALSE,
'default' => 0,
'size' => 'tiny',
),
'created' => array(
'description' => 'The Unix timestamp when the node was created.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default'=> 0,
),
'last_comment_timestamp' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.timestamp.',
),
'comment_count' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The total number of comments on this node.',
),
),
'indexes' => array(
'forum_topics' => array('tid', 'sticky', 'last_comment_timestamp'),
),
'foreign keys' => array(
'node' => 'nid',
'taxonomy_term_data' => 'tid',
),
);
return $schema;
}
......@@ -119,3 +188,74 @@ function forum_update_7000() {
db_drop_index('forum', 'nid');
db_add_index('forum', 'forum_topic', array('nid', 'tid'));
}
/**
* Create new {forum_index} table.
*/
function forum_update_7001() {
$forum_index = array(
'description' => 'Maintains denormalized information about node/term relationships.',
'fields' => array(
'nid' => array(
'description' => 'The {node}.nid this record tracks.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'title' => array(
'description' => 'The title of this node, always treated as non-markup plain text.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'tid' => array(
'description' => 'The term ID.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(
'description' => 'Boolean indicating whether the node is sticky.',
'type' => 'int',
'not null' => FALSE,
'default' => 0,
'size' => 'tiny',
),
'created' => array(
'description' => 'The Unix timestamp when the node was created.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default'=> 0,
),
'last_comment_timestamp' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.timestamp.',
),
'comment_count' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The total number of comments on this node.',
),
),
'indexes' => array(
'forum_topics' => array('tid', 'sticky', 'last_comment_timestamp'),
),
'foreign keys' => array(
'node' => 'nid',
'taxonomy_term_data' => 'tid',
),
);
db_create_table($ret, 'forum_index', $forum_index);
db_query('INSERT INTO {forum_index} (SELECT n.nid, n.title, f.tid, n.sticky, n.created, ncs.last_comment_timestamp, ncs.comment_count FROM {node} n INNER JOIN {forum} f on n.vid = f.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid)');
return $ret;
}
This diff is collapsed.
......@@ -131,8 +131,7 @@ class ForumTestCase extends DrupalWebTestCase {
$edit = array(
'name' => $title,
'description' => $description,
'machine_name' => drupal_strtolower($this->randomName()),
'help' => '',
'machine_name' => drupal_strtolower(drupal_substr($this->randomName(), 3, 9)),
);
// Edit the vocabulary.
......@@ -251,7 +250,7 @@ class ForumTestCase extends DrupalWebTestCase {
$edit = array(
'title' => $title,
"body[$langcode][0][value]" => $body,
'taxonomy[1]' => $tid
"taxonomy_forums[$langcode][value]" => $tid,
);
// TODO The taxonomy select value is set by drupal code when the tid is part
......@@ -341,7 +340,7 @@ class ForumTestCase extends DrupalWebTestCase {
$langcode = FIELD_LANGUAGE_NONE;
$edit["body[$langcode][0][value]"] = $this->randomName(256);
// Assume the topic is initially associated with $forum.
$edit['taxonomy[1]'] = $this->root_forum['tid'];
$edit["taxonomy_forums[$langcode][value]"] = $this->root_forum['tid'];
$edit['shadow'] = TRUE;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Forum topic %title has been updated.', array('%title' => $edit['title'])), t('Forum node was edited'));
......
......@@ -85,7 +85,7 @@ function node_object_prepare($node) {
// If this is a new node, fill in the default values.
if (!isset($node->nid)) {
foreach (array('status', 'promote', 'sticky') as $key) {
$node->$key = in_array($key, $node_options);
$node->$key = (int) in_array($key, $node_options);
}
global $user;
$node->uid = $user->uid;
......@@ -543,9 +543,6 @@ function node_revision_revert_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
$node_revision->revision = 1;
$node_revision->log = t('Copy of the revision from %date.', array('%date' => format_date($node_revision->revision_timestamp)));
if (module_exists('taxonomy')) {
$node_revision->taxonomy = array_keys($node_revision->taxonomy);
}
node_save($node_revision);
......
......@@ -1355,14 +1355,15 @@ class DrupalDataApiTest extends DrupalWebTestCase {
$this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.'));
// Insert an object record for a table with a multi-field primary key.
$vocabulary_node_type = new stdClass();
$vocabulary_node_type->vid = $vocabulary->vid;
$vocabulary_node_type->type = 'page';
$insert_result = drupal_write_record('taxonomy_vocabulary_node_type', $vocabulary_node_type);
$node_access = new stdClass();
$node_access->nid = mt_rand();
$node_access->gid = mt_rand();
$node_access->realm = $this->randomName();
$insert_result = drupal_write_record('node_access', $node_access);
$this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.'));
// Update the record.
$update_result = drupal_write_record('taxonomy_vocabulary_node_type', $vocabulary_node_type, array('vid', 'type'));
$update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
$this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.'));
}
......
......@@ -17,14 +17,8 @@ function taxonomy_overview_vocabularies($form) {
$vocabularies = taxonomy_get_vocabularies();
$form['#tree'] = TRUE;
foreach ($vocabularies as $vocabulary) {
$types = array();
foreach ($vocabulary->nodes as $type) {
$node_type = node_type_get_name($type);
$types[] = $node_type ? check_plain($node_type) : check_plain($type);
}
$form[$vocabulary->vid]['#vocabulary'] = $vocabulary;
$form[$vocabulary->vid]['name'] = array('#markup' => check_plain($vocabulary->name));
$form[$vocabulary->vid]['types'] = array('#markup' => implode(', ', $types));
$form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta' => 10, '#default_value' => $vocabulary->weight);
$form[$vocabulary->vid]['edit'] = array('#markup' => l(t('edit vocabulary'), "admin/structure/taxonomy/$vocabulary->vid"));
$form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'), "admin/structure/taxonomy/$vocabulary->vid/list"));
......@@ -71,7 +65,6 @@ function theme_taxonomy_overview_vocabularies($form) {
$row = array();
$row[] = drupal_render($vocabulary['name']);
$row[] = drupal_render($vocabulary['types']);
if (isset($vocabulary['weight'])) {
$vocabulary['weight']['#attributes']['class'] = array('vocabulary-weight');
$row[] = drupal_render($vocabulary['weight']);
......@@ -87,7 +80,7 @@ function theme_taxonomy_overview_vocabularies($form) {
$rows[] = array(array('data' => t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => url('admin/structure/taxonomy/add'))), 'colspan' => '5'));
}
$header = array(t('Vocabulary name'), t('Content types'));
$header = array(t('Vocabulary name'));
if (isset($form['submit'])) {
$header[] = t('Weight');
drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight');
......@@ -110,12 +103,7 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
'name' => '',
'machine_name' => '',
'description' => '',
'help' => '',
'nodes' => array(),
'hierarchy' => 0,
'tags' => 0,
'multiple' => 0,
'required' => 0,
'weight' => 0,
);
$form['#vocabulary'] = (object) $edit;
......@@ -155,47 +143,11 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings),
),
);
$form['help'] = array(
'#type' => 'textfield',
'#title' => t('Help text'),
'#maxlength' => 255,
'#default_value' => $edit['help'],
'#description' => t('Instructions to present to the user when selecting terms, e.g., <em>"Enter a comma separated list of words"</em>.'),
);
$form['description'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
'#default_value' => $edit['description'],
);
$form['nodes'] = array(
'#type' => 'checkboxes',
'#title' => t('Apply to content types'),
'#default_value' => $edit['nodes'],
'#options' => array_map('check_plain', node_type_get_names()),
);
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('Settings'),
'#collapsible' => TRUE,
);
$form['settings']['tags'] = array(
'#type' => 'checkbox',
'#title' => t('Tags'),
'#default_value' => $edit['tags'],
'#description' => t('Terms are created by users when submitting posts by typing a comma separated list.'),
);
$form['settings']['multiple'] = array(
'#type' => 'checkbox',
'#title' => t('Multiple select'),
'#default_value' => $edit['multiple'],
'#description' => t('Allows posts to have more than one term from this vocabulary (always true for tags).'),
);
$form['settings']['required'] = array(
'#type' => 'checkbox',
'#title' => t('Required'),
'#default_value' => $edit['required'],
'#description' => t('At least one term in this vocabulary must be selected when submitting a post.'),
);
// Set the hierarchy to "multiple parents" by default. This simplifies the
// vocabulary form and standardizes the term form.
$form['hierarchy'] = array(
......@@ -225,6 +177,11 @@ function taxonomy_form_vocabulary_validate($form, &$form_state) {
if (!preg_match('!^[a-z0-9_]+$!', $form_state['values']['machine_name'])) {
form_set_error('machine_name', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
}
// Restrict machine names to 21 characters to avoid exceeding the limit
// for field names.
if (drupal_strlen($machine_name) > 21) {
form_set_error('machine_name', t('The machine-readable name must not exceed 21 characters.'));
}
// Do not allow duplicate machine names.
$vocabularies = taxonomy_get_vocabularies();
......@@ -246,8 +203,6 @@ function taxonomy_form_vocabulary_submit($form, &$form_state) {
$form_state['confirm_delete'] = TRUE;
return;
}
// Fix up the nodes array to remove unchecked nodes.
$form_state['values']['nodes'] = array_filter($form_state['values']['nodes']);
$vocabulary = (object) $form_state['values'];
if ($vocabulary->machine_name != $old_vocabulary->machine_name) {
field_attach_rename_bundle($old_vocabulary->machine_name, $vocabulary->machine_name);
......@@ -306,106 +261,71 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) {
// An array of the terms to be displayed on this page.
$current_page = array();
// Case for free tagging.
if ($vocabulary->tags) {
// We are not calling taxonomy_get_tree because that might fail with a big
// number of tags in the freetagging vocabulary.
$query = db_select('taxonomy_term_data', 't')->extend('PagerDefault');
$query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid');
$query->addTag('term_access');
$query->condition('t.vid', $vocabulary->vid);
// Store count in total entries and use this as count query.
$count_query = db_select('taxonomy_term_data', 't');
$count_query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid');
$count_query->addTag('term_access');
$count_query->condition('t.vid', $vocabulary->vid);
$count_query->addExpression('COUNT(t.tid)');
$total_entries = $count_query->execute();
$query->setCountQuery($count_query);
$result = $query
->fields('t')
->fields('h', array('parent'))
->orderBy('weight')
->orderBy('name')
->limit($page_increment)
->execute();
foreach ($result as $term) {
$key = 'tid:' . $term->tid . ':0';
$current_page[$key] = $term;
$page_entries++;
$term_deltas = array();
$tree = taxonomy_get_tree($vocabulary->vid);
$term = current($tree);
do {
// In case this tree is completely empty.
if (empty($term)) {
break;
}
// Count entries before the current page.
if ($page && ($page * $page_increment) > $before_entries && !isset($back_peddle)) {
$before_entries++;
continue;
}
// Count entries after the current page.
elseif ($page_entries > $page_increment && isset($complete_tree)) {
$after_entries++;
continue;
}
}
// Case for restricted vocabulary.
else {
$term_deltas = array();
$tree = taxonomy_get_tree($vocabulary->vid);
$term = current($tree);
do {
// In case this tree is completely empty.
if (empty($term)) {
break;
}
// Count entries before the current page.
if ($page && ($page * $page_increment) > $before_entries && !isset($back_peddle)) {
$before_entries++;
continue;
}
// Count entries after the current page.
elseif ($page_entries > $page_increment && isset($complete_tree)) {
$after_entries++;
continue;
}
// Do not let a term start the page that is not at the root.
if (isset($term->depth) && ($term->depth > 0) && !isset($back_peddle)) {
$back_peddle = 0;
while ($pterm = prev($tree)) {
$before_entries--;
$back_peddle++;
if ($pterm->depth == 0) {
prev($tree);
continue 2; // Jump back to the start of the root level parent.
}
}
}
$back_peddle = isset($back_peddle) ? $back_peddle : 0;
// Continue rendering the tree until we reach the a new root item.
if ($page_entries >= $page_increment + $back_peddle + 1 && $term->depth == 0 && $root_entries > 1) {
$complete_tree = TRUE;
// This new item at the root level is the first item on the next page.
$after_entries++;
continue;
}
if ($page_entries >= $page_increment + $back_peddle) {
$forward_peddle++;
// Do not let a term start the page that is not at the root.
if (isset($term->depth) && ($term->depth > 0) && !isset($back_peddle)) {
$back_peddle = 0;
while ($pterm = prev($tree)) {
$before_entries--;
$back_peddle++;
if ($pterm->depth == 0) {
prev($tree);
continue 2; // Jump back to the start of the root level parent.
}
}
}
$back_peddle = isset($back_peddle) ? $back_peddle : 0;
// Continue rendering the tree until we reach the a new root item.
if ($page_entries >= $page_increment + $back_peddle + 1 && $term->depth == 0 && $root_entries > 1) {
$complete_tree = TRUE;
// This new item at the root level is the first item on the next page.
$after_entries++;
continue;
}
if ($page_entries >= $page_increment + $back_peddle) {
$forward_peddle++;
}
// Finally, if we've gotten down this far, we're rendering a term on this page.
$page_entries++;
$term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0;
$key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
// Finally, if we've gotten down this far, we're rendering a term on this page.
$page_entries++;
$term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0;
$key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
// Keep track of the first term displayed on this page.
if ($page_entries == 1) {
$form['#first_tid'] = $term->tid;
}
// Keep a variable to make sure at least 2 root elements are displayed.
if ($term->parents[0] == 0) {
$root_entries++;
}
$current_page[$key] = $term;
} while ($term = next($tree));
// Keep track of the first term displayed on this page.
if ($page_entries == 1) {
$form['#first_tid'] = $term->tid;
}
// Keep a variable to make sure at least 2 root elements are displayed.
if ($term->parents[0] == 0) {
$root_entries++;
}
$current_page[$key] = $term;
} while ($term = next($tree));
// Because we didn't use a pager query, set the necessary pager variables.
$total_entries = $before_entries + $page_entries + $after_entries;
$pager_total_items[0] = $total_entries;
$pager_page_array[0] = $page;
$pager_total[0] = ceil($total_entries / $page_increment);
}
// Because we didn't use a pager query, set the necessary pager variables.
$total_entries = $before_entries + $page_entries + $after_entries;
$pager_total_items[0] = $total_entries;
$pager_page_array[0] = $page;
$pager_total[0] = ceil($total_entries / $page_increment);
// If this form was already submitted once, it's probably hit a validation
// error. Ensure the form is rebuilt in the same order as the user submitted.
......@@ -433,7 +353,7 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) {
}
$form[$key]['view'] = array('#markup' => l($term->name, "taxonomy/term/$term->tid"));
if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) > 1) {
if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
$form['#parent_fields'] = TRUE;
$form[$key]['tid'] = array(
'#type' => 'hidden',
......@@ -460,7 +380,7 @@ function taxonomy_overview_terms($form, &$form_state, $vocabulary) {
$form['#forward_peddle'] = $forward_peddle;
$form['#empty_text'] = t('No terms available. <a href="@link">Add term</a>.', array('@link' => url('admin/structure/taxonomy/' . $vocabulary->vid . '/list/add')));
if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) > 1) {
if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save')
......@@ -690,7 +610,6 @@ function taxonomy_form_term($form, &$form_state, $vocabulary, $edit = array()) {
$form['#term'] = $edit;
$form['#term']['parent'] = $parent;
$form['#vocabulary'] = $vocabulary;
$form['#vocabulary']->nodes = drupal_map_assoc($vocabulary->nodes);
$form['#builder_function'] = 'taxonomy_form_term_submit_builder';
// Check for confirmation forms.
......@@ -747,7 +666,7 @@ function taxonomy_form_term($form, &$form_state, $vocabulary, $edit = array()) {
}
$exclude[] = $edit['tid'];
$form['advanced']['parent'] = _taxonomy_term_select(t('Parents'), $parent, $vocabulary->vid, t('Parent terms') . '.', 1, '<' . t('root') . '>', $exclude);
$form['advanced']['parent'] = _taxonomy_term_select(t('Parents'), $parent, $vocabulary->vid, t('Parent terms') . '.', '<' . t('root') . '>', $exclude);
}
$form['advanced']['synonyms'] = array(
'#type' => 'textarea',
......@@ -814,7 +733,7 @@ function taxonomy_form_term_submit($form, &$form_state) {
return;
}
// Rebuild the form to confirm enabling multiple parents.
elseif ($form_state['clicked_button']['#value'] == t('Save') && !$form['#vocabulary']->tags && count($form_state['values']['parent']) > 1 && $form['#vocabulary']->hierarchy < 2) {
elseif ($form_state['clicked_button']['#value'] == t('Save') && count($form_state['values']['parent']) > 1 && $form['#vocabulary']->hierarchy < 2) {
$form_state['rebuild'] = TRUE;
$form_state['confirm_parents'] = TRUE;
return;
......@@ -834,26 +753,24 @@ function taxonomy_form_term_submit($form, &$form_state) {