[ Index ]

PHP Cross Reference of MyBB 1.8.24

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


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