PageRenderTime 265ms CodeModel.GetById 81ms app.highlight 121ms RepoModel.GetById 39ms app.codeStats 2ms

/forum/includes/functions_admin.php

https://bitbucket.org/itoxable/chiron-gaming
PHP | 3345 lines | 2471 code | 537 blank | 337 comment | 409 complexity | 643f3af482fa6c2e417ba8ce46d7575a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3*
   4* @package acp
   5* @version $Id$
   6* @copyright (c) 2005 phpBB Group
   7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
   8*
   9*/
  10
  11/**
  12* @ignore
  13*/
  14if (!defined('IN_PHPBB'))
  15{
  16	exit;
  17}
  18
  19/**
  20* Recalculate Nested Sets
  21*
  22* @param int	$new_id	first left_id (should start with 1)
  23* @param string	$pkey	primary key-column (containing the id for the parent_id of the children)
  24* @param string	$table	constant or fullname of the table
  25* @param int	$parent_id parent_id of the current set (default = 0)
  26* @param array	$where	contains strings to compare closer on the where statement (additional)
  27*
  28* @author EXreaction
  29*/
  30function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
  31{
  32	global $db;
  33
  34	$sql = 'SELECT *
  35		FROM ' . $table . '
  36		WHERE parent_id = ' . (int) $parent_id .
  37		((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
  38		ORDER BY left_id ASC';
  39	$result = $db->sql_query($sql);
  40	while ($row = $db->sql_fetchrow($result))
  41	{
  42		// First we update the left_id for this module
  43		if ($row['left_id'] != $new_id)
  44		{
  45			$db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  46		}
  47		$new_id++;
  48
  49		// Then we go through any children and update their left/right id's
  50		recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
  51
  52		// Then we come back and update the right_id for this module
  53		if ($row['right_id'] != $new_id)
  54		{
  55			$db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  56		}
  57		$new_id++;
  58	}
  59	$db->sql_freeresult($result);
  60}
  61
  62/**
  63* Simple version of jumpbox, just lists authed forums
  64*/
  65function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false)
  66{
  67	global $db, $user, $auth;
  68
  69	// This query is identical to the jumpbox one
  70	$sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
  71		FROM ' . FORUMS_TABLE . '
  72		ORDER BY left_id ASC';
  73	$result = $db->sql_query($sql, 600);
  74
  75	$right = 0;
  76	$padding_store = array('0' => '');
  77	$padding = '';
  78	$forum_list = ($return_array) ? array() : '';
  79
  80	// Sometimes it could happen that forums will be displayed here not be displayed within the index page
  81	// This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
  82	// If this happens, the padding could be "broken"
  83
  84	while ($row = $db->sql_fetchrow($result))
  85	{
  86		if ($row['left_id'] < $right)
  87		{
  88			$padding .= '&nbsp; &nbsp;';
  89			$padding_store[$row['parent_id']] = $padding;
  90		}
  91		else if ($row['left_id'] > $right + 1)
  92		{
  93			$padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
  94		}
  95
  96		$right = $row['right_id'];
  97		$disabled = false;
  98
  99		if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
 100		{
 101			if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id'])))
 102			{
 103				$disabled = true;
 104			}
 105		}
 106		else if (!$ignore_acl)
 107		{
 108			continue;
 109		}
 110
 111		if (
 112			((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
 113			||
 114			// Non-postable forum with no subforums, don't display
 115			($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
 116			||
 117			($row['forum_type'] != FORUM_POST && $ignore_nonpost)
 118			)
 119		{
 120			$disabled = true;
 121		}
 122
 123		if ($return_array)
 124		{
 125			// Include some more information...
 126			$selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
 127			$forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
 128		}
 129		else
 130		{
 131			$selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
 132			$forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
 133		}
 134	}
 135	$db->sql_freeresult($result);
 136	unset($padding_store);
 137
 138	return $forum_list;
 139}
 140
 141/**
 142* Generate size select options
 143*/
 144function size_select_options($size_compare)
 145{
 146	global $user;
 147
 148	$size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
 149	$size_types = array('b', 'kb', 'mb');
 150
 151	$s_size_options = '';
 152
 153	for ($i = 0, $size = sizeof($size_types_text); $i < $size; $i++)
 154	{
 155		$selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
 156		$s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
 157	}
 158
 159	return $s_size_options;
 160}
 161
 162/**
 163* Generate list of groups (option fields without select)
 164*
 165* @param int $group_id The default group id to mark as selected
 166* @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
 167* @param int $manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only.
 168*
 169* @return string The list of options.
 170*/
 171function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
 172{
 173	global $db, $user, $config;
 174
 175	$exclude_sql = ($exclude_ids !== false && sizeof($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
 176	$sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
 177	$sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
 178
 179	$sql = 'SELECT group_id, group_name, group_type
 180		FROM ' . GROUPS_TABLE . "
 181		$exclude_sql
 182		$sql_and
 183		$sql_founder
 184		ORDER BY group_type DESC, group_name ASC";
 185	$result = $db->sql_query($sql);
 186
 187	$s_group_options = '';
 188	while ($row = $db->sql_fetchrow($result))
 189	{
 190		$selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
 191		$s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
 192	}
 193	$db->sql_freeresult($result);
 194
 195	return $s_group_options;
 196}
 197
 198/**
 199* Obtain authed forums list
 200*/
 201function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
 202{
 203	global $db, $auth;
 204	static $forum_rows;
 205
 206	if (!isset($forum_rows))
 207	{
 208		// This query is identical to the jumpbox one
 209		$expire_time = ($no_cache) ? 0 : 600;
 210
 211		$sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
 212			FROM ' . FORUMS_TABLE . '
 213			ORDER BY left_id ASC';
 214		$result = $db->sql_query($sql, $expire_time);
 215
 216		$forum_rows = array();
 217
 218		$right = $padding = 0;
 219		$padding_store = array('0' => 0);
 220
 221		while ($row = $db->sql_fetchrow($result))
 222		{
 223			if ($row['left_id'] < $right)
 224			{
 225				$padding++;
 226				$padding_store[$row['parent_id']] = $padding;
 227			}
 228			else if ($row['left_id'] > $right + 1)
 229			{
 230				// Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
 231				// @todo digging deep to find out "how" this can happen.
 232				$padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
 233			}
 234
 235			$right = $row['right_id'];
 236			$row['padding'] = $padding;
 237
 238			$forum_rows[] = $row;
 239		}
 240		$db->sql_freeresult($result);
 241		unset($padding_store);
 242	}
 243
 244	$rowset = array();
 245	foreach ($forum_rows as $row)
 246	{
 247		if ($postable_only && $row['forum_type'] != FORUM_POST)
 248		{
 249			continue;
 250		}
 251
 252		if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
 253		{
 254			$rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
 255		}
 256	}
 257
 258	return $rowset;
 259}
 260
 261/**
 262* Get forum branch
 263*/
 264function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
 265{
 266	global $db;
 267
 268	switch ($type)
 269	{
 270		case 'parents':
 271			$condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
 272		break;
 273
 274		case 'children':
 275			$condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
 276		break;
 277
 278		default:
 279			$condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
 280		break;
 281	}
 282
 283	$rows = array();
 284
 285	$sql = 'SELECT f2.*
 286		FROM ' . FORUMS_TABLE . ' f1
 287		LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
 288		WHERE f1.forum_id = $forum_id
 289		ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
 290	$result = $db->sql_query($sql);
 291
 292	while ($row = $db->sql_fetchrow($result))
 293	{
 294		if (!$include_forum && $row['forum_id'] == $forum_id)
 295		{
 296			continue;
 297		}
 298
 299		$rows[] = $row;
 300	}
 301	$db->sql_freeresult($result);
 302
 303	return $rows;
 304}
 305
 306/**
 307* Copies permissions from one forum to others
 308*
 309* @param int	$src_forum_id		The source forum we want to copy permissions from
 310* @param array	$dest_forum_ids		The destination forum(s) we want to copy to
 311* @param bool	$clear_dest_perms	True if destination permissions should be deleted
 312* @param bool	$add_log			True if log entry should be added
 313*
 314* @return bool						False on error
 315*
 316* @author bantu
 317*/
 318function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
 319{
 320	global $db;
 321
 322	// Only one forum id specified
 323	if (!is_array($dest_forum_ids))
 324	{
 325		$dest_forum_ids = array($dest_forum_ids);
 326	}
 327
 328	// Make sure forum ids are integers
 329	$src_forum_id = (int) $src_forum_id;
 330	$dest_forum_ids = array_map('intval', $dest_forum_ids);
 331
 332	// No source forum or no destination forums specified
 333	if (empty($src_forum_id) || empty($dest_forum_ids))
 334	{
 335		return false;
 336	}
 337
 338	// Check if source forum exists
 339	$sql = 'SELECT forum_name
 340		FROM ' . FORUMS_TABLE . '
 341		WHERE forum_id = ' . $src_forum_id;
 342	$result = $db->sql_query($sql);
 343	$src_forum_name = $db->sql_fetchfield('forum_name');
 344	$db->sql_freeresult($result);
 345
 346	// Source forum doesn't exist
 347	if (empty($src_forum_name))
 348	{
 349		return false;
 350	}
 351
 352	// Check if destination forums exists
 353	$sql = 'SELECT forum_id, forum_name
 354		FROM ' . FORUMS_TABLE . '
 355		WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 356	$result = $db->sql_query($sql);
 357
 358	$dest_forum_ids = $dest_forum_names = array();
 359	while ($row = $db->sql_fetchrow($result))
 360	{
 361		$dest_forum_ids[]	= (int) $row['forum_id'];
 362		$dest_forum_names[]	= $row['forum_name'];
 363	}
 364	$db->sql_freeresult($result);
 365
 366	// No destination forum exists
 367	if (empty($dest_forum_ids))
 368	{
 369		return false;
 370	}
 371
 372	// From the mysql documentation:
 373	// Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
 374	// in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
 375	// Due to this we stay on the safe side if we do the insertion "the manual way"
 376
 377	// Rowsets we're going to insert
 378	$users_sql_ary = $groups_sql_ary = array();
 379
 380	// Query acl users table for source forum data
 381	$sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
 382		FROM ' . ACL_USERS_TABLE . '
 383		WHERE forum_id = ' . $src_forum_id;
 384	$result = $db->sql_query($sql);
 385
 386	while ($row = $db->sql_fetchrow($result))
 387	{
 388		$row = array(
 389			'user_id'			=> (int) $row['user_id'],
 390			'auth_option_id'	=> (int) $row['auth_option_id'],
 391			'auth_role_id'		=> (int) $row['auth_role_id'],
 392			'auth_setting'		=> (int) $row['auth_setting'],
 393		);
 394
 395		foreach ($dest_forum_ids as $dest_forum_id)
 396		{
 397			$users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 398		}
 399	}
 400	$db->sql_freeresult($result);
 401
 402	// Query acl groups table for source forum data
 403	$sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
 404		FROM ' . ACL_GROUPS_TABLE . '
 405		WHERE forum_id = ' . $src_forum_id;
 406	$result = $db->sql_query($sql);
 407
 408	while ($row = $db->sql_fetchrow($result))
 409	{
 410		$row = array(
 411			'group_id'			=> (int) $row['group_id'],
 412			'auth_option_id'	=> (int) $row['auth_option_id'],
 413			'auth_role_id'		=> (int) $row['auth_role_id'],
 414			'auth_setting'		=> (int) $row['auth_setting'],
 415		);
 416
 417		foreach ($dest_forum_ids as $dest_forum_id)
 418		{
 419			$groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 420		}
 421	}
 422	$db->sql_freeresult($result);
 423
 424	$db->sql_transaction('begin');
 425
 426	// Clear current permissions of destination forums
 427	if ($clear_dest_perms)
 428	{
 429		$sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
 430			WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 431		$db->sql_query($sql);
 432
 433		$sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
 434			WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 435		$db->sql_query($sql);
 436	}
 437
 438	$db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
 439	$db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
 440
 441	if ($add_log)
 442	{
 443		add_log('admin', 'LOG_FORUM_COPIED_PERMISSIONS', $src_forum_name, implode(', ', $dest_forum_names));
 444	}
 445
 446	$db->sql_transaction('commit');
 447
 448	return true;
 449}
 450
 451/**
 452* Get physical file listing
 453*/
 454function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png')
 455{
 456	$matches = array($dir => array());
 457
 458	// Remove initial / if present
 459	$rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
 460	// Add closing / if not present
 461	$rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
 462
 463	// Remove initial / if present
 464	$dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
 465	// Add closing / if not present
 466	$dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
 467
 468	if (!is_dir($rootdir . $dir))
 469	{
 470		return $matches;
 471	}
 472
 473	$dh = @opendir($rootdir . $dir);
 474
 475	if (!$dh)
 476	{
 477		return $matches;
 478	}
 479
 480	while (($fname = readdir($dh)) !== false)
 481	{
 482		if (is_file("$rootdir$dir$fname"))
 483		{
 484			if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
 485			{
 486				$matches[$dir][] = $fname;
 487			}
 488		}
 489		else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
 490		{
 491			$matches += filelist($rootdir, "$dir$fname", $type);
 492		}
 493	}
 494	closedir($dh);
 495
 496	return $matches;
 497}
 498
 499/**
 500* Move topic(s)
 501*/
 502function move_topics($topic_ids, $forum_id, $auto_sync = true)
 503{
 504	global $db;
 505
 506	if (empty($topic_ids))
 507	{
 508		return;
 509	}
 510
 511	$forum_ids = array($forum_id);
 512
 513	if (!is_array($topic_ids))
 514	{
 515		$topic_ids = array($topic_ids);
 516	}
 517
 518	$sql = 'DELETE FROM ' . TOPICS_TABLE . '
 519		WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
 520			AND forum_id = ' . $forum_id;
 521	$db->sql_query($sql);
 522
 523	if ($auto_sync)
 524	{
 525		$sql = 'SELECT DISTINCT forum_id
 526			FROM ' . TOPICS_TABLE . '
 527			WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 528		$result = $db->sql_query($sql);
 529
 530		while ($row = $db->sql_fetchrow($result))
 531		{
 532			$forum_ids[] = $row['forum_id'];
 533		}
 534		$db->sql_freeresult($result);
 535	}
 536
 537	$table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
 538	foreach ($table_ary as $table)
 539	{
 540		$sql = "UPDATE $table
 541			SET forum_id = $forum_id
 542			WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 543		$db->sql_query($sql);
 544	}
 545	unset($table_ary);
 546
 547	if ($auto_sync)
 548	{
 549		sync('forum', 'forum_id', $forum_ids, true, true);
 550		unset($forum_ids);
 551	}
 552}
 553
 554/**
 555* Move post(s)
 556*/
 557function move_posts($post_ids, $topic_id, $auto_sync = true)
 558{
 559	global $db;
 560
 561	if (!is_array($post_ids))
 562	{
 563		$post_ids = array($post_ids);
 564	}
 565
 566	$forum_ids = array();
 567	$topic_ids = array($topic_id);
 568
 569	$sql = 'SELECT DISTINCT topic_id, forum_id
 570		FROM ' . POSTS_TABLE . '
 571		WHERE ' . $db->sql_in_set('post_id', $post_ids);
 572	$result = $db->sql_query($sql);
 573
 574	while ($row = $db->sql_fetchrow($result))
 575	{
 576		$forum_ids[] = (int) $row['forum_id'];
 577		$topic_ids[] = (int) $row['topic_id'];
 578	}
 579	$db->sql_freeresult($result);
 580
 581	$sql = 'SELECT forum_id
 582		FROM ' . TOPICS_TABLE . '
 583		WHERE topic_id = ' . $topic_id;
 584	$result = $db->sql_query($sql);
 585	$forum_row = $db->sql_fetchrow($result);
 586	$db->sql_freeresult($result);
 587
 588	if (!$forum_row)
 589	{
 590		trigger_error('NO_TOPIC');
 591	}
 592
 593	$sql = 'UPDATE ' . POSTS_TABLE . '
 594		SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
 595		WHERE " . $db->sql_in_set('post_id', $post_ids);
 596	$db->sql_query($sql);
 597
 598	$sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
 599		SET topic_id = $topic_id, in_message = 0
 600		WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
 601	$db->sql_query($sql);
 602
 603	if ($auto_sync)
 604	{
 605		$forum_ids[] = (int) $forum_row['forum_id'];
 606
 607		sync('topic_reported', 'topic_id', $topic_ids);
 608		sync('topic_attachment', 'topic_id', $topic_ids);
 609		sync('topic', 'topic_id', $topic_ids, true);
 610		sync('forum', 'forum_id', $forum_ids, true, true);
 611	}
 612
 613	// Update posted information
 614	update_posted_info($topic_ids);
 615}
 616
 617/**
 618* Remove topic(s)
 619*/
 620function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
 621{
 622	global $db, $config;
 623
 624	$approved_topics = 0;
 625	$forum_ids = $topic_ids = array();
 626
 627	if ($where_type === 'range')
 628	{
 629		$where_clause = $where_ids;
 630	}
 631	else
 632	{
 633		$where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
 634
 635		if (!sizeof($where_ids))
 636		{
 637			return array('topics' => 0, 'posts' => 0);
 638		}
 639
 640		$where_clause = $db->sql_in_set($where_type, $where_ids);
 641	}
 642
 643	// Making sure that delete_posts does not call delete_topics again...
 644	$return = array(
 645		'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
 646	);
 647
 648	$sql = 'SELECT topic_id, forum_id, topic_approved, topic_moved_id
 649		FROM ' . TOPICS_TABLE . '
 650		WHERE ' . $where_clause;
 651	$result = $db->sql_query($sql);
 652
 653	while ($row = $db->sql_fetchrow($result))
 654	{
 655		$forum_ids[] = $row['forum_id'];
 656		$topic_ids[] = $row['topic_id'];
 657
 658		if ($row['topic_approved'] && !$row['topic_moved_id'])
 659		{
 660			$approved_topics++;
 661		}
 662	}
 663	$db->sql_freeresult($result);
 664
 665	$return['topics'] = sizeof($topic_ids);
 666
 667	if (!sizeof($topic_ids))
 668	{
 669		return $return;
 670	}
 671
 672	$db->sql_transaction('begin');
 673
 674	$table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
 675
 676	foreach ($table_ary as $table)
 677	{
 678		$sql = "DELETE FROM $table
 679			WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 680		$db->sql_query($sql);
 681	}
 682	unset($table_ary);
 683
 684	$moved_topic_ids = array();
 685
 686	// update the other forums
 687	$sql = 'SELECT topic_id, forum_id
 688		FROM ' . TOPICS_TABLE . '
 689		WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
 690	$result = $db->sql_query($sql);
 691
 692	while ($row = $db->sql_fetchrow($result))
 693	{
 694		$forum_ids[] = $row['forum_id'];
 695		$moved_topic_ids[] = $row['topic_id'];
 696	}
 697	$db->sql_freeresult($result);
 698
 699	if (sizeof($moved_topic_ids))
 700	{
 701		$sql = 'DELETE FROM ' . TOPICS_TABLE . '
 702			WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
 703		$db->sql_query($sql);
 704	}
 705
 706	$db->sql_transaction('commit');
 707
 708	if ($auto_sync)
 709	{
 710		sync('forum', 'forum_id', array_unique($forum_ids), true, true);
 711		sync('topic_reported', $where_type, $where_ids);
 712	}
 713
 714	if ($approved_topics)
 715	{
 716		set_config_count('num_topics', $approved_topics * (-1), true);
 717	}
 718
 719	return $return;
 720}
 721
 722/**
 723* Remove post(s)
 724*/
 725function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
 726{
 727	global $db, $config, $phpbb_root_path, $phpEx;
 728
 729	if ($where_type === 'range')
 730	{
 731		$where_clause = $where_ids;
 732	}
 733	else
 734	{
 735		if (is_array($where_ids))
 736		{
 737			$where_ids = array_unique($where_ids);
 738		}
 739		else
 740		{
 741			$where_ids = array($where_ids);
 742		}
 743
 744		if (!sizeof($where_ids))
 745		{
 746			return false;
 747		}
 748
 749		$where_ids = array_map('intval', $where_ids);
 750
 751/*		Possible code for splitting post deletion
 752		if (sizeof($where_ids) >= 1001)
 753		{
 754			// Split into chunks of 1000
 755			$chunks = array_chunk($where_ids, 1000);
 756
 757			foreach ($chunks as $_where_ids)
 758			{
 759				delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
 760			}
 761
 762			return;
 763		}*/
 764
 765		$where_clause = $db->sql_in_set($where_type, $where_ids);
 766	}
 767
 768	$approved_posts = 0;
 769	$post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
 770
 771	$sql = 'SELECT post_id, poster_id, post_approved, post_postcount, topic_id, forum_id
 772		FROM ' . POSTS_TABLE . '
 773		WHERE ' . $where_clause;
 774	$result = $db->sql_query($sql);
 775
 776	while ($row = $db->sql_fetchrow($result))
 777	{
 778		$post_ids[] = (int) $row['post_id'];
 779		$poster_ids[] = (int) $row['poster_id'];
 780		$topic_ids[] = (int) $row['topic_id'];
 781		$forum_ids[] = (int) $row['forum_id'];
 782
 783		if ($row['post_postcount'] && $post_count_sync && $row['post_approved'])
 784		{
 785			$post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
 786		}
 787
 788		if ($row['post_approved'])
 789		{
 790			$approved_posts++;
 791		}
 792	}
 793	$db->sql_freeresult($result);
 794
 795	if (!sizeof($post_ids))
 796	{
 797		return false;
 798	}
 799
 800	$db->sql_transaction('begin');
 801
 802	$table_ary = array(POSTS_TABLE, REPORTS_TABLE);
 803
 804	foreach ($table_ary as $table)
 805	{
 806		$sql = "DELETE FROM $table
 807			WHERE " . $db->sql_in_set('post_id', $post_ids);
 808		$db->sql_query($sql);
 809	}
 810	unset($table_ary);
 811
 812	// Adjust users post counts
 813	if (sizeof($post_counts) && $post_count_sync)
 814	{
 815		foreach ($post_counts as $poster_id => $substract)
 816		{
 817			$sql = 'UPDATE ' . USERS_TABLE . '
 818				SET user_posts = 0
 819				WHERE user_id = ' . $poster_id . '
 820				AND user_posts < ' . $substract;
 821			$db->sql_query($sql);
 822
 823			$sql = 'UPDATE ' . USERS_TABLE . '
 824				SET user_posts = user_posts - ' . $substract . '
 825				WHERE user_id = ' . $poster_id . '
 826				AND user_posts >= ' . $substract;
 827			$db->sql_query($sql);
 828		}
 829	}
 830
 831	// Remove topics now having no posts?
 832	if (sizeof($topic_ids))
 833	{
 834		$sql = 'SELECT topic_id
 835			FROM ' . POSTS_TABLE . '
 836			WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
 837			GROUP BY topic_id';
 838		$result = $db->sql_query($sql);
 839
 840		while ($row = $db->sql_fetchrow($result))
 841		{
 842			$remove_topics[] = $row['topic_id'];
 843		}
 844		$db->sql_freeresult($result);
 845
 846		// Actually, those not within remove_topics should be removed. ;)
 847		$remove_topics = array_diff($topic_ids, $remove_topics);
 848	}
 849
 850	// Remove the message from the search index
 851	$search_type = basename($config['search_type']);
 852
 853	if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx))
 854	{
 855		trigger_error('NO_SUCH_SEARCH_MODULE');
 856	}
 857
 858	include_once("{$phpbb_root_path}includes/search/$search_type.$phpEx");
 859
 860	$error = false;
 861	$search = new $search_type($error);
 862
 863	if ($error)
 864	{
 865		trigger_error($error);
 866	}
 867
 868	$search->index_remove($post_ids, $poster_ids, $forum_ids);
 869
 870	delete_attachments('post', $post_ids, false);
 871
 872	$db->sql_transaction('commit');
 873
 874	// Resync topics_posted table
 875	if ($posted_sync)
 876	{
 877		update_posted_info($topic_ids);
 878	}
 879
 880	if ($auto_sync)
 881	{
 882		sync('topic_reported', 'topic_id', $topic_ids);
 883		sync('topic', 'topic_id', $topic_ids, true);
 884		sync('forum', 'forum_id', $forum_ids, true, true);
 885	}
 886
 887	if ($approved_posts)
 888	{
 889		set_config_count('num_posts', $approved_posts * (-1), true);
 890	}
 891
 892	// We actually remove topics now to not be inconsistent (the delete_topics function calls this function too)
 893	if (sizeof($remove_topics) && $call_delete_topics)
 894	{
 895		delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
 896	}
 897
 898	return sizeof($post_ids);
 899}
 900
 901/**
 902* Delete Attachments
 903*
 904* @param string $mode can be: post|message|topic|attach|user
 905* @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
 906* @param bool $resync set this to false if you are deleting posts or topics
 907*/
 908function delete_attachments($mode, $ids, $resync = true)
 909{
 910	global $db, $config;
 911
 912	// 0 is as bad as an empty array
 913	if (empty($ids))
 914	{
 915		return false;
 916	}
 917
 918	if (is_array($ids))
 919	{
 920		$ids = array_unique($ids);
 921		$ids = array_map('intval', $ids);
 922	}
 923	else
 924	{
 925		$ids = array((int) $ids);
 926	}
 927
 928	$sql_where = '';
 929
 930	switch ($mode)
 931	{
 932		case 'post':
 933		case 'message':
 934			$sql_id = 'post_msg_id';
 935			$sql_where = ' AND in_message = ' . ($mode == 'message' ? 1 : 0);
 936		break;
 937
 938		case 'topic':
 939			$sql_id = 'topic_id';
 940		break;
 941
 942		case 'user':
 943			$sql_id = 'poster_id';
 944		break;
 945
 946		case 'attach':
 947		default:
 948			$sql_id = 'attach_id';
 949			$mode = 'attach';
 950		break;
 951	}
 952
 953	$post_ids = $message_ids = $topic_ids = $physical = array();
 954
 955	// Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
 956	$sql = 'SELECT post_msg_id, topic_id, in_message, physical_filename, thumbnail, filesize, is_orphan
 957			FROM ' . ATTACHMENTS_TABLE . '
 958			WHERE ' . $db->sql_in_set($sql_id, $ids);
 959
 960	$sql .= $sql_where;
 961
 962	$result = $db->sql_query($sql);
 963
 964	while ($row = $db->sql_fetchrow($result))
 965	{
 966		// We only need to store post/message/topic ids if resync is enabled and the file is not orphaned
 967		if ($resync && !$row['is_orphan'])
 968		{
 969			if (!$row['in_message'])
 970			{
 971				$post_ids[] = $row['post_msg_id'];
 972				$topic_ids[] = $row['topic_id'];
 973			}
 974			else
 975			{
 976				$message_ids[] = $row['post_msg_id'];
 977			}
 978		}
 979
 980		$physical[] = array('filename' => $row['physical_filename'], 'thumbnail' => $row['thumbnail'], 'filesize' => $row['filesize'], 'is_orphan' => $row['is_orphan']);
 981	}
 982	$db->sql_freeresult($result);
 983
 984	// Delete attachments
 985	$sql = 'DELETE FROM ' . ATTACHMENTS_TABLE . '
 986		WHERE ' . $db->sql_in_set($sql_id, $ids);
 987
 988	$sql .= $sql_where;
 989
 990	$db->sql_query($sql);
 991	$num_deleted = $db->sql_affectedrows();
 992
 993	if (!$num_deleted)
 994	{
 995		return 0;
 996	}
 997
 998	// Delete attachments from filesystem
 999	$space_removed = $files_removed = 0;
1000	foreach ($physical as $file_ary)
1001	{
1002		if (phpbb_unlink($file_ary['filename'], 'file', true) && !$file_ary['is_orphan'])
1003		{
1004			// Only non-orphaned files count to the file size
1005			$space_removed += $file_ary['filesize'];
1006			$files_removed++;
1007		}
1008
1009		if ($file_ary['thumbnail'])
1010		{
1011			phpbb_unlink($file_ary['filename'], 'thumbnail', true);
1012		}
1013	}
1014
1015	if ($space_removed || $files_removed)
1016	{
1017		set_config_count('upload_dir_size', $space_removed * (-1), true);
1018		set_config_count('num_files', $files_removed * (-1), true);
1019	}
1020
1021	// If we do not resync, we do not need to adjust any message, post, topic or user entries
1022	if (!$resync)
1023	{
1024		return $num_deleted;
1025	}
1026
1027	// No more use for the original ids
1028	unset($ids);
1029
1030	// Now, we need to resync posts, messages, topics. We go through every one of them
1031	$post_ids = array_unique($post_ids);
1032	$message_ids = array_unique($message_ids);
1033	$topic_ids = array_unique($topic_ids);
1034
1035	// Update post indicators for posts now no longer having attachments
1036	if (sizeof($post_ids))
1037	{
1038		// Just check which posts are still having an assigned attachment not orphaned by querying the attachments table
1039		$sql = 'SELECT post_msg_id
1040			FROM ' . ATTACHMENTS_TABLE . '
1041			WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
1042				AND in_message = 0
1043				AND is_orphan = 0';
1044		$result = $db->sql_query($sql);
1045
1046		$remaining_ids = array();
1047		while ($row = $db->sql_fetchrow($result))
1048		{
1049			$remaining_ids[] = $row['post_msg_id'];
1050		}
1051		$db->sql_freeresult($result);
1052
1053		// Now only unset those ids remaining
1054		$post_ids = array_diff($post_ids, $remaining_ids);
1055
1056		if (sizeof($post_ids))
1057		{
1058			$sql = 'UPDATE ' . POSTS_TABLE . '
1059				SET post_attachment = 0
1060				WHERE ' . $db->sql_in_set('post_id', $post_ids);
1061			$db->sql_query($sql);
1062		}
1063	}
1064
1065	// Update message table if messages are affected
1066	if (sizeof($message_ids))
1067	{
1068		// Just check which messages are still having an assigned attachment not orphaned by querying the attachments table
1069		$sql = 'SELECT post_msg_id
1070			FROM ' . ATTACHMENTS_TABLE . '
1071			WHERE ' . $db->sql_in_set('post_msg_id', $message_ids) . '
1072				AND in_message = 1
1073				AND is_orphan = 0';
1074		$result = $db->sql_query($sql);
1075
1076		$remaining_ids = array();
1077		while ($row = $db->sql_fetchrow($result))
1078		{
1079			$remaining_ids[] = $row['post_msg_id'];
1080		}
1081		$db->sql_freeresult($result);
1082
1083		// Now only unset those ids remaining
1084		$message_ids = array_diff($message_ids, $remaining_ids);
1085
1086		if (sizeof($message_ids))
1087		{
1088			$sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1089				SET message_attachment = 0
1090				WHERE ' . $db->sql_in_set('msg_id', $message_ids);
1091			$db->sql_query($sql);
1092		}
1093	}
1094
1095	// Now update the topics. This is a bit trickier, because there could be posts still having attachments within the topic
1096	if (sizeof($topic_ids))
1097	{
1098		// Just check which topics are still having an assigned attachment not orphaned by querying the attachments table (much less entries expected)
1099		$sql = 'SELECT topic_id
1100			FROM ' . ATTACHMENTS_TABLE . '
1101			WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1102				AND is_orphan = 0';
1103		$result = $db->sql_query($sql);
1104
1105		$remaining_ids = array();
1106		while ($row = $db->sql_fetchrow($result))
1107		{
1108			$remaining_ids[] = $row['topic_id'];
1109		}
1110		$db->sql_freeresult($result);
1111
1112		// Now only unset those ids remaining
1113		$topic_ids = array_diff($topic_ids, $remaining_ids);
1114
1115		if (sizeof($topic_ids))
1116		{
1117			$sql = 'UPDATE ' . TOPICS_TABLE . '
1118				SET topic_attachment = 0
1119				WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1120			$db->sql_query($sql);
1121		}
1122	}
1123
1124	return $num_deleted;
1125}
1126
1127/**
1128* Deletes shadow topics pointing to a specified forum.
1129*
1130* @param int		$forum_id		The forum id
1131* @param string		$sql_more		Additional WHERE statement, e.g. t.topic_time < (time() - 1234)
1132* @param bool		$auto_sync		Will call sync() if this is true
1133*
1134* @return array		Array with affected forums
1135*
1136* @author bantu
1137*/
1138function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true)
1139{
1140	global $db;
1141
1142	if (!$forum_id)
1143	{
1144		// Nothing to do.
1145		return;
1146	}
1147
1148	// Set of affected forums we have to resync
1149	$sync_forum_ids = array();
1150
1151	// Amount of topics we select and delete at once.
1152	$batch_size = 500;
1153
1154	do
1155	{
1156		$sql = 'SELECT t2.forum_id, t2.topic_id
1157			FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t
1158			WHERE t2.topic_moved_id = t.topic_id
1159				AND t.forum_id = ' . (int) $forum_id . '
1160				' . (($sql_more) ? 'AND ' . $sql_more : '');
1161		$result = $db->sql_query_limit($sql, $batch_size);
1162
1163		$topic_ids = array();
1164		while ($row = $db->sql_fetchrow($result))
1165		{
1166			$topic_ids[] = (int) $row['topic_id'];
1167
1168			$sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
1169		}
1170		$db->sql_freeresult($result);
1171
1172		if (!empty($topic_ids))
1173		{
1174			$sql = 'DELETE FROM ' . TOPICS_TABLE . '
1175				WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1176			$db->sql_query($sql);
1177		}
1178	}
1179	while (sizeof($topic_ids) == $batch_size);
1180
1181	if ($auto_sync)
1182	{
1183		sync('forum', 'forum_id', $sync_forum_ids, true, true);
1184	}
1185
1186	return $sync_forum_ids;
1187}
1188
1189/**
1190* Update/Sync posted information for topics
1191*/
1192function update_posted_info(&$topic_ids)
1193{
1194	global $db, $config;
1195
1196	if (empty($topic_ids) || !$config['load_db_track'])
1197	{
1198		return;
1199	}
1200
1201	// First of all, let us remove any posted information for these topics
1202	$sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
1203		WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1204	$db->sql_query($sql);
1205
1206	// Now, let us collect the user/topic combos for rebuilding the information
1207	$sql = 'SELECT poster_id, topic_id
1208		FROM ' . POSTS_TABLE . '
1209		WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1210			AND poster_id <> ' . ANONYMOUS . '
1211		GROUP BY poster_id, topic_id';
1212	$result = $db->sql_query($sql);
1213
1214	$posted = array();
1215	while ($row = $db->sql_fetchrow($result))
1216	{
1217		// Add as key to make them unique (grouping by) and circumvent empty keys on array_unique
1218		$posted[$row['poster_id']][] = $row['topic_id'];
1219	}
1220	$db->sql_freeresult($result);
1221
1222	// Now add the information...
1223	$sql_ary = array();
1224	foreach ($posted as $user_id => $topic_row)
1225	{
1226		foreach ($topic_row as $topic_id)
1227		{
1228			$sql_ary[] = array(
1229				'user_id'		=> (int) $user_id,
1230				'topic_id'		=> (int) $topic_id,
1231				'topic_posted'	=> 1,
1232			);
1233		}
1234	}
1235	unset($posted);
1236
1237	$db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary);
1238}
1239
1240/**
1241* Delete attached file
1242*/
1243function phpbb_unlink($filename, $mode = 'file', $entry_removed = false)
1244{
1245	global $db, $phpbb_root_path, $config;
1246
1247	// Because of copying topics or modifications a physical filename could be assigned more than once. If so, do not remove the file itself.
1248	$sql = 'SELECT COUNT(attach_id) AS num_entries
1249		FROM ' . ATTACHMENTS_TABLE . "
1250		WHERE physical_filename = '" . $db->sql_escape(utf8_basename($filename)) . "'";
1251	$result = $db->sql_query($sql);
1252	$num_entries = (int) $db->sql_fetchfield('num_entries');
1253	$db->sql_freeresult($result);
1254
1255	// Do not remove file if at least one additional entry with the same name exist.
1256	if (($entry_removed && $num_entries > 0) || (!$entry_removed && $num_entries > 1))
1257	{
1258		return false;
1259	}
1260
1261	$filename = ($mode == 'thumbnail') ? 'thumb_' . utf8_basename($filename) : utf8_basename($filename);
1262	return @unlink($phpbb_root_path . $config['upload_path'] . '/' . $filename);
1263}
1264
1265/**
1266* All-encompasing sync function
1267*
1268* Exaples:
1269* <code>
1270* sync('topic', 'topic_id', 123);			// resync topic #123
1271* sync('topic', 'forum_id', array(2, 3));	// resync topics from forum #2 and #3
1272* sync('topic');							// resync all topics
1273* sync('topic', 'range', 'topic_id BETWEEN 1 AND 60');	// resync a range of topics/forums (only available for 'topic' and 'forum' modes)
1274* </code>
1275*
1276* Modes:
1277* - forum				Resync complete forum
1278* - topic				Resync topics
1279* - topic_moved			Removes topic shadows that would be in the same forum as the topic they link to
1280* - topic_approved		Resyncs the topic_approved flag according to the status of the first post
1281* - post_reported		Resyncs the post_reported flag, relying on actual reports
1282* - topic_reported		Resyncs the topic_reported flag, relying on post_reported flags
1283* - post_attachement	Same as post_reported, but with attachment flags
1284* - topic_attachement	Same as topic_reported, but with attachment flags
1285*/
1286function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false)
1287{
1288	global $db;
1289
1290	if (is_array($where_ids))
1291	{
1292		$where_ids = array_unique($where_ids);
1293		$where_ids = array_map('intval', $where_ids);
1294	}
1295	else if ($where_type != 'range')
1296	{
1297		$where_ids = ($where_ids) ? array((int) $where_ids) : array();
1298	}
1299
1300	if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_approved' || $mode == 'topic_reported' || $mode == 'post_reported')
1301	{
1302		if (!$where_type)
1303		{
1304			$where_sql = '';
1305			$where_sql_and = 'WHERE';
1306		}
1307		else if ($where_type == 'range')
1308		{
1309			// Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60'
1310			$where_sql = 'WHERE (' . $mode[0] . ".$where_ids)";
1311			$where_sql_and = $where_sql . "\n\tAND";
1312		}
1313		else
1314		{
1315			// Do not sync the "global forum"
1316			$where_ids = array_diff($where_ids, array(0));
1317
1318			if (!sizeof($where_ids))
1319			{
1320				// Empty array with IDs. This means that we don't have any work to do. Just return.
1321				return;
1322			}
1323
1324			// Limit the topics/forums we are syncing, use specific topic/forum IDs.
1325			// $where_type contains the field for the where clause (forum_id, topic_id)
1326			$where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1327			$where_sql_and = $where_sql . "\n\tAND";
1328		}
1329	}
1330	else
1331	{
1332		if (!sizeof($where_ids))
1333		{
1334			return;
1335		}
1336
1337		// $where_type contains the field for the where clause (forum_id, topic_id)
1338		$where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1339		$where_sql_and = $where_sql . "\n\tAND";
1340	}
1341
1342	switch ($mode)
1343	{
1344		case 'topic_moved':
1345			$db->sql_transaction('begin');
1346			switch ($db->sql_layer)
1347			{
1348				case 'mysql4':
1349				case 'mysqli':
1350					$sql = 'DELETE FROM ' . TOPICS_TABLE . '
1351						USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1352						WHERE t1.topic_moved_id = t2.topic_id
1353							AND t1.forum_id = t2.forum_id";
1354					$db->sql_query($sql);
1355				break;
1356
1357				default:
1358					$sql = 'SELECT t1.topic_id
1359						FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1360						WHERE t1.topic_moved_id = t2.topic_id
1361							AND t1.forum_id = t2.forum_id";
1362					$result = $db->sql_query($sql);
1363
1364					$topic_id_ary = array();
1365					while ($row = $db->sql_fetchrow($result))
1366					{
1367						$topic_id_ary[] = $row['topic_id'];
1368					}
1369					$db->sql_freeresult($result);
1370
1371					if (!sizeof($topic_id_ary))
1372					{
1373						return;
1374					}
1375
1376					$sql = 'DELETE FROM ' . TOPICS_TABLE . '
1377						WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary);
1378					$db->sql_query($sql);
1379
1380				break;
1381			}
1382
1383			$db->sql_transaction('commit');
1384			break;
1385
1386		case 'topic_approved':
1387
1388			$db->sql_transaction('begin');
1389			switch ($db->sql_layer)
1390			{
1391				case 'mysql4':
1392				case 'mysqli':
1393					$sql = 'UPDATE ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1394						SET t.topic_approved = p.post_approved
1395						$where_sql_and t.topic_first_post_id = p.post_id";
1396					$db->sql_query($sql);
1397				break;
1398
1399				default:
1400					$sql = 'SELECT t.topic_id, p.post_approved
1401						FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1402						$where_sql_and p.post_id = t.topic_first_post_id
1403							AND p.post_approved <> t.topic_approved";
1404					$result = $db->sql_query($sql);
1405
1406					$topic_ids = array();
1407					while ($row = $db->sql_fetchrow($result))
1408					{
1409						$topic_ids[] = $row['topic_id'];
1410					}
1411					$db->sql_freeresult($result);
1412
1413					if (!sizeof($topic_ids))
1414					{
1415						return;
1416					}
1417
1418					$sql = 'UPDATE ' . TOPICS_TABLE . '
1419						SET topic_approved = 1 - topic_approved
1420						WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1421					$db->sql_query($sql);
1422				break;
1423			}
1424
1425			$db->sql_transaction('commit');
1426			break;
1427
1428		case 'post_reported':
1429			$post_ids = $post_reported = array();
1430
1431			$db->sql_transaction('begin');
1432
1433			$sql = 'SELECT p.post_id, p.post_reported
1434				FROM ' . POSTS_TABLE . " p
1435				$where_sql
1436				GROUP BY p.post_id, p.post_reported";
1437			$result = $db->sql_query($sql);
1438
1439			while ($row = $db->sql_fetchrow($result))
1440			{
1441				$post_ids[$row['post_id']] = $row['post_id'];
1442				if ($row['post_reported'])
1443				{
1444					$post_reported[$row['post_id']] = 1;
1445				}
1446			}
1447			$db->sql_freeresult($result);
1448
1449			$sql = 'SELECT DISTINCT(post_id)
1450				FROM ' . REPORTS_TABLE . '
1451				WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
1452					AND report_closed = 0';
1453			$result = $db->sql_query($sql);
1454
1455			$post_ids = array();
1456			while ($row = $db->sql_fetchrow($result))
1457			{
1458				if (!isset($post_reported[$row['post_id']]))
1459				{
1460					$post_ids[] = $row['post_id'];
1461				}
1462				else
1463				{
1464					unset($post_reported[$row['post_id']]);
1465				}
1466			}
1467			$db->sql_freeresult($result);
1468
1469			// $post_reported should be empty by now, if it's not it contains
1470			// posts that are falsely flagged as reported
1471			foreach ($post_reported as $post_id => $void)
1472			{
1473				$post_ids[] = $post_id;
1474			}
1475
1476			if (sizeof($post_ids))
1477			{
1478				$sql = 'UPDATE ' . POSTS_TABLE . '
1479					SET post_reported = 1 - post_reported
1480					WHERE ' . $db->sql_in_set('post_id', $post_ids);
1481				$db->sql_query($sql);
1482			}
1483
1484			$db->sql_transaction('commit');
1485			break;
1486
1487		case 'topic_reported':
1488			if ($sync_extra)
1489			{
1490				sync('post_reported', $where_type, $where_ids);
1491			}
1492
1493			$topic_ids = $topic_reported = array();
1494
1495			$db->sql_transaction('begin');
1496
1497			$sql = 'SELECT DISTINCT(t.topic_id)
1498				FROM ' . POSTS_TABLE . " t
1499				$where_sql_and t.post_reported = 1";
1500			$result = $db->sql_query($sql);
1501
1502			while ($row = $db->sql_fetchrow($result))
1503			{
1504				$topic_reported[$row['topic_id']] = 1;
1505			}
1506			$db->sql_freeresult($result);
1507
1508			$sql = 'SELECT t.topic_id, t.topic_reported
1509				FROM ' . TOPICS_TABLE . " t
1510				$where_sql";
1511			$result = $db->sql_query($sql);
1512
1513			while ($row = $db->sql_fetchrow($result))
1514			{
1515				if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']]))
1516				{
1517					$topic_ids[] = $row['topic_id'];
1518				}
1519			}
1520			$db->sql_freeresult($result);
1521
1522			if (sizeof($topic_ids))
1523			{
1524				$sql = 'UPDATE ' . TOPICS_TABLE . '
1525					SET topic_reported = 1 - topic_reported
1526					WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1527				$db->sql_query($sql);
1528			}
1529
1530			$db->sql_transaction('commit');
1531			break;
1532
1533		case 'post_attachment':
1534			$post_ids = $post_attachment = array();
1535
1536			$db->sql_transaction('begin');
1537
1538			$sql = 'SELECT p.post_id, p.post_attachment
1539				FROM ' . POSTS_TABLE . " p
1540				$where_sql
1541				GROUP BY p.post_id, p.post_attachment";
1542			$result = $db->sql_query($sql);
1543
1544			while ($row = $db->sql_fetchrow($result))
1545			{
1546				$post_ids[$row['post_id']] = $row['post_id'];
1547				if ($row['post_attachment'])
1548				{
1549					$post_attachment[$row['post_id']] = 1;
1550				}
1551			}
1552			$db->sql_freeresult($result);
1553
1554			$sql = 'SELECT DISTINCT(post_msg_id)
1555				FROM ' . ATTACHMENTS_TABLE . '
1556				WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
1557					AND in_message = 0';
1558			$result = $db->sql_query($sql);
1559
1560			$post_ids = array();
1561			while ($row = $db->sql_fetchrow($result))
1562			{
1563				if (!isset($post_attachment[$row['post_msg_id']]))
1564				{
1565					$post_ids[] = $row['post_msg_id'];
1566				}
1567				else
1568				{
1569					unset($post_attachment[$row['post_msg_id']]);
1570				}
1571			}
1572			$db->sql_freeresult($result);
1573
1574			// $post_attachment should be empty by now, if it's not it contains
1575			// posts that are falsely flagged as having attachments
1576			foreach ($post_attachment as $post_id => $void)
1577			{
1578				$post_ids[] = $post_id;
1579			}
1580
1581			if (sizeof($post_ids))
1582			{
1583				$sql = 'UPDATE ' . POSTS_TABLE . '
1584					SET post_attachment = 1 - post_attachment
1585					WHERE ' . $db->sql_in_set('post_id', $post_ids);
1586				$db->sql_query($sql);
1587			}
1588
1589			$db->sql_transaction('commit');
1590			break;
1591
1592		case 'topic_attachment':
1593			if ($sync_extra)
1594			{
1595				sync('post_attachment', $where_type, $where_ids);
1596			}
1597
1598			$topic_ids = $topic_attachment = array();
1599
1600			$db->sql_transaction('begin');
1601
1602			$sql = 'SELECT DISTINCT(t.topic_id)
1603				FROM ' . POSTS_TABLE . " t
1604				$where_sql_and t.post_attachment = 1";
1605			$result = $db->sql_query($sql);
1606
1607			while ($row = $db->sql_fetchrow($result))
1608			{
1609				$topic_attachment[$row['topic_id']] = 1;
1610			}
1611			$db->sql_freeresult($result);
1612
1613			$sql = 'SELECT t.topic_id, t.topic_attachment
1614				FROM ' . TOPICS_TABLE . " t
1615				$where_sql";
1616			$result = $db->sql_query($sql);
1617
1618			while ($row = $db->sql_fetchrow($result))
1619			{
1620				if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']]))
1621				{
1622					$topic_ids[] = $row['topic_id'];
1623				}
1624			}
1625			$db->sql_freeresult($result);
1626
1627			if (sizeof($topic_ids))
1628			{
1629				$sql = 'UPDATE ' . TOPICS_TABLE . '
1630					SET topic_attachment = 1 - topic_attachment
1631					WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1632				$db->sql_query($sql);
1633			}
1634
1635			$db->sql_transaction('commit');
1636
1637			break;
1638
1639		case 'forum':
1640
1641			$db->sql_transaction('begin');
1642
1643			// 1: Get the list of all forums
1644			$sql = 'SELECT f.*
1645				FROM ' . FORUMS_TABLE . " f
1646				$where_sql";
1647			$result = $db->sql_query($sql);
1648
1649			$forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array();
1650			while ($row = $db->sql_fetchrow($result))
1651			{
1652				if ($row['forum_type'] == FORUM_LINK)
1653				{
1654					continue;
1655				}
1656
1657				$forum_id = (int) $row['forum_id'];
1658				$forum_ids[$forum_id] = $forum_id;
1659
1660				$forum_data[$forum_id] = $row;
1661				if ($sync_extra)
1662				{
1663					$forum_data[$forum_id]['posts'] = 0;
1664					$forum_data[$forum_id]['topics'] = 0;
1665					$forum_data[$forum_id]['topics_real'] = 0;
1666				}
1667				$forum_data[$forum_id]['last_post_id'] = 0;
1668				$forum_data[$forum_id]['last_post_subject'] = '';
1669				$forum_data[$forum_id]['last_post_time'] = 0;
1670				$forum_data[$forum_id]['last_poster_id'] = 0;
1671				$forum_data[$forum_id]['last_poster_name'] = '';
1672				$forum_data[$forum_id]['last_poster_colour'] = '';
1673			}
1674			$db->sql_freeresult($result);
1675
1676			if (!sizeof($forum_ids))
1677			{
1678				break;
1679			}
1680
1681			$forum_ids = array_values($forum_ids);
1682
1683			// 2: Get topic counts for each forum (optional)
1684			if ($sync_extra)
1685			{
1686				$sql = 'SELECT forum_id, topic_approved, COUNT(topic_id) AS forum_topics
1687					FROM ' . TOPICS_TABLE . '
1688					WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . '
1689					GROUP BY forum_id, topic_approved';
1690				$result = $db->sql_query($sql);
1691
1692				while ($row = $db->sql_fetchrow($result))
1693				{
1694					$forum_id = (int) $row['forum_id'];
1695					$forum_data[$forum_id]['topics_real'] += $row['forum_topics'];
1696
1697					if ($row['topic_approved'])
1698					{
1699						$forum_data[$forum_id]['topics'] = $row['forum_topics'];
1700					}
1701				}
1702				$db->sql_freeresult($result);
1703			}
1704
1705			// 3: Get post count for each forum (optional)
1706			if ($sync_extra)
1707			{
1708				if (sizeof($forum_ids) == 1)
1709				{
1710					$sql = 'SELECT SUM(t.topic_replies + 1) AS forum_posts
1711						FROM ' . TOPICS_TABLE . ' t
1712						WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1713							AND t.topic_approved = 1
1714							AND t.topic_status <> ' . ITEM_MOVED;
1715				}
1716				else
1717				{
1718					$sql = 'SELECT t.forum_id, SUM(t.topic_replies + 1) AS forum_posts
1719						FROM ' . TOPICS_TABLE . ' t
1720						WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1721							AND t.topic_approved = 1
1722							AND t.topic_status <> ' . ITEM_MOVED . '
1723						GROUP BY t.forum_id';
1724				}
1725
1726				$result = $db->sql_query($sql);
1727
1728				while ($row = $db->sql_fetchrow($result))
1729				{
1730					$forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1731
1732					$forum_data[$forum_id]['posts'] = (int) $row['forum_posts'];
1733				}
1734				$db->sql_freeresult($result);
1735			}
1736
1737			// 4: Get last_post_id for each forum
1738			if (sizeof($forum_ids) == 1)
1739			{
1740				$sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id
1741					FROM ' . TOPICS_TABLE . ' t
1742					WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1743						AND t.topic_approved = 1';
1744			}
1745			else
1746			{
1747				$sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id
1748					FROM ' . TOPICS_TABLE . ' t
1749					WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1750						AND t.topic_approved = 1
1751					GROUP BY t.forum_id';
1752			}
1753
1754			$result = $db->sql_query($sql);
1755
1756			while ($row = $db->sql_fetchrow($result))
1757			{
1758				$forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1759
1760				$forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id'];
1761
1762				$post_ids[] = $row['last_post_id'];
1763			}
1764			$db->sql_freeresult($result);
1765
1766			// 5: Retrieve last_post infos
1767			if (sizeof($post_ids))
1768			{
1769				$sql = 'SELECT p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour
1770					FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
1771					WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
1772						AND p.poster_id = u.user_id';
1773				$result = $db->sql_query($sql);
1774
1775				while ($row = $db->sql_fetchrow($result))
1776				{
1777					$post_info[$row['post_id']] = $row;
1778				}
1779				$db->sql_freeresult($result);
1780
1781				foreach ($forum_data as $forum_id => $data)
1782				{
1783					if ($data['last_post_id'])
1784					{
1785						if (isset($post_info[$data['last_post_id']]))
1786						{
1787							$forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject'];
1788							$forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time'];
1789							$forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id'];
1790							$forum_data[$forum_id]['last_poster_name'] = ($post_info[$data['last_post_id']]['poster_id'] != ANONYMOUS) ? $post_info[$data['last_post_id']]['username'] : $post_info[$data['last_post_id']]['post_username'];
1791							$forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour'];
1792						}
1793						else
1794						{
1795							// For some reason we did not find the post in the db
1796							$forum_data[$forum_id]['last_post_id'] = 0;
1797							$forum_data[$forum_id]['last_post_subject'] = '';
1798							$forum_data[$forum_id]['last_post_time'] = 0;
1799							$forum_data[$forum_id]['last_poster_id'] = 0;
1800							$forum_data[$forum_id]['last_poster_name'] = '';
1801							$forum_data[$forum_id]['last_poster_colour'] = '';
1802						}
1803					}
1804				}
1805				unset($post_info);
1806			}
1807
1808			// 6: Now do that thing
1809			$fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
1810
1811			if ($sync_extra)
1812			{
1813				array_push($fieldnames, 'posts', 'topics', 'topics_real');
1814			}
1815
1816			

Large files files are truncated, but you can click here to view the full file