[ Index ]

PHP Cross Reference of MyBB 1.8.32

title

Body

[close]

/inc/ -> class_moderation.php (source)

   1  <?php
   2  /**
   3   * MyBB 1.8
   4   * Copyright 2014 MyBB Group, All Rights Reserved
   5   *
   6   * Website: http://www.mybb.com
   7   * License: http://www.mybb.com/about/license
   8   *
   9   */
  10  
  11  class Moderation
  12  {
  13      /**
  14       * Close one or more threads
  15       *
  16       * @param array|int $tids Thread ID(s)
  17       * @return boolean true
  18       */
  19  	function close_threads($tids)
  20      {
  21          global $db, $plugins;
  22  
  23          if(!is_array($tids))
  24          {
  25              $tids = array($tids);
  26          }
  27  
  28          // Make sure we only have valid values
  29          $tids = array_map('intval', $tids);
  30  
  31          $plugins->run_hooks("class_moderation_close_threads", $tids);
  32  
  33          $tid_list = implode(',', $tids);
  34  
  35          $openthread = array(
  36              "closed" => 1,
  37          );
  38          $db->update_query("threads", $openthread, "tid IN ($tid_list) AND closed NOT LIKE 'moved|%'");
  39  
  40          return true;
  41      }
  42  
  43      /**
  44       * Open one or more threads
  45       *
  46       * @param array|int $tids Thread ID(s)
  47       * @return boolean
  48       */
  49  
  50  	function open_threads($tids)
  51      {
  52          global $db, $plugins;
  53  
  54          if(!is_array($tids))
  55          {
  56              $tids = array($tids);
  57          }
  58  
  59          if(empty($tids))
  60          {
  61              return false;
  62          }
  63  
  64          // Make sure we only have valid values
  65          $tids = array_map('intval', $tids);
  66  
  67          $plugins->run_hooks("class_moderation_open_threads", $tids);
  68  
  69          $tid_list = implode(',', $tids);
  70  
  71          $closethread = array(
  72              "closed" => 0,
  73          );
  74          $db->update_query("threads", $closethread, "tid IN ($tid_list)");
  75  
  76          return true;
  77      }
  78  
  79      /**
  80       * Stick one or more threads
  81       *
  82       * @param array|int $tids Thread ID(s)
  83       * @return boolean
  84       */
  85  	function stick_threads($tids)
  86      {
  87          global $db, $plugins;
  88  
  89          if(!is_array($tids))
  90          {
  91              $tids = array($tids);
  92          }
  93  
  94          if(empty($tids))
  95          {
  96              return false;
  97          }
  98  
  99          // Make sure we only have valid values
 100          $tids = array_map('intval', $tids);
 101  
 102          $plugins->run_hooks("class_moderation_stick_threads", $tids);
 103  
 104          $tid_list = implode(',', $tids);
 105  
 106          $stickthread = array(
 107              "sticky" => 1,
 108          );
 109          $db->update_query("threads", $stickthread, "tid IN ($tid_list)");
 110  
 111          return true;
 112      }
 113  
 114      /**
 115       * Unstick one or more thread
 116       *
 117       * @param array|int $tids Thread ID(s)
 118       * @return boolean
 119       */
 120  	function unstick_threads($tids)
 121      {
 122          global $db, $plugins;
 123  
 124          if(!is_array($tids))
 125          {
 126              $tids = array($tids);
 127          }
 128  
 129          if(empty($tids))
 130          {
 131              return false;
 132          }
 133  
 134          // Make sure we only have valid values
 135          $tids = array_map('intval', $tids);
 136  
 137          $plugins->run_hooks("class_moderation_unstick_threads", $tids);
 138  
 139          $tid_list = implode(',', $tids);
 140  
 141          $unstickthread = array(
 142              "sticky" => 0,
 143          );
 144          $db->update_query("threads", $unstickthread, "tid IN ($tid_list)");
 145  
 146          return true;
 147      }
 148  
 149      /**
 150       * Remove redirects that redirect to the specified thread
 151       *
 152       * @param int $tid Thread ID of the thread
 153       * @return boolean
 154       */
 155  	function remove_redirects($tid)
 156      {
 157          global $db, $plugins;
 158  
 159          $plugins->run_hooks("class_moderation_remove_redirects", $tid);
 160  
 161          // Delete the redirects
 162          $tid = (int)$tid;
 163          if(empty($tid))
 164          {
 165              return false;
 166          }
 167  
 168          $query = $db->simple_select('threads', 'tid', "closed='moved|$tid'");
 169          while($redirect_tid = $db->fetch_field($query, 'tid'))
 170          {
 171              $this->delete_thread($redirect_tid);
 172          }
 173  
 174          return true;
 175      }
 176  
 177      /**
 178       * Delete a thread
 179       *
 180       * @param int $tid Thread ID of the thread
 181       * @return boolean
 182       */
 183  	function delete_thread($tid)
 184      {
 185          global $db, $cache, $plugins;
 186  
 187          $tid = (int)$tid;
 188  
 189          $plugins->run_hooks("class_moderation_delete_thread_start", $tid);
 190  
 191          $thread = get_thread($tid);
 192          if(!$thread)
 193          {
 194              return false;
 195          }
 196          $forum = get_forum($thread['fid']);
 197  
 198          $userposts = array();
 199  
 200          // Find the pid, uid, visibility, and forum post count status
 201          $query = $db->simple_select('posts', 'pid, uid, visible', "tid='{$tid}'");
 202          $pids = array();
 203          $num_unapproved_posts = $num_approved_posts = $num_deleted_posts = 0;
 204          while($post = $db->fetch_array($query))
 205          {
 206              $pids[] = $post['pid'];
 207  
 208              if(!function_exists("remove_attachments"))
 209              {
 210                  require_once  MYBB_ROOT."inc/functions_upload.php";
 211              }
 212  
 213              // Remove attachments
 214              remove_attachments($post['pid']);
 215  
 216              // If the post is unapproved, count it!
 217              if(($post['visible'] == 0 && $thread['visible'] != -1) || $thread['visible'] == 0)
 218              {
 219                  $num_unapproved_posts++;
 220              }
 221              elseif($post['visible'] == -1 || $thread['visible'] == -1)
 222              {
 223                  $num_deleted_posts++;
 224              }
 225              else
 226              {
 227                  $num_approved_posts++;
 228  
 229                  // Count the post counts for each user to be subtracted
 230                  if($forum['usepostcounts'] != 0)
 231                  {
 232                      if(!isset($userposts[$post['uid']]['num_posts']))
 233                      {
 234                          $userposts[$post['uid']]['num_posts'] = 0;
 235                      }
 236                      ++$userposts[$post['uid']]['num_posts'];
 237                  }
 238              }
 239          }
 240  
 241          if($forum['usethreadcounts'] != 0 && substr($thread['closed'], 0, 6) != 'moved|')
 242          {
 243              if(!isset($userposts[$thread['uid']]['num_threads']))
 244              {
 245                  $userposts[$thread['uid']]['num_threads'] = 0;
 246              }
 247              ++$userposts[$thread['uid']]['num_threads'];
 248          }
 249  
 250          // Remove post count from users
 251          if($thread['visible'] == 1)
 252          {
 253              if(!empty($userposts))
 254              {
 255                  foreach($userposts as $uid => $subtract)
 256                  {
 257                      $update_array = array();
 258  
 259                      if(isset($subtract['num_posts']))
 260                      {
 261                          $update_array['postnum'] = "-{$subtract['num_posts']}";
 262                      }
 263  
 264                      if(isset($subtract['num_threads']))
 265                      {
 266                          $update_array['threadnum'] = "-{$subtract['num_threads']}";
 267                      }
 268  
 269                      update_user_counters($uid, $update_array);
 270                  }
 271              }
 272          }
 273          // Delete posts and their attachments
 274          if(!empty($pids))
 275          {
 276              $pids = implode(',', $pids);
 277              $db->delete_query("posts", "pid IN ($pids)");
 278              $db->delete_query("attachments", "pid IN ($pids)");
 279              $db->delete_query("reportedcontent", "id IN ($pids) AND (type = 'post' OR type = '')");
 280          }
 281  
 282          // Delete threads, redirects, subscriptions, polls, and poll votes
 283          $db->delete_query("threads", "tid='$tid'");
 284          $query = $db->simple_select('threads', 'tid', "closed='moved|$tid'");
 285          while($redirect_tid = $db->fetch_field($query, 'tid'))
 286          {
 287              $this->delete_thread($redirect_tid);
 288          }
 289          $db->delete_query("threadsubscriptions", "tid='$tid'");
 290          $db->delete_query("polls", "tid='$tid'");
 291          $db->delete_query("pollvotes", "pid='".$thread['poll']."'");
 292          $db->delete_query("threadsread", "tid='$tid'");
 293          $db->delete_query("threadratings", "tid='$tid'");
 294  
 295          $updated_counters = array(
 296              "posts" => "-{$num_approved_posts}",
 297              "unapprovedposts" => "-{$num_unapproved_posts}",
 298              "deletedposts" => "-{$num_deleted_posts}"
 299          );
 300  
 301          if($thread['visible'] == 1)
 302          {
 303              $updated_counters['threads'] = -1;
 304          }
 305          elseif($thread['visible'] == -1)
 306          {
 307              $updated_counters['deletedthreads'] = -1;
 308          }
 309          else
 310          {
 311              $updated_counters['unapprovedthreads'] = -1;
 312          }
 313  
 314          if(strpos($thread['closed'], 'moved|') !== false)
 315          {
 316              // Redirect
 317              if($thread['visible'] == 1)
 318              {
 319                  $updated_counters['posts'] = -1;
 320              }
 321              elseif($thread['visible'] == -1)
 322              {
 323                  $updated_counters['deletedposts'] = -1;
 324              }
 325              else
 326              {
 327                  $updated_counters['unapprovedposts'] = -1;
 328              }
 329          }
 330  
 331          // Update forum count
 332          update_forum_counters($thread['fid'], $updated_counters);
 333          update_forum_lastpost($thread['fid']);
 334          mark_reports($tid, 'thread');
 335  
 336          $plugins->run_hooks("class_moderation_delete_thread", $tid);
 337  
 338          return true;
 339      }
 340  
 341      /**
 342       * Delete a poll
 343       *
 344       * @param int $pid Poll id
 345       * @return boolean
 346       */
 347  	function delete_poll($pid)
 348      {
 349          global $db, $plugins;
 350  
 351          $pid = (int)$pid;
 352  
 353          if(empty($pid))
 354          {
 355              return false;
 356          }
 357  
 358          $plugins->run_hooks("class_moderation_delete_poll", $pid);
 359  
 360          $db->delete_query("polls", "pid='$pid'");
 361          $db->delete_query("pollvotes", "pid='$pid'");
 362          $pollarray = array(
 363              'poll' => '0',
 364          );
 365          $db->update_query("threads", $pollarray, "poll='$pid'");
 366  
 367          return true;
 368      }
 369  
 370      /**
 371       * Approve one or more threads
 372       *
 373       * @param array|int $tids Thread ID(s)
 374       * @return boolean
 375       */
 376  	function approve_threads($tids)
 377      {
 378          global $db, $cache, $plugins;
 379  
 380          if(!is_array($tids))
 381          {
 382              $tids = array($tids);
 383          }
 384  
 385          if(empty($tids))
 386          {
 387              return false;
 388          }
 389  
 390          // Make sure we only have valid values
 391          $tids = array_map('intval', $tids);
 392  
 393          $tid_list = $forum_counters = $user_counters = $posts_to_approve = array();
 394  
 395          $tids_list = implode(",", $tids);
 396          $query = $db->simple_select("threads", "*", "tid IN ($tids_list)");
 397  
 398          while($thread = $db->fetch_array($query))
 399          {
 400              if($thread['visible'] == 1 || $thread['visible'] == -1)
 401              {
 402                  continue;
 403              }
 404              $tid_list[] = $thread['tid'];
 405  
 406              $forum = get_forum($thread['fid']);
 407  
 408              if(!isset($forum_counters[$forum['fid']]))
 409              {
 410                  $forum_counters[$forum['fid']] = array(
 411                      'num_posts' => 0,
 412                      'num_threads' => 0,
 413                      'num_deleted_posts' => 0,
 414                      'num_unapproved_posts' => 0
 415                  );
 416              }
 417  
 418              if(!isset($user_counters[$thread['uid']]))
 419              {
 420                  $user_counters[$thread['uid']] = array(
 421                      'num_posts' => 0,
 422                      'num_threads' => 0
 423                  );
 424              }
 425  
 426              ++$forum_counters[$forum['fid']]['num_threads'];
 427              $forum_counters[$forum['fid']]['num_posts'] += $thread['replies']+1; // Remove implied visible from count
 428              $forum_counters[$forum['fid']]['num_deleted_posts'] += $thread['deletedposts'];
 429              $forum_counters[$forum['fid']]['num_unapproved_posts'] += $thread['deletedposts']+$thread['replies']+1;
 430  
 431              if($forum['usepostcounts'] != 0)
 432              {
 433                  // On approving thread restore user post counts
 434                  $query2 = $db->simple_select("posts", "COUNT(pid) as posts, uid", "tid='{$thread['tid']}' AND (visible='1' OR pid='{$thread['firstpost']}') AND uid > 0 GROUP BY uid");
 435                  while($counter = $db->fetch_array($query2))
 436                  {
 437                      $user_counters[$counter['uid']]['num_posts'] += $counter['posts'];
 438                  }
 439              }
 440  
 441              if($forum['usethreadcounts'] != 0 && substr($thread['closed'], 0, 6) != 'moved|')
 442              {
 443                  ++$user_counters[$thread['uid']]['num_threads'];
 444              }
 445  
 446              $posts_to_approve[] = $thread['firstpost'];
 447          }
 448  
 449          if(!empty($tid_list))
 450          {
 451              $tid_moved_list = "";
 452              $comma = "";
 453              foreach($tid_list as $tid)
 454              {
 455                  $tid_moved_list .= "{$comma}'moved|{$tid}'";
 456                  $comma = ",";
 457              }
 458              $tid_list = implode(',', $tid_list);
 459              $approve = array(
 460                  "visible" => 1
 461              );
 462              $db->update_query("threads", $approve, "tid IN ($tid_list)");
 463              // Approve redirects, too
 464              $redirect_tids = array();
 465              $query = $db->simple_select('threads', 'tid', "closed IN ({$tid_moved_list})");
 466              while($redirect_tid = $db->fetch_field($query, 'tid'))
 467              {
 468                  $redirect_tids[] = $redirect_tid;
 469              }
 470              if(!empty($redirect_tids))
 471              {
 472                  $this->approve_threads($redirect_tids);
 473              }
 474              if(!empty($posts_to_approve))
 475              {
 476                  $db->update_query("posts", $approve, "pid IN (".implode(',', $posts_to_approve).")");
 477              }
 478  
 479              $plugins->run_hooks("class_moderation_approve_threads", $tids);
 480  
 481              if(!empty($forum_counters))
 482              {
 483                  foreach($forum_counters as $fid => $counters)
 484                  {
 485                      // Update stats
 486                      $update_array = array(
 487                          "threads" => "+{$counters['num_threads']}",
 488                          "unapprovedthreads" => "-{$counters['num_threads']}",
 489                          "posts" => "+{$counters['num_posts']}",
 490                          "unapprovedposts" => "-{$counters['num_unapproved_posts']}",
 491                          "deletedposts" => "+{$counters['num_deleted_posts']}"
 492                      );
 493                      update_forum_counters($fid, $update_array);
 494                      update_forum_lastpost($fid);
 495                  }
 496              }
 497  
 498              if(!empty($user_counters))
 499              {
 500                  foreach($user_counters as $uid => $counters)
 501                  {
 502                      $update_array = array(
 503                          "postnum" => "+{$counters['num_posts']}",
 504                          "threadnum" => "+{$counters['num_threads']}",
 505                      );
 506                      update_user_counters($uid, $update_array);
 507                  }
 508              }
 509          }
 510          return true;
 511      }
 512  
 513      /**
 514       * Unapprove one or more threads
 515       *
 516       * @param array|int $tids Thread ID(s)
 517       * @return boolean
 518       */
 519  	function unapprove_threads($tids)
 520      {
 521          global $db, $cache, $plugins;
 522  
 523          if(!is_array($tids))
 524          {
 525              $tids = array($tids);
 526          }
 527  
 528          if(empty($tids))
 529          {
 530              return false;
 531          }
 532  
 533          // Make sure we only have valid values
 534          $tids = array_map('intval', $tids);
 535  
 536          $tid_list = implode(',', $tids);
 537          $tid_moved_list = "";
 538          $comma = "";
 539          foreach($tids as $tid)
 540          {
 541              $tid_moved_list .= "{$comma}'moved|{$tid}'";
 542              $comma = ",";
 543          }
 544  
 545          $forum_counters = $user_counters = $posts_to_unapprove = array();
 546  
 547          $tids_list = implode(",", $tids);
 548          $query = $db->simple_select("threads", "*", "tid IN ($tids_list)");
 549  
 550          while($thread = $db->fetch_array($query))
 551          {
 552              $forum = get_forum($thread['fid']);
 553  
 554              if($thread['visible'] == 1 || $thread['visible'] == -1)
 555              {
 556                  if(!isset($forum_counters[$forum['fid']]))
 557                  {
 558                      $forum_counters[$forum['fid']] = array(
 559                          'num_threads' => 0,
 560                          'num_posts' => 0,
 561                          'num_unapprovedthreads' => 0,
 562                          'num_unapprovedposts' => 0,
 563                          'num_deletedthreads' => 0,
 564                          'num_deletedposts' => 0
 565                      );
 566                  }
 567  
 568                  if(!isset($user_counters[$thread['uid']]))
 569                  {
 570                      $user_counters[$thread['uid']] = array(
 571                          'num_posts' => 0,
 572                          'num_threads' => 0
 573                      );
 574                  }
 575  
 576                  ++$forum_counters[$forum['fid']]['num_unapprovedthreads'];
 577                  $forum_counters[$forum['fid']]['num_unapprovedposts'] += $thread['replies']+$thread['deletedposts']+1;
 578  
 579                  if($thread['visible'] == 1)
 580                  {
 581                      ++$forum_counters[$forum['fid']]['num_threads'];
 582                      $forum_counters[$forum['fid']]['num_posts'] += $thread['replies']+1; // Add implied invisible to count
 583                      $forum_counters[$forum['fid']]['num_deletedposts'] += $thread['deletedposts'];
 584                  }
 585                  else
 586                  {
 587                      ++$forum_counters[$forum['fid']]['num_deletedthreads'];
 588                      $forum_counters[$forum['fid']]['num_deletedposts'] += $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1; // Add implied invisible to count
 589                      $forum_counters[$forum['fid']]['num_unapprovedposts'] += $thread['unapprovedposts'];
 590                  }
 591  
 592                  // On unapproving thread update user post counts
 593                  if($thread['visible'] == 1 && $forum['usepostcounts'] != 0)
 594                  {
 595                      $query2 = $db->simple_select("posts", "COUNT(pid) AS posts, uid", "tid='{$thread['tid']}' AND (visible='1' OR pid='{$thread['firstpost']}') AND uid > 0 GROUP BY uid");
 596                      while($counter = $db->fetch_array($query2))
 597                      {
 598                          $user_counters[$counter['uid']]['num_posts'] += $counter['posts'];
 599                      }
 600                  }
 601  
 602                  if($thread['visible'] == 1 && $forum['usethreadcounts'] != 0 && substr($thread['closed'], 0, 6) != 'moved|')
 603                  {
 604                      ++$user_counters[$thread['uid']]['num_threads'];
 605                  }
 606  
 607              }
 608              $posts_to_unapprove[] = $thread['firstpost'];
 609          }
 610  
 611          $approve = array(
 612              "visible" => 0
 613          );
 614          $db->update_query("threads", $approve, "tid IN ($tid_list)");
 615          // Unapprove redirects, too
 616          $redirect_tids = array();
 617          $query = $db->simple_select('threads', 'tid', "closed IN ({$tid_moved_list})");
 618          while($redirect_tid = $db->fetch_field($query, 'tid'))
 619          {
 620              $redirect_tids[] = $redirect_tid;
 621          }
 622          if(!empty($redirect_tids))
 623          {
 624              $this->unapprove_threads($redirect_tids);
 625          }
 626          if(!empty($posts_to_unapprove))
 627          {
 628              $db->update_query("posts", $approve, "pid IN (".implode(',', $posts_to_unapprove).")");
 629          }
 630  
 631          $plugins->run_hooks("class_moderation_unapprove_threads", $tids);
 632  
 633          if(!empty($forum_counters))
 634          {
 635              foreach($forum_counters as $fid => $counters)
 636              {
 637                  // Update stats
 638                  $update_array = array(
 639                      "threads" => "-{$counters['num_threads']}",
 640                      "unapprovedthreads" => "+{$counters['num_unapprovedthreads']}",
 641                      "posts" => "-{$counters['num_posts']}",
 642                      "unapprovedposts" => "+{$counters['num_unapprovedposts']}",
 643                      "deletedthreads" => "-{$counters['num_deletedthreads']}",
 644                      "deletedposts" => "-{$counters['num_deletedposts']}"
 645                  );
 646                  update_forum_counters($fid, $update_array);
 647                  update_forum_lastpost($fid);
 648              }
 649          }
 650  
 651          if(!empty($user_counters))
 652          {
 653              foreach($user_counters as $uid => $counters)
 654              {
 655                  $update_array = array(
 656                      "postnum" => "-{$counters['num_posts']}",
 657                      "threadnum" => "-{$counters['num_threads']}",
 658                  );
 659                  update_user_counters($uid, $update_array);
 660              }
 661          }
 662  
 663          return true;
 664      }
 665  
 666      /**
 667       * Delete a specific post
 668       *
 669       * @param int $pid Post ID
 670       * @return boolean
 671       */
 672  	function delete_post($pid)
 673      {
 674          global $db, $cache, $plugins;
 675  
 676          $pid = $plugins->run_hooks("class_moderation_delete_post_start", $pid);
 677          // Get pid, uid, fid, tid, visibility, forum post count status of post
 678          $pid = (int)$pid;
 679          $query = $db->query("
 680              SELECT p.pid, p.uid, p.fid, p.tid, p.visible, t.visible as threadvisible
 681              FROM ".TABLE_PREFIX."posts p
 682              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
 683              WHERE p.pid='$pid'
 684          ");
 685          $post = $db->fetch_array($query);
 686          if(!$post)
 687          {
 688              return false;
 689          }
 690  
 691          $forum = get_forum($post['fid']);
 692          // If post counts enabled in this forum and it hasn't already been unapproved, remove 1
 693          if($forum['usepostcounts'] != 0 && $post['visible'] != -1 && $post['visible'] != 0 && $post['threadvisible'] != 0 && $post['threadvisible'] != -1)
 694          {
 695              update_user_counters($post['uid'], array('postnum' => "-1"));
 696          }
 697  
 698          if(!function_exists("remove_attachments"))
 699          {
 700              require  MYBB_ROOT."inc/functions_upload.php";
 701          }
 702  
 703          // Remove attachments
 704          remove_attachments($pid);
 705  
 706          // Delete the post
 707          $db->delete_query("posts", "pid='$pid'");
 708  
 709          // Remove any reports attached to this post
 710          $db->delete_query("reportedcontent", "id='{$pid}' AND (type = 'post' OR type = '')");
 711  
 712          // Update unapproved post count
 713          if($post['visible'] == 0)
 714          {
 715              $update_array = array(
 716                  "unapprovedposts" => "-1"
 717              );
 718          }
 719          elseif($post['visible'] == -1)
 720          {
 721              $update_array = array(
 722                  "deletedposts" => "-1"
 723              );
 724          }
 725          else
 726          {
 727              $update_array = array(
 728                  "replies" => "-1"
 729              );
 730          }
 731  
 732          $plugins->run_hooks("class_moderation_delete_post", $post['pid']);
 733  
 734          update_thread_counters($post['tid'], $update_array);
 735          update_last_post($post['tid']);
 736  
 737          // Update unapproved post count
 738          if(($post['visible'] == 0 && $post['threadvisible'] != -1) || $post['threadvisible'] == 0)
 739          {
 740              $update_array = array(
 741                  "unapprovedposts" => "-1"
 742              );
 743          }
 744          elseif($post['visible'] == -1 || $post['threadvisible'] == -1)
 745          {
 746              $update_array = array(
 747                  "deletedposts" => "-1"
 748              );
 749          }
 750          else
 751          {
 752              $update_array = array(
 753                  "posts" => "-1"
 754              );
 755          }
 756  
 757          update_forum_counters($post['fid'], $update_array);
 758          update_forum_lastpost($post['fid']);
 759  
 760          return true;
 761      }
 762  
 763      /**
 764       * Merge posts within thread
 765       *
 766       * @param array $pids Post IDs to be merged
 767       * @param int $tid Thread ID (Set to 0 if posts from multiple threads are selected)
 768       * @return int ID of the post into which all other posts are merged
 769       */
 770  	function merge_posts($pids=array(), $tid=0, $sep="new_line")
 771      {
 772          global $db, $plugins;
 773  
 774          // Make sure we only have valid values
 775          $pids = array_map('intval', $pids);
 776  
 777          if(empty($pids) || count($pids) < 2)
 778          {
 779              return false;
 780          }
 781  
 782          $pidin = implode(',', $pids);
 783          $attachment_count = 0;
 784  
 785          $first = 1;
 786          // Get the messages to be merged
 787          $query = $db->query("
 788              SELECT p.pid, p.uid, p.fid, p.tid, p.visible, p.message, t.visible AS threadvisible, t.replies AS threadreplies, t.firstpost AS threadfirstpost, t.unapprovedposts AS threadunapprovedposts, COUNT(a.aid) AS attachmentcount
 789              FROM ".TABLE_PREFIX."posts p
 790              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
 791              LEFT JOIN ".TABLE_PREFIX."attachments a ON (a.pid=p.pid AND a.visible=1)
 792              WHERE p.pid IN($pidin)
 793              GROUP BY p.pid
 794              ORDER BY p.dateline ASC, p.pid ASC
 795          ");
 796          $message = '';
 797          $threads = $forum_counters = $thread_counters = $user_counters = array();
 798          while($post = $db->fetch_array($query))
 799          {
 800              $threads[$post['tid']] = $post['tid'];
 801              if(!isset($thread_counters[$post['tid']]))
 802              {
 803                  $thread_counters[$post['tid']] = array(
 804                      'replies' => 0,
 805                      'unapprovedposts' => 0,
 806                      'deletedposts' => 0,
 807                      'attachmentcount' => 0
 808                  );
 809              }
 810              if($first == 1)
 811              { // all posts will be merged into this one
 812                  $masterpid = $post['pid'];
 813                  $message = $post['message'];
 814                  $fid = $post['fid'];
 815                  $mastertid = $post['tid'];
 816                  $first = 0;
 817                  $visible = $post['visible'];
 818              }
 819              else
 820              {
 821                   // these are the selected posts
 822                  if($sep == "new_line")
 823                  {
 824                      $message .= "\n\n {$post['message']}";
 825                  }
 826                  else
 827                  {
 828                      $message .= "[hr]{$post['message']}";
 829                  }
 830  
 831                  if(!isset($forum_counters[$post['fid']]))
 832                  {
 833                      $forum_counters[$post['fid']] = array(
 834                          'num_posts' => 0,
 835                          'unapprovedposts' => 0,
 836                          'deletedposts' => 0
 837                      );
 838                  }
 839  
 840                  if($post['visible'] == 1)
 841                  {
 842                      --$thread_counters[$post['tid']]['replies'];
 843                      $forum = get_forum($post['fid']);
 844                      if(!isset($user_counters[$post['uid']]))
 845                      {
 846                          $user_counters[$post['uid']] = array(
 847                              'num_posts' => 0,
 848                              'num_threads' => 0
 849                          );
 850                      }
 851                      // Subtract 1 from user's post count
 852                      if($forum['usepostcounts'] != 0 && $post['threadvisible'] == 1)
 853                      {
 854                          // Update post count of the user of the merged posts
 855                          --$user_counters[$post['uid']]['num_posts'];
 856                      }
 857                      if($post['threadfirstpost'] == $post['pid'] && $forum['usethreadcounts'] != 0 && $post['threadvisible'] == 1)
 858                      {
 859                          --$user_counters[$post['uid']]['num_threads'];
 860                      }
 861                      $thread_counters[$post['tid']]['attachmentcount'] -= $post['attachmentcount'];
 862                  }
 863                  elseif($post['visible'] == 0)
 864                  {
 865                      // Subtract 1 unapproved post from post's thread
 866                      --$thread_counters[$post['tid']]['unapprovedposts'];
 867                  }
 868                  elseif($post['visible'] == -1)
 869                  {
 870                      // Subtract 1 deleted post from post's thread
 871                      --$thread_counters[$post['tid']]['deletedposts'];
 872                  }
 873  
 874                  // Subtract 1 post from post's forum
 875                  if($post['threadvisible'] == 1 && $post['visible'] == 1)
 876                  {
 877                      --$forum_counters[$post['fid']]['num_posts'];
 878                  }
 879                  elseif($post['threadvisible'] == 0 || ($post['visible'] == 0 && $post['threadvisible'] != -1))
 880                  {
 881                      --$forum_counters[$post['fid']]['unapprovedposts'];
 882                  }
 883                  else
 884                  {
 885                      --$forum_counters[$post['fid']]['deletedposts'];
 886                  }
 887  
 888                  // Add attachment count to thread
 889                  if($visible == 1)
 890                  {
 891                      $thread_counters[$mastertid]['attachmentcount'] += $post['attachmentcount'];
 892                  }
 893              }
 894          }
 895  
 896          // Update the message
 897          $mergepost = array(
 898              "message" => $db->escape_string($message),
 899          );
 900          $db->update_query("posts", $mergepost, "pid = '{$masterpid}'");
 901  
 902          // Delete the extra posts
 903          $db->delete_query("posts", "pid IN({$pidin}) AND pid != '{$masterpid}'");
 904  
 905          // Update pid for attachments
 906          $mergepost2 = array(
 907              "pid" => $masterpid,
 908          );
 909          $db->update_query("attachments", $mergepost2, "pid IN({$pidin})");
 910  
 911          // If the first post of a thread is merged out, the first should be updated
 912          $query = $db->simple_select("threads", "tid, uid, fid, visible", "firstpost IN({$pidin}) AND firstpost != '{$masterpid}'");
 913          while($thread = $db->fetch_array($query))
 914          {
 915              // In some cases the first post of a thread changes
 916              // Therefore resync the visible field to make sure they're the same if they're not
 917              $query = $db->simple_select("posts", "pid, uid, visible", "tid='{$thread['tid']}'", array('order_by' => 'dateline, pid', 'limit' => 1));
 918              $new_firstpost = $db->fetch_array($query);
 919              if($thread['visible'] != $new_firstpost['visible'])
 920              {
 921                  $db->update_query("posts", array('visible' => $thread['visible']), "pid='{$new_firstpost['pid']}'");
 922                  // Correct counters
 923                  if($new_firstpost['visible'] == 1)
 924                  {
 925                      --$thread_counters[$thread['tid']]['replies'];
 926                  }
 927                  elseif($new_firstpost['visible'] == -1)
 928                  {
 929                      --$thread_counters[$thread['tid']]['deletedposts'];
 930                  }
 931                  else
 932                  {
 933                      --$thread_counters[$thread['tid']]['unapprovedposts'];
 934                  }
 935                  if($thread['visible'] == 1)
 936                  {
 937                      ++$thread_counters[$thread['tid']]['replies'];
 938                  }
 939                  elseif($thread['visible'] == -1)
 940                  {
 941                      ++$thread_counters[$thread['tid']]['deletedposts'];
 942                  }
 943                  else
 944                  {
 945                      ++$thread_counters[$thread['tid']]['unapprovedposts'];
 946                  }
 947              }
 948  
 949              if($new_firstpost['uid'] != $thread['uid'] && $forum['usethreadcounts'] != 0 && $thread['visible'] == 1)
 950              {
 951                  if(!isset($user_counters[$new_firstpost['uid']]))
 952                  {
 953                      $user_counters[$new_firstpost['uid']] = array(
 954                          'num_posts' => 0,
 955                          'num_threads' => 0
 956                      );
 957                  }
 958                  ++$user_counters[$new_firstpost['uid']]['num_threads'];
 959              }
 960              update_first_post($thread['tid']);
 961          }
 962  
 963          $arguments = array("pids" => $pids, "tid" => $tid);
 964          $plugins->run_hooks("class_moderation_merge_posts", $arguments);
 965  
 966          if(!empty($thread_counters))
 967          {
 968              foreach($thread_counters as $tid => $counters)
 969              {
 970                  $counters = array(
 971                      'replies' => signed($counters['replies']),
 972                      'unapprovedposts' => signed($counters['unapprovedposts']),
 973                      'deletedposts' => signed($counters['deletedposts']),
 974                      'attachmentcount' => signed($counters['attachmentcount'])
 975                  );
 976                  update_thread_counters($tid, $counters);
 977                  update_last_post($tid);
 978              }
 979          }
 980  
 981          if(!empty($forum_counters))
 982          {
 983              foreach($forum_counters as $fid => $counters)
 984              {
 985                  $updated_forum_stats = array(
 986                      'posts' => signed($counters['num_posts']),
 987                      'unapprovedposts' => signed($counters['unapprovedposts']),
 988                      'deletedposts' => signed($counters['deletedposts'])
 989                  );
 990                  update_forum_counters($fid, $updated_forum_stats);
 991                  update_forum_lastpost($fid);
 992              }
 993          }
 994  
 995          if(!empty($user_counters))
 996          {
 997              foreach($user_counters as $uid => $counters)
 998              {
 999                  $update_array = array(
1000                      "postnum" => "+{$counters['num_posts']}",
1001                      "threadnum" => "+{$counters['num_threads']}"
1002                  );
1003                  update_user_counters($uid, $update_array);
1004              }
1005          }
1006  
1007          return $masterpid;
1008      }
1009  
1010      /**
1011       * Move/copy thread
1012       *
1013       * @param int $tid Thread to be moved
1014       * @param int $new_fid Destination forum
1015       * @param string $method Method of movement (redirect, copy, move)
1016       * @param int $redirect_expire Expiry timestamp for redirect
1017       * @return int Thread ID
1018       */
1019  	function move_thread($tid, $new_fid, $method="redirect", $redirect_expire=0)
1020      {
1021          global $db, $plugins;
1022  
1023          // Get thread info
1024          $tid = (int)$tid;
1025          $new_fid = (int)$new_fid;
1026          $redirect_expire = (int)$redirect_expire;
1027  
1028          $thread = get_thread($tid, true);
1029  
1030          $newforum = get_forum($new_fid);
1031          if(!$thread || !$newforum)
1032          {
1033              return false;
1034          }
1035          $fid = $thread['fid'];
1036          $forum = get_forum($fid);
1037  
1038          $num_threads = $num_unapproved_threads = $num_posts = $num_unapproved_posts = $num_deleted_posts = $num_deleted_threads = 0;
1039  
1040          if($thread['visible'] == 1)
1041          {
1042              $num_threads++;
1043              $num_posts = $thread['replies']+1;
1044              $num_unapproved_posts = $thread['unapprovedposts'];
1045              $num_deleted_posts = $thread['deletedposts'];
1046          }
1047          elseif($thread['visible'] == -1)
1048          {
1049              $num_deleted_threads++;
1050              // Implied forum deleted count for deleted threads
1051              $num_deleted_posts = $thread['replies']+$thread['deletedposts']+$thread['unapprovedposts']+1;
1052          }
1053          else
1054          {
1055              $num_unapproved_threads++;
1056              // Implied forum unapproved count for unapproved threads
1057              $num_unapproved_posts = $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1;
1058          }
1059  
1060          switch($method)
1061          {
1062              case "redirect": // move (and leave redirect) thread
1063                  $arguments = array("tid" => $tid, "new_fid" => $new_fid);
1064                  $plugins->run_hooks("class_moderation_move_thread_redirect", $arguments);
1065  
1066                  $query = $db->simple_select('threads', 'tid', "closed='moved|$tid' AND fid='$new_fid'");
1067                  while($redirect_tid = $db->fetch_field($query, 'tid'))
1068                  {
1069                      $this->delete_thread($redirect_tid);
1070                  }
1071                  $changefid = array(
1072                      "fid" => $new_fid,
1073                  );
1074                  $db->update_query("threads", $changefid, "tid='$tid'");
1075                  $db->update_query("posts", $changefid, "tid='$tid'");
1076  
1077                  // If the thread has a prefix and the destination forum doesn't accept that prefix, remove the prefix
1078                  if($thread['prefix'] != 0)
1079                  {
1080                      switch($db->type)
1081                      {
1082                          case "pgsql":
1083                          case "sqlite":
1084                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(','||forums||',' LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1085                              break;
1086                          default:
1087                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(CONCAT(',',forums,',') LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1088                      }
1089                      if($db->fetch_field($query, "num_prefixes") == 0)
1090                      {
1091                          $sqlarray = array(
1092                              "prefix" => 0,
1093                          );
1094                          $db->update_query("threads", $sqlarray, "tid='$tid'");
1095                      }
1096                  }
1097  
1098                  $threadarray = array(
1099                      "fid" => $thread['fid'],
1100                      "subject" => $db->escape_string($thread['subject']),
1101                      "icon" => $thread['icon'],
1102                      "uid" => $thread['uid'],
1103                      "username" => $db->escape_string($thread['username']),
1104                      "dateline" => $thread['dateline'],
1105                      "lastpost" => $thread['lastpost'],
1106                      "lastposteruid" => $thread['lastposteruid'],
1107                      "lastposter" => $db->escape_string($thread['lastposter']),
1108                      "views" => 0,
1109                      "replies" => 0,
1110                      "closed" => "moved|$tid",
1111                      "sticky" => $thread['sticky'],
1112                      "visible" => (int)$thread['visible'],
1113                      "notes" => ''
1114                  );
1115                  $redirect_tid = $db->insert_query("threads", $threadarray);
1116                  if($redirect_expire)
1117                  {
1118                      $this->expire_thread($redirect_tid, $redirect_expire);
1119                  }
1120  
1121                  // If we're moving back to a forum where we left a redirect, delete the rediect
1122                  $query = $db->simple_select("threads", "tid", "closed LIKE 'moved|".(int)$tid."' AND fid='".(int)$new_fid."'");
1123                  while($redirect_tid = $db->fetch_field($query, 'tid'))
1124                  {
1125                      $this->delete_thread($redirect_tid);
1126                  }
1127                   break;
1128              case "copy":// copy thread
1129  
1130                  $threadarray = array(
1131                      "fid" => $new_fid,
1132                      "subject" => $db->escape_string($thread['subject']),
1133                      "icon" => $thread['icon'],
1134                      "uid" => $thread['uid'],
1135                      "username" => $db->escape_string($thread['username']),
1136                      "dateline" => $thread['dateline'],
1137                      "firstpost" => 0,
1138                      "lastpost" => $thread['lastpost'],
1139                      "lastposteruid" => $thread['lastposteruid'],
1140                      "lastposter" => $db->escape_string($thread['lastposter']),
1141                      "views" => $thread['views'],
1142                      "replies" => $thread['replies'],
1143                      "closed" => $thread['closed'],
1144                      "sticky" => $thread['sticky'],
1145                      "visible" => (int)$thread['visible'],
1146                      "unapprovedposts" => $thread['unapprovedposts'],
1147                      "deletedposts" => $thread['deletedposts'],
1148                      "attachmentcount" => $thread['attachmentcount'],
1149                      "prefix" => $thread['prefix'],
1150                      "notes" => ''
1151                  );
1152  
1153                  $arguments = array("tid" => $tid, "new_fid" => $new_fid);
1154                  $plugins->run_hooks("class_moderation_copy_thread", $arguments);
1155  
1156                  // If the thread has a prefix and the destination forum doesn't accept that prefix, don't copy the prefix
1157                  if($threadarray['prefix'] != 0)
1158                  {
1159                      switch($db->type)
1160                      {
1161                          case "pgsql":
1162                          case "sqlite":
1163                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(','||forums||',' LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1164                              break;
1165                          default:
1166                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(CONCAT(',',forums,',') LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1167                      }
1168                      if($db->fetch_field($query, "num_prefixes") == 0)
1169                      {
1170                          $threadarray['prefix'] = 0;
1171                      }
1172                  }
1173  
1174                  $newtid = $db->insert_query("threads", $threadarray);
1175  
1176                  if($thread['poll'] != 0)
1177                  {
1178                      $query = $db->simple_select("polls", "*", "tid = '{$thread['tid']}'");
1179                      $poll = $db->fetch_array($query);
1180  
1181                      $poll_array = array(
1182                          'tid' => $newtid,
1183                          'question' => $db->escape_string($poll['question']),
1184                          'dateline' => $poll['dateline'],
1185                          'options' => $db->escape_string($poll['options']),
1186                          'votes' => $db->escape_string($poll['votes']),
1187                          'numoptions' => $poll['numoptions'],
1188                          'numvotes' => $poll['numvotes'],
1189                          'timeout' => $poll['timeout'],
1190                          'closed' => $poll['closed'],
1191                          'multiple' => $poll['multiple'],
1192                          'public' => $poll['public']
1193                      );
1194                      $new_pid = $db->insert_query("polls", $poll_array);
1195  
1196                      $query = $db->simple_select("pollvotes", "*", "pid = '{$poll['pid']}'");
1197                      while($pollvote = $db->fetch_array($query))
1198                      {
1199                          $pollvote_array = array(
1200                              'pid' => $new_pid,
1201                              'uid' => $pollvote['uid'],
1202                              'voteoption' => $pollvote['voteoption'],
1203                              'dateline' => $pollvote['dateline'],
1204                          );
1205                          $db->insert_query("pollvotes", $pollvote_array);
1206                      }
1207  
1208                      $db->update_query("threads", array('poll' => $new_pid), "tid='{$newtid}'");
1209                  }
1210  
1211                  $query = $db->simple_select("posts", "*", "tid = '{$thread['tid']}'");
1212                  while($post = $db->fetch_array($query))
1213                  {
1214                      $post_array = array(
1215                          'tid' => $newtid,
1216                          'fid' => $new_fid,
1217                          'subject' => $db->escape_string($post['subject']),
1218                          'icon' => $post['icon'],
1219                          'uid' => $post['uid'],
1220                          'username' => $db->escape_string($post['username']),
1221                          'dateline' => $post['dateline'],
1222                          'ipaddress' => $db->escape_binary($post['ipaddress']),
1223                          'includesig' => $post['includesig'],
1224                          'smilieoff' => $post['smilieoff'],
1225                          'edituid' => $post['edituid'],
1226                          'edittime' => $post['edittime'],
1227                          'visible' => $post['visible'],
1228                          'message' => $db->escape_string($post['message']),
1229                      );
1230                      $pid = $db->insert_query("posts", $post_array);
1231  
1232                      // Properly set our new firstpost in our new thread
1233                      if($thread['firstpost'] == $post['pid'])
1234                      {
1235                          $db->update_query("threads", array('firstpost' => $pid), "tid='{$newtid}'");
1236                      }
1237  
1238                      // Insert attachments for this post
1239                      $query2 = $db->simple_select("attachments", "*", "pid = '{$post['pid']}'");
1240                      while($attachment = $db->fetch_array($query2))
1241                      {
1242                          $attachment_array = array(
1243                              'pid' => $pid,
1244                              'uid' => $attachment['uid'],
1245                              'filename' => $db->escape_string($attachment['filename']),
1246                              'filetype' => $db->escape_string($attachment['filetype']),
1247                              'filesize' => $attachment['filesize'],
1248                              'attachname' => $db->escape_string($attachment['attachname']),
1249                              'downloads' => $attachment['downloads'],
1250                              'visible' => $attachment['visible'],
1251                              'thumbnail' => $db->escape_string($attachment['thumbnail'])
1252                          );
1253                          $new_aid = $db->insert_query("attachments", $attachment_array);
1254  
1255                          $post['message'] = str_replace("[attachment={$attachment['aid']}]", "[attachment={$new_aid}]", $post['message']);
1256                      }
1257  
1258                      if(strpos($post['message'], "[attachment=") !== false)
1259                      {
1260                          $db->update_query("posts", array('message' => $db->escape_string($post['message'])), "pid='{$pid}'");
1261                      }
1262                  }
1263  
1264                  update_thread_data($newtid);
1265  
1266                  $the_thread = $newtid;
1267                  break;
1268              default:
1269              case "move": // plain move thread
1270                  $arguments = array("tid" => $tid, "new_fid" => $new_fid);
1271                  $plugins->run_hooks("class_moderation_move_simple", $arguments);
1272  
1273                  $sqlarray = array(
1274                      "fid" => $new_fid,
1275                  );
1276                  $db->update_query("threads", $sqlarray, "tid='$tid'");
1277                  $db->update_query("posts", $sqlarray, "tid='$tid'");
1278  
1279                  // If the thread has a prefix and the destination forum doesn't accept that prefix, remove the prefix
1280                  if($thread['prefix'] != 0)
1281                  {
1282                      switch($db->type)
1283                      {
1284                          case "pgsql":
1285                          case "sqlite":
1286                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(','||forums||',' LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1287                              break;
1288                          default:
1289                              $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(CONCAT(',',forums,',') LIKE '%,$new_fid,%' OR forums='-1') AND pid='".$thread['prefix']."'");
1290                      }
1291                      if($db->fetch_field($query, "num_prefixes") == 0)
1292                      {
1293                          $sqlarray = array(
1294                              "prefix" => 0,
1295                          );
1296                          $db->update_query("threads", $sqlarray, "tid='$tid'");
1297                      }
1298                  }
1299  
1300                  // If we're moving back to a forum where we left a redirect, delete the rediect
1301                  $query = $db->simple_select("threads", "tid", "closed LIKE 'moved|".(int)$tid."' AND fid='".(int)$new_fid."'");
1302                  while($redirect_tid = $db->fetch_field($query, 'tid'))
1303                  {
1304                      $this->delete_thread($redirect_tid);
1305                  }
1306                  break;
1307          }
1308  
1309          // Do post and thread count changes if changing between countable and non-countable forums
1310          $query = $db->query("
1311              SELECT COUNT(p.pid) AS posts, u.uid
1312              FROM ".TABLE_PREFIX."posts p
1313              LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid)
1314              WHERE p.tid='$tid' AND p.visible=1
1315              GROUP BY u.uid
1316              ORDER BY posts DESC
1317          ");
1318          while($posters = $db->fetch_array($query))
1319          {
1320              $pcount = 0;
1321              if($forum['usepostcounts'] == 1 && $method != 'copy' && $newforum['usepostcounts'] == 0 && $thread['visible'] == 1)
1322              {
1323                  $pcount -= $posters['posts'];
1324              }
1325              if(($forum['usepostcounts'] == 0 || $method == 'copy') && $newforum['usepostcounts'] == 1 && $thread['visible'] == 1)
1326              {
1327                  $pcount += $posters['posts'];
1328              }
1329  
1330              if($pcount > 0)
1331              {
1332                  update_user_counters($posters['uid'], array('postnum' => "+$pcount"));
1333              }
1334              elseif($pcount < 0)
1335              {
1336                  update_user_counters($posters['uid'], array('postnum' => $pcount));
1337              }
1338          }
1339  
1340          if($forum['usethreadcounts'] == 1 && $method != 'copy' && $newforum['usethreadcounts'] == 0 && $thread['visible'] == 1)
1341          {
1342              update_user_counters($thread['uid'], array('threadnum' => "-1"));
1343          }
1344          elseif(($forum['usethreadcounts'] == 0 || $method == 'copy') && $newforum['usethreadcounts'] == 1 && $thread['visible'] == 1)
1345          {
1346              update_user_counters($thread['uid'], array('threadnum' => "+1"));
1347          }
1348  
1349          // Update forum counts
1350          $update_array = array(
1351              "threads" => "+{$num_threads}",
1352              "unapprovedthreads" => "+{$num_unapproved_threads}",
1353              "posts" => "+{$num_posts}",
1354              "unapprovedposts" => "+{$num_unapproved_posts}",
1355              "deletedthreads" => "+{$num_deleted_threads}",
1356              "deletedposts" => "+{$num_deleted_posts}"
1357          );
1358          update_forum_counters($new_fid, $update_array);
1359          update_forum_lastpost($new_fid);
1360  
1361          if($method != "copy")
1362          {
1363              // The redirect needs to be counted, too
1364              if($method == "redirect")
1365              {
1366                  if($thread['visible'] == -1)
1367                  {
1368                      --$num_deleted_threads;
1369                      --$num_deleted_posts;
1370                  }
1371                  elseif($thread['visible'] == 0)
1372                  {
1373                      --$num_unapproved_threads;
1374                      --$num_unapproved_posts;
1375                  }
1376                  else
1377                  {
1378                      --$num_threads;
1379                      --$num_posts;
1380                  }
1381              }
1382              $update_array = array(
1383                  "threads" => "-{$num_threads}",
1384                  "unapprovedthreads" => "-{$num_unapproved_threads}",
1385                  "posts" => "-{$num_posts}",
1386                  "unapprovedposts" => "-{$num_unapproved_posts}",
1387                  "deletedthreads" => "-{$num_deleted_threads}",
1388                  "deletedposts" => "-{$num_deleted_posts}"
1389              );
1390              update_forum_counters($fid, $update_array);
1391              update_forum_lastpost($fid);
1392          }
1393  
1394          if(isset($newtid))
1395          {
1396              return $newtid;
1397          }
1398          else
1399          {
1400              // Remove thread subscriptions for the users who no longer have permission to view the thread
1401              $this->remove_thread_subscriptions($tid, false, $new_fid);
1402  
1403              return $tid;
1404          }
1405      }
1406  
1407      /**
1408       * Merge one thread into another
1409       *
1410       * @param int $mergetid Thread that will be merged into destination
1411       * @param int $tid Destination thread
1412       * @param string $subject New thread subject
1413       * @return boolean
1414       */
1415  	function merge_threads($mergetid, $tid, $subject)
1416      {
1417          global $db, $mybb, $mergethread, $thread, $plugins, $cache;
1418  
1419          $mergetid = (int)$mergetid;
1420          $tid = (int)$tid;
1421  
1422          if(!isset($mergethread['tid']) || $mergethread['tid'] != $mergetid)
1423          {
1424              $mergethread = get_thread($mergetid);
1425          }
1426          if(!isset($thread['tid']) || $thread['tid'] != $tid)
1427          {
1428              $thread = get_thread($tid);
1429          }
1430  
1431          if(!$mergethread || !$thread)
1432          {
1433              return false;
1434          }
1435  
1436          $forum_cache = $cache->read('forums');
1437  
1438          $threadarray = array();
1439          if(!$thread['poll'] && $mergethread['poll'])
1440          {
1441              $threadarray['poll'] = $mergethread['poll'];
1442              $sqlarray = array(
1443                  "tid" => $tid,
1444              );
1445              $db->update_query("polls", $sqlarray, "tid='".(int)$mergethread['tid']."'");
1446          }
1447          // Both the old and the new thread have polls? Remove one
1448          elseif($mergethread['poll'])
1449          {
1450              $db->delete_query("polls", "pid='{$mergethread['poll']}'");
1451              $db->delete_query("pollvotes", "pid='{$mergethread['poll']}'");
1452          }
1453  
1454          $subject = $db->escape_string($subject);
1455          $threadarray['subject'] = $subject;
1456  
1457          $user_posts = array();
1458          if($thread['visible'] != $mergethread['visible'] || $forum_cache[$thread['fid']]['usepostcounts'] != $forum_cache[$mergethread['fid']]['usepostcounts'])
1459          {
1460              $query = $db->query("
1461                  SELECT uid, COUNT(pid) AS postnum
1462                  FROM ".TABLE_PREFIX."posts
1463                  WHERE tid='{$mergetid}' AND visible=1
1464                  GROUP BY uid
1465              ");
1466              while($post = $db->fetch_array($query))
1467              {
1468                  // Update user counters
1469                  if($mergethread['visible'] == 1 && $forum_cache[$mergethread['fid']]['usepostcounts'] == 1)
1470                  {
1471                      $user_posts[$post['uid']]['postnum'] -= $post['postnum'];
1472                  }
1473                  elseif($thread['visible'] == 1 && $forum_cache[$thread['fid']]['usepostcounts'] == 1)
1474                  {
1475                      $user_posts[$post['uid']]['postnum'] += $post['postnum'];
1476                  }
1477              }
1478          }
1479  
1480          $sqlarray = array(
1481              "tid" => $tid,
1482              "fid" => $thread['fid'],
1483              "replyto" => 0,
1484          );
1485          $db->update_query("posts", $sqlarray, "tid='{$mergetid}'");
1486  
1487          $sqlarray = array(
1488              "closed" => "moved|{$tid}",
1489          );
1490          $db->update_query("threads", $sqlarray, "closed='moved|{$mergetid}'");
1491          $sqlarray = array(
1492              "tid" => $tid,
1493          );
1494  
1495          // Update the thread ratings
1496          $new_numrating = $thread['numratings'] + $mergethread['numratings'];
1497          $new_threadrating = $thread['totalratings'] + $mergethread['totalratings'];
1498  
1499          $threadarray["numratings"] = $new_numrating;
1500          $threadarray["totalratings"] = $new_threadrating;
1501          $db->update_query("threads", $threadarray, "tid = '{$tid}'");
1502  
1503          // Check if we have a thread subscription already for our new thread
1504          $subscriptions = array();
1505  
1506          $query = $db->simple_select("threadsubscriptions", "tid, uid", "tid='{$mergetid}' OR tid='{$tid}'");
1507          while($subscription = $db->fetch_array($query))
1508          {
1509              if(!isset($subscriptions[$subscription['tid']]))
1510              {
1511                  $subscriptions[$subscription['tid']] = array();
1512              }
1513              $subscriptions[$subscription['tid']][] = $subscription['uid'];
1514          }
1515  
1516          // Update any subscriptions for the merged thread
1517          if(!empty($subscriptions[$mergetid]))
1518           {
1519              $update_users = array();
1520              foreach($subscriptions[$mergetid] as $user)
1521              {
1522                  if(!isset($subscriptions[$tid]) || !in_array($user, $subscriptions[$tid]))
1523                  {
1524                      // User doesn't have a $tid subscription
1525                      $update_users[] = $user;
1526                  }
1527              }
1528  
1529              if(!empty($update_users))
1530              {
1531                  $update_array = array(
1532                      "tid" => $tid
1533                  );
1534  
1535                  $update_users = implode(",", $update_users);
1536                  $db->update_query("threadsubscriptions", $update_array, "tid = '{$mergetid}' AND uid IN ({$update_users})");
1537              }
1538           }
1539  
1540          // Remove source thread subscriptions
1541          $db->delete_query("threadsubscriptions", "tid = '{$mergetid}'");
1542  
1543          $arguments = array("mergetid" => $mergetid, "tid" => $tid, "subject" => $subject);
1544          $plugins->run_hooks("class_moderation_merge_threads", $arguments);
1545  
1546          $this->delete_thread($mergetid);
1547  
1548          // Add the former first post
1549          if($mergethread['visible'] == 1)
1550          {
1551              ++$mergethread['replies'];
1552          }
1553          elseif($mergethread['visible'] == -1)
1554          {
1555              ++$mergethread['deletedposts'];
1556          }
1557          else
1558          {
1559              ++$mergethread['unapprovedposts'];
1560          }
1561  
1562          // In some cases the thread we may be merging with may cause us to have a new firstpost if it is an older thread
1563          // Therefore resync the visible field to make sure they're the same if they're not
1564          $query = $db->simple_select("posts", "pid, uid, visible", "tid='{$tid}'", array('order_by' => 'dateline, pid', 'limit' => 1));
1565          $new_firstpost = $db->fetch_array($query);
1566          if($thread['visible'] != $new_firstpost['visible'])
1567          {
1568              $db->update_query("posts", array('visible' => $thread['visible']), "pid='{$new_firstpost['pid']}'");
1569              if($new_firstpost['visible'] == 1 && $forum_cache[$thread['fid']]['usepostcounts'] == 1)
1570              {
1571                  --$user_posts[$post['uid']]['postnum'];
1572              }
1573              elseif($thread['visible'] == 1 && $forum_cache[$thread['fid']]['usepostcounts'] == 1)
1574              {
1575                  ++$user_posts[$post['uid']]['postnum'];
1576              }
1577          }
1578          // Update first post if needed
1579          if($new_firstpost['pid'] != $thread['firstpost'])
1580          {
1581              update_first_post($thread['tid']);
1582          }
1583  
1584          // Update thread count if thread has a new firstpost and is visible
1585          if($thread['uid'] != $new_firstpost['uid'] && $thread['visible'] == 1 && $forum_cache[$thread['fid']]['usethreadcounts'] == 1)
1586          {
1587              if(!isset($user_posts[$thread['uid']]['threadnum']))
1588              {
1589                  $user_posts[$thread['uid']]['threadnum'] = 0;
1590              }
1591              --$user_posts[$thread['uid']]['threadnum'];
1592              if(!isset($user_posts[$new_firstpost['uid']]['threadnum']))
1593              {
1594                  $user_posts[$new_firstpost['uid']]['threadnum'] = 0;
1595              }
1596              ++$user_posts[$new_firstpost['uid']]['threadnum'];
1597          }
1598  
1599          // Thread is not in current forum
1600          if($mergethread['fid'] != $thread['fid'])
1601          {
1602              // If new thread is unapproved, implied counter comes in to effect
1603              if($thread['visible'] == 0)
1604              {
1605                  $updated_stats = array(
1606                      "unapprovedposts" => '+'.($mergethread['replies']+$mergethread['unapprovedposts']+$mergethread['deletedposts'])
1607                  );
1608              }
1609              elseif($thread['visible'] == -1)
1610              {
1611                  $updated_stats = array(
1612                      "deletedposts" => '+'.($mergethread['replies']+$mergethread['deletedposts']+$mergethread['unapprovedposts'])
1613                  );
1614              }
1615              else
1616              {
1617                  $updated_stats = array(
1618                      "posts" => "+{$mergethread['replies']}",
1619                      "unapprovedposts" => "+{$mergethread['unapprovedposts']}",
1620                      "deletedposts" => "+{$mergethread['deletedposts']}"
1621                  );
1622              }
1623              update_forum_counters($thread['fid'], $updated_stats);
1624  
1625              // If old thread is unapproved, implied counter comes in to effect
1626              if($mergethread['visible'] == 0)
1627              {
1628                  $updated_stats = array(
1629                      "unapprovedposts" => '-'.($mergethread['replies']+$mergethread['unapprovedposts']+$mergethread['deletedposts'])
1630                  );
1631              }
1632              elseif($mergethread['visible'] == -1)
1633              {
1634                  $updated_stats = array(
1635                      "deletedposts" => '-'.($mergethread['replies']+$mergethread['deletedposts']+$mergethread['unapprovedposts'])
1636                  );
1637              }
1638              else
1639              {
1640                  $updated_stats = array(
1641                      "posts" => "-{$mergethread['replies']}",
1642                      "unapprovedposts" => "-{$mergethread['unapprovedposts']}",
1643                      "deletedposts" => "-{$mergethread['deletedposts']}"
1644                  );
1645              }
1646              update_forum_counters($mergethread['fid'], $updated_stats);
1647              update_forum_lastpost($mergethread['fid']);
1648          }
1649          // Visibility changed
1650          elseif($mergethread['visible'] != $thread['visible'])
1651          {
1652              $updated_stats = array(
1653                  'posts' => 0,
1654                  'unapprovedposts' => 0,
1655                  'deletedposts' => 0
1656              );
1657  
1658              // If old thread is unapproved, implied counter comes in to effect
1659              if($mergethread['visible'] == 0)
1660              {
1661                  $updated_stats['unapprovedposts'] -= $mergethread['replies']+$mergethread['deletedposts'];
1662                  $updated_stats['posts'] += $mergethread['replies'];
1663                  $updated_stats['deletedposts'] += $mergethread['deletedposts'];
1664              }
1665              elseif($mergethread['visible'] == -1)
1666              {
1667                  $updated_stats['deletedposts'] -= $mergethread['replies']+$mergethread['unapprovedposts'];
1668                  $updated_stats['posts'] += $mergethread['replies'];
1669                  $updated_stats['unapprovedposts'] += $mergethread['unapprovedposts'];
1670              }
1671  
1672              // If new thread is unapproved, implied counter comes in to effect
1673              if($thread['visible'] == 0)
1674              {
1675                  $updated_stats['unapprovedposts'] += $mergethread['replies']+$mergethread['deletedposts'];
1676                  $updated_stats['posts'] -= $mergethread['replies'];
1677                  $updated_stats['deletedposts'] -= $mergethread['deletedposts'];
1678              }
1679              elseif($thread['visible'] == -1)
1680              {
1681                  $updated_stats['deletedposts'] += $mergethread['replies']+$mergethread['unapprovedposts'];
1682                  $updated_stats['posts'] -= $mergethread['replies'];
1683                  $updated_stats['unapprovedposts'] -= $mergethread['unapprovedposts'];
1684              }
1685  
1686              $new_stats = array();
1687              if($updated_stats['posts'] < 0)
1688              {
1689                  $new_stats['posts'] = $updated_stats['posts'];
1690              }
1691              elseif($updated_stats['posts'] > 0)
1692              {
1693                  $new_stats['posts'] = "+{$updated_stats['posts']}";
1694              }
1695  
1696              if($updated_stats['unapprovedposts'] < 0)
1697              {
1698                  $new_stats['unapprovedposts'] = $updated_stats['unapprovedposts'];
1699              }
1700              elseif($updated_stats['unapprovedposts'] > 0)
1701              {
1702                  $new_stats['unapprovedposts'] = "+{$updated_stats['unapprovedposts']}";
1703              }
1704  
1705              if($updated_stats['deletedposts'] < 0)
1706              {
1707                  $new_stats['deletedposts'] = $updated_stats['deletedposts'];
1708              }
1709              elseif($updated_stats['deletedposts'] > 0)
1710              {
1711                  $new_stats['deletedposts'] = "+{$updated_stats['deletedposts']}";
1712              }
1713  
1714              if(!empty($new_stats))
1715              {
1716                  update_forum_counters($mergethread['fid'], $new_stats);
1717                  update_forum_lastpost($mergethread['fid']);
1718              }
1719          }
1720  
1721          if($thread['visible'] != $new_firstpost['visible'])
1722          {
1723              // Correct counters
1724              if($new_firstpost['visible'] == 1)
1725              {
1726                  --$mergethread['replies'];
1727              }
1728              elseif($new_firstpost['visible'] == -1)
1729              {
1730                  --$mergethread['deletedposts'];
1731              }
1732              else
1733              {
1734                  --$mergethread['unapprovedposts'];
1735              }
1736              if($thread['visible'] == 1)
1737              {
1738                  ++$mergethread['replies'];
1739              }
1740              elseif($thread['visible'] == -1)
1741              {
1742                  ++$mergethread['deletedposts'];
1743              }
1744              else
1745              {
1746                  ++$mergethread['unapprovedposts'];
1747              }
1748          }
1749  
1750          // Update user counters
1751          foreach($user_posts as $uid => $counters)
1752          {
1753              $update_array = array(
1754                  "postnum" => "+{$counters['postnum']}",
1755                  "threadnum" => "+{$counters['threadnum']}",
1756              );
1757              update_user_counters($uid, $update_array);
1758          }
1759  
1760          $updated_stats = array(
1761              "replies" => "+{$mergethread['replies']}",
1762              "attachmentcount" => "+{$mergethread['attachmentcount']}",
1763              "unapprovedposts" => "+{$mergethread['unapprovedposts']}",
1764              "deletedposts" => "+{$mergethread['deletedposts']}"
1765          );
1766          update_thread_counters($tid, $updated_stats);
1767          update_last_post($tid);
1768  
1769          // Forum last post has to be updated after thread
1770          update_forum_lastpost($thread['fid']);
1771          return true;
1772      }
1773  
1774      /**
1775       * Split posts into a new/existing thread
1776       *
1777       * @param array $pids PIDs of posts to split
1778       * @param int $tid Original thread ID (this is only used as a base for the new
1779       * thread; it can be set to 0 when the posts specified are coming from more
1780       * than 1 thread)
1781       * @param int $moveto Destination forum
1782       * @param string $newsubject New thread subject
1783       * @param int $destination_tid TID if moving into existing thread
1784       * @return int|bool New thread ID or false on failure
1785       */
1786  	function split_posts($pids, $tid, $moveto, $newsubject, $destination_tid=0)
1787      {
1788          global $db, $thread, $plugins, $cache;
1789  
1790          $tid = (int)$tid;
1791          $moveto = (int)$moveto;
1792          $newtid = (int)$destination_tid;
1793  
1794          // Make sure we only have valid values
1795          $pids = array_map('intval', $pids);
1796  
1797          $pids_list = implode(',', $pids);
1798  
1799          // Get forum infos
1800          $forum_cache = $cache->read('forums');
1801  
1802          if(empty($pids) || !$forum_cache[$moveto])
1803          {
1804              return false;
1805          }
1806  
1807          // Get the first split post
1808          $query = $db->simple_select('posts', 'pid,uid,visible,icon,username,dateline', 'pid IN ('.$pids_list.')', array('order_by' => 'dateline, pid', 'limit' => 1));
1809  
1810          $post_info = $db->fetch_array($query);
1811  
1812          $visible = $post_info['visible'];
1813  
1814          $forum_counters[$moveto] = array(
1815              'threads' => 0,
1816              'deletedthreads' => 0,
1817              'unapprovedthreads' => 0,
1818              'posts' => 0,
1819              'unapprovedposts' => 0,
1820              'deletedposts' => 0
1821          );
1822  
1823          $user_counters = array();
1824  
1825          if($destination_tid == 0)
1826          {
1827              // Splitting into a new thread
1828              // Create the new thread
1829              $newsubject = $db->escape_string($newsubject);
1830              $newthread = array(
1831                  "fid" => $moveto,
1832                  "subject" => $newsubject,
1833                  "icon" => (int)$post_info['icon'],
1834                  "uid" => (int)$post_info['uid'],
1835                  "username" => $db->escape_string($post_info['username']),
1836                  "dateline" => (int)$post_info['dateline'],
1837                  "firstpost" => $post_info['pid'],
1838                  "lastpost" => 0,
1839                  "lastposter" => '',
1840                  "visible" => (int)$visible,
1841                  "notes" => ''
1842              );
1843              $newtid = $db->insert_query("threads", $newthread);
1844  
1845              if($visible == 1)
1846              {
1847                  ++$forum_counters[$moveto]['threads'];
1848                  if(!isset($user_counters[$newthread['uid']]))
1849                  {
1850                      $user_counters[$newthread['uid']] = array(
1851                          'postnum' => 0,
1852                          'threadnum' => 0
1853                      );
1854                  }
1855                  ++$user_counters[$newthread['uid']]['threadnum'];
1856              }
1857              elseif($visible == -1)
1858              {
1859                  ++$forum_counters[$moveto]['deletedthreads'];
1860              }
1861              else
1862              {
1863                  // Unapproved thread?
1864                  ++$forum_counters[$moveto]['unapprovedthreads'];
1865              }
1866          }
1867          else
1868          {
1869              $newthread = get_thread($newtid);
1870              if(!$newthread)
1871              {
1872                  return false;
1873              }
1874              $moveto = $newthread['fid'];
1875          }
1876  
1877          // Get selected posts before moving forums to keep old fid
1878          $original_posts_query = $db->query("
1879              SELECT p.pid, p.tid, p.fid, p.visible, p.uid, p.dateline, t.visible as threadvisible, t.firstpost, COUNT(a.aid) as postattachmentcount
1880              FROM ".TABLE_PREFIX."posts p
1881              LEFT JOIN ".TABLE_PREFIX."threads t ON (p.tid=t.tid)
1882              LEFT JOIN ".TABLE_PREFIX."attachments a ON (a.pid=p.pid AND a.visible=1)
1883              WHERE p.pid IN ($pids_list)
1884              GROUP BY p.pid
1885          ");
1886  
1887          // Move the selected posts over
1888          $sqlarray = array(
1889              "tid" => $newtid,
1890              "fid" => $moveto,
1891              "replyto" => 0
1892          );
1893          $db->update_query("posts", $sqlarray, "pid IN ($pids_list)");
1894  
1895          $thread_counters[$newtid] = array(
1896              'replies' => 0,
1897              'unapprovedposts' => 0,
1898              'deletedposts' => 0,
1899              'attachmentcount' => 0
1900          );
1901  
1902          // Get posts being merged
1903          while($post = $db->fetch_array($original_posts_query))
1904          {
1905              if(!isset($thread_counters[$post['tid']]))
1906              {
1907                  $thread_counters[$post['tid']] = array(
1908                      'replies' => 0,
1909                      'unapprovedposts' => 0,
1910                      'deletedposts' => 0,
1911                      'attachmentcount' => 0
1912                  );
1913              }
1914              if(!isset($forum_counters[$post['fid']]))
1915              {
1916                  $forum_counters[$post['fid']] = array(
1917                      'posts' => 0,
1918                      'unapprovedposts' => 0,
1919                      'deletedposts' => 0
1920                  );
1921              }
1922              if(!isset($user_counters[$post['uid']]))
1923              {
1924                  $user_counters[$post['uid']] = array(
1925                      'postnum' => 0,
1926                      'threadnum' => 0
1927                  );
1928              }
1929              if($post['visible'] == 1)
1930              {
1931                  // Modify users' post counts
1932                  if($post['threadvisible'] == 1 && $forum_cache[$post['fid']]['usepostcounts'] == 1 && ($forum_cache[$moveto]['usepostcounts'] == 0 || $newthread['visible'] != 1))
1933                  {
1934                      // Moving into a forum that doesn't count post counts
1935                      --$user_counters[$post['uid']]['postnum'];
1936                  }
1937  
1938                  // Subtract 1 from the old thread's replies
1939                  --$thread_counters[$post['tid']]['replies'];
1940              }
1941              elseif($post['visible'] == 0)
1942              {
1943                  // Unapproved post
1944                  // Subtract 1 from the old thread's unapproved posts
1945                  --$thread_counters[$post['tid']]['unapprovedposts'];
1946              }
1947              elseif($post['visible'] == -1)
1948              {
1949                  // Soft deleted post
1950                  // Subtract 1 from the old thread's deleted posts
1951                  --$thread_counters[$post['tid']]['deletedposts'];
1952              }
1953  
1954              // Subtract 1 from the old forum's posts
1955              if($post['threadvisible'] == 1 && $post['visible'] == 1)
1956              {
1957                  --$forum_counters[$post['fid']]['posts'];
1958              }
1959              elseif($post['threadvisible'] == 0 || ($post['visible'] == 0 && $post['threadvisible'] == 1))
1960              {
1961                  --$forum_counters[$post['fid']]['unapprovedposts'];
1962              }
1963              else
1964              {
1965                  --$forum_counters[$post['fid']]['deletedposts'];
1966              }
1967  
1968              // Subtract attachment counts from old thread and add to new thread (which are counted regardless of post or attachment unapproval at time of coding)
1969              $thread_counters[$post['tid']]['attachmentcount'] -= $post['postattachmentcount'];
1970              $thread_counters[$newtid]['attachmentcount'] += $post['postattachmentcount'];
1971  
1972              if($post['firstpost'] == $post['pid'])
1973              {
1974                  // In some cases the first post of a thread changes
1975                  // Therefore resync the visible field to make sure they're the same if they're not
1976                  $query = $db->simple_select("posts", "pid, visible, uid", "tid='{$post['tid']}'", array('order_by' => 'dateline, pid', 'limit' => 1));
1977                  $new_firstpost = $db->fetch_array($query);
1978  
1979                  if(!isset($user_counters[$new_firstpost['uid']]))
1980                  {
1981                      $user_counters[$new_firstpost['uid']] = array(
1982                          'postnum' => 0,
1983                          'threadnum' => 0
1984                      );
1985                  }
1986  
1987                  // Update post counters if visibility changes
1988                  if($post['threadvisible'] != $new_firstpost['visible'])
1989                  {
1990                      $db->update_query("posts", array('visible' => $post['threadvisible']), "pid='{$new_firstpost['pid']}'");
1991                      // Subtract new first post
1992                      if($new_firstpost['visible'] == 1)
1993                      {
1994                          --$thread_counters[$post['tid']]['replies'];
1995                          if($post['threadvisible'] == 1 && $forum_cache[$post['fid']]['usepostcounts'] == 1)
1996                          {
1997                              --$user_counters[$new_firstpost['uid']]['postnum'];
1998                          }
1999                      }
2000                      elseif($new_firstpost['visible'] == -1)
2001                      {
2002                          --$thread_counters[$post['tid']]['deletedposts'];
2003                      }
2004                      else
2005                      {
2006                          --$thread_counters[$post['tid']]['unapprovedposts'];
2007                      }
2008                      if($post['threadvisible'] == 0 || ($new_firstpost['visible'] == 0 && $post['threadvisible'] == 1))
2009                      {
2010                          --$forum_counters[$post['fid']]['unapprovedposts'];
2011                      }
2012                      else
2013                      {
2014                          --$forum_counters[$post['fid']]['deletedposts'];
2015                      }
2016  
2017                      // Add old first post
2018                      if($post['threadvisible'] == 1)
2019                      {
2020                          ++$thread_counters[$post['tid']]['replies'];
2021                          ++$forum_counters[$post['fid']]['posts'];
2022                          if($forum_cache[$post['fid']]['usepostcounts'] == 1)
2023                          {
2024                              ++$user_counters[$new_firstpost['uid']]['postnum'];
2025                          }
2026                      }
2027                      elseif($post['threadvisible'] == -1)
2028                      {
2029                          ++$thread_counters[$post['tid']]['deletedposts'];
2030                          ++$forum_counters[$post['fid']]['deletedposts'];
2031                      }
2032                      else
2033                      {
2034                          ++$thread_counters[$post['tid']]['unapprovedposts'];
2035                          ++$forum_counters[$post['fid']]['unapprovedposts'];
2036                      }
2037                  }
2038  
2039                  // Update user thread counter if thread opener changes
2040                  if($post['threadvisible'] == 1 && $forum_cache[$post['fid']]['usethreadcounts'] == 1 && $post['uid'] != $new_firstpost['uid'])
2041                  {
2042                      // Subtract thread from old thread opener
2043                      --$user_counters[$post['uid']]['threadnum'];
2044                      // Add thread to new thread opener
2045                      ++$user_counters[$new_firstpost['uid']]['threadnum'];
2046                  }
2047                  update_first_post($post['tid']);
2048              }
2049  
2050              // This is the new first post of an existing thread?
2051              if($post['pid'] == $post_info['pid'] && $post['dateline'] < $newthread['dateline'])
2052              {
2053                  // Update post counters if visibility changes
2054                  if($post['visible'] != $newthread['visible'])
2055                  {
2056                      $db->update_query("posts", array('visible' => $newthread['visible']), "pid='{$post['pid']}'");
2057  
2058                      // This is needed to update the forum counters correctly
2059                      $post['visible'] = $newthread['visible'];
2060                  }
2061  
2062                  // Update user thread counter if thread opener changes
2063                  if($newthread['visible'] == 1 && $forum_cache[$newthread['fid']]['usethreadcounts'] == 1 && $post['uid'] != $newthread['uid'])
2064                  {
2065                      // Add thread to new thread opener
2066                      ++$user_counters[$post['uid']]['threadnum'];
2067                      if(!isset($user_counters[$newthread['uid']]))
2068                      {
2069                          $user_counters[$newthread['uid']] = array(
2070                              'postnum' => 0,
2071                              'threadnum' => 0
2072                          );
2073                      }
2074                      // Subtract thread from old thread opener
2075                      --$user_counters[$newthread['uid']]['threadnum'];
2076                  }
2077                  update_first_post($newtid);
2078              }
2079  
2080              if($post['visible'] == 1)
2081              {
2082                  // Modify users' post counts
2083                  if($newthread['visible'] == 1 && ($forum_cache[$post['fid']]['usepostcounts'] == 0 || $post['threadvisible'] != 1) && $forum_cache[$moveto]['usepostcounts'] == 1)
2084                  {
2085                      // Moving into a forum that does count post counts
2086                      ++$user_counters[$post['uid']]['postnum'];
2087                  }
2088  
2089                  // Add 1 to the new thread's replies
2090                  ++$thread_counters[$newtid]['replies'];
2091              }
2092              elseif($post['visible'] == 0)
2093              {
2094                  // Unapproved post
2095                  // Add 1 to the new thread's unapproved posts
2096                  ++$thread_counters[$newtid]['unapprovedposts'];
2097              }
2098              elseif($post['visible'] == -1)
2099              {
2100                  // Soft deleted post
2101                  // Add 1 to the new thread's deleted posts
2102                  ++$thread_counters[$newtid]['deletedposts'];
2103              }
2104  
2105              // Add 1 to the new forum's posts
2106              if($newthread['visible'] == 1 && $post['visible'] == 1)
2107              {
2108                  ++$forum_counters[$moveto]['posts'];
2109              }
2110              elseif($newthread['visible'] == 0 || ($post['visible'] == 0 && $newthread['visible'] == 1))
2111              {
2112                  ++$forum_counters[$moveto]['unapprovedposts'];
2113              }
2114              else
2115              {
2116                  ++$forum_counters[$moveto]['deletedposts'];
2117              }
2118          }
2119  
2120          if($destination_tid == 0 && $newthread['visible'] == 1)
2121          {
2122              // If splitting into a new thread, subtract one from the thread's reply count to compensate for the original post
2123              --$thread_counters[$newtid]['replies'];
2124          }
2125          elseif($destination_tid == 0 && $newthread['visible'] == 0)
2126          {
2127              // If splitting into a new thread, subtract one from the thread's reply count to compensate for the original post
2128              --$thread_counters[$newtid]['unapprovedposts'];
2129          }
2130          elseif($destination_tid == 0 && $newthread['visible'] == -1)
2131          {
2132              // If splitting into a new thread, subtract one from the thread's reply count to compensate for the original post
2133              --$thread_counters[$newtid]['deletedposts'];
2134          }
2135  
2136          $arguments = array("pids" => $pids, "tid" => $tid, "moveto" => $moveto, "newsubject" => $newsubject, "destination_tid" => $destination_tid);
2137          $plugins->run_hooks("class_moderation_split_posts", $arguments);
2138  
2139          // Update user post counts
2140          if(!empty($user_counters))
2141          {
2142              foreach($user_counters as $uid => $counters)
2143              {
2144                  foreach($counters as $key => $counter)
2145                  {
2146                      if($counter >= 0)
2147                      {
2148                          $counters[$key] = "+{$counter}"; // add the addition operator for query
2149                      }
2150                  }
2151                  update_user_counters($uid, $counters);
2152              }
2153          }
2154  
2155          // Update thread counters
2156          if(is_array($thread_counters))
2157          {
2158              foreach($thread_counters as $tid => $counters)
2159              {
2160                  if($tid == $newtid)
2161                  {
2162                      // Update the subject of the first post in the new thread
2163                      $query = $db->simple_select("posts", "pid", "tid='$newtid'", array('order_by' => 'dateline, pid', 'limit' => 1));
2164                      $newthread = $db->fetch_array($query);
2165                      $sqlarray = array(
2166                          "subject" => $newsubject,
2167                          "replyto" => 0
2168                      );
2169                      $db->update_query("posts", $sqlarray, "pid='{$newthread['pid']}'");
2170                  }
2171                  else
2172                  {
2173                      // Update the subject of the first post in the old thread
2174                      $query = $db->query("
2175                          SELECT p.pid, t.subject
2176                          FROM ".TABLE_PREFIX."posts p
2177                          LEFT JOIN ".TABLE_PREFIX."threads t ON (p.tid=t.tid)
2178                          WHERE p.tid='{$tid}'
2179                          ORDER BY p.dateline ASC, p.pid ASC
2180                          LIMIT 1
2181                      ");
2182                      $oldthread = $db->fetch_array($query);
2183                      $sqlarray = array(
2184                          "subject" => $db->escape_string($oldthread['subject']),
2185                          "replyto" => 0
2186                      );
2187                      $db->update_query("posts", $sqlarray, "pid='{$oldthread['pid']}'");
2188                  }
2189  
2190                  foreach($counters as $key => $counter)
2191                  {
2192                      if($counter >= 0)
2193                      {
2194                          $counters[$key] = "+{$counter}";
2195                      }
2196                  }
2197                  update_thread_counters($tid, $counters);
2198                  update_last_post($tid);
2199              }
2200          }
2201  
2202          // Update forum counters
2203          if(!empty($forum_counters))
2204          {
2205              foreach($forum_counters as $fid => $counters)
2206              {
2207                  foreach($counters as $key => $counter)
2208                  {
2209                      if($counter >= 0)
2210                      {
2211                          $counters[$key] = "+{$counter}";
2212                      }
2213                  }
2214                  update_forum_counters($fid, $counters);
2215                  update_forum_lastpost($fid);
2216              }
2217          }
2218  
2219          return $newtid;
2220      }
2221  
2222      /**
2223       * Move multiple threads to new forum
2224       *
2225       * @param array $tids Thread IDs
2226       * @param int $moveto Destination forum
2227       * @return boolean
2228       *
2229       * @deprecated Iterate over move_thread instead
2230       */
2231  	function move_threads($tids, $moveto)
2232      {
2233          global $db, $plugins;
2234  
2235          // Make sure we only have valid values
2236          $tids = array_map('intval', $tids);
2237  
2238          $tid_list = implode(',', $tids);
2239  
2240          $moveto = (int)$moveto;
2241  
2242          $newforum = get_forum($moveto);
2243  
2244          if(empty($tids) || !$newforum)
2245          {
2246              return false;
2247          }
2248  
2249          $total_posts = $total_unapproved_posts = $total_deleted_posts = $total_threads = $total_unapproved_threads = $total_deleted_threads = 0;
2250          $forum_counters = $user_counters = array();
2251          $query = $db->simple_select("threads", "fid, visible, replies, unapprovedposts, deletedposts, tid, uid", "tid IN ($tid_list)");
2252          while($thread = $db->fetch_array($query))
2253          {
2254              $forum = get_forum($thread['fid']);
2255  
2256              if(!isset($forum_counters[$thread['fid']]))
2257              {
2258                  $forum_counters[$thread['fid']] = array(
2259                      'posts' => 0,
2260                      'threads' => 0,
2261                      'unapprovedposts' => 0,
2262                      'unapprovedthreads' => 0,
2263                      'deletedthreads' => 0,
2264                      'deletedposts' => 0
2265                  );
2266              }
2267  
2268              if(!isset($user_counters[$thread['uid']]))
2269              {
2270                  $user_counters[$thread['uid']] = array(
2271                      'num_posts' => 0,
2272                      'num_threads' => 0
2273                  );
2274              }
2275  
2276              if($thread['visible'] == 1)
2277              {
2278                  $total_posts += $thread['replies']+1;
2279                  $total_unapproved_posts += $thread['unapprovedposts'];
2280                  $total_deleted_posts += $thread['deletedposts'];
2281                  $forum_counters[$thread['fid']]['posts'] += $thread['replies']+1;
2282                  $forum_counters[$thread['fid']]['unapprovedposts'] += $thread['unapprovedposts'];
2283                  $forum_counters[$thread['fid']]['deletedposts'] += $thread['deletedposts'];
2284  
2285                  $forum_counters[$thread['fid']]['threads']++;
2286                  ++$total_threads;
2287  
2288                  if($newforum['usethreadcounts'] == 1 && $forum['usethreadcounts'] == 0)
2289                  {
2290                      ++$user_counters[$thread['uid']]['num_threads'];
2291                  }
2292                  else if($newforum['usethreadcounts'] == 0 && $forum['usethreadcounts'] == 1)
2293                  {
2294                      --$user_counters[$thread['uid']]['num_threads'];
2295                  }
2296  
2297                  $query1 = $db->query("
2298                      SELECT COUNT(p.pid) AS posts, u.uid
2299                      FROM ".TABLE_PREFIX."posts p
2300                      LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid)
2301                      WHERE p.tid = '{$thread['tid']}' AND p.visible=1
2302                      GROUP BY u.uid
2303                      ORDER BY posts DESC
2304                  ");
2305                  while($posters = $db->fetch_array($query1))
2306                  {
2307                      if(!isset($user_counters[$posters['uid']]))
2308                      {
2309                          $user_counters[$posters['uid']] = array(
2310                              'num_posts' => 0,
2311                              'num_threads' => 0
2312                          );
2313                      }
2314  
2315                      if($newforum['usepostcounts'] != 0 && $forum['usepostcounts'] == 0)
2316                      {
2317                          $user_counters[$posters['uid']]['num_posts'] += $posters['posts'];
2318                      }
2319                      else if($newforum['usepostcounts'] == 0 && $forum['usepostcounts'] != 0)
2320                      {
2321                          $user_counters[$posters['uid']]['num_posts'] -= $posters['posts'];
2322                      }
2323                  }
2324              }
2325              elseif($thread['visible'] == -1)
2326              {
2327                  $total_deleted_posts += $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1;
2328  
2329                  $forum_counters[$thread['fid']]['deletedposts'] += $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1; // Implied deleted posts counter for deleted threads
2330  
2331                  $forum_counters[$thread['fid']]['deletedthreads']++;
2332                  ++$total_deleted_threads;
2333              }
2334              else
2335              {
2336                  $total_unapproved_posts += $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1;
2337  
2338                  $forum_counters[$thread['fid']]['unapprovedposts'] += $thread['replies']+$thread['unapprovedposts']+$thread['deletedposts']+1; // Implied unapproved posts counter for unapproved threads
2339  
2340                  $forum_counters[$thread['fid']]['unapprovedthreads']++;
2341                  ++$total_unapproved_threads;
2342              }
2343  
2344              // Remove old redirects
2345              $redirects_query = $db->simple_select('threads', 'tid', "closed='moved|{$thread['tid']}' AND fid='$moveto'");
2346              while($redirect_tid = $db->fetch_field($redirects_query, 'tid'))
2347              {
2348                  $this->delete_thread($redirect_tid);
2349              }
2350          }
2351  
2352          $sqlarray = array(
2353              "fid" => $moveto,
2354          );
2355          $db->update_query("threads", $sqlarray, "tid IN ($tid_list)");
2356          $db->update_query("posts", $sqlarray, "tid IN ($tid_list)");
2357  
2358          // If any of the thread has a prefix and the destination forum doesn't accept that prefix, remove the prefix
2359          $query = $db->simple_select("threads", "tid, prefix", "tid IN ($tid_list) AND prefix != 0");
2360          while($thread = $db->fetch_array($query))
2361          {
2362              switch($db->type)
2363              {
2364                  case "pgsql":
2365                  case "sqlite":
2366                      $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(','||forums||',' LIKE '%,$moveto,%' OR forums='-1') AND pid='".$thread['prefix']."'");
2367                      break;
2368                  default:
2369                      $query = $db->simple_select("threadprefixes", "COUNT(*) as num_prefixes", "(CONCAT(',',forums,',') LIKE '%,$moveto,%' OR forums='-1') AND pid='".$thread['prefix']."'");
2370              }
2371              if($db->fetch_field($query, "num_prefixes") == 0)
2372              {
2373                  $sqlarray = array(
2374                      "prefix" => 0,
2375                  );
2376                  $db->update_query("threads", $sqlarray, "tid = '{$thread['tid']}'");
2377              }
2378          }
2379  
2380          $arguments = array("tids" => $tids, "moveto" => $moveto);
2381          $plugins->run_hooks("class_moderation_move_threads", $arguments);
2382  
2383          if(!empty($user_counters))
2384          {
2385              foreach($user_counters as $uid => $counters)
2386              {
2387                  $update_array = array(
2388                      "postnum" => "+{$counters['num_posts']}",
2389                      "threadnum" => "+{$counters['num_threads']}",
2390                  );
2391                  update_user_counters($uid, $update_array);
2392              }
2393          }
2394  
2395          if(is_array($forum_counters))
2396          {
2397              foreach($forum_counters as $fid => $counter)
2398              {
2399                  $updated_count = array(
2400                      'posts' => "-{$counter['posts']}",
2401                      'threads' => "-{$counter['threads']}",
2402                      'unapprovedposts' => "-{$counter['unapprovedposts']}",
2403                      'unapprovedthreads' => "-{$counter['unapprovedthreads']}",
2404                      'deletedposts' => "-{$counter['deletedposts']}",
2405                      'deletedthreads' => "-{$counter['deletedthreads']}"
2406  
2407                  );
2408                  update_forum_counters($fid, $updated_count);
2409                  update_forum_lastpost($fid);
2410              }
2411          }
2412  
2413          $updated_count = array(
2414              "threads" => "+{$total_threads}",
2415              "unapprovedthreads" => "+{$total_unapproved_threads}",
2416              "posts" => "+{$total_posts}",
2417              "unapprovedposts" => "+{$total_unapproved_posts}",
2418              'deletedposts' => "+{$total_deleted_posts}",
2419              "deletedthreads" => "+{$total_deleted_threads}"
2420          );
2421  
2422          update_forum_counters($moveto, $updated_count);
2423          update_forum_lastpost($moveto);
2424  
2425          // Remove thread subscriptions for the users who no longer have permission to view the thread
2426          $this->remove_thread_subscriptions($tid_list, false, $moveto);
2427  
2428          return true;
2429      }
2430  
2431      /**
2432       * Approve multiple posts
2433       *
2434       * @param array $pids PIDs
2435       * @return boolean
2436       */
2437  	function approve_posts($pids)
2438      {
2439          global $db, $cache, $plugins;
2440  
2441          $num_posts = 0;
2442  
2443          if(empty($pids))
2444          {
2445              return false;
2446          }
2447  
2448          // Make sure we only have valid values
2449          $pids = array_map('intval', $pids);
2450  
2451          $pid_list = implode(',', $pids);
2452          $pids = $threads_to_update = array();
2453  
2454          // Make visible
2455          $approve = array(
2456              "visible" => 1,
2457          );
2458  
2459          // We have three cases we deal with in these code segments:
2460          // 1) We're approving specific unapproved posts
2461          // 1.1) if the thread is approved
2462          // 1.2) if the thread is unapproved
2463          // 2) We're approving the firstpost of the thread, therefore approving the thread itself
2464          // 3) We're doing both 1 and 2
2465          $query = $db->query("
2466              SELECT p.tid
2467              FROM ".TABLE_PREFIX."posts p
2468              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
2469              WHERE p.pid IN ($pid_list) AND p.visible = '0' AND t.firstpost = p.pid AND t.visible = 0
2470          ");
2471          while($post = $db->fetch_array($query))
2472          {
2473              // This is the first post in the thread so we're approving the whole thread.
2474              $threads_to_update[] = $post['tid'];
2475          }
2476  
2477          if(!empty($threads_to_update))
2478          {
2479              $this->approve_threads($threads_to_update);
2480          }
2481  
2482          $thread_counters = $forum_counters = $user_counters = array();
2483  
2484          $query = $db->query("
2485              SELECT p.pid, p.tid, p.fid, p.uid, t.visible AS threadvisible
2486              FROM ".TABLE_PREFIX."posts p
2487              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
2488              WHERE p.pid IN ($pid_list) AND p.visible = '0' AND t.firstpost != p.pid
2489          ");
2490          while($post = $db->fetch_array($query))
2491          {
2492              $pids[] = $post['pid'];
2493  
2494              if(!isset($thread_counters[$post['tid']]))
2495              {
2496                  $thread_counters[$post['tid']] = array(
2497                      'replies' => 0
2498                  );
2499              }
2500  
2501              ++$thread_counters[$post['tid']]['replies'];
2502  
2503              // If the thread of this post is unapproved then we've already taken into account this counter as implied.
2504              // Updating it again would cause it to double count
2505              if($post['threadvisible'] == 1)
2506              {
2507                  if(!isset($forum_counters[$post['fid']]))
2508                  {
2509                      $forum_counters[$post['fid']] = array(
2510                          'num_posts' => 0
2511                      );
2512                  }
2513                  ++$forum_counters[$post['fid']]['num_posts'];
2514              }
2515  
2516              $forum = get_forum($post['fid']);
2517  
2518              // If post counts enabled in this forum and the thread is approved, add 1
2519              if($forum['usepostcounts'] != 0 && $post['threadvisible'] == 1)
2520              {
2521                  if(!isset($user_counters[$post['uid']]))
2522                  {
2523                      $user_counters[$post['uid']] = 0;
2524                  }
2525                  ++$user_counters[$post['uid']];
2526              }
2527          }
2528  
2529          if(empty($pids) && empty($threads_to_update))
2530          {
2531              return false;
2532          }
2533  
2534          if(!empty($pids))
2535          {
2536              $where = "pid IN (".implode(',', $pids).")";
2537              $db->update_query("posts", $approve, $where);
2538          }
2539  
2540          $plugins->run_hooks("class_moderation_approve_posts", $pids);
2541  
2542          if(!empty($thread_counters))
2543          {
2544              foreach($thread_counters as $tid => $counters)
2545              {
2546                  $counters_update = array(
2547                      "unapprovedposts" => "-".$counters['replies'],
2548                      "replies" => "+".$counters['replies']
2549                  );
2550                  update_thread_counters($tid, $counters_update);
2551                  update_last_post($tid);
2552              }
2553          }
2554  
2555          if(!empty($forum_counters))
2556          {
2557              foreach($forum_counters as $fid => $counters)
2558              {
2559                  $updated_forum_stats = array(
2560                      'posts' => "+{$counters['num_posts']}",
2561                      'unapprovedposts' => "-{$counters['num_posts']}",
2562                  );
2563                  update_forum_counters($fid, $updated_forum_stats);
2564                  update_forum_lastpost($fid);
2565              }
2566          }
2567  
2568          if(!empty($user_counters))
2569          {
2570              foreach($user_counters as $uid => $counter)
2571              {
2572                  update_user_counters($uid, array('postnum' => "+{$counter}"));
2573              }
2574          }
2575  
2576          return true;
2577      }
2578  
2579      /**
2580       * Unapprove multiple posts
2581       *
2582       * @param array $pids PIDs
2583       * @return boolean
2584       */
2585  	function unapprove_posts($pids)
2586      {
2587          global $db, $cache, $plugins;
2588  
2589          if(empty($pids))
2590          {
2591              return false;
2592          }
2593  
2594          // Make sure we only have valid values
2595          $pids = array_map('intval', $pids);
2596  
2597          $pid_list = implode(',', $pids);
2598          $pids = $threads_to_update = array();
2599  
2600          // Make invisible
2601          $approve = array(
2602              "visible" => 0,
2603          );
2604  
2605          // We have three cases we deal with in these code segments:
2606          // 1) We're unapproving specific approved posts
2607          // 1.1) if the thread is approved
2608          // 1.2) if the thread is unapproved
2609          // 1.3) if the thread is deleted
2610          // 2) We're unapproving the firstpost of the thread, therefore unapproving the thread itself
2611          // 3) We're doing both 1 and 2
2612          $query = $db->query("
2613              SELECT p.tid
2614              FROM ".TABLE_PREFIX."posts p
2615              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
2616              WHERE p.pid IN ($pid_list) AND p.visible IN (-1,1) AND t.firstpost = p.pid AND t.visible IN (-1,1)
2617          ");
2618          while($post = $db->fetch_array($query))
2619          {
2620              // This is the first post in the thread so we're unapproving the whole thread.
2621              $threads_to_update[] = $post['tid'];
2622          }
2623  
2624          if(!empty($threads_to_update))
2625          {
2626              $this->unapprove_threads($threads_to_update);
2627          }
2628  
2629          $thread_counters = $forum_counters = $user_counters = array();
2630  
2631          $query = $db->query("
2632              SELECT p.pid, p.tid, p.visible, p.fid, p.uid, t.visible AS threadvisible
2633              FROM ".TABLE_PREFIX."posts p
2634              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
2635              WHERE p.pid IN ($pid_list) AND p.visible IN (-1,1) AND t.firstpost != p.pid
2636          ");
2637          while($post = $db->fetch_array($query))
2638          {
2639              $pids[] = $post['pid'];
2640  
2641              if(!isset($thread_counters[$post['tid']]))
2642              {
2643                  $thread_counters[$post['tid']] = array(
2644                      'replies' => 0,
2645                      'unapprovedposts' => 0,
2646                      'deletedposts' => 0
2647                  );
2648              }
2649  
2650              ++$thread_counters[$post['tid']]['unapprovedposts'];
2651              if($post['visible'] == 1)
2652              {
2653                  ++$thread_counters[$post['tid']]['replies'];
2654              }
2655              else
2656              {
2657                  ++$thread_counters[$post['tid']]['deletedposts'];
2658              }
2659  
2660              if(!isset($forum_counters[$post['fid']]))
2661              {
2662                  $forum_counters[$post['fid']] = array(
2663                      'num_posts' => 0,
2664                      'num_unapproved_posts' => 0,
2665                      'num_deleted_posts' => 0
2666                  );
2667              }
2668  
2669              // If the thread of this post is unapproved then we've already taken into account this counter as implied.
2670              // Updating it again would cause it to double count
2671              if($post['threadvisible'] != 0)
2672              {
2673                  ++$forum_counters[$post['fid']]['num_unapproved_posts'];
2674                  if($post['visible'] == 1)
2675                  {
2676                      ++$forum_counters[$post['fid']]['num_posts'];
2677                  }
2678                  else
2679                  {
2680                      ++$forum_counters[$post['fid']]['num_deleted_posts'];
2681                  }
2682              }
2683  
2684              $forum = get_forum($post['fid']);
2685  
2686              // If post counts enabled in this forum and the thread is approved, subtract 1
2687              if($forum['usepostcounts'] != 0 && $post['visible'] == 1 && $post['threadvisible'] == 1)
2688              {
2689                  if(!isset($user_counters[$post['uid']]))
2690                  {
2691                      $user_counters[$post['uid']] = 0;
2692                  }
2693                  --$user_counters[$post['uid']];
2694              }
2695          }
2696  
2697          if(empty($pids) && empty($threads_to_update))
2698          {
2699              return false;
2700          }
2701  
2702          if(!empty($pids))
2703          {
2704              $where = "pid IN (".implode(',', $pids).")";
2705              $db->update_query("posts", $approve, $where);
2706          }
2707  
2708          $plugins->run_hooks("class_moderation_unapprove_posts", $pids);
2709  
2710          if(!empty($thread_counters))
2711          {
2712              foreach($thread_counters as $tid => $counters)
2713              {
2714                  $counters_update = array(
2715                      "unapprovedposts" => "+".$counters['unapprovedposts'],
2716                      "replies" => "-".$counters['replies'],
2717                      "deletedposts" => "-".$counters['deletedposts']
2718                  );
2719  
2720                  update_thread_counters($tid, $counters_update);
2721                  update_last_post($tid);
2722              }
2723          }
2724  
2725          if(!empty($forum_counters))
2726          {
2727              foreach($forum_counters as $fid => $counters)
2728              {
2729                  $updated_forum_stats = array(
2730                      'posts' => "-{$counters['num_posts']}",
2731                      'unapprovedposts' => "+{$counters['num_unapproved_posts']}",
2732                      'deletedposts' => "-{$counters['num_deleted_posts']}"
2733                  );
2734                  update_forum_counters($fid, $updated_forum_stats);
2735                  update_forum_lastpost($fid);
2736              }
2737          }
2738  
2739          if(!empty($user_counters))
2740          {
2741              foreach($user_counters as $uid => $counter)
2742              {
2743                  update_user_counters($uid, array('postnum' => "{$counter}"));
2744              }
2745          }
2746  
2747          return true;
2748      }
2749  
2750      /**
2751       * Change thread subject
2752       *
2753       * @param int|array $tids Thread ID(s)
2754       * @param string $format Format of new subject (with {subject})
2755       * @return boolean
2756       */
2757  	function change_thread_subject($tids, $format)
2758      {
2759          global $db, $mybb, $plugins;
2760  
2761          // Get tids into list
2762          if(!is_array($tids))
2763          {
2764              $tids = array($tids);
2765          }
2766  
2767          // Make sure we only have valid values
2768          $tids = array_map('intval', $tids);
2769  
2770          if(empty($tids))
2771          {
2772              return false;
2773          }
2774  
2775          $tid_list = implode(',', $tids);
2776  
2777          // Get original subject
2778          $query = $db->query("
2779              SELECT u.uid, u.username, t.tid, t.subject FROM ".TABLE_PREFIX."threads t
2780              LEFT JOIN ".TABLE_PREFIX."users u ON t.uid=u.uid
2781              WHERE tid IN ($tid_list)
2782          ");
2783          while($thread = $db->fetch_array($query))
2784          {
2785              // Update threads and first posts with new subject
2786              $find = array('{username}', 'author', '{subject}');
2787              $replace = array($mybb->user['username'], $thread['username'], $thread['subject']);
2788  
2789              $new_subject = str_ireplace($find, $replace, $format);
2790  
2791              $args = array(
2792                  'thread' => &$thread,
2793                  'new_subject' => &$new_subject,
2794              );
2795  
2796              $plugins->run_hooks("class_moderation_change_thread_subject_newsubject", $args);
2797  
2798              $update_subject = array(
2799                  "subject" => $db->escape_string($new_subject)
2800              );
2801              $db->update_query("threads", $update_subject, "tid='{$thread['tid']}'");
2802              $db->update_query("posts", $update_subject, "tid='{$thread['tid']}' AND replyto='0'");
2803          }
2804  
2805          $arguments = array("tids" => $tids, "format" => $format);
2806          $plugins->run_hooks("class_moderation_change_thread_subject", $arguments);
2807  
2808          return true;
2809      }
2810  
2811      /**
2812       * Add thread expiry
2813       *
2814       * @param int $tid Thread ID
2815       * @param int $deletetime Timestamp when the thread is deleted
2816       * @return boolean
2817       */
2818  	function expire_thread($tid, $deletetime)
2819      {
2820          global $db, $plugins;
2821  
2822          $tid = (int)$tid;
2823  
2824          if(empty($tid))
2825          {
2826              return false;
2827          }
2828  
2829          $update_thread = array(
2830              "deletetime" => (int)$deletetime
2831          );
2832          $db->update_query("threads", $update_thread, "tid='{$tid}'");
2833  
2834          $arguments = array("tid" => $tid, "deletetime" => $deletetime);
2835          $plugins->run_hooks("class_moderation_expire_thread", $arguments);
2836  
2837          return true;
2838      }
2839  
2840      /**
2841       * Toggle post visibility (approved/unapproved)
2842       *
2843       * @param array $pids Post IDs
2844       * @return boolean true
2845       */
2846  	function toggle_post_visibility($pids)
2847      {
2848          global $db;
2849  
2850          // Make sure we only have valid values
2851          $pids = array_map('intval', $pids);
2852  
2853          $pid_list = implode(',', $pids);
2854          $query = $db->simple_select("posts", 'pid, visible', "pid IN ($pid_list)");
2855          while($post = $db->fetch_array($query))
2856          {
2857              if($post['visible'] != 0)
2858              {
2859                  $unapprove[] = $post['pid'];
2860              }
2861              else
2862              {
2863                  $approve[] = $post['pid'];
2864              }
2865          }
2866          if(is_array($unapprove))
2867          {
2868              $this->unapprove_posts($unapprove);
2869          }
2870          if(is_array($approve))
2871          {
2872              $this->approve_posts($approve);
2873          }
2874          return true;
2875      }
2876  
2877      /**
2878       * Toggle post visibility (deleted/restored)
2879       *
2880       * @param array $pids Post IDs
2881       * @return boolean true
2882       */
2883  	function toggle_post_softdelete($pids)
2884      {
2885          global $db;
2886  
2887          // Make sure we only have valid values
2888          $pids = array_map('intval', $pids);
2889  
2890          $pid_list = implode(',', $pids);
2891          $query = $db->simple_select("posts", 'pid, visible', "pid IN ($pid_list)");
2892          while($post = $db->fetch_array($query))
2893          {
2894              if($post['visible'] != -1)
2895              {
2896                  $delete[] = $post['pid'];
2897              }
2898              else
2899              {
2900                  $restore[] = $post['pid'];
2901              }
2902          }
2903          if(is_array($delete))
2904          {
2905              $this->soft_delete_posts($delete);
2906          }
2907          if(is_array($restore))
2908          {
2909              $this->restore_posts($restore);
2910          }
2911          return true;
2912      }
2913  
2914      /**
2915       * Toggle thread visibility (approved/unapproved)
2916       *
2917       * @param array $tids Thread IDs
2918       * @param int $fid Forum ID
2919       * @return boolean true
2920       */
2921  	function toggle_thread_visibility($tids, $fid)
2922      {
2923          global $db;
2924  
2925          // Make sure we only have valid values
2926          $tids = array_map('intval', $tids);
2927          $fid = (int)$fid;
2928  
2929          $tid_list = implode(',', $tids);
2930          $query = $db->simple_select("threads", 'tid, visible', "tid IN ($tid_list)");
2931          while($thread = $db->fetch_array($query))
2932          {
2933              if($thread['visible'] != 0)
2934              {
2935                  $unapprove[] = $thread['tid'];
2936              }
2937              else
2938              {
2939                  $approve[] = $thread['tid'];
2940              }
2941          }
2942          if(is_array($unapprove))
2943          {
2944              $this->unapprove_threads($unapprove, $fid);
2945          }
2946          if(is_array($approve))
2947          {
2948              $this->approve_threads($approve, $fid);
2949          }
2950          return true;
2951      }
2952  
2953      /**
2954       * Toggle thread visibility (deleted/restored)
2955       *
2956       * @param array $tids Thread IDs
2957       * @return boolean true
2958       */
2959  	function toggle_thread_softdelete($tids)
2960      {
2961          global $db;
2962  
2963          // Make sure we only have valid values
2964          $tids = array_map('intval', $tids);
2965  
2966          $tid_list = implode(',', $tids);
2967          $query = $db->simple_select("threads", 'tid, visible', "tid IN ($tid_list)");
2968          while($thread = $db->fetch_array($query))
2969          {
2970              if($thread['visible'] != -1)
2971              {
2972                  $delete[] = $thread['tid'];
2973              }
2974              else
2975              {
2976                  $restore[] = $thread['tid'];
2977              }
2978          }
2979          if(is_array($delete))
2980          {
2981              $this->soft_delete_threads($delete);
2982          }
2983          if(is_array($restore))
2984          {
2985              $this->restore_threads($restore);
2986          }
2987          return true;
2988      }
2989  
2990      /**
2991       * Toggle threads open/closed
2992       *
2993       * @param array $tids Thread IDs
2994       * @return boolean true
2995       */
2996  	function toggle_thread_status($tids)
2997      {
2998          global $db;
2999  
3000          // Make sure we only have valid values
3001          $tids = array_map('intval', $tids);
3002  
3003          $tid_list = implode(',', $tids);
3004          $query = $db->simple_select("threads", 'tid, closed', "tid IN ($tid_list)");
3005          while($thread = $db->fetch_array($query))
3006          {
3007              if($thread['closed'] == 1)
3008              {
3009                  $open[] = $thread['tid'];
3010              }
3011              elseif($thread['closed'] == 0)
3012              {
3013                  $close[] = $thread['tid'];
3014              }
3015          }
3016          if(is_array($open))
3017          {
3018              $this->open_threads($open);
3019          }
3020          if(is_array($close))
3021          {
3022              $this->close_threads($close);
3023          }
3024          return true;
3025      }
3026  
3027      /**
3028       * Toggle threads stick/unstick
3029       *
3030       * @param array $tids Thread IDs
3031       * @return boolean true
3032       */
3033  	function toggle_thread_importance($tids)
3034      {
3035          global $db;
3036  
3037          // Make sure we only have valid values
3038          $tids = array_map('intval', $tids);
3039  
3040          $stick = array();
3041          $unstick = array();
3042  
3043          $tid_list = implode(',', $tids);
3044          $query = $db->simple_select("threads", 'tid, sticky', "tid IN ($tid_list)");
3045          while($thread = $db->fetch_array($query))
3046          {
3047              if($thread['sticky'] == 0)
3048              {
3049                  $stick[] = $thread['tid'];
3050              }
3051              elseif($thread['sticky'] == 1)
3052              {
3053                  $unstick[] = $thread['tid'];
3054              }
3055          }
3056          if(!empty($stick))
3057          {
3058              $this->stick_threads($stick);
3059          }
3060          if(!empty($unstick))
3061          {
3062              $this->unstick_threads($unstick);
3063          }
3064          return true;
3065      }
3066  
3067      /**
3068       * Remove thread subscriptions (from one or multiple threads in the same forum)
3069       *
3070       * @param int|array $tids Thread ID, or an array of thread IDs from the same forum.
3071       * @param boolean $all True (default) to delete all subscriptions, false to only delete subscriptions from users with no permission to read the thread
3072       * @param int $fid (Only applies if $all is false) The forum ID of the thread
3073       * @return boolean
3074       */
3075  	function remove_thread_subscriptions($tids, $all = true, $fid = 0)
3076      {
3077          global $db, $plugins;
3078  
3079          // Format thread IDs
3080          if(!is_array($tids))
3081          {
3082              $tids = array($tids);
3083          }
3084  
3085          if(empty($tids))
3086          {
3087              return false;
3088          }
3089  
3090          // Make sure we only have valid values
3091          $tids = array_map('intval', $tids);
3092          $fid = (int)$fid;
3093  
3094          $tids_csv = implode(',', $tids);
3095  
3096          // Delete only subscriptions from users who no longer have permission to read the thread.
3097          if(!$all)
3098          {
3099              // Get groups that cannot view the forum or its threads
3100              $forum_parentlist = get_parent_list($fid);
3101              $query = $db->simple_select("forumpermissions", "gid", "fid IN ({$forum_parentlist}) AND (canview=0 OR canviewthreads=0)");
3102              $groups = array();
3103              $additional_groups = '';
3104              while($group = $db->fetch_array($query))
3105              {
3106                  $groups[] = $group['gid'];
3107                  switch($db->type)
3108                  {
3109                      case "pgsql":
3110                      case "sqlite":
3111                          $additional_groups .= " OR ','||u.additionalgroups||',' LIKE ',{$group['gid']},'";
3112                          break;
3113                      default:
3114                          $additional_groups .= " OR CONCAT(',',u.additionalgroups,',') LIKE ',{$group['gid']},'";
3115                  }
3116              }
3117              // If there are groups found, delete subscriptions from users in these groups
3118              if(count($groups) > 0)
3119              {
3120                  $groups_csv = implode(',', $groups);
3121                  $query = $db->query("
3122                      SELECT s.tid, u.uid
3123                      FROM ".TABLE_PREFIX."threadsubscriptions s
3124                      LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=s.uid)
3125                      WHERE s.tid IN ({$tids_csv})
3126                      AND (u.usergroup IN ({$groups_csv}){$additional_groups})
3127                  ");
3128                  while($subscription = $db->fetch_array($query))
3129                  {
3130                      $db->delete_query("threadsubscriptions", "uid='{$subscription['uid']}' AND tid='{$subscription['tid']}'");
3131                  }
3132              }
3133          }
3134          // Delete all subscriptions of this thread
3135          else
3136          {
3137              $db->delete_query("threadsubscriptions", "tid IN ({$tids_csv})");
3138          }
3139  
3140          $arguments = array("tids" => $tids, "all" => $all, "fid" => $fid);
3141          $plugins->run_hooks("class_moderation_remove_thread_subscriptions", $arguments);
3142  
3143          return true;
3144      }
3145  
3146      /**
3147       * Apply a thread prefix (to one or multiple threads in the same forum)
3148       *
3149       * @param int|array $tids Thread ID, or an array of thread IDs from the same forum.
3150       * @param int $prefix Prefix ID to apply to the threads
3151       * @return bool
3152       */
3153  	function apply_thread_prefix($tids, $prefix = 0)
3154      {
3155          global $db, $plugins;
3156  
3157          // Format thread IDs
3158          if(!is_array($tids))
3159          {
3160              $tids = array($tids);
3161          }
3162  
3163          if(empty($tids))
3164          {
3165              return false;
3166          }
3167  
3168          // Make sure we only have valid values
3169          $tids = array_map('intval', $tids);
3170          $tids_csv = implode(',', $tids);
3171  
3172          $update_thread = array('prefix' => (int)$prefix);
3173          $db->update_query('threads', $update_thread, "tid IN ({$tids_csv})");
3174  
3175          $arguments = array('tids' => $tids, 'prefix' => $prefix);
3176  
3177          $plugins->run_hooks('class_moderation_apply_thread_prefix', $arguments);
3178  
3179          return true;
3180      }
3181  
3182      /**
3183       * Soft delete multiple posts
3184       *
3185       * @param array $pids PIDs
3186       * @return boolean
3187       */
3188  	function soft_delete_posts($pids)
3189      {
3190          global $db, $cache, $plugins;
3191  
3192          if(empty($pids))
3193          {
3194              return false;
3195          }
3196  
3197          // Make sure we only have valid values
3198          $pids = array_map('intval', $pids);
3199  
3200          $pid_list = implode(',', $pids);
3201          $pids = $threads_to_update = array();
3202  
3203          // Make invisible
3204          $update = array(
3205              "visible" => -1,
3206          );
3207  
3208          // We have three cases we deal with in these code segments:
3209          // 1) We're deleting specific approved posts
3210          // 1.1) if the thread is approved
3211          // 1.2) if the thread is unapproved
3212          // 1.3) if the thread is deleted
3213          // 2) We're deleting the firstpost of the thread, therefore deleting the thread itself
3214          // 3) We're doing both 1 and 2
3215          $query = $db->query("
3216              SELECT p.tid
3217              FROM ".TABLE_PREFIX."posts p
3218              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
3219              WHERE p.pid IN ($pid_list) AND p.visible IN (0,1) AND t.firstpost = p.pid AND t.visible IN (0,1)
3220          ");
3221          while($post = $db->fetch_array($query))
3222          {
3223              // This is the first post in the thread so we're deleting the whole thread.
3224              $threads_to_update[] = $post['tid'];
3225          }
3226  
3227          if(!empty($threads_to_update))
3228          {
3229              $this->soft_delete_threads($threads_to_update);
3230          }
3231  
3232          $thread_counters = $forum_counters = $user_counters = array();
3233  
3234          $query = $db->query("
3235              SELECT p.pid, p.tid, p.visible, f.fid, f.usepostcounts, p.uid, t.visible AS threadvisible
3236              FROM ".TABLE_PREFIX."posts p
3237              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
3238              LEFT JOIN ".TABLE_PREFIX."forums f ON (f.fid=p.fid)
3239              WHERE p.pid IN ($pid_list) AND p.visible IN (0,1) AND t.firstpost != p.pid
3240          ");
3241          while($post = $db->fetch_array($query))
3242          {
3243              $pids[] = $post['pid'];
3244  
3245              if(!isset($thread_counters[$post['tid']]))
3246              {
3247                  $thread_counters[$post['tid']] = array(
3248                      'replies' => 0,
3249                      'unapprovedposts' => 0,
3250                      'deletedposts' => 0
3251                  );
3252              }
3253  
3254              ++$thread_counters[$post['tid']]['deletedposts'];
3255              if($post['visible'] == 1)
3256              {
3257                  ++$thread_counters[$post['tid']]['replies'];
3258              }
3259              else
3260              {
3261                  ++$thread_counters[$post['tid']]['unapprovedposts'];
3262              }
3263  
3264              if(!isset($forum_counters[$post['fid']]))
3265              {
3266                  $forum_counters[$post['fid']] = array(
3267                      'num_posts' => 0,
3268                      'num_unapproved_posts' => 0,
3269                      'num_deleted_posts' => 0
3270                  );
3271              }
3272  
3273              // If the thread of this post is deleted then we've already taken into account this counter as implied.
3274              // Updating it again would cause it to double count
3275              if($post['threadvisible'] == 1)
3276              {
3277                  ++$forum_counters[$post['fid']]['num_deleted_posts'];
3278                  if($post['visible'] == 1)
3279                  {
3280                      ++$forum_counters[$post['fid']]['num_posts'];
3281                  }
3282                  else
3283                  {
3284                      ++$forum_counters[$post['fid']]['num_unapproved_posts'];
3285                  }
3286              }
3287  
3288              // If post counts enabled in this forum and the thread is approved, subtract 1
3289              if($post['usepostcounts'] != 0 && $post['threadvisible'] == 1 && $post['visible'] == 1)
3290              {
3291                  if(!isset($user_counters[$post['uid']]))
3292                  {
3293                      $user_counters[$post['uid']] = 0;
3294                  }
3295                  --$user_counters[$post['uid']];
3296              }
3297          }
3298  
3299          if(empty($pids) && empty($threads_to_update))
3300          {
3301              return false;
3302          }
3303  
3304          if(!empty($pids))
3305          {
3306              $where = "pid IN (".implode(',', $pids).")";
3307              $db->update_query("posts", $update, $where);
3308              mark_reports($pids, "posts");
3309          }
3310  
3311          $plugins->run_hooks("class_moderation_soft_delete_posts", $pids);
3312  
3313          if(is_array($thread_counters))
3314          {
3315              foreach($thread_counters as $tid => $counters)
3316              {
3317                  $counters_update = array(
3318                      "unapprovedposts" => "-".$counters['unapprovedposts'],
3319                      "replies" => "-".$counters['replies'],
3320                      "deletedposts" => "+".$counters['deletedposts']
3321                  );
3322  
3323                  update_thread_counters($tid, $counters_update);
3324                  update_last_post($tid);
3325              }
3326          }
3327  
3328          if(is_array($forum_counters))
3329          {
3330              foreach($forum_counters as $fid => $counters)
3331              {
3332                  $updated_forum_stats = array(
3333                      'posts' => "-{$counters['num_posts']}",
3334                      'unapprovedposts' => "-{$counters['num_unapproved_posts']}",
3335                      'deletedposts' => "+{$counters['num_deleted_posts']}"
3336                  );
3337                  update_forum_counters($fid, $updated_forum_stats);
3338                  update_forum_lastpost($fid);
3339              }
3340          }
3341  
3342          if(!empty($user_counters))
3343          {
3344              foreach($user_counters as $uid => $counter)
3345              {
3346                  update_user_counters($uid, array('postnum' => "{$counter}"));
3347              }
3348          }
3349  
3350          return true;
3351      }
3352  
3353      /**
3354       * Restore multiple posts
3355       *
3356       * @param array $pids PIDs
3357       * @return boolean
3358       */
3359  	function restore_posts($pids)
3360      {
3361          global $db, $cache, $plugins;
3362  
3363          $num_posts = 0;
3364  
3365          if(empty($pids))
3366          {
3367              return false;
3368          }
3369  
3370          // Make sure we only have valid values
3371          $pids = array_map('intval', $pids);
3372  
3373          $pid_list = implode(',', $pids);
3374          $pids = $threads_to_update = array();
3375  
3376          // Make visible
3377          $update = array(
3378              "visible" => 1,
3379          );
3380  
3381          // We have three cases we deal with in these code segments:
3382          // 1) We're approving specific restored posts
3383          // 1.1) if the thread is deleted
3384          // 1.2) if the thread is restored
3385          // 2) We're restoring the firstpost of the thread, therefore restoring the thread itself
3386          // 3) We're doing both 1 and 2
3387          $query = $db->query("
3388              SELECT p.tid
3389              FROM ".TABLE_PREFIX."posts p
3390              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
3391              WHERE p.pid IN ($pid_list) AND p.visible = '-1' AND t.firstpost = p.pid AND t.visible = -1
3392          ");
3393          while($post = $db->fetch_array($query))
3394          {
3395              // This is the first post in the thread so we're approving the whole thread.
3396              $threads_to_update[] = $post['tid'];
3397          }
3398  
3399          if(!empty($threads_to_update))
3400          {
3401              $this->restore_threads($threads_to_update);
3402          }
3403  
3404          $thread_counters = $forum_counters = $user_counters = array();
3405  
3406          $query = $db->query("
3407              SELECT p.pid, p.tid, f.fid, f.usepostcounts, p.uid, t.visible AS threadvisible
3408              FROM ".TABLE_PREFIX."posts p
3409              LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
3410              LEFT JOIN ".TABLE_PREFIX."forums f ON (f.fid=p.fid)
3411              WHERE p.pid IN ($pid_list) AND p.visible = '-1' AND t.firstpost != p.pid
3412          ");
3413          while($post = $db->fetch_array($query))
3414          {
3415              $pids[] = $post['pid'];
3416  
3417              if(!isset($thread_counters[$post['tid']]))
3418              {
3419                  $thread_counters[$post['tid']] = array(
3420                      'replies' => 0
3421                  );
3422              }
3423  
3424              ++$thread_counters[$post['tid']]['replies'];
3425  
3426              // If the thread of this post is deleted then we've already taken into account this counter as implied.
3427              // Updating it again would cause it to double count
3428              if($post['threadvisible'] == 1)
3429              {
3430                  if(!isset($forum_counters[$post['fid']]))
3431                  {
3432                      $forum_counters[$post['fid']] = array(
3433                          'num_posts' => 0
3434                      );
3435                  }
3436                  ++$forum_counters[$post['fid']]['num_posts'];
3437              }
3438  
3439              // If post counts enabled in this forum and the thread is approved, add 1
3440              if($post['usepostcounts'] != 0 && $post['threadvisible'] == 1)
3441              {
3442                  if(!isset($user_counters[$post['uid']]))
3443                  {
3444                      $user_counters[$post['uid']] = 0;
3445                  }
3446                  ++$user_counters[$post['uid']];
3447  
3448              }
3449          }
3450  
3451          if(empty($pids) && empty($threads_to_update))
3452          {
3453              return false;
3454          }
3455  
3456          if(!empty($pids))
3457          {
3458              $where = "pid IN (".implode(',', $pids).")";
3459              $db->update_query("posts", $update, $where);
3460          }
3461  
3462          $plugins->run_hooks("class_moderation_restore_posts", $pids);
3463  
3464          if(is_array($thread_counters))
3465          {
3466              foreach($thread_counters as $tid => $counters)
3467              {
3468                  $counters_update = array(
3469                      "deletedposts" => "-".$counters['replies'],
3470                      "replies" => "+".$counters['replies']
3471                  );
3472                  update_thread_counters($tid, $counters_update);
3473                  update_last_post($tid);
3474              }
3475          }
3476  
3477          if(is_array($forum_counters))
3478          {
3479              foreach($forum_counters as $fid => $counters)
3480              {
3481                  $updated_forum_stats = array(
3482                      'posts' => "+{$counters['num_posts']}",
3483                      'deletedposts' => "-{$counters['num_posts']}"
3484                  );
3485                  update_forum_counters($fid, $updated_forum_stats);
3486                  update_forum_lastpost($fid);
3487              }
3488          }
3489  
3490          if(!empty($user_counters))
3491          {
3492              foreach($user_counters as $uid => $counter)
3493              {
3494                  update_user_counters($uid, array('postnum' => "+{$counter}"));
3495              }
3496          }
3497  
3498          return true;
3499      }
3500  
3501      /**
3502       * Restore one or more threads
3503       *
3504       * @param array|int $tids Thread ID(s)
3505       * @return boolean true
3506       */
3507  	function restore_threads($tids)
3508      {
3509          global $db, $cache, $plugins;
3510  
3511          if(!is_array($tids))
3512          {
3513              $tids = array($tids);
3514          }
3515  
3516          if(empty($tids))
3517          {
3518              return false;
3519          }
3520  
3521          // Make sure we only have valid values
3522          $tids = array_map('intval', $tids);
3523  
3524          $tid_list = $forum_counters = $user_counters = $posts_to_restore = array();
3525  
3526          $tids_list = implode(",", $tids);
3527          $query = $db->simple_select("threads", "*", "tid IN ($tids_list)");
3528  
3529          while($thread = $db->fetch_array($query))
3530          {
3531              if($thread['visible'] != -1)
3532              {
3533                  continue;
3534              }
3535              $tid_list[] = $thread['tid'];
3536  
3537              $forum = get_forum($thread['fid']);
3538  
3539              if(!isset($forum_counters[$forum['fid']]))
3540              {
3541                  $forum_counters[$forum['fid']] = array(
3542                      'num_posts' => 0,
3543                      'num_threads' => 0,
3544                      'num_deleted_posts' => 0,
3545                      'num_unapproved_posts' => 0
3546                  );
3547              }
3548  
3549              if(!isset($user_counters[$thread['uid']]))
3550              {
3551                  $user_counters[$thread['uid']] = array(
3552                      'num_posts' => 0,
3553                      'num_threads' => 0
3554                  );
3555              }
3556  
3557              ++$forum_counters[$forum['fid']]['num_threads'];
3558              $forum_counters[$forum['fid']]['num_posts'] += $thread['replies']+1; // Remove implied visible from count
3559              $forum_counters[$forum['fid']]['num_deleted_posts'] += $thread['replies']+$thread['unapprovedposts']+1;
3560              $forum_counters[$forum['fid']]['num_unapproved_posts'] += $thread['unapprovedposts'];
3561  
3562              if($forum['usepostcounts'] != 0)
3563              {
3564                  // On approving thread restore user post counts
3565                  $query2 = $db->simple_select("posts", "COUNT(pid) as posts, uid", "tid='{$thread['tid']}' AND (visible='1' OR pid='{$thread['firstpost']}') AND uid > 0 GROUP BY uid");
3566                  while($counter = $db->fetch_array($query2))
3567                  {
3568                      if(!isset($user_counters[$counter['uid']]['num_posts']))
3569                      {
3570                          $user_counters[$counter['uid']]['num_posts'] = 0;
3571                      }
3572                      $user_counters[$counter['uid']]['num_posts'] += $counter['posts'];
3573                  }
3574              }
3575  
3576              if($forum['usethreadcounts'] != 0 && substr($thread['closed'], 0, 6) != 'moved|')
3577              {
3578                  ++$user_counters[$thread['uid']]['num_threads'];
3579              }
3580  
3581              $posts_to_restore[] = $thread['firstpost'];
3582          }
3583  
3584          if(!empty($tid_list))
3585          {
3586              $tid_moved_list = "";
3587              $comma = "";
3588              foreach($tid_list as $tid)
3589              {
3590                  $tid_moved_list .= "{$comma}'moved|{$tid}'";
3591                  $comma = ",";
3592              }
3593              $tid_list = implode(',', $tid_list);
3594              $update = array(
3595                  "visible" => 1
3596              );
3597              $db->update_query("threads", $update, "tid IN ($tid_list)");
3598              // Restore redirects, too
3599              $redirect_tids = array();
3600              $query = $db->simple_select('threads', 'tid', "closed IN ({$tid_moved_list})");
3601              while($redirect_tid = $db->fetch_field($query, 'tid'))
3602              {
3603                  $redirect_tids[] = $redirect_tid;
3604              }
3605              if(!empty($redirect_tids))
3606              {
3607                  $this->restore_threads($redirect_tids);
3608              }
3609              if(!empty($posts_to_restore))
3610              {
3611                  $db->update_query("posts", $update, "pid IN (".implode(',', $posts_to_restore).")");
3612              }
3613  
3614              $plugins->run_hooks("class_moderation_restore_threads", $tids);
3615  
3616              if(is_array($forum_counters))
3617              {
3618                  foreach($forum_counters as $fid => $counters)
3619                  {
3620                      // Update stats
3621                      $update_array = array(
3622                          "threads" => "+{$counters['num_threads']}",
3623                          "posts" => "+{$counters['num_posts']}",
3624                          "unapprovedposts" => "+{$counters['num_unapproved_posts']}",
3625                          "deletedposts" => "-{$counters['num_deleted_posts']}",
3626                          "deletedthreads" => "-{$counters['num_threads']}"
3627                      );
3628                      update_forum_counters($fid, $update_array);
3629                      update_forum_lastpost($fid);
3630                  }
3631              }
3632  
3633              if(!empty($user_counters))
3634              {
3635                  foreach($user_counters as $uid => $counters)
3636                  {
3637                      $update_array = array(
3638                          "postnum" => "+{$counters['num_posts']}",
3639                          "threadnum" => "+{$counters['num_threads']}",
3640                      );
3641                      update_user_counters($uid, $update_array);
3642                  }
3643              }
3644          }
3645          return true;
3646      }
3647  
3648      /**
3649       * Soft delete one or more threads
3650       *
3651       * @param array|int Thread ID(s)
3652       * @return boolean
3653       */
3654  	function soft_delete_threads($tids)
3655      {
3656          global $db, $cache, $plugins;
3657  
3658          if(!is_array($tids))
3659          {
3660              $tids = array($tids);
3661          }
3662  
3663          if(empty($tids))
3664          {
3665              return false;
3666          }
3667  
3668          // Make sure we only have valid values
3669          $tids = array_map('intval', $tids);
3670  
3671          $tid_list = implode(',', $tids);
3672          $tid_moved_list = "";
3673          $comma = "";
3674          foreach($tids as $tid)
3675          {
3676              $tid_moved_list .= "{$comma}'moved|{$tid}'";
3677              $comma = ",";
3678          }
3679  
3680          $forum_counters = $user_counters = $posts_to_delete = array();
3681  
3682          $tids_list = implode(",", $tids);
3683          $query = $db->simple_select("threads", "*", "tid IN ($tids_list)");
3684  
3685          while($thread = $db->fetch_array($query))
3686          {
3687              $forum = get_forum($thread['fid']);
3688  
3689              if($thread['visible'] == 1 || $thread['visible'] == 0)
3690              {
3691                  if(!isset($forum_counters[$forum['fid']]))
3692                  {
3693                      $forum_counters[$forum['fid']] = array(
3694                          'num_posts' => 0,
3695                          'num_threads' => 0,
3696                          'num_deleted_threads' => 0,
3697                          'num_deleted_posts' => 0,
3698                          'unapproved_threads' => 0,
3699                          'unapproved_posts' => 0
3700                      );
3701                  }
3702  
3703                  if(!isset($user_counters[$thread['uid']]))
3704                  {
3705                      $user_counters[$thread['uid']] = array(
3706                          'num_posts' => 0,
3707                          'num_threads' => 0
3708                      );
3709                  }
3710  
3711                  ++$forum_counters[$forum['fid']]['num_deleted_threads'];
3712                  $forum_counters[$forum['fid']]['num_deleted_posts'] += $thread['replies']+$thread['unapprovedposts']+1;
3713  
3714                  if($thread['visible'] == 1)
3715                  {
3716                      ++$forum_counters[$forum['fid']]['num_threads'];
3717                      $forum_counters[$forum['fid']]['num_posts'] += $thread['replies']+1; // Add implied invisible to count
3718                      $forum_counters[$forum['fid']]['unapproved_posts'] += $thread['unapprovedposts'];
3719                  }
3720                  else
3721                  {
3722                      ++$forum_counters[$forum['fid']]['unapproved_threads'];
3723                      $forum_counters[$forum['fid']]['unapproved_posts'] += $thread['replies']+$thread['deletedposts']+$thread['unapprovedposts']+1; // Add implied invisible to count
3724                      $forum_counters[$forum['fid']]['num_deleted_posts'] += $thread['deletedposts'];
3725                  }
3726  
3727                  // On unapproving thread update user post counts
3728                  if($thread['visible'] == 1 && $forum['usepostcounts'] != 0)
3729                  {
3730                      $query2 = $db->simple_select("posts", "COUNT(pid) AS posts, uid", "tid='{$thread['tid']}' AND (visible='1' OR pid='{$thread['firstpost']}') AND uid > 0 GROUP BY uid");
3731                      while($counter = $db->fetch_array($query2))
3732                      {
3733                          if(!isset($user_counters[$counter['uid']]['num_posts']))
3734                          {
3735                              $user_counters[$counter['uid']]['num_posts'] = 0;
3736                          }
3737                          $user_counters[$counter['uid']]['num_posts'] += $counter['posts'];
3738                      }
3739                  }
3740  
3741                  if($thread['visible'] == 1 && $forum['usethreadcounts'] != 0 && substr($thread['closed'], 0, 6) != 'moved|')
3742                  {
3743                      ++$user_counters[$thread['uid']]['num_threads'];
3744                  }
3745              }
3746              $posts_to_delete[] = $thread['firstpost'];
3747          }
3748  
3749          $update = array(
3750              "visible" => -1
3751          );
3752          $db->update_query("threads", $update, "tid IN ($tid_list)");
3753          // Soft delete redirects, too
3754          $redirect_tids = array();
3755          $query = $db->simple_select('threads', 'tid', "closed IN ({$tid_moved_list})");
3756  
3757          mark_reports($tids, "threads");
3758  
3759          while($redirect_tid = $db->fetch_field($query, 'tid'))
3760          {
3761              $redirect_tids[] = $redirect_tid;
3762          }
3763          if(!empty($redirect_tids))
3764          {
3765              $this->soft_delete_threads($redirect_tids);
3766          }
3767          if(!empty($posts_to_delete))
3768          {
3769              $db->update_query("posts", $update, "pid IN (".implode(',', $posts_to_delete).")");
3770          }
3771  
3772          $plugins->run_hooks("class_moderation_soft_delete_threads", $tids);
3773  
3774          if(is_array($forum_counters))
3775          {
3776              foreach($forum_counters as $fid => $counters)
3777              {
3778                  // Update stats
3779                  $update_array = array(
3780                      "threads" => "-{$counters['num_threads']}",
3781                      "unapprovedthreads" => "-{$counters['unapproved_threads']}",
3782                      "posts" => "-{$counters['num_posts']}",
3783                      "unapprovedposts" => "-{$counters['unapproved_posts']}",
3784                      "deletedposts" => "+{$counters['num_deleted_posts']}",
3785                      "deletedthreads" => "+{$counters['num_deleted_threads']}"
3786                  );
3787                  update_forum_counters($fid, $update_array);
3788                  update_forum_lastpost($fid);
3789              }
3790          }
3791  
3792          if(!empty($user_counters))
3793          {
3794              foreach($user_counters as $uid => $counters)
3795              {
3796                  $update_array = array(
3797                      "postnum" => "-{$counters['num_posts']}",
3798                      "threadnum" => "-{$counters['num_threads']}",
3799                  );
3800                  update_user_counters($uid, $update_array);
3801              }
3802          }
3803  
3804          return true;
3805      }
3806  }


2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup Cross-referenced by PHPXref