From 4e3cdbac01a5294469a9bc172700042000c3b28d Mon Sep 17 00:00:00 2001
From: Jonathan Jordan <jojonaloha@1579186.no-reply.drupal.org>
Date: Thu, 10 Jul 2014 13:50:20 -0700
Subject: [PATCH] Issue #206820 by pillarsdotnet, catch, JamesAn, boydjd: Added
 Relocate or delete topics on forum deletion to avoid leaving stale data in
 database.

---
 modules/forum/forum.admin.inc | 195 ++++++++++++++++++++++++++++++++++++++++--
 modules/forum/forum.install   |   4 +-
 modules/forum/forum.module    |   6 +-
 modules/forum/forum.test      |   2 +-
 4 files changed, 198 insertions(+), 9 deletions(-)

diff --git a/modules/forum/forum.admin.inc b/modules/forum/forum.admin.inc
index 712cf54..9f6bb4a 100644
--- a/modules/forum/forum.admin.inc
+++ b/modules/forum/forum.admin.inc
@@ -21,7 +21,7 @@
  */
 function forum_form_main($type, $edit = array()) {
   $edit = (array) $edit;
-  if ((isset($_POST['op']) && $_POST['op'] == t('Delete')) || !empty($_POST['confirm'])) {
+  if ((isset($_POST['op']) && ($_POST['op'] == t('Delete') || $_POST['op'] == t('Move'))) || !empty($_POST['confirm'])) {
     return drupal_get_form('forum_confirm_delete', $edit['tid']);
   }
   switch ($type) {
@@ -207,26 +207,211 @@ function forum_form_container($form, &$form_state, $edit = array()) {
  */
 function forum_confirm_delete($form, &$form_state, $tid) {
   $term = taxonomy_term_load($tid);
+  $nids = taxonomy_select_nodes(array($tid), FALSE);
+  $topics = node_load_multiple($nids);
+  foreach ($topics as $nid => $topic) {
+    // We keep the actual tid in forum table, if it's different from the
+    // current tid then it means the topic appears in two forums, one of
+    // them is a shadow copy.
+    if (isset($topic->forum_tid) && $topic->forum_tid!= $tid) {
+      unset($topics[$nid]);
+    }
+  }
+
+  $question = t('Are you sure you want to delete the forum %name?', array('%name' => $term->name));
+  $description = format_plural(count($topics), 'This forum contains 1 topic. Deleting a forum or container will also delete its sub-forums and topics. This action cannot be undone.', 'This forum contains @count topics. Deleting a forum or container will also delete its sub-forums and topics. This action cannot be undone.');
+
+  drupal_set_title($question, PASS_THROUGH);
 
   $form['tid'] = array('#type' => 'value', '#value' => $tid);
   $form['name'] = array('#type' => 'value', '#value' => $term->name);
+  $form['description'] = array('#markup' => $description);
 
-  return confirm_form($form, t('Are you sure you want to delete the forum %name?', array('%name' => $term->name)), 'admin/structure/forum', t('Deleting a forum or container will also delete its sub-forums, if any. To delete posts in this forum, visit <a href="@content">content administration</a> first. This action cannot be undone.', array('@content' => url('admin/content'))), t('Delete'), t('Cancel'));
+  if (count($topics) > 0) {
+    // Enable language column if translation module is enabled
+    // or if we have any node with language.
+    $multilanguage = (module_exists('translation') || db_query_range("SELECT 1 FROM {node} WHERE language <> ''", 0, 1)->fetchField());
+
+    // Build topic listing for sortable table
+    $languages = language_list();
+    $topics_checkbox = array();
+    foreach ($topics as $topic) {
+      $topics_checkbox[$topic->nid] = '';
+      $options = empty($topic->language) ? array() : array('language' => $languages[$topic->language]);
+      $form['title'][$topic->nid] = array('#markup' => l($topic->title, 'node/' . $topic->nid, $options) . ' ' . theme('mark', array('type' => node_mark($topic->nid, $topic->changed))));
+      $form['username'][$topic->nid] = array('#markup' => format_username($topic));
+      $form['status'][$topic->nid] =  array('#markup' => ($topic->status ? t('published') : t('not published')));
+      $form['changed'][$topic->nid] = array('#markup' => format_date($topic->changed, 'small'));
+      if ($multilanguage) {
+        $form['language'][$topic->nid] = array('#markup' => empty($topic->language) ? t('Language neutral') : t($languages[$topic->language]->name));
+      }
+    }
+    $form['topics'] = array(
+      '#type' => 'checkboxes',
+      '#options' => $topics_checkbox,
+    );
+    $form['pager'] = array('#markup' => theme('pager'));
+
+    // Build the 'Move selected topics' form.
+    $form['move'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Move selected topics'),
+      '#prefix' => '<div class="container-inline">',
+      '#suffix' => '</div>',
+    );
+    // Get a drop-down menu of other forum/containers to move topics.
+    $dst_element = _forum_parent_select($tid, t('Move topics to'), 'forum');
+    // Enable the 'Move selected topics' form only if there are other forums/containers.
+    if (count($dst_element['#options']) > 1) {
+      unset($dst_element['#options'][0]);
+      $form['move']['tid_dst'] = $dst_element;
+      $form['move']['submit'] = array(
+        '#type' => 'submit',
+        '#value' => t('Move'),
+        '#validate' => array('forum_confirm_delete_move_validate'),
+        '#submit' => array('forum_confirm_delete_move_submit'),
+      );
+    }
+    else {
+      // If there are no destinations, disable 'Move selected topics' form and remove table checkboxes.
+      $form['move']['no_dst'] = array('#markup' => t('There are no other forums to move these topics.'));
+      unset($form['topics']);
+    }
+  }
+
+  $form = confirm_form($form, t('Are you sure you want to delete the forum %name?', array('%name' => $term->name)), 'admin/structure/forum', t('Deleting a forum or container will also delete its sub-forums, if any. To delete posts in this forum, visit <a href="@content">content administration</a> first. This action cannot be undone.', array('@content' => url('admin/content'))), t('Delete'));
+  $form['#theme'] = 'forum_confirm_delete';
+  return $form;
 }
 
 /**
  * Form submission handler for forum_confirm_delete().
  */
 function forum_confirm_delete_submit($form, &$form_state) {
-  taxonomy_term_delete($form_state['values']['tid']);
-  drupal_set_message(t('The forum %term and all sub-forums have been deleted.', array('%term' => $form_state['values']['name'])));
-  watchdog('content', 'forum: deleted %term and all its sub-forums.', array('%term' => $form_state['values']['name']));
+  $tid = $form_state['values']['tid'];
+  $nids = taxonomy_select_nodes(array($tid), FALSE);
+  $topics = node_load_multiple($nids);
+  foreach ($topics as $nid => $topic) {
+    // We keep the actual tid in forum table, if it's different from the
+    // current tid then it means the topic appears in two forums, one of
+    // them is a shadow copy.
+    if (isset($topic->forum_tid) && $topic->forum_tid!= $tid)
+      unset($topics[$nid]);
+  }
+  $nids = array_keys($topics);
+
+  // Delete the forum/container as well as all nodes that belong to it.
+  if (count($nids) > 0) {
+    node_delete_multiple($nids);
+  }
+  taxonomy_term_delete($tid);
 
+  drupal_set_message(t('The forum %term, all sub-forums, and all children topics have been deleted.', array('%term' => $form_state['values']['name'])));
+  watchdog('content', 'forum: deleted %term, all its sub-forums, and all its children topics.', array('%term' => $form_state['values']['name']));
   $form_state['redirect'] = 'admin/structure/forum';
   return;
 }
 
 /**
+ * Process forum delete confirmation move submissions.
+ *
+ * Execute the chosen 'Move location options' on the selected topics.
+ */
+function forum_confirm_delete_move_validate($form, &$form_state) {
+  $tid_dst = $form_state['values']['tid_dst'];
+  $term_dst = forum_forum_load($tid_dst);
+  if ($term_dst->container) {
+    form_set_error('move][tid_dst', t('The item %name is a forum container, not a forum. Select one of the forums below instead.', array('%name' => $term_dst->name)));
+  }
+}
+
+/**
+ * Process forum delete confirmation move submissions.
+ *
+ * Execute the chosen 'Move location options' on the selected topics.
+ */
+function forum_confirm_delete_move_submit($form, &$form_state) {
+  $tid_src = $form_state['values']['tid'];
+  $tid_dst = $form_state['values']['tid_dst'];
+  // Filter out unchecked nodes
+  $topics = array_filter($form_state['values']['topics']);
+  if (!empty($topics) && $tid_dst) {
+    $topics = node_load_multiple($topics);
+
+    foreach ($topics as $topic) {
+      $topic->tid = $tid_dst;
+      $topic->forum_tid = $tid_dst;
+      $topic->taxonomy_forums[LANGUAGE_NONE][0]['tid'] = $tid_dst;
+      node_save($topic);
+    }
+  }
+  // Return to the forum_confirm_delete form with these topics now deleted.
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Theme forum delete confirmation.
+ *
+ * @ingroup themeable
+ */
+function theme_forum_confirm_delete($variables) {
+  $form = $variables['form'];
+  $output = '';
+
+  if (isset($form['move'])) {
+    $output .= drupal_render($form['move']);
+
+    // Multilingual column
+    $multilanguage = isset($form['language']);
+
+    // Build the sortable table header.
+    $header = array();
+    $header[] = theme('table_select_header_cell');
+    $header[] = array('data' => t('Title'), 'field' => 'n.title');
+    $header[] = array('data' => t('Author'), 'field' => 'u.name');
+    $header[] = array('data' => t('Status'), 'field' => 'n.status');
+    $header[] = array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc');
+    if ($multilanguage) {
+      $header[] = array('data' => t('Language'), 'field' => 'n.language');
+    }
+
+    $has_topics = isset($form['title']) && is_array($form['title']);
+    if ($has_topics) {
+      $rows = array();
+      foreach (element_children($form['title']) as $key) {
+        $row = array();
+        if (isset($form['move']['tid_dst'])) {
+          $row[] = drupal_render($form['topics'][$key]);
+        }
+        $row[] = drupal_render($form['title'][$key]);
+        $row[] = drupal_render($form['username'][$key]);
+        $row[] = drupal_render($form['status'][$key]);
+        $row[] = drupal_render($form['changed'][$key]);
+        if ($multilanguage) {
+          $row[] = drupal_render($form['language'][$key]);
+        }
+        $rows[] = $row;
+      }
+    }
+    else {
+      $rows[] = array(
+        array('data' => t('No content available.'), 'colspan' => count($header)),
+      );
+    }
+
+    $output .= theme('table', array('header' => $header, 'rows' => $rows));
+
+    if ($form['pager']['#markup']) {
+      $output .= drupal_render($form['pager']);
+    }
+  }
+
+  $output .= drupal_render_children($form);
+
+  return $output;
+}
+
+/**
  * Form constructor for the forum settings page.
  *
  * @see forum_menu()
diff --git a/modules/forum/forum.install b/modules/forum/forum.install
index 57e116b..569e625 100644
--- a/modules/forum/forum.install
+++ b/modules/forum/forum.install
@@ -201,7 +201,7 @@ function forum_schema() {
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default'=> 0,
+        'default' => 0,
       ),
       'last_comment_timestamp' => array(
         'type' => 'int',
@@ -303,7 +303,7 @@ function forum_update_7001() {
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default'=> 0,
+        'default' => 0,
       ),
       'last_comment_timestamp' => array(
         'type' => 'int',
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index 575de36..efd6df6 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -80,6 +80,10 @@ function forum_theme() {
       'render element' => 'form',
       'file' => 'forum.admin.inc',
     ),
+    'forum_confirm_delete' => array(
+      'render element' => 'form',
+      'file' => 'forum.admin.inc',
+    ),
   );
 }
 
@@ -307,7 +311,7 @@ function forum_node_validate($node, $form) {
           form_set_error('taxonomy_forums', t('Select a forum.'));
           continue;
         }
-        $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid',0 , 1, array(
+        $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid', 0, 1, array(
           ':tid' => $term->tid,
           ':vid' => $term->vid,
         ))->fetchField();
diff --git a/modules/forum/forum.test b/modules/forum/forum.test
index bc68a3e..90f401b 100644
--- a/modules/forum/forum.test
+++ b/modules/forum/forum.test
@@ -223,7 +223,7 @@ class ForumTestCase extends DrupalWebTestCase {
 
     // Create an orphan forum item.
     $this->drupalLogin($this->admin_user);
-    $this->drupalPost('node/add/forum', array('title' => $this->randomName(10), 'body[' . LANGUAGE_NONE .'][0][value]' => $this->randomName(120)), t('Save'));
+    $this->drupalPost('node/add/forum', array('title' => $this->randomName(10), 'body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(120)), t('Save'));
 
     $nid_count = db_query('SELECT COUNT(nid) FROM {node}')->fetchField();
     $this->assertEqual(0, $nid_count, 'A forum node was not created when missing a forum vocabulary.');
-- 
1.8.5.2 (Apple Git-48)

