Commit | Line | Data |
---|---|---|
e3667539 | 1 | <?php |
a9229942 | 2 | |
e3667539 | 3 | namespace wcf\system\worker; |
a9229942 | 4 | |
557115b5 | 5 | use wcf\data\reaction\type\ReactionTypeCache; |
2772d4eb MW |
6 | use wcf\data\user\avatar\UserAvatar; |
7 | use wcf\data\user\avatar\UserAvatarEditor; | |
8 | use wcf\data\user\avatar\UserAvatarList; | |
25f41e0c | 9 | use wcf\data\user\cover\photo\IWebpUserCoverPhoto; |
8a413435 | 10 | use wcf\data\user\User; |
e3667539 | 11 | use wcf\data\user\UserEditor; |
157054c9 | 12 | use wcf\data\user\UserList; |
e3667539 | 13 | use wcf\data\user\UserProfileAction; |
7e058783 | 14 | use wcf\system\bbcode\BBCodeHandler; |
25f41e0c | 15 | use wcf\system\cache\runtime\UserProfileRuntimeCache; |
e3667539 | 16 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
c5c9e424 | 17 | use wcf\system\exception\SystemException; |
b8c48208 | 18 | use wcf\system\html\input\HtmlInputProcessor; |
2772d4eb | 19 | use wcf\system\image\ImageHandler; |
e3667539 MW |
20 | use wcf\system\WCF; |
21 | ||
22 | /** | |
23 | * Worker implementation for updating users. | |
a9229942 TD |
24 | * |
25 | * @author Marcel Werk | |
26 | * @copyright 2001-2019 WoltLab GmbH | |
27 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
28 | * @package WoltLabSuite\Core\System\Worker | |
29 | * | |
30 | * @method UserList getObjectList() | |
e3667539 | 31 | */ |
a9229942 TD |
32 | class UserRebuildDataWorker extends AbstractRebuildDataWorker |
33 | { | |
34 | /** | |
35 | * @inheritDoc | |
36 | */ | |
37 | protected $objectListClassName = UserList::class; | |
38 | ||
39 | /** | |
40 | * @inheritDoc | |
41 | */ | |
42 | protected $limit = 50; | |
43 | ||
44 | /** | |
45 | * @inheritDoc | |
46 | */ | |
47 | protected function initObjectList() | |
48 | { | |
49 | parent::initObjectList(); | |
50 | ||
51 | $this->objectList->sqlSelects = 'user_option_value.userOption' . User::getUserOptionID('aboutMe') . ' AS aboutMe'; | |
d3bd0a85 MS |
52 | $this->objectList->sqlJoins = " |
53 | LEFT JOIN wcf" . WCF_N . "_user_option_value user_option_value | |
54 | ON user_option_value.userID = user_table.userID"; | |
a9229942 TD |
55 | $this->objectList->sqlOrderBy = 'user_table.userID'; |
56 | } | |
57 | ||
58 | /** | |
59 | * @inheritDoc | |
60 | */ | |
61 | public function execute() | |
62 | { | |
63 | parent::execute(); | |
64 | ||
65 | $users = $userIDs = []; | |
66 | foreach ($this->getObjectList() as $user) { | |
67 | $users[] = new UserEditor($user); | |
68 | $userIDs[] = $user->userID; | |
69 | } | |
70 | ||
71 | // update user ranks | |
72 | if (!empty($users)) { | |
73 | $action = new UserProfileAction($users, 'updateUserOnlineMarking'); | |
74 | $action->executeAction(); | |
75 | } | |
76 | ||
77 | if (!empty($userIDs)) { | |
78 | // update article counter | |
79 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
80 | $conditionBuilder->add('user_table.userID IN (?)', [$userIDs]); | |
81 | $sql = "UPDATE wcf" . WCF_N . "_user user_table | |
82 | SET articles = ( | |
83 | SELECT COUNT(*) | |
84 | FROM wcf" . WCF_N . "_article | |
85 | WHERE userID = user_table.userID | |
86 | ) | |
87 | " . $conditionBuilder; | |
88 | $statement = WCF::getDB()->prepareStatement($sql); | |
89 | $statement->execute($conditionBuilder->getParameters()); | |
90 | ||
91 | // update like counter | |
92 | if (MODULE_LIKE) { | |
93 | $sql = "UPDATE wcf" . WCF_N . "_user user_table | |
94 | SET"; | |
95 | ||
96 | $reactionTypeIDs = \array_keys(ReactionTypeCache::getInstance()->getReactionTypes()); | |
97 | if (!empty($reactionTypeIDs)) { | |
98 | $sql .= " | |
99 | likesReceived = ( | |
100 | SELECT COUNT(*) | |
101 | FROM wcf" . WCF_N . "_like | |
102 | WHERE objectUserID = user_table.userID | |
103 | AND reactionTypeID IN (" . \implode(',', $reactionTypeIDs) . ") | |
104 | )"; | |
105 | } else { | |
106 | $sql .= " likesReceived = 0"; | |
107 | } | |
108 | ||
109 | $sql .= " " . $conditionBuilder; | |
110 | $statement = WCF::getDB()->prepareStatement($sql); | |
111 | $statement->execute($conditionBuilder->getParameters()); | |
112 | } | |
113 | ||
114 | // update trophy points | |
115 | if (MODULE_TROPHY) { | |
116 | $sql = "UPDATE wcf" . WCF_N . "_user user_table | |
117 | SET trophyPoints = ( | |
118 | SELECT COUNT(*) | |
119 | FROM wcf" . WCF_N . "_user_trophy user_trophy | |
120 | LEFT JOIN wcf" . WCF_N . "_trophy trophy | |
c240c98a | 121 | ON user_trophy.trophyID = trophy.trophyID |
a9229942 | 122 | LEFT JOIN wcf" . WCF_N . "_category trophy_category |
c240c98a | 123 | ON trophy.categoryID = trophy_category.categoryID |
a9229942 TD |
124 | WHERE user_trophy.userID = user_table.userID |
125 | AND trophy.isDisabled = 0 | |
126 | AND trophy_category.isDisabled = 0 | |
127 | ) | |
128 | " . $conditionBuilder; | |
129 | $statement = WCF::getDB()->prepareStatement($sql); | |
130 | $statement->execute($conditionBuilder->getParameters()); | |
131 | } | |
132 | ||
133 | // update signatures and about me | |
134 | $sql = "UPDATE wcf" . WCF_N . "_user_option_value | |
135 | SET userOption" . User::getUserOptionID('aboutMe') . " = ? | |
136 | WHERE userID = ?"; | |
137 | $statement = WCF::getDB()->prepareStatement($sql); | |
138 | ||
139 | // retrieve permissions | |
140 | $userIDs = []; | |
141 | foreach ($users as $user) { | |
142 | $userIDs[] = $user->userID; | |
143 | } | |
144 | $userPermissions = $this->getBulkUserPermissions( | |
145 | $userIDs, | |
146 | ['user.message.disallowedBBCodes', 'user.signature.disallowedBBCodes'] | |
147 | ); | |
148 | ||
149 | $htmlInputProcessor = new HtmlInputProcessor(); | |
150 | WCF::getDB()->beginTransaction(); | |
151 | /** @var UserEditor $user */ | |
152 | foreach ($users as $user) { | |
153 | BBCodeHandler::getInstance()->setDisallowedBBCodes(\explode( | |
154 | ',', | |
155 | $this->getBulkUserPermissionValue( | |
156 | $userPermissions, | |
157 | $user->userID, | |
158 | 'user.signature.disallowedBBCodes' | |
159 | ) | |
160 | )); | |
161 | ||
162 | if (!$user->signatureEnableHtml) { | |
163 | $htmlInputProcessor->process( | |
164 | $user->signature, | |
165 | 'com.woltlab.wcf.user.signature', | |
166 | $user->userID, | |
167 | true | |
168 | ); | |
169 | ||
170 | $user->update([ | |
171 | 'signature' => $htmlInputProcessor->getHtml(), | |
172 | 'signatureEnableHtml' => 1, | |
173 | ]); | |
174 | } else { | |
175 | $htmlInputProcessor->reprocess($user->signature, 'com.woltlab.wcf.user.signature', $user->userID); | |
176 | $user->update(['signature' => $htmlInputProcessor->getHtml()]); | |
177 | } | |
178 | ||
179 | if ($user->aboutMe) { | |
180 | BBCodeHandler::getInstance()->setDisallowedBBCodes(\explode( | |
181 | ',', | |
182 | $this->getBulkUserPermissionValue( | |
183 | $userPermissions, | |
184 | $user->userID, | |
185 | 'user.message.disallowedBBCodes' | |
186 | ) | |
187 | )); | |
188 | ||
189 | if (!$user->signatureEnableHtml) { | |
190 | $htmlInputProcessor->process( | |
191 | $user->aboutMe, | |
192 | 'com.woltlab.wcf.user.aboutMe', | |
193 | $user->userID, | |
194 | true | |
195 | ); | |
196 | } else { | |
197 | $htmlInputProcessor->reprocess($user->aboutMe, 'com.woltlab.wcf.user.aboutMe', $user->userID); | |
198 | } | |
199 | ||
200 | $html = $htmlInputProcessor->getHtml(); | |
201 | // MySQL's TEXT type allows for 65,535 bytes, hence we need to count | |
202 | // the bytes rather than the actual amount of characters | |
203 | if (\strlen($html) > 65535) { | |
204 | // content does not fit the available space, and any | |
205 | // attempts to truncate it will yield awkward results | |
206 | $html = ''; | |
207 | } | |
208 | ||
209 | $statement->execute([$html, $user->userID]); | |
210 | } | |
211 | } | |
212 | WCF::getDB()->commitTransaction(); | |
213 | ||
214 | // update old/imported avatars | |
215 | $avatarList = new UserAvatarList(); | |
216 | $avatarList->getConditionBuilder()->add('user_avatar.userID IN (?)', [$userIDs]); | |
217 | $avatarList->getConditionBuilder()->add( | |
71a3289c AE |
218 | '( |
219 | (user_avatar.width <> ? OR user_avatar.height <> ?) | |
220 | OR (user_avatar.hasWebP = ? AND user_avatar.avatarExtension <> ?) | |
221 | )', | |
222 | [ | |
223 | UserAvatar::AVATAR_SIZE, | |
224 | UserAvatar::AVATAR_SIZE, | |
4084160a | 225 | 0, |
71a3289c AE |
226 | "gif", |
227 | ] | |
a9229942 TD |
228 | ); |
229 | $avatarList->readObjects(); | |
230 | foreach ($avatarList as $avatar) { | |
231 | $editor = new UserAvatarEditor($avatar); | |
232 | if (!\file_exists($avatar->getLocation()) || @\getimagesize($avatar->getLocation()) === false) { | |
233 | // delete avatars that are missing or broken | |
234 | $editor->delete(); | |
235 | continue; | |
236 | } | |
237 | ||
238 | $width = $avatar->width; | |
239 | $height = $avatar->height; | |
240 | if ($width != $height) { | |
241 | // make avatar quadratic | |
242 | $width = $height = \min($width, $height, UserAvatar::AVATAR_SIZE); | |
243 | $adapter = ImageHandler::getInstance()->getAdapter(); | |
244 | ||
245 | try { | |
246 | $adapter->loadFile($avatar->getLocation()); | |
247 | } catch (SystemException $e) { | |
248 | // broken image | |
249 | $editor->delete(); | |
250 | continue; | |
251 | } | |
252 | ||
253 | $thumbnail = $adapter->createThumbnail($width, $height, false); | |
254 | $adapter->writeImage($thumbnail, $avatar->getLocation()); | |
255 | // Clear thumbnail as soon as possible to free up the memory. | |
256 | $thumbnail = null; | |
257 | } | |
258 | ||
259 | if ($width != UserAvatar::AVATAR_SIZE || $height != UserAvatar::AVATAR_SIZE) { | |
260 | // resize avatar | |
261 | $adapter = ImageHandler::getInstance()->getAdapter(); | |
262 | ||
263 | try { | |
264 | $adapter->loadFile($avatar->getLocation()); | |
265 | } catch (SystemException $e) { | |
266 | // broken image | |
267 | $editor->delete(); | |
268 | continue; | |
269 | } | |
270 | ||
271 | $adapter->resize(0, 0, $width, $height, UserAvatar::AVATAR_SIZE, UserAvatar::AVATAR_SIZE); | |
272 | $adapter->writeImage($adapter->getImage(), $avatar->getLocation()); | |
273 | $width = $height = UserAvatar::AVATAR_SIZE; | |
274 | } | |
275 | ||
f68296a6 | 276 | $editor->deleteLegacyThumbnails(); |
71a3289c AE |
277 | $editor->createAvatarVariant(); |
278 | ||
a9229942 TD |
279 | $editor->update([ |
280 | 'width' => $width, | |
281 | 'height' => $height, | |
282 | ]); | |
283 | } | |
25f41e0c AE |
284 | |
285 | // Create WebP variants of existing cover photos. | |
286 | $userProfiles = UserProfileRuntimeCache::getInstance()->getObjects($userIDs); | |
287 | foreach ($userProfiles as $userProfile) { | |
288 | if ($userProfile->coverPhotoHash) { | |
289 | $coverPhoto = $userProfile->getCoverPhoto(true); | |
290 | if ($coverPhoto instanceof IWebpUserCoverPhoto) { | |
291 | $coverPhoto->createWebpVariant(); | |
292 | } | |
293 | } | |
294 | } | |
a9229942 TD |
295 | } |
296 | } | |
e3667539 | 297 | } |