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