diff --git a/includes/ajaxHandler/admin.class.php b/includes/ajaxHandler/admin.class.php index d048071a..ed14b572 100644 --- a/includes/ajaxHandler/admin.class.php +++ b/includes/ajaxHandler/admin.class.php @@ -5,7 +5,7 @@ if (!defined('AOWOW_REVISION')) class AjaxAdmin extends AjaxHandler { - protected $validParams = ['screenshots', 'siteconfig', 'weight-presets', 'spawn-override', 'guide']; + protected $validParams = ['screenshots', 'siteconfig', 'weight-presets', 'spawn-override', 'guide', 'comment']; protected $_get = array( 'action' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ], 'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdListUnsigned'], @@ -89,6 +89,13 @@ class AjaxAdmin extends AjaxHandler $this->handler = 'guideManage'; } + else if ($this->params[0] == 'comment') + { + if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD)) + return; + + $this->handler = 'commentOutOfDate'; + } } // get all => null (optional) @@ -526,6 +533,28 @@ class AjaxAdmin extends AjaxHandler return '-1'; } + protected function commentOutOfDate() : string + { + $ok = false; + switch ($this->_post['status']) + { + case 0: // up to date + if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id'])) + if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'])) + $rep->close(Report::STATUS_CLOSED_WONTFIX); + break; + case 1: // outdated, mark as deleted and clear other flags (sticky + outdated) + if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = ?d, `deleteUserId` = ?d, `deleteDate` = ?d WHERE `id` = ?d', CC_FLAG_DELETED, User::$id, time(), $this->_post['id'])) + if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'])) + $rep->close(Report::STATUS_CLOSED_SOLVED); + break; + default: + trigger_error('AjaxHandler::comentOutOfDate - called with invalid status'); + } + + return $ok ? '1' : '0'; + } + /***************************/ /* additional input filter */ diff --git a/includes/ajaxHandler/comment.class.php b/includes/ajaxHandler/comment.class.php index c635391f..18cc8c34 100644 --- a/includes/ajaxHandler/comment.class.php +++ b/includes/ajaxHandler/comment.class.php @@ -177,7 +177,7 @@ class AjaxComment extends AjaxHandler } // in theory, there is a username passed alongside... lets just use the current user (see user.js) - $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d, deleteUserId = ?d, deleteDate = UNIX_TIMESTAMP() WHERE id IN (?a){ AND userId = ?d}', + $ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` IN (?a){ AND `userId` = ?d}', CC_FLAG_DELETED, User::$id, $this->_post['id'], @@ -187,13 +187,14 @@ class AjaxComment extends AjaxHandler // deflag hasComment if ($ok) { - $coInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~b.flags) & ?d, 1, 0) as hasMore, b.type, b.typeId FROM ?_comments a JOIN ?_comments b ON a.type = b.type AND a.typeId = b.typeId WHERE a.id = ?d', + $coInfo = DB::Aowow()->select('SELECT IF(BIT_OR(~b.`flags`) & ?d, 1, 0) AS hasMore, b.`type`, b.`typeId` FROM ?_comments a JOIN ?_comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN (?a) GROUP BY b.`type`, b.`typeId`', CC_FLAG_DELETED, $this->_post['id'] ); - if (!$coInfo['hasMore'] && ($tbl = Type::getClassAttrib($coInfo['type'], 'dataTable'))) - DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']); + foreach ($coInfo as $co) + if (!$co['hasMore'] && ($tbl = Type::getClassAttrib($co['type'], 'dataTable'))) + DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $co['typeId']); } else trigger_error('AjaxComment::handleCommentDelete - user #'.User::$id.' could not flag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR); @@ -208,7 +209,7 @@ class AjaxComment extends AjaxHandler } // in theory, there is a username passed alongside... lets just use the current user (see user.js) - $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id IN (?a){ AND userId = deleteUserId AND deleteUserId = ?d}', + $ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` IN (?a){ AND `userId` = `deleteUserId` AND `deleteUserId` = ?d}', CC_FLAG_DELETED, $this->_post['id'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id @@ -217,9 +218,10 @@ class AjaxComment extends AjaxHandler // reflag hasComment if ($ok) { - $coInfo = DB::Aowow()->selectRow('SELECT type, typeId FROM ?_comments WHERE id = ?d', $this->_post['id']); - if ($tbl = Type::getClassAttrib($coInfo['type'], 'dataTable')) - DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']); + $coInfo = DB::Aowow()->select('SELECT `type`, `typeId` FROM ?_comments WHERE `id` IN (?a) GROUP BY `type`, `typeId`', $this->_post['id']); + foreach ($coInfo as $co) + if ($tbl = Type::getClassAttrib($co['type'], 'dataTable')) + DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $co['typeId']); } else trigger_error('AjaxComment::handleCommentUndelete - user #'.User::$id.' could not unflag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR); @@ -300,19 +302,24 @@ class AjaxComment extends AjaxHandler if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated { if (!$this->_post['remove']) - $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | 0x4 WHERE id = ?d', $this->_post['id'][0]); + $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]); else - $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~0x4 WHERE id = ?d', $this->_post['id'][0]); + $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]); } - else if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND `userId` = ?d', 1, 17, $this->_post['id'][0], User::$id)) - return Lang::main('alreadyReport'); - else if (User::$id && !$this->_post['reason'] || mb_strlen($this->_post['reason']) < self::REPLY_LENGTH_MIN) - return Lang::main('textTooShort'); - else if (User::$id) // only report as outdated - $ok = Util::createReport(1, 17, $this->_post['id'][0], '[Outdated Comment] '.$this->_post['reason']); + else // try to report as outdated + { + $report = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'][0]); + if ($report->create($this->_post['reason'])) + $ok = true; // the script expects the actual characters 'ok' not some json string like "ok" + else + return Lang::main('intError'); - if ($ok) // this one is very special; as in: completely retarded - return 'ok'; // the script expects the actual characters 'ok' not some string like "ok" + if (count($report->getSimilar()) >= 5) // 5 or more reports on the same comment: trigger flag + $ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]); + } + + if ($ok) + return 'ok'; else trigger_error('AjaxComment::handleCommentOutOfDate - failed to update comment in db', E_USER_ERROR); diff --git a/includes/ajaxHandler/contactus.class.php b/includes/ajaxHandler/contactus.class.php index d2a2517e..a15bfc49 100644 --- a/includes/ajaxHandler/contactus.class.php +++ b/includes/ajaxHandler/contactus.class.php @@ -6,15 +6,15 @@ if (!defined('AOWOW_REVISION')) class AjaxContactus extends AjaxHandler { protected $_post = array( - 'mode' => ['filter' => FILTER_SANITIZE_NUMBER_INT], - 'reason' => ['filter' => FILTER_SANITIZE_NUMBER_INT], - 'ua' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], - 'appname' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], - 'page' => ['filter' => FILTER_SANITIZE_URL], - 'desc' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], - 'id' => ['filter' => FILTER_SANITIZE_NUMBER_INT], - 'relatedurl' => ['filter' => FILTER_SANITIZE_URL], - 'email' => ['filter' => FILTER_SANITIZE_EMAIL] + 'mode' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'], + 'reason' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'], + 'ua' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], + 'appname' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], + 'page' => ['filter' => FILTER_SANITIZE_URL ], + 'desc' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW], + 'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'], + 'relatedurl' => ['filter' => FILTER_SANITIZE_URL ], + 'email' => ['filter' => FILTER_SANITIZE_EMAIL ] ); public function __construct(array $params) @@ -35,58 +35,13 @@ class AjaxContactus extends AjaxHandler */ protected function handleContactUs() : string { - $mode = $this->_post['mode']; - $rsn = $this->_post['reason']; - $ua = $this->_post['ua']; - $app = $this->_post['appname']; - $url = $this->_post['page']; - $desc = $this->_post['desc']; - $subj = $this->_post['id']; - - $contexts = array( - [1, 2, 3, 4, 5, 6, 7, 8], - [15, 16, 17, 18, 19, 20], - [30, 31, 32, 33, 34, 35, 36, 37], - [45, 46, 47, 48], - [60, 61], - [45, 46, 47, 48], - [45, 46, 48] - ); - - if ($mode === null || $rsn === null || $ua === null || $app === null || $url === null) - { - trigger_error('AjaxContactus::handleContactUs - malformed contact request received', E_USER_ERROR); - return Lang::main('intError'); - } - - if (!isset($contexts[$mode]) || !in_array($rsn, $contexts[$mode])) - { - trigger_error('AjaxContactus::handleContactUs - report has invalid context (mode:'.$mode.' / reason:'.$rsn.')', E_USER_ERROR); - return Lang::main('intError'); - } - - if (!$desc) - return 3; - - if (mb_strlen($desc) > 500) - return 2; - - if (!User::$id && !User::$ip) - { - trigger_error('AjaxContactus::handleContactUs - could not determine IP for anonymous user', E_USER_ERROR); - return Lang::main('intError'); - } - - // check already reported - $field = User::$id ? 'userId' : 'ip'; - if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $mode, $rsn, $subj, $field, User::$id ?: User::$ip)) - return 7; - - if (Util::createReport($mode, $rsn, $subj, $desc, $ua, $app, $url, $this->_post['relatedurl'], $this->_post['email'])) + $report = new Report($this->_post['mode'], $this->_post['reason'], $this->_post['id']); + if ($report->create($this->_post['desc'], $this->_post['ua'], $this->_post['appname'], $this->_post['page'], $this->_post['relatedurl'], $this->_post['email'])) return 0; - - trigger_error('AjaxContactus::handleContactUs - write to db failed', E_USER_ERROR); - return Lang::main('intError'); + else if ($report->errorCode > 0) + return $report->errorCode; + else + return Lang::main('intError'); } } diff --git a/includes/community.class.php b/includes/community.class.php index 522d4b33..a11a75ed 100644 --- a/includes/community.class.php +++ b/includes/community.class.php @@ -96,6 +96,7 @@ class CommunityContent {c.replyTo <> ?d AND} {c.replyTo = ?d AND} {ur.entry IS ?n AND} + {(c.flags & ?d) AND} ((c.flags & ?d) = 0 OR c.userId = ?d OR ?d) GROUP BY c.id @@ -143,6 +144,7 @@ class CommunityContent empty($params['replies']) ? DBSIMPLE_SKIP : 0, // i dont know, how to switch the sign around !empty($params['replies']) ? DBSIMPLE_SKIP : 0, empty($params['unrated']) ? DBSIMPLE_SKIP : null, + empty($params['flags']) ? DBSIMPLE_SKIP : $params['flags'], CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR), diff --git a/includes/utilities.php b/includes/utilities.php index ebaa856f..4ba5f6f5 100644 --- a/includes/utilities.php +++ b/includes/utilities.php @@ -1574,32 +1574,6 @@ abstract class Util ); } - static function createReport($mode, $reason, $subject, $desc, $userAgent = null, $appName = null, $url = null, $relUrl = null, $email = null) - { - $update = array( - 'userId' => User::$id, - 'createDate' => time(), - 'mode' => $mode, - 'reason' => $reason, - 'subject' => $subject ?: 0, // not set for utility, tools and misc pages - 'ip' => User::$ip, - 'description' => $desc, - 'userAgent' => $userAgent ?: $_SERVER['HTTP_USER_AGENT'], - 'appName' => $appName ?: (get_browser(null, true)['browser'] ?: '') - ); - - if ($url) - $update['url'] = $url; - - if ($relUrl) - $update['relatedurl'] = $relUrl; - - if ($email) - $update['email'] = $email; - - return DB::Aowow()->query('INSERT INTO ?_reports (?#) VALUES (?a)', array_keys($update), array_values($update)); - } - // orientation is 2*M_PI for a full circle, increasing counterclockwise static function O2Deg($o) { @@ -1871,4 +1845,271 @@ abstract class Type } } + +class Report +{ + public const MODE_GENERAL = 0; + public const MODE_COMMENT = 1; + public const MODE_FORUM_POST = 2; + public const MODE_SCREENSHOT = 3; + public const MODE_CHARACTER = 4; + public const MODE_VIDEO = 5; + public const MODE_GUIDE = 6; + + public const GEN_FEEDBACK = 1; + public const GEN_BUG_REPORT = 2; + public const GEN_TYPO_TRANSLATION = 3; + public const GEN_OP_ADVERTISING = 4; + public const GEN_OP_PARTNERSHIP = 5; + public const GEN_PRESS_INQUIRY = 6; + public const GEN_MISCELLANEOUS = 7; + public const GEN_MISINFORMATION = 8; + public const CO_ADVERTISING = 15; + public const CO_INACCURATE = 16; + public const CO_OUT_OF_DATE = 17; + public const CO_SPAM = 18; + public const CO_INAPPROPRIATE = 19; + public const CO_MISCELLANEOUS = 20; + public const FO_ADVERTISING = 30; + public const FO_AVATAR = 31; + public const FO_INACCURATE = 32; + public const FO_OUT_OF_DATE = 33; + public const FO_SPAM = 34; + public const FO_STICKY_REQUEST = 35; + public const FO_INAPPROPRIATE = 36; + public const FO_MISCELLANEOUS = 37; + public const SS_INACCURATE = 45; + public const SS_OUT_OF_DATE = 46; + public const SS_INAPPROPRIATE = 47; + public const SS_MISCELLANEOUS = 48; + public const PR_INACCURATE_DATA = 60; + public const PR_MISCELLANEOUS = 61; + public const VI_INACCURATE = 45; + public const VI_OUT_OF_DATE = 46; + public const VI_INAPPROPRIATE = 47; + public const VI_MISCELLANEOUS = 48; + public const AR_INACCURATE = 45; + public const AR_OUT_OF_DATE = 46; + public const AR_MISCELLANEOUS = 48; + + private /* array */ $context = array( + self::MODE_GENERAL => array( + self::GEN_FEEDBACK => true, + self::GEN_BUG_REPORT => true, + self::GEN_TYPO_TRANSLATION => true, + self::GEN_OP_ADVERTISING => true, + self::GEN_OP_PARTNERSHIP => true, + self::GEN_PRESS_INQUIRY => true, + self::GEN_MISCELLANEOUS => true, + self::GEN_MISINFORMATION => true + ), + self::MODE_COMMENT => array( + self::CO_ADVERTISING => U_GROUP_MODERATOR, + self::CO_INACCURATE => true, + self::CO_OUT_OF_DATE => true, + self::CO_SPAM => U_GROUP_MODERATOR, + self::CO_INAPPROPRIATE => U_GROUP_MODERATOR, + self::CO_MISCELLANEOUS => U_GROUP_MODERATOR + ), + self::MODE_FORUM_POST => array( + self::FO_ADVERTISING => U_GROUP_MODERATOR, + self::FO_AVATAR => true, + self::FO_INACCURATE => true, + self::FO_OUT_OF_DATE => U_GROUP_MODERATOR, + self::FO_SPAM => U_GROUP_MODERATOR, + self::FO_STICKY_REQUEST => U_GROUP_MODERATOR, + self::FO_INAPPROPRIATE => U_GROUP_MODERATOR + ), + self::MODE_SCREENSHOT => array( + self::SS_INACCURATE => true, + self::SS_OUT_OF_DATE => true, + self::SS_INAPPROPRIATE => U_GROUP_MODERATOR, + self::SS_MISCELLANEOUS => U_GROUP_MODERATOR + ), + self::MODE_CHARACTER => array( + self::PR_INACCURATE_DATA => true, + self::PR_MISCELLANEOUS => true + ), + self::MODE_VIDEO => array( + self::VI_INACCURATE => true, + self::VI_OUT_OF_DATE => true, + self::VI_INAPPROPRIATE => U_GROUP_MODERATOR, + self::VI_MISCELLANEOUS => U_GROUP_MODERATOR + ), + self::MODE_GUIDE => array( + self::AR_INACCURATE => true, + self::AR_OUT_OF_DATE => true, + self::AR_MISCELLANEOUS => true + ) + ); + + private const ERR_NONE = 0; // aka: success + private const ERR_INVALID_CAPTCHA = 1; // captcha not in use + private const ERR_DESC_TOO_LONG = 2; + private const ERR_NO_DESC = 3; + private const ERR_ALREADY_REPORTED = 7; + private const ERR_MISCELLANEOUS = -1; + + public const STATUS_OPEN = 0; + public const STATUS_ASSIGNED = 1; + public const STATUS_CLOSED_WONTFIX = 2; + public const STATUS_CLOSED_SOLVED = 3; + + private /* int */ $mode = 0; + private /* int */ $reason = 0; + private /* int */ $subject = 0; + + public /* readonly int */ $errorCode; + + + public function __construct(int $mode, int $reason, int $subject = 0) + { + if ($mode < 0 || $reason <= 0 || !$subject) + { + trigger_error('AjaxContactus::handleContactUs - malformed contact request received', E_USER_ERROR); + $this->errorCode = self::ERR_MISCELLANEOUS; + return; + } + + if (!isset($this->context[$mode][$reason])) + { + trigger_error('AjaxContactus::handleContactUs - report has invalid context (mode:'.$mode.' / reason:'.$reason.')', E_USER_ERROR); + $this->errorCode = self::ERR_MISCELLANEOUS; + return; + } + + if (!User::$id && !User::$ip) + { + trigger_error('AjaxContactus::handleContactUs - could not determine IP for anonymous user', E_USER_ERROR); + $this->errorCode = self::ERR_MISCELLANEOUS; + return; + } + + $this->mode = $mode; + $this->reason = $reason; + $this->subject = $subject; // 0 for utility, tools and misc pages? + } + + private function checkTargetContext() : int + { + // check already reported + $field = User::$id ? 'userId' : 'ip'; + if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $this->mode, $this->reason, $this->subject, $field, User::$id ?: User::$ip)) + return self::ERR_ALREADY_REPORTED; + + // check targeted post/postOwner staff status + $ctxCheck = $this->context[$this->mode][$this->reason]; + if (is_int($ctxCheck)) + { + $roles = User::$groups; + if ($this->mode == self::MODE_COMMENT) + $roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_comments WHERE `id` = ?d', $this->subject); + // else if if ($this->mode == self::MODE_FORUM_POST) + // $roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_forum_posts WHERE `id` = ?d', $this->subject); + + return $roles & $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS; + } + else + return $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS; + + // Forum not in use, else: + // check post owner + // User::$id == post.op && !post.sticky; + // check user custom avatar + // g_users[post.user].avatar == 2 && (post.roles & U_GROUP_MODERATOR) == 0 + } + + public function create(string $desc, ?string $userAgent = null, ?string $appName = null, ?string $pageUrl = null, ?string $relUrl = null, ?string $email = null) : bool + { + if ($this->errorCode) + return false; + + if (!$desc) + { + $this->errorCode = self::ERR_NO_DESC; + return false; + } + + if (mb_strlen($desc) > 500) + { + $this->errorCode = self::ERR_DESC_TOO_LONG; + return false; + } + + if($err = $this->checkTargetContext()) + { + $this->errorCode = $err; + return false; + } + + $update = array( + 'userId' => User::$id, + 'createDate' => time(), + 'mode' => $this->mode, + 'reason' => $this->reason, + 'subject' => $this->subject, + 'ip' => User::$ip, + 'description' => $desc, + 'userAgent' => $userAgent ?: $_SERVER['HTTP_USER_AGENT'], + 'appName' => $appName ?: (get_browser(null, true)['browser'] ?: '') + ); + + if ($pageUrl) + $update['url'] = $pageUrl; + + if ($relUrl) + $update['relatedurl'] = $relUrl; + + if ($email) + $update['email'] = $email; + + return DB::Aowow()->query('INSERT INTO ?_reports (?#) VALUES (?a)', array_keys($update), array_values($update)); + } + + public function getSimilar(int ...$status) : array + { + if ($this->errorCode) + return []; + + foreach ($status as &$s) + if ($s < self::STATUS_OPEN || $s > self::STATUS_CLOSED_SOLVED) + unset($s); + + return DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, r.* FROM ?_reports r WHERE {`status` IN (?a) AND }`mode` = ?d AND `reason` = ?d AND `subject` = ?d', + $status ?: DBSIMPLE_SKIP, $this->mode, $this->reason, $this->subject); + } + + public function close(int $closeStatus, bool $inclAssigned = false) : bool + { + if ($closeStatus != self::STATUS_CLOSED_SOLVED && $closeStatus != self::STATUS_CLOSED_WONTFIX) + return false; + + if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD)) + return false; + + $fromStatus = [self::STATUS_OPEN]; + if ($inclAssigned) + $fromStatus[] = self::STATUS_ASSIGNED; + + if ($reports = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `userId` FROM ?_reports WHERE `status` IN (?a) AND `mode` = ?d AND `reason` = ?d AND `subject` = ?d', + $fromStatus, $this->mode, $this->reason, $this->subject)) + { + DB::Aowow()->query('UPDATE ?_reports SET `status` = ?d, `assigned` = 0 WHERE `id` IN (?a)', $closeStatus, array_keys($reports)); + + foreach ($reports as $rId => $uId) + Util::gainSiteReputation($uId, $closeStatus == self::STATUS_CLOSED_SOLVED ? SITEREP_ACTION_GOOD_REPORT : SITEREP_ACTION_BAD_REPORT, ['id' => $rId]); + + return true; + } + + return false; + } + + public function reopen(int $assignedTo = 0) : bool + { + // assignedTo = 0 ? status = STATUS_OPEN : status = STATUS_ASSIGNED, userId = assignedTo + return false; + } +} + ?> diff --git a/pages/admin.php b/pages/admin.php index 70b3b0c3..46e0f782 100644 --- a/pages/admin.php +++ b/pages/admin.php @@ -65,6 +65,22 @@ class AdminPage extends GenericPage array_push($this->path, 1, 25); $this->name = 'Pending Guides'; break; + case 'out-of-date': + $this->reqUGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD; + $this->generator = 'handleOutOfDate'; + $this->tpl = 'list-page-generic'; + + array_push($this->path, 1, 23); + $this->name = 'Out of Date Comments'; + break; + case 'reports': + $this->reqUGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_EDITOR | U_GROUP_MOD | U_GROUP_LOCALIZER | U_GROUP_SCREENSHOT | U_GROUP_VIDEO; + $this->generator = 'handleReports'; + $this->tpl = 'admin/reports'; + + array_push($this->path, 5); + $this->name = 'Reports'; + break; default: // error out through unset template } @@ -280,6 +296,22 @@ class AdminPage extends GenericPage ), 'guideAdminCol']; } + private function handleOutOfDate() : void + { + $data = CommunityContent::getCommentPreviews(['flags' => CC_FLAG_OUTDATED]); + + $this->lvTabs[] = ['commentpreview', array( + 'data' => $data, + 'extraCols' => '$_' + ), 'commentAdminCol']; + } + + private function handleReports() : void + { + // todo: handle reports listing + // + } + private function configAddRow($r) { $buff = '