Fix code formatting
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / like / LikeAction.class.php
1 <?php
2 namespace wcf\data\like;
3 use wcf\data\like\object\LikeObjectEditor;
4 use wcf\data\object\type\ObjectTypeCache;
5 use wcf\data\user\User;
6 use wcf\data\user\UserEditor;
7 use wcf\data\AbstractDatabaseObjectAction;
8 use wcf\data\IGroupedUserListAction;
9 use wcf\system\exception\PermissionDeniedException;
10 use wcf\system\exception\UserInputException;
11 use wcf\system\like\LikeHandler;
12 use wcf\system\user\activity\event\UserActivityEventHandler;
13 use wcf\system\user\activity\point\UserActivityPointHandler;
14 use wcf\system\user\GroupedUserList;
15 use wcf\system\WCF;
16
17 /**
18 * Executes like-related actions.
19 *
20 * @author Alexander Ebert
21 * @copyright 2001-2014 WoltLab GmbH
22 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
23 * @package com.woltlab.wcf
24 * @subpackage data.like
25 * @category Community Framework
26 */
27 class LikeAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction {
28 /**
29 * @see \wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess
30 */
31 protected $allowGuestAccess = array('getGroupedUserList', 'getLikeDetails', 'load');
32
33 /**
34 * @see \wcf\data\AbstractDatabaseObjectAction::$className
35 */
36 protected $className = 'wcf\data\like\LikeEditor';
37
38 /**
39 * likeable object
40 * @var \wcf\data\like\object\ILikeObject
41 */
42 public $likeableObject = null;
43
44 /**
45 * object type object
46 * @var \wcf\data\object\type\ObjectType
47 */
48 public $objectType = null;
49
50 /**
51 * like object type provider object
52 * @var \wcf\data\like\ILikeObjectTypeProvider
53 */
54 public $objectTypeProvider = null;
55
56 /**
57 * Validates parameters to fetch like details.
58 */
59 public function validateGetLikeDetails() {
60 $this->validateObjectParameters();
61 }
62
63 /**
64 * Returns like details.
65 *
66 * @return array<string>
67 */
68 public function getLikeDetails() {
69 $sql = "SELECT userID, likeValue
70 FROM wcf".WCF_N."_like
71 WHERE objectID = ?
72 AND objectTypeID = ?
73 ORDER BY time DESC";
74 $statement = WCF::getDB()->prepareStatement($sql);
75 $statement->execute(array(
76 $this->parameters['data']['objectID'],
77 $this->objectType->objectTypeID
78 ));
79 $data = array(
80 Like::LIKE => array(),
81 Like::DISLIKE => array()
82 );
83 while ($row = $statement->fetchArray()) {
84 $data[$row['likeValue']][] = $row['userID'];
85 }
86
87 $values = array();
88 if (!empty($data[Like::LIKE])) {
89 $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
90 $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
91 }
92 if (!empty($data[Like::DISLIKE])) {
93 $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
94 $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
95 }
96
97 // load user profiles
98 GroupedUserList::loadUsers();
99
100 WCF::getTPL()->assign(array(
101 'groupedUsers' => $values
102 ));
103
104 return array(
105 'containerID' => $this->parameters['data']['containerID'],
106 'template' => WCF::getTPL()->fetch('groupedUserList')
107 );
108 }
109
110 /**
111 * Validates parameters for like-related actions.
112 */
113 public function validateLike() {
114 $this->validateObjectParameters();
115
116 // check permissions
117 if (!WCF::getUser()->userID || !WCF::getSession()->getPermission('user.like.canLike')) {
118 throw new PermissionDeniedException();
119 }
120
121 // check if liking own content but forbidden by configuration
122 $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
123 $this->likeableObject->setObjectType($this->objectType);
124 if (!LIKE_ALLOW_FOR_OWN_CONTENT && ($this->likeableObject->getUserID() == WCF::getUser()->userID)) {
125 throw new PermissionDeniedException();
126 }
127 }
128
129 /**
130 * @see \wcf\data\like\LikeAction::updateLike()
131 */
132 public function like() {
133 return $this->updateLike(Like::LIKE);
134 }
135
136 /**
137 * @see \wcf\data\like\LikeAction::validateLike()
138 */
139 public function validateDislike() {
140 if (!LIKE_ENABLE_DISLIKE) {
141 throw new PermissionDeniedException();
142 }
143
144 $this->validateLike();
145 }
146
147 /**
148 * @see \wcf\data\like\LikeAction::updateLike()
149 */
150 public function dislike() {
151 return $this->updateLike(Like::DISLIKE);
152 }
153
154 /**
155 * Sets like/dislike for an object, executing this method again with the same parameters
156 * will revert the status (removing like/dislike).
157 *
158 * @return array
159 */
160 protected function updateLike($likeValue) {
161 $likeData = LikeHandler::getInstance()->like($this->likeableObject, WCF::getUser(), $likeValue);
162
163 // handle activity event
164 if (UserActivityEventHandler::getInstance()->getObjectTypeID($this->objectType->objectType.'.recentActivityEvent')) {
165 if ($likeData['data']['liked'] == 1) {
166 UserActivityEventHandler::getInstance()->fireEvent($this->objectType->objectType.'.recentActivityEvent', $this->parameters['data']['objectID'], $this->likeableObject->getLanguageID());
167 }
168 else {
169 UserActivityEventHandler::getInstance()->removeEvents($this->objectType->objectType.'.recentActivityEvent', array($this->parameters['data']['objectID']));
170 }
171 }
172
173 // get stats
174 return array(
175 'likes' => ($likeData['data']['likes'] === null) ? 0 : $likeData['data']['likes'],
176 'dislikes' => ($likeData['data']['dislikes'] === null) ? 0 : $likeData['data']['dislikes'],
177 'cumulativeLikes' => ($likeData['data']['cumulativeLikes'] === null) ? 0 : $likeData['data']['cumulativeLikes'],
178 'isLiked' => ($likeData['data']['liked'] == 1) ? 1 : 0,
179 'isDisliked' => ($likeData['data']['liked'] == -1) ? 1 : 0,
180 'containerID' => $this->parameters['data']['containerID'],
181 'newValue' => $likeData['newValue'],
182 'oldValue' => $likeData['oldValue'],
183 'users' => $likeData['users']
184 );
185 }
186
187 /**
188 * Validates permissions for given object.
189 */
190 protected function validateObjectParameters() {
191 if (!MODULE_LIKE) {
192 throw new PermissionDeniedException();
193 }
194
195 $this->readString('containerID', false, 'data');
196 $this->readInteger('objectID', false, 'data');
197 $this->readString('objectType', false, 'data');
198
199 $this->objectType = LikeHandler::getInstance()->getObjectType($this->parameters['data']['objectType']);
200 if ($this->objectType === null) {
201 throw new UserInputException('objectType');
202 }
203
204 $this->objectTypeProvider = $this->objectType->getProcessor();
205 $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
206 $this->likeableObject->setObjectType($this->objectType);
207 if (!$this->objectTypeProvider->checkPermissions($this->likeableObject)) {
208 throw new PermissionDeniedException();
209 }
210 }
211
212 /**
213 * @see \wcf\data\IGroupedUserListAction::validateGetGroupedUserList()
214 */
215 public function validateGetGroupedUserList() {
216 $this->validateObjectParameters();
217
218 $this->readInteger('pageNo');
219 }
220
221 /**
222 * @see \wcf\data\IGroupedUserListAction::getGroupedUserList()
223 */
224 public function getGroupedUserList() {
225 // fetch number of pages
226 $sql = "SELECT COUNT(*)
227 FROM wcf".WCF_N."_like
228 WHERE objectID = ?
229 AND objectTypeID = ?";
230 $statement = WCF::getDB()->prepareStatement($sql);
231 $statement->execute(array(
232 $this->parameters['data']['objectID'],
233 $this->objectType->objectTypeID
234 ));
235 $pageCount = ceil($statement->fetchColumn() / 20);
236
237 $sql = "SELECT userID, likeValue
238 FROM wcf".WCF_N."_like
239 WHERE objectID = ?
240 AND objectTypeID = ?
241 ORDER BY likeValue DESC, time DESC";
242 $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
243 $statement->execute(array(
244 $this->parameters['data']['objectID'],
245 $this->objectType->objectTypeID
246 ));
247 $data = array(
248 Like::LIKE => array(),
249 Like::DISLIKE => array()
250 );
251 while ($row = $statement->fetchArray()) {
252 $data[$row['likeValue']][] = $row['userID'];
253 }
254
255 $values = array();
256 if (!empty($data[Like::LIKE])) {
257 $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
258 $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
259 }
260 if (!empty($data[Like::DISLIKE])) {
261 $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
262 $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
263 }
264
265 // load user profiles
266 GroupedUserList::loadUsers();
267
268 WCF::getTPL()->assign(array(
269 'groupedUsers' => $values
270 ));
271
272 return array(
273 'containerID' => $this->parameters['data']['containerID'],
274 'pageCount' => $pageCount,
275 'template' => WCF::getTPL()->fetch('groupedUserList')
276 );
277 }
278
279 /**
280 * Validates parameters to load likes.
281 */
282 public function validateLoad() {
283 $this->readInteger('lastLikeTime', true);
284 $this->readInteger('userID');
285 $this->readInteger('likeValue');
286 $this->readString('likeType');
287 }
288
289 /**
290 * Loads a list of likes.
291 *
292 * @return array
293 */
294 public function load() {
295 $likeList = new ViewableLikeList();
296 if ($this->parameters['lastLikeTime']) {
297 $likeList->getConditionBuilder()->add("like_table.time < ?", array($this->parameters['lastLikeTime']));
298 }
299 if ($this->parameters['likeType'] == 'received') {
300 $likeList->getConditionBuilder()->add("like_table.objectUserID = ?", array($this->parameters['userID']));
301 }
302 else {
303 $likeList->getConditionBuilder()->add("like_table.userID = ?", array($this->parameters['userID']));
304 }
305 $likeList->getConditionBuilder()->add("like_table.likeValue = ?", array($this->parameters['likeValue']));
306 $likeList->readObjects();
307 if (!count($likeList)) {
308 return array();
309 }
310
311 // parse template
312 WCF::getTPL()->assign(array(
313 'likeList' => $likeList
314 ));
315
316 return array(
317 'lastLikeTime' => $likeList->getLastLikeTime(),
318 'template' => WCF::getTPL()->fetch('userProfileLikeItem')
319 );
320 }
321
322 /**
323 * Copies likes from one object id to another.
324 */
325 public function copy() {
326 $sourceObjectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.like.likeableObject', $this->parameters['sourceObjectType']);
327 $targetObjectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.like.likeableObject', $this->parameters['targetObjectType']);
328
329 //
330 // step 1) get data
331 //
332
333 // get like object
334 $sql = "SELECT *
335 FROM wcf".WCF_N."_like_object
336 WHERE objectTypeID = ?
337 AND objectID = ?";
338 $statement = WCF::getDB()->prepareStatement($sql);
339 $statement->execute(array(
340 $sourceObjectType->objectTypeID,
341 $this->parameters['sourceObjectID']
342 ));
343 $row = $statement->fetchArray();
344
345 // no (dis-)likes at all
346 if ($row === false) {
347 return;
348 }
349
350 unset($row['likeObjectID']);
351 $row['objectTypeID'] = $targetObjectType->objectTypeID;
352 $row['objectID'] = $this->parameters['targetObjectID'];
353 $newLikeObject = LikeObjectEditor::create($row);
354
355 //
356 // step 2) copy
357 //
358
359 $sql = "INSERT INTO wcf".WCF_N."_like
360 (objectID, objectTypeID, objectUserID, userID, time, likeValue)
361 SELECT ".$this->parameters['targetObjectID'].", ".$targetObjectType->objectTypeID.", objectUserID, userID, time, likeValue
362 FROM wcf".WCF_N."_like
363 WHERE objectTypeID = ?
364 AND objectID = ?";
365 $statement = WCF::getDB()->prepareStatement($sql);
366 $statement->execute(array(
367 $sourceObjectType->objectTypeID,
368 $this->parameters['sourceObjectID']
369 ));
370
371 //
372 // step 3) update owner
373 //
374
375 if ($newLikeObject->objectUserID) {
376 $sql = "SELECT COUNT(*) AS count
377 FROM wcf".WCF_N."_like
378 WHERE objectTypeID = ?
379 AND objectID = ?
380 AND likeValue = ?";
381 $statement = WCF::getDB()->prepareStatement($sql);
382 $statement->execute(array(
383 $targetObjectType->objectTypeID,
384 $this->parameters['targetObjectID'],
385 Like::LIKE
386 ));
387 $row = $statement->fetchArray();
388
389 if ($row['count']) {
390 // update received likes
391 $userEditor = new UserEditor(new User($newLikeObject->objectUserID));
392 $userEditor->updateCounters(array(
393 'likesReceived' => $row['count']
394 ));
395
396 // add activity points
397 UserActivityPointHandler::getInstance()->fireEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', array($newLikeObject->objectUserID => $row['count']));
398 }
399 }
400 }
401 }