| 1 | <?php |
| 2 | |
| 3 | namespace wcf\system\moderation\queue; |
| 4 | |
| 5 | use wcf\data\moderation\queue\ModerationQueue; |
| 6 | use wcf\data\moderation\queue\ModerationQueueAction; |
| 7 | use wcf\data\moderation\queue\ViewableModerationQueue; |
| 8 | use wcf\data\object\type\ObjectTypeCache; |
| 9 | use wcf\system\cache\builder\UserGroupOptionCacheBuilder; |
| 10 | use wcf\system\cache\runtime\UserProfileRuntimeCache; |
| 11 | use wcf\system\exception\InvalidObjectTypeException; |
| 12 | use wcf\system\request\LinkHandler; |
| 13 | use wcf\system\user\notification\object\ModerationQueueUserNotificationObject; |
| 14 | use wcf\system\user\notification\object\type\TMultiRecipientModerationQueueCommentUserNotificationObjectType; |
| 15 | use wcf\system\user\notification\UserNotificationHandler; |
| 16 | use wcf\system\WCF; |
| 17 | |
| 18 | /** |
| 19 | * Moderation queue implementation for reports. |
| 20 | * |
| 21 | * @author Alexander Ebert |
| 22 | * @copyright 2001-2019 WoltLab GmbH |
| 23 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
| 24 | */ |
| 25 | class ModerationQueueReportManager extends AbstractModerationQueueManager |
| 26 | { |
| 27 | /** |
| 28 | * @inheritDoc |
| 29 | */ |
| 30 | protected $definitionName = 'com.woltlab.wcf.moderation.report'; |
| 31 | |
| 32 | /** |
| 33 | * Returns true if given item was already reported. |
| 34 | * |
| 35 | * @param string $objectType |
| 36 | * @param int $objectID |
| 37 | * @return bool |
| 38 | */ |
| 39 | public function isAlreadyReported($objectType, $objectID) |
| 40 | { |
| 41 | $objectTypeID = $this->getObjectTypeID($objectType); |
| 42 | |
| 43 | $sql = "SELECT COUNT(*) |
| 44 | FROM wcf" . WCF_N . "_moderation_queue |
| 45 | WHERE objectTypeID = ? |
| 46 | AND objectID = ?"; |
| 47 | $statement = WCF::getDB()->prepareStatement($sql); |
| 48 | $statement->execute([ |
| 49 | $objectTypeID, |
| 50 | $objectID, |
| 51 | ]); |
| 52 | |
| 53 | return $statement->fetchSingleColumn() > 0; |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Returns true if the object with the given data has a pending report. |
| 58 | * A pending report has a status other than done. |
| 59 | * |
| 60 | * @param string $objectType |
| 61 | * @param int $objectID |
| 62 | * @return bool |
| 63 | */ |
| 64 | public function hasPendingReport($objectType, $objectID) |
| 65 | { |
| 66 | $objectTypeID = $this->getObjectTypeID($objectType); |
| 67 | |
| 68 | $sql = "SELECT COUNT(*) |
| 69 | FROM wcf" . WCF_N . "_moderation_queue |
| 70 | WHERE objectTypeID = ? |
| 71 | AND objectID = ? |
| 72 | AND status IN (?, ?)"; |
| 73 | $statement = WCF::getDB()->prepareStatement($sql); |
| 74 | $statement->execute([ |
| 75 | $objectTypeID, |
| 76 | $objectID, |
| 77 | ModerationQueue::STATUS_OUTSTANDING, |
| 78 | ModerationQueue::STATUS_PROCESSING, |
| 79 | ]); |
| 80 | |
| 81 | return $statement->fetchSingleColumn() > 0; |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Returns true if current user can report given content. |
| 86 | * |
| 87 | * @param string $objectType |
| 88 | * @param int $objectID |
| 89 | * @return bool |
| 90 | */ |
| 91 | public function canReport($objectType, $objectID) |
| 92 | { |
| 93 | return $this->getProcessor($objectType)->canReport($objectID); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * @inheritDoc |
| 98 | */ |
| 99 | public function getLink($queueID) |
| 100 | { |
| 101 | return LinkHandler::getInstance()->getLink('ModerationReport', [ |
| 102 | 'id' => $queueID, |
| 103 | 'forceFrontend' => true, |
| 104 | ]); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Returns rendered template for reported content. |
| 109 | * |
| 110 | * @param ViewableModerationQueue $queue |
| 111 | * @return string |
| 112 | */ |
| 113 | public function getReportedContent(ViewableModerationQueue $queue) |
| 114 | { |
| 115 | return $this->getProcessor(null, $queue->objectTypeID)->getReportedContent($queue); |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * Returns the reported object. |
| 120 | * |
| 121 | * @param string $objectType |
| 122 | * @param int $objectID |
| 123 | * @return \wcf\data\IUserContent |
| 124 | */ |
| 125 | public function getReportedObject($objectType, $objectID) |
| 126 | { |
| 127 | return $this->getProcessor($objectType)->getReportedObject($objectID); |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * Adds a report for specified content. |
| 132 | * |
| 133 | * @param string $objectType |
| 134 | * @param int $objectID |
| 135 | * @param string $message |
| 136 | * @param array $additionalData |
| 137 | * @throws InvalidObjectTypeException |
| 138 | */ |
| 139 | public function addReport($objectType, $objectID, $message, array $additionalData = []) |
| 140 | { |
| 141 | if (!$this->isValid($objectType)) { |
| 142 | throw new InvalidObjectTypeException($objectType, 'com.woltlab.wcf.moderation.report'); |
| 143 | } |
| 144 | |
| 145 | $additionalData['message'] = $message; |
| 146 | $this->addEntry( |
| 147 | $this->getObjectTypeID($objectType), |
| 148 | $objectID, |
| 149 | $this->getProcessor($objectType)->getContainerID($objectID), |
| 150 | $additionalData |
| 151 | ); |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * @inheritDoc |
| 156 | */ |
| 157 | protected function addEntry($objectTypeID, $objectID, $containerID = 0, array $additionalData = []) |
| 158 | { |
| 159 | $sql = "SELECT queueID |
| 160 | FROM wcf" . WCF_N . "_moderation_queue |
| 161 | WHERE objectTypeID = ? |
| 162 | AND objectID = ? |
| 163 | AND status <> ?"; |
| 164 | $statement = WCF::getDB()->prepareStatement($sql); |
| 165 | $statement->execute([ |
| 166 | $objectTypeID, |
| 167 | $objectID, |
| 168 | ModerationQueue::STATUS_DONE, |
| 169 | ]); |
| 170 | $row = $statement->fetchArray(); |
| 171 | |
| 172 | if ($row === false) { |
| 173 | $objectAction = new ModerationQueueAction([], 'create', [ |
| 174 | 'data' => [ |
| 175 | 'objectTypeID' => $objectTypeID, |
| 176 | 'objectID' => $objectID, |
| 177 | 'containerID' => $containerID, |
| 178 | 'userID' => WCF::getUser()->userID ?: null, |
| 179 | 'time' => TIME_NOW, |
| 180 | 'additionalData' => \serialize($additionalData), |
| 181 | ], |
| 182 | ]); |
| 183 | $objectAction->executeAction(); |
| 184 | $queue = $objectAction->getReturnValues()['returnValues']; |
| 185 | } else { |
| 186 | $objectAction = new ModerationQueueAction([$row['queueID']], 'update', [ |
| 187 | 'data' => [ |
| 188 | 'status' => ModerationQueue::STATUS_OUTSTANDING, |
| 189 | 'containerID' => $containerID, |
| 190 | 'userID' => WCF::getUser()->userID ?: null, |
| 191 | 'time' => TIME_NOW, |
| 192 | 'additionalData' => \serialize($additionalData), |
| 193 | ], |
| 194 | ]); |
| 195 | $objectAction->executeAction(); |
| 196 | $queue = new ModerationQueue($row['queueID']); |
| 197 | } |
| 198 | |
| 199 | ModerationQueueManager::getInstance()->resetModerationCount(); |
| 200 | |
| 201 | $this->notifyModerators($queue); |
| 202 | } |
| 203 | |
| 204 | private function notifyModerators(ModerationQueue $queue): void |
| 205 | { |
| 206 | /** @see TMultiRecipientModerationQueueCommentUserNotificationObjectType::loadModerators() */ |
| 207 | $userGroupOptionCache = UserGroupOptionCacheBuilder::getInstance()->getData(); |
| 208 | $canUseModerationOption = $userGroupOptionCache['options']['mod.general.canUseModeration']; |
| 209 | |
| 210 | $sql = "SELECT DISTINCT userID |
| 211 | FROM ( |
| 212 | SELECT userID |
| 213 | FROM wcf1_user_to_group |
| 214 | WHERE groupID IN ( |
| 215 | SELECT groupID |
| 216 | FROM wcf1_user_group_option_value |
| 217 | WHERE optionID = ? |
| 218 | AND optionValue = ? |
| 219 | ) |
| 220 | ) users_in_groups_with_access |
| 221 | WHERE userID NOT IN ( |
| 222 | SELECT userID |
| 223 | FROM wcf1_user_to_group |
| 224 | WHERE groupID IN ( |
| 225 | SELECT groupID |
| 226 | FROM wcf1_user_group_option_value |
| 227 | WHERE optionID = ? |
| 228 | AND optionValue = ? |
| 229 | ) |
| 230 | ) |
| 231 | AND userID NOT IN ( |
| 232 | SELECT userID |
| 233 | FROM wcf1_moderation_queue_to_user |
| 234 | WHERE queueID = ? |
| 235 | )"; |
| 236 | $statement = WCF::getDB()->prepare($sql); |
| 237 | $statement->execute([ |
| 238 | $canUseModerationOption->optionID, |
| 239 | 1, |
| 240 | $canUseModerationOption->optionID, |
| 241 | -1, |
| 242 | $queue->queueID, |
| 243 | ]); |
| 244 | $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); |
| 245 | if (!$userIDs) { |
| 246 | return; |
| 247 | } |
| 248 | UserProfileRuntimeCache::getInstance()->cacheObjectIDs($userIDs); |
| 249 | $objectType = ObjectTypeCache::getInstance()->getObjectType($queue->objectTypeID); |
| 250 | $processor = $objectType->getProcessor(); |
| 251 | \assert($processor instanceof IModerationQueueHandler); |
| 252 | |
| 253 | $userIDs = \array_filter($userIDs, function ($userID) use ($processor, $queue) { |
| 254 | return $processor->isAffectedUser($queue, $userID); |
| 255 | }); |
| 256 | if ($userIDs === []) { |
| 257 | return; |
| 258 | } |
| 259 | foreach ($userIDs as $userID) { |
| 260 | $user = UserProfileRuntimeCache::getInstance()->getObject($userID); |
| 261 | ModerationQueueManager::getInstance()->setAssignment([$queue->queueID => 1], $user->getDecoratedObject()); |
| 262 | } |
| 263 | |
| 264 | UserNotificationHandler::getInstance()->fireEvent( |
| 265 | 'report', |
| 266 | 'com.woltlab.wcf.moderation.queue', |
| 267 | new ModerationQueueUserNotificationObject($queue), |
| 268 | $userIDs |
| 269 | ); |
| 270 | } |
| 271 | } |