Validate the `pageNo` in UserTrophyAction::validateGetGroupedUserTrophyList()
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / media / MediaAction.class.php
1 <?php
2 namespace wcf\data\media;
3 use wcf\data\category\CategoryNodeTree;
4 use wcf\data\AbstractDatabaseObjectAction;
5 use wcf\data\ISearchAction;
6 use wcf\data\IUploadAction;
7 use wcf\system\acl\simple\SimpleAclHandler;
8 use wcf\system\category\CategoryHandler;
9 use wcf\system\clipboard\ClipboardHandler;
10 use wcf\system\database\util\PreparedStatementConditionBuilder;
11 use wcf\system\exception\IllegalLinkException;
12 use wcf\system\exception\PermissionDeniedException;
13 use wcf\system\exception\UserInputException;
14 use wcf\system\language\I18nHandler;
15 use wcf\system\language\LanguageFactory;
16 use wcf\system\request\LinkHandler;
17 use wcf\system\upload\DefaultUploadFileSaveStrategy;
18 use wcf\system\upload\MediaReplaceUploadFileValidationStrategy;
19 use wcf\system\upload\MediaUploadFileValidationStrategy;
20 use wcf\system\upload\UploadFile;
21 use wcf\system\WCF;
22 use wcf\util\FileUtil;
23
24 /**
25 * Executes media file-related actions.
26 *
27 * @author Matthias Schmidt
28 * @copyright 2001-2019 WoltLab GmbH
29 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
30 * @package WoltLabSuite\Core\Data\Media
31 * @since 3.0
32 *
33 * @method Media create()
34 * @method MediaEditor[] getObjects()
35 * @method MediaEditor getSingleObject()
36 */
37 class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction, IUploadAction {
38 /**
39 * number of media files per media manager dialog page
40 */
41 const ITEMS_PER_MANAGER_DIALOG_PAGE = 50;
42
43 /**
44 * @inheritDoc
45 */
46 public function validateUpload() {
47 WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
48
49 $this->readBoolean('imagesOnly', true);
50 $this->readInteger('categoryID', true);
51
52 /** @noinspection PhpUndefinedMethodInspection */
53 $this->parameters['__files']->validateFiles(new MediaUploadFileValidationStrategy($this->parameters['imagesOnly']));
54
55 if ($this->parameters['categoryID']) {
56 $category = CategoryHandler::getInstance()->getCategory($this->parameters['categoryID']);
57 if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
58 throw new UserInputException('categoryID');
59 }
60 }
61 }
62
63 /**
64 * @inheritDoc
65 */
66 public function upload() {
67 $additionalData = ['username' => WCF::getUser()->username];
68 if ($this->parameters['categoryID']) {
69 $additionalData['categoryID'] = $this->parameters['categoryID'];
70 }
71
72 // save files
73 $saveStrategy = new DefaultUploadFileSaveStrategy(self::class, [
74 'generateThumbnails' => true,
75 'rotateImages' => true
76 ], $additionalData);
77
78 /** @noinspection PhpUndefinedMethodInspection */
79 $this->parameters['__files']->saveFiles($saveStrategy);
80
81 /** @var Media[] $mediaFiles */
82 $mediaFiles = $saveStrategy->getObjects();
83
84 $result = [
85 'errors' => [],
86 'media' => []
87 ];
88
89 if (!empty($mediaFiles)) {
90 $mediaIDs = $mediaToFileID = [];
91 foreach ($mediaFiles as $internalFileID => $media) {
92 $mediaIDs[] = $media->mediaID;
93 $mediaToFileID[$media->mediaID] = $internalFileID;
94 }
95
96 // fetch media objects from database
97 $mediaList = new ViewableMediaList();
98 $mediaList->setObjectIDs($mediaIDs);
99 $mediaList->readObjects();
100
101 foreach ($mediaList as $media) {
102 $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
103 }
104 }
105
106 /** @var UploadFile[] $files */
107 /** @noinspection PhpUndefinedMethodInspection */
108 $files = $this->parameters['__files']->getFiles();
109 foreach ($files as $file) {
110 if ($file->getValidationErrorType()) {
111 $result['errors'][$file->getInternalFileID()] = [
112 'filename' => $file->getFilename(),
113 'filesize' => $file->getFilesize(),
114 'errorType' => $file->getValidationErrorType()
115 ];
116 }
117 }
118
119 return $result;
120 }
121
122 /**
123 * Generates thumbnails.
124 */
125 public function generateThumbnails() {
126 if (empty($this->objects)) {
127 $this->readObjects();
128 }
129
130 $saveStrategy = new DefaultUploadFileSaveStrategy(self::class);
131
132 foreach ($this->getObjects() as $mediaEditor) {
133 if ($mediaEditor->getDecoratedObject()->isImage) {
134 $saveStrategy->generateThumbnails($mediaEditor->getDecoratedObject());
135 }
136 }
137 }
138
139 /**
140 * Returns the data of the media file to be returned by AJAX requests.
141 *
142 * @param Media|ViewableMedia $media media files whose data will be returned
143 * @return string[]
144 */
145 protected function getMediaData($media) {
146 return [
147 'altText' => $media instanceof ViewableMedia ? $media->altText : [],
148 'caption' => $media instanceof ViewableMedia ? $media->caption : [],
149 'captionEnableHtml' => $media->captionEnableHtml,
150 'categoryID' => $media->categoryID,
151 'elementTag' => $media instanceof ViewableMedia ? $media->getElementTag($this->parameters['elementTagSize'] ?? 144) : '',
152 'elementTag48' => $media instanceof ViewableMedia ? $media->getElementTag(48) : '',
153 'fileHash' => $media->fileHash,
154 'filename' => $media->filename,
155 'filesize' => $media->filesize,
156 'formattedFilesize' => FileUtil::formatFilesize($media->filesize),
157 'fileType' => $media->fileType,
158 'height' => $media->height,
159 'languageID' => $media->languageID,
160 'imageDimensions' => $media->isImage ? WCF::getLanguage()->getDynamicVariable('wcf.media.imageDimensions.value', [
161 'media' => $media,
162 ]) : '',
163 'isImage' => $media->isImage,
164 'isMultilingual' => $media->isMultilingual,
165 'largeThumbnailHeight' => $media->largeThumbnailHeight,
166 'largeThumbnailLink' => $media->largeThumbnailType ? $media->getThumbnailLink('large') : '',
167 'largeThumbnailType' => $media->largeThumbnailType,
168 'largeThumbnailWidth' => $media->largeThumbnailWidth,
169 'link' => $media->getLink(),
170 'mediaID' => $media->mediaID,
171 'mediumThumbnailHeight' => $media->mediumThumbnailHeight,
172 'mediumThumbnailLink' => $media->mediumThumbnailType ? $media->getThumbnailLink('medium') : '',
173 'mediumThumbnailType' => $media->mediumThumbnailType,
174 'mediumThumbnailWidth' => $media->mediumThumbnailWidth,
175 'smallThumbnailHeight' => $media->smallThumbnailHeight,
176 'smallThumbnailLink' => $media->smallThumbnailType ? $media->getThumbnailLink('small') : '',
177 'smallThumbnailTag' => $media->smallThumbnailType ? $media->getThumbnailTag('small') : '',
178 'smallThumbnailType' => $media->smallThumbnailType,
179 'smallThumbnailWidth' => $media->smallThumbnailWidth,
180 'tinyThumbnailHeight' => $media->tinyThumbnailHeight,
181 'tinyThumbnailLink' => $media->tinyThumbnailType ? $media->getThumbnailLink('tiny') : '',
182 'tinyThumbnailType' => $media->tinyThumbnailType,
183 'tinyThumbnailWidth' => $media->tinyThumbnailWidth,
184 'title' => $media instanceof ViewableMedia ? $media->title : [],
185 'uploadTime' => $media->uploadTime,
186 'userID' => $media->userID,
187 'userLink' => $media->userID ? LinkHandler::getInstance()->getLink('User', [
188 'id' => $media->userID,
189 'title' => $media->username
190 ]) : '',
191 'userLinkElement' => $media instanceof ViewableMedia ? WCF::getTPL()->fetchString(
192 WCF::getTPL()->getCompiler()->compileString('userLink', '{user object=$userProfile}')['template'],
193 ['userProfile' => $media->getUserProfile()]
194 ) : '',
195 'username' => $media->username,
196 'width' => $media->width
197 ];
198 }
199
200 /**
201 * Validates the 'getManagementDialog' action.
202 */
203 public function validateGetManagementDialog() {
204 if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
205 throw new PermissionDeniedException();
206 }
207
208 $this->readBoolean('imagesOnly', true);
209
210 $this->readString('mode');
211 if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
212 throw new UserInputException('mode');
213 }
214 }
215
216 /**
217 * Returns the dialog to manage media.
218 *
219 * @return string[]
220 */
221 public function getManagementDialog() {
222 $mediaList = new ViewableMediaList();
223 if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
224 $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
225 }
226 if ($this->parameters['imagesOnly']) {
227 $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
228 }
229 $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
230 $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
231 $mediaList->readObjects();
232
233 $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
234 $categoryList->setMaxDepth(0);
235
236 return [
237 'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')),
238 'media' => $this->getI18nMediaData($mediaList),
239 'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
240 'template' => WCF::getTPL()->fetch('mediaManager', 'wcf', [
241 'categoryList' => $categoryList,
242 'mediaList' => $mediaList,
243 'mode' => $this->parameters['mode']
244 ])
245 ];
246 }
247
248 /**
249 * Returns the complete i18n data of the media files in the given list.
250 *
251 * @param MediaList $mediaList
252 * @return array
253 */
254 protected function getI18nMediaData(MediaList $mediaList) {
255 if (!count($mediaList)) return [];
256
257 $conditionBuilder = new PreparedStatementConditionBuilder();
258 $conditionBuilder->add('mediaID IN (?)', [$mediaList->getObjectIDs()]);
259
260 $sql = "SELECT *
261 FROM wcf".WCF_N."_media_content
262 ".$conditionBuilder;
263 $statement = WCF::getDB()->prepareStatement($sql);
264 $statement->execute($conditionBuilder->getParameters());
265
266 $mediaData = [];
267 while ($row = $statement->fetchArray()) {
268 if (!isset($mediaData[$row['mediaID']])) {
269 $mediaData[$row['mediaID']] = [
270 'altText' => [],
271 'caption' => [],
272 'title' => []
273 ];
274 }
275
276 $mediaData[$row['mediaID']]['altText'][intval($row['languageID'])] = $row['altText'];
277 $mediaData[$row['mediaID']]['caption'][intval($row['languageID'])] = $row['caption'];
278 $mediaData[$row['mediaID']]['title'][intval($row['languageID'])] = $row['title'];
279 }
280
281 $i18nMediaData = [];
282 foreach ($mediaList as $media) {
283 if (!isset($mediaData[$media->mediaID])) {
284 $mediaData[$media->mediaID] = [];
285 }
286
287 $i18nMediaData[$media->mediaID] = array_merge($this->getMediaData($media), $mediaData[$media->mediaID]);
288 }
289
290 return $i18nMediaData;
291 }
292
293 /**
294 * Validates the 'getEditorDialog' action.
295 */
296 public function validateGetEditorDialog() {
297 WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
298
299 $this->getSingleObject();
300
301 if (!$this->getSingleObject()->canManage()) {
302 throw new PermissionDeniedException();
303 }
304 }
305
306 /**
307 * Returns the template for the media editor.
308 *
309 * @return string[]
310 */
311 public function getEditorDialog() {
312 $mediaList = new ViewableMediaList();
313 $mediaList->setObjectIDs([$this->getSingleObject()->mediaID]);
314 $mediaList->readObjects();
315 $media = $mediaList->search($this->getSingleObject()->mediaID);
316
317 I18nHandler::getInstance()->register('title_' . $media->mediaID);
318 I18nHandler::getInstance()->register('caption_' . $media->mediaID);
319 I18nHandler::getInstance()->register('altText_' . $media->mediaID);
320 I18nHandler::getInstance()->assignVariables();
321
322 $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
323 $categoryList->setMaxDepth(0);
324
325 return [
326 'availableLanguageCount' => count(LanguageFactory::getInstance()->getLanguages()),
327 'categoryIDs' => array_keys(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category')),
328 'mediaData' => $this->getI18nMediaData($mediaList)[$this->getSingleObject()->mediaID],
329 'template' => WCF::getTPL()->fetch('mediaEditor', 'wcf', [
330 '__aclSimplePrefix' => 'mediaEditor_' . $media->mediaID . '_',
331 '__aclInputName' => 'mediaEditor_' . $media->mediaID . '_aclValues',
332 '__languageChooserPrefix' => 'mediaEditor_' . $media->mediaID . '_',
333 'aclValues' => SimpleAclHandler::getInstance()->getValues('com.woltlab.wcf.media', $media->mediaID),
334 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
335 'categoryList' => $categoryList,
336 'languageID' => WCF::getUser()->languageID,
337 'languages' => LanguageFactory::getInstance()->getLanguages(),
338 'media' => $media
339 ])
340 ];
341 }
342
343 /**
344 * @inheritDoc
345 */
346 public function validateUpdate() {
347 WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
348
349 if (empty($this->objects)) {
350 $this->readObjects();
351
352 if (empty($this->objects)) {
353 throw new UserInputException('objectIDs');
354 }
355 }
356
357 if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
358 foreach ($this->getObjects() as $media) {
359 if ($media->userID != WCF::getUser()->userID) {
360 throw new PermissionDeniedException();
361 }
362 }
363 }
364
365 $this->readInteger('categoryID', true, 'data');
366 $this->readInteger('languageID', true, 'data');
367 $this->readBoolean('isMultilingual', true, 'data');
368 $this->readInteger('captionEnableHtml', true, 'data');
369
370 if (count(LanguageFactory::getInstance()->getLanguages()) > 1) {
371 // languageID: convert zero to null
372 if (!$this->parameters['data']['languageID']) $this->parameters['data']['languageID'] = null;
373
374 // isMultilingual: convert boolean to integer
375 $this->parameters['data']['isMultilingual'] = intval($this->parameters['data']['isMultilingual']);
376 }
377 else {
378 $this->parameters['data']['isMultilingual'] = 0;
379 $this->parameters['data']['languageID'] = WCF::getLanguage()->languageID;
380 }
381
382 // if data is not multilingual, a language id has to be given
383 if (!$this->parameters['data']['isMultilingual'] && !$this->parameters['data']['languageID']) {
384 throw new UserInputException('languageID');
385 }
386
387 // check language id
388 if ($this->parameters['data']['languageID'] && !LanguageFactory::getInstance()->getLanguage($this->parameters['data']['languageID'])) {
389 throw new UserInputException('languageID');
390 }
391
392 // check category id
393 if ($this->parameters['data']['categoryID']) {
394 $category = CategoryHandler::getInstance()->getCategory($this->parameters['data']['categoryID']);
395 if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
396 throw new UserInputException('categoryID');
397 }
398 }
399 }
400
401 /**
402 * @inheritDoc
403 */
404 public function update() {
405 if (isset($this->parameters['data']['categoryID']) && $this->parameters['data']['categoryID'] === 0) {
406 $this->parameters['data']['categoryID'] = null;
407 }
408
409 if (empty($this->objects)) {
410 $this->readObjects();
411 }
412
413 parent::update();
414
415 if (count($this->objects) == 1 && (isset($this->parameters['title']) || isset($this->parameters['caption']) || isset($this->parameters['altText']))) {
416 $media = reset($this->objects);
417
418 $isMultilingual = $media->isMultilingual;
419 if (isset($this->parameters['data']['isMultilingual'])) {
420 $isMultilingual = $this->parameters['data']['isMultilingual'];
421 }
422
423 $sql = "DELETE FROM wcf".WCF_N."_media_content
424 WHERE mediaID = ?";
425 $statement = WCF::getDB()->prepareStatement($sql);
426 $statement->execute([$media->mediaID]);
427
428 $sql = "INSERT INTO wcf".WCF_N."_media_content
429 (mediaID, languageID, title, caption, altText)
430 VALUES (?, ?, ?, ?, ?)";
431 $statement = WCF::getDB()->prepareStatement($sql);
432
433 if (!$isMultilingual) {
434 $languageID = $media->languageID;
435 if (isset($this->parameters['data']['languageID'])) {
436 $languageID = $this->parameters['data']['languageID'];
437 }
438 $statement->execute([
439 $media->mediaID,
440 $languageID,
441 isset($this->parameters['title'][$languageID]) ? mb_substr($this->parameters['title'][$languageID], 0, 255) : '',
442 isset($this->parameters['caption'][$languageID]) ? $this->parameters['caption'][$languageID] : '',
443 isset($this->parameters['altText'][$languageID]) ? mb_substr($this->parameters['altText'][$languageID], 0, 255) : ''
444 ]);
445 }
446 else {
447 $languages = LanguageFactory::getInstance()->getLanguages();
448 foreach ($languages as $language) {
449 $title = $caption = $altText = '';
450 foreach (['title', 'caption', 'altText'] as $type) {
451 if (isset($this->parameters[$type])) {
452 if (is_array($this->parameters[$type])) {
453 if (isset($this->parameters[$type][$language->languageID])) {
454 /** @noinspection PhpVariableVariableInspection */
455 $$type = $this->parameters[$type][$language->languageID];
456 }
457 }
458 else {
459 /** @noinspection PhpVariableVariableInspection */
460 $$type = $this->parameters[$type];
461 }
462 }
463 }
464
465 $statement->execute([
466 $media->mediaID,
467 $language->languageID,
468 mb_substr($title, 0, 255),
469 $caption,
470 mb_substr($altText, 0, 255)
471 ]);
472 }
473 }
474
475 if (!empty($this->parameters['aclValues'])) {
476 SimpleAclHandler::getInstance()->setValues('com.woltlab.wcf.media', $media->mediaID, $this->parameters['aclValues']);
477 }
478 }
479 }
480
481 /**
482 * @inheritDoc
483 */
484 public function validateGetSearchResultList() {
485 if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
486 throw new PermissionDeniedException();
487 }
488
489 $this->readString('searchString', true);
490 $this->readInteger('categoryID', true);
491
492 $this->readBoolean('imagesOnly', true);
493
494 $this->readString('mode');
495 if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
496 throw new UserInputException('mode');
497 }
498
499 $this->readInteger('pageNo', true);
500 if (!$this->parameters['pageNo']) {
501 $this->parameters['pageNo'] = 1;
502 }
503 if ($this->parameters['pageNo'] < 1) {
504 throw new UserInputException('pageNo');
505 }
506 }
507
508 /**
509 * @inheritDoc
510 */
511 public function getSearchResultList() {
512 $mediaList = new MediaList();
513 $mediaList->addSearchConditions($this->parameters['searchString']);
514 if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
515 $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
516 }
517 if ($this->parameters['imagesOnly']) {
518 $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
519 }
520 if ($this->parameters['categoryID']) {
521 $mediaList->getConditionBuilder()->add('media.categoryID = ?', [$this->parameters['categoryID']]);
522 }
523 $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
524 $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
525 $mediaList->sqlOffset = ($this->parameters['pageNo'] - 1) * static::ITEMS_PER_MANAGER_DIALOG_PAGE;
526 $mediaList->readObjectIDs();
527
528 if (empty($mediaList->getObjectIDs())) {
529 // check if page is requested that might have existed but does not exist anymore due to deleted
530 // media files
531 if ($this->parameters['pageNo'] > 1 && $this->parameters['searchString'] === '' && !$this->parameters['categoryID']) {
532 // request media dialog page with highest page number
533 $parameters = $this->parameters;
534 $parameters['pageNo'] = ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE);
535
536 return (new MediaAction($this->objects, 'getSearchResultList', $parameters))->executeAction()['returnValues'];
537 }
538
539 return [
540 'template' => WCF::getLanguage()->getDynamicVariable('wcf.media.search.noResults')
541 ];
542 }
543
544 $viewableMediaList = new ViewableMediaList();
545 $viewableMediaList->setObjectIDs($mediaList->getObjectIDs());
546 $viewableMediaList->readObjects();
547
548 return [
549 'media' => $this->getI18nMediaData($viewableMediaList),
550 'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
551 'pageNo' => $this->parameters['pageNo'],
552 'template' => WCF::getTPL()->fetch('mediaListItems', 'wcf', [
553 'mediaList' => $viewableMediaList,
554 'mode' => $this->parameters['mode']
555 ])
556 ];
557 }
558
559 /**
560 * @inheritDoc
561 */
562 public function validateDelete() {
563 WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
564
565 if (empty($this->objects)) {
566 $this->readObjects();
567
568 if (empty($this->objects)) {
569 throw new UserInputException('objectIDs');
570 }
571 }
572
573 if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
574 foreach ($this->getObjects() as $media) {
575 if ($media->userID != WCF::getUser()->userID) {
576 throw new PermissionDeniedException();
577 }
578 }
579 }
580 }
581
582 /**
583 * @inheritDoc
584 */
585 public function delete() {
586 if (empty($this->objects)) {
587 $this->readObjects();
588 }
589
590 foreach ($this->getObjects() as $mediaEditor) {
591 $mediaEditor->deleteFiles();
592 }
593
594 parent::delete();
595
596 $this->unmarkItems();
597 }
598
599 /**
600 * Unmarks the media files with the given ids. If no media ids are given,
601 * all media files currently loaded are unmarked.
602 *
603 * @param integer[] $mediaIDs ids of the media files to be unmarked
604 */
605 protected function unmarkItems(array $mediaIDs = []) {
606 if (empty($mediaIDs)) {
607 foreach ($this->getObjects() as $media) {
608 $mediaIDs[] = $media->mediaID;
609 }
610 }
611
612 if (!empty($mediaIDs)) {
613 ClipboardHandler::getInstance()->unmark($mediaIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media'));
614 }
615 }
616
617 /**
618 * Validates the `getSetCategoryDialog` action.
619 *
620 * @throws PermissionDeniedException if user is not allowed to set category of media files
621 * @throws IllegalLinkException if no media file categories exist
622 */
623 public function validateGetSetCategoryDialog() {
624 if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia')) {
625 throw new PermissionDeniedException();
626 }
627
628 if (empty(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category'))) {
629 throw new IllegalLinkException();
630 }
631 }
632
633 /**
634 * Returns the dialog to set the category of multiple media files.
635 *
636 * @return string[]
637 */
638 public function getSetCategoryDialog() {
639 $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
640 $categoryList->setMaxDepth(0);
641
642 return [
643 'template' => WCF::getTPL()->fetch('__mediaSetCategoryDialog', 'wcf', [
644 'categoryList' => $categoryList
645 ])
646 ];
647 }
648
649 /**
650 * Validates the `setCategory` action.
651 *
652 * @throws PermissionDeniedException if user is not allowed to edit a requested media file
653 * @throws UserInputException if no object ids are given
654 */
655 public function validateSetCategory() {
656 $this->validateGetSetCategoryDialog();
657
658 if (empty($this->objects)) {
659 $this->readObjects();
660
661 if (empty($this->objects)) {
662 throw new UserInputException('objectIDs');
663 }
664 }
665
666 if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
667 foreach ($this->getObjects() as $media) {
668 if ($media->userID != WCF::getUser()->userID) {
669 throw new PermissionDeniedException();
670 }
671 }
672 }
673
674 $this->readInteger('categoryID', true);
675 }
676
677 /**
678 * Sets the category of multiple media files.
679 */
680 public function setCategory() {
681 $conditionBuilder = new PreparedStatementConditionBuilder();
682 $conditionBuilder->add('mediaID IN (?)', [$this->objectIDs]);
683
684 $sql = "UPDATE wcf" . WCF_N . "_media
685 SET categoryID = ?
686 " . $conditionBuilder;
687 $statement = WCF::getDB()->prepareStatement($sql);
688 $statement->execute(array_merge(
689 [$this->parameters['categoryID'] ?: null],
690 $conditionBuilder->getParameters()
691 ));
692
693 $this->unmarkItems();
694 }
695
696 /**
697 * Validates the `replaceFile` action.
698 *
699 * @since 5.3
700 */
701 public function validateReplaceFile() {
702 WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
703
704 $this->getSingleObject();
705
706 /** @noinspection PhpUndefinedMethodInspection */
707 $this->parameters['__files']->validateFiles(
708 new MediaReplaceUploadFileValidationStrategy($this->getSingleObject()->getDecoratedObject())
709 );
710 }
711
712 /**
713 * Replaces the actual file of a media file.
714 *
715 * @return array
716 * @since 5.3
717 */
718 public function replaceFile() {
719 $saveStrategy = new DefaultUploadFileSaveStrategy(static::class, [
720 'action' => 'update',
721 'generateThumbnails' => true,
722 'object' => $this->getSingleObject()->getDecoratedObject(),
723 'rotateImages' => true,
724 ], [
725 'fileUpdateTime' => TIME_NOW,
726 'userID' => $this->getSingleObject()->userID,
727 'username' => $this->getSingleObject()->username,
728 ]);
729
730 /** @noinspection PhpUndefinedMethodInspection */
731 $this->parameters['__files']->saveFiles($saveStrategy);
732
733 /** @var Media[] $mediaFiles */
734 $mediaFiles = $saveStrategy->getObjects();
735
736 $result = [
737 'errors' => [],
738 'media' => []
739 ];
740
741 if (!empty($mediaFiles)) {
742 $mediaIDs = $mediaToFileID = [];
743 foreach ($mediaFiles as $internalFileID => $media) {
744 $mediaIDs[] = $media->mediaID;
745 $mediaToFileID[$media->mediaID] = $internalFileID;
746 }
747
748 // fetch media objects from database
749 $mediaList = new ViewableMediaList();
750 $mediaList->setObjectIDs($mediaIDs);
751 $mediaList->readObjects();
752
753 foreach ($mediaList as $media) {
754 $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
755 }
756 }
757
758 /** @var UploadFile[] $files */
759 /** @noinspection PhpUndefinedMethodInspection */
760 $files = $this->parameters['__files']->getFiles();
761 foreach ($files as $file) {
762 if ($file->getValidationErrorType()) {
763 $result['errors'][$file->getInternalFileID()] = [
764 'filename' => $file->getFilename(),
765 'filesize' => $file->getFilesize(),
766 'errorType' => $file->getValidationErrorType()
767 ];
768 }
769 }
770
771 $outdatedMediaFile = $this->getSingleObject();
772 $updatedMediaFile = new Media($this->getSingleObject()->mediaID);
773
774 // Delete *old* files using the non-updated local media editor object if the new file is
775 // stored in a different location.
776 if (empty($result['errors']) && $updatedMediaFile->getLocation() !== $outdatedMediaFile->getLocation()) {
777 $outdatedMediaFile->deleteFiles();
778 }
779
780 return $result;
781 }
782 }