From: Matthias Schmidt Date: Wed, 29 Jun 2016 16:55:14 +0000 (+0200) Subject: Update media implementation X-Git-Tag: 3.0.0_Beta_1~1308 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=306b6bdd994267c3251d0fa2daf832609e1963bb;p=GitHub%2FWoltLab%2FWCF.git Update media implementation --- diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabMedia.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabMedia.js index be1e56f92d..7a359f064c 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabMedia.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabMedia.js @@ -7,8 +7,10 @@ $.Redactor.prototype.WoltLabMedia = function() { $(button).addClass('jsMediaEditorButton'); require(['WoltLab/WCF/Media/Manager/Editor'], function(MediaManagerEditor) { - new MediaManagerEditor(); - }); + new MediaManagerEditor({ + editor: this + }); + }.bind(this)); }, }; }; diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js index c1c122ba22..77a3dc1873 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js @@ -82,19 +82,13 @@ define( */ _clipboardAction: function(actionData) { // only consider events if the action has been executed - if (actionData.responseData === null) { - return; - } - - switch (actionData.data.actionName) { - case 'com.woltlab.wcf.media.delete': - var mediaIds = actionData.responseData.objectIDs; - for (var i = 0, length = mediaIds.length; i < length; i++) { - this.removeMedia(~~mediaIds[i], true); - } - - UiNotification.show(); - break; + if (actionData.data.actionName === 'com.woltlab.wcf.media.delete' && actionData.responseData === null) { + var mediaIds = actionData.responseData.objectIDs; + for (var i = 0, length = mediaIds.length; i < length; i++) { + this.removeMedia(~~mediaIds[i], true); + } + + UiNotification.show(); } }, diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js index 16de1dac62..a35467d28d 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js @@ -1,12 +1,12 @@ /** - * Provides the media manager dialog for selecting media for input elements. + * Provides the media manager dialog for selecting media for Redactor editors. * * @author Matthias Schmidt * @copyright 2001-2016 WoltLab GmbH * @license GNU Lesser General Public License * @module WoltLab/WCF/Media/Manager/Editor */ -define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Manager/Base'], function(Core, DomTraverse, Language, UiDialog, MediaManagerBase) { +define(['Core', 'Dictionary', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Manager/Base'], function(Core, Dictionary, DomTraverse, Language, UiDialog, MediaManagerBase) { "use strict"; /** @@ -20,8 +20,8 @@ define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Mana for (var i = 0, length = this._buttons.length; i < length; i++) { this._buttons[i].addEventListener(WCF_CLICK_EVENT, this._click.bind(this)); } - - this._activeButton = null; + this._mediaToInsert = new Dictionary(); + this._mediaToInsertByClipboard = false; }; Core.inherit(MediaManagerEditor, MediaManagerBase, { /** @@ -44,6 +44,68 @@ define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Mana } }, + /** + * Builds the dialog to setup inserting media files. + */ + _buildInsertDialog: function() { + var thumbnailOptions = ''; + + var sizes = ['small', 'medium', 'large']; + var size, option; + lengthLoop: for (var i = 0, length = sizes.length; i < length; i++) { + size = sizes[i]; + + // make sure that all thumbnails support the thumbnail size + for (var j = 0, mediaLength = this._mediaToInsert.length; j < mediaLength; j++) { + if (!this._mediaToInsert[i][size + 'ThumbnailType']) { + continue lengthLoop; + } + } + + thumbnailOptions += ''; + } + thumbnailOptions += ''; + + var dialog = '
' + + (this._mediaToInsert.size > 1 ? '
' + + '
' + Language.get('wcf.media.insert.type') + '
' + + '
' + + '' + + '
' + + '
' : '') + + '
' + + '
' + Language.get('wcf.media.insert.imageSize') + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
' + + '' + + '
'; + + UiDialog.open({ + _dialogSetup: (function() { + return { + id: this._getInsertDialogId(), + options: { + onClose: this._editorClose.bind(this), + onSetup: function(content) { + elByClass('buttonPrimary', content)[0].addEventListener(WCF_CLICK_EVENT, this._insertMedia.bind(this)); + }.bind(this), + title: Language.get('wcf.media.insert') + }, + source: dialog + } + }).bind(this) + }); + }, + /** * @see WoltLab/WCF/Media/Manager/Base#_click */ @@ -53,92 +115,112 @@ define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Mana MediaManagerEditor._super.prototype._click.call(this, event); }, - _insertMedia: function() { - // TODO + /** + * @see WoltLab/WCF/Media/Manager/Base#_clipboardAction + */ + _clipboardAction: function(actionData) { + MediaManagerEditor._super.prototype._clipboardAction.call(this, actionData); + + if (actionData.data.actionName === 'com.woltlab.wcf.media.insert') { + this.insertMedia(actionData.data.parameters.objectIDs, true); + } + }, + + /** + * Returns the id of the insert dialog based on the media files to be inserted. + * + * @return {string} insert dialog id + */ + _getInsertDialogId: function() { + var dialogId = 'mediaInsert'; + + this._mediaToInsert.forEach(function(media, mediaId) { + dialogId += '-' + mediaId; + }); + + return dialogId; + }, + + /** + * Inserts media files into redactor. + * + * @param {Event?} event + */ + _insertMedia: function(event) { + var insertType = 'separate'; + var thumbnailSize; + + // update insert options with selected values if method is called by clicking on 'insert' button + // in dialog + if (event) { + UiDialog.close(this._getInsertDialogId()); + + var dialogContent = event.currentTarget.closest('.dialogContent'); + + if (this._mediaToInsert.size > 1) { + insertType = elBySel('select[name=insertType]', dialogContent).value; + } + thumbnailSize = elBySel('select[name=thumbnailSize]', dialogContent).value; + } + + // TODO: media to be inserted is located in dictionary this._mediaToInsert + // TODO: insertType = 'separate' or 'gallery' (last case only possible if multiple media files are inserted and all of them are images) + // TODO: thumbnailSize = 'small', 'media', 'large' or 'original' + // TODO: redactor is accessible by this._options.editor + throw new Error("TODO: implement me") + + if (this._mediaToInsertByClipboard) { + // TODO: unmark in clipboard + } + + this._mediaToInsert = new Dictionary(); + this._mediaToInsertByClipboard = false; + + // todo: close manager dialog? }, + /** + * Handles clicking on the insert button. + * + * @param {Event} event insert button click event + */ _openInsertDialog: function(event) { - var media = this._mediaData.get(~~elData(event.currentTarget, 'object-id')); + this.insertMedia([~~elData(event.currentTarget, 'object-id')]); + }, + + /** + * Prepares insertion of the media files with the given ids. + * + * @param {array} mediaIds ids of the media files to be inserted + * @param {boolean?} insertedByClipboard is true if the media files are inserted by clipboard + */ + insertMedia: function(mediaIds, insertedByClipboard) { + this._mediaToInsert = new Dictionary(); + this._mediaToInsertByClipboard = insertedByClipboard || false; + + // open the insert dialog if all media files are images + var imagesOnly = true, media; + for (var i = 0, length = mediaIds.length; i < length; i++) { + media = this._mediaData.get(mediaIds[i]); + this._mediaToInsert.set(media.mediaID, media); + + if (!media.isImage) { + imagesOnly = false; + } + } - // check if media file is image and has at least small thumbnail - // to show insertion options - if (media.isImage && media.smallThumbnailType) { + if (imagesOnly) { UiDialog.close(this); - var dialogId = 'mediaInsert' + media.mediaID; + var dialogId = this._getInsertDialogId(); if (UiDialog.getDialog(dialogId)) { UiDialog.openStatic(dialogId); } else { - var dialog = elCreate('div'); - - var fieldset = elCreate('fieldset'); - dialog.appendChild(fieldset); - - var dl = elCreate('dl'); - fieldset.appendChild(dl); - - var dt = elCreate('dt'); - dt.textContent = Language.get('wcf.media.insert.imageSize'); - dl.appendChild(dt); - - var dd = elCreate('dd'); - dl.appendChild(dd); - - var select = elCreate('select'); - dd.appendChild(select); - - var sizes = ['small', 'medium', 'large']; - var size, option; - for (var i = 0, length = sizes.length; i < length; i++) { - size = sizes[i]; - - if (media[size + 'ThumbnailType']) { - option = elCreate('option'); - elAttr(option, 'value', size); - option.textContent = Language.get('wcf.media.insert.imageSize.' + size, { - height: media[size + 'ThumbnailHeight'], - width: media[size + 'ThumbnailWidth'] - }); - select.appendChild(option); - } - } - - option = elCreate('option'); - elAttr(option, 'value', 'original'); - option.textContent = Language.get('wcf.media.insert.imageSize.original', { - height: media.height, - width: media.width - }); - select.appendChild(option); - - var formSubmit = elCreate('div'); - formSubmit.className = 'formSubmit'; - dialog.appendChild(formSubmit); - - var submitButton = elCreate('button'); - submitButton.className = 'buttonPrimary'; - submitButton.textContent = Language.get('wcf.global.button.insert'); - elData(submitButton, 'object-id', media.mediaID); - submitButton.addEventListener(WCF_CLICK_EVENT, this._insertMedia.bind(this)); - formSubmit.appendChild(submitButton); - - UiDialog.open({ - _dialogSetup: (function() { - return { - id: dialogId, - options: { - onClose: this._editorClose.bind(this), - title: Language.get('wcf.media.insert') - }, - source: dialog.outerHTML - } - }).bind(this) - }); + this._buildInsertDialog(); } } else { - // insert media - // TODO + this._insertMedia(); } }, @@ -156,7 +238,7 @@ define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Mana MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement); // add media insertion icon - var smallButtons = elBySel('> nav.buttonGroupNavigation > ul.smallButtons', mediaElement); + var smallButtons = elBySel('nav.buttonGroupNavigation > ul.smallButtons', mediaElement); var listItem = elCreate('li'); smallButtons.appendChild(listItem); diff --git a/wcfsetup/install/files/lib/data/article/content/ViewableArticleContent.class.php b/wcfsetup/install/files/lib/data/article/content/ViewableArticleContent.class.php index 0bb734358b..78da2ee575 100644 --- a/wcfsetup/install/files/lib/data/article/content/ViewableArticleContent.class.php +++ b/wcfsetup/install/files/lib/data/article/content/ViewableArticleContent.class.php @@ -80,6 +80,8 @@ class ViewableArticleContent extends DatabaseObjectDecorator { if ($this->image === null) { if ($this->imageID) { $this->image = ViewableMedia::getMedia($this->imageID); + + $this->image->setLinkParameters(['articleID' => $this->articleID]); } } @@ -89,9 +91,10 @@ class ViewableArticleContent extends DatabaseObjectDecorator { /** * Sets the article's image. * - * @param ViewableMedia $image + * @param ViewableMedia $image */ public function setImage(ViewableMedia $image) { $this->image = $image; + $this->image->setLinkParameters(['articleID' => $this->articleID]); } } diff --git a/wcfsetup/install/files/lib/data/box/Box.class.php b/wcfsetup/install/files/lib/data/box/Box.class.php index ba8d6d05f8..ce4c1e5089 100644 --- a/wcfsetup/install/files/lib/data/box/Box.class.php +++ b/wcfsetup/install/files/lib/data/box/Box.class.php @@ -341,6 +341,8 @@ class Box extends DatabaseObject { $this->image = ViewableMedia::getMedia($boxContent[0]['imageID']); } + $this->image->setLinkParameters(['boxID' => $this->boxID]); + return $this->image; } diff --git a/wcfsetup/install/files/lib/data/media/Media.class.php b/wcfsetup/install/files/lib/data/media/Media.class.php index 0e6e933b2f..dd10d0d1ed 100644 --- a/wcfsetup/install/files/lib/data/media/Media.class.php +++ b/wcfsetup/install/files/lib/data/media/Media.class.php @@ -3,7 +3,6 @@ namespace wcf\data\media; use wcf\data\DatabaseObject; use wcf\data\ILinkableObject; use wcf\data\IThumbnailFile; -use wcf\system\exception\SystemException; use wcf\system\request\IRouteController; use wcf\system\request\LinkHandler; use wcf\system\WCF; @@ -16,7 +15,7 @@ use wcf\system\WCF; * @license GNU Lesser General Public License * @package WoltLabSuite\Core\Data\Media * @since 3.0 - * + * * @property-read integer $mediaID * @property-read string $filename * @property-read integer $filesize @@ -52,7 +51,13 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, * i18n media data grouped by language id for all language * @var string[][] */ - protected $i18nData = null; + protected $i18nData; + + /** + * parameters used to build the link to the media file + * @var array + */ + protected $linkParameters = []; /** * @inheritDoc @@ -94,11 +99,20 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, /** * @inheritDoc */ - public function getLink() { - return LinkHandler::getInstance()->getLink('Media', [ + public function getLink($articleID = null, $boxID = null, $messageObjectType = null, $messageID = null) { + return LinkHandler::getInstance()->getLink('Media', array_merge($this->linkParameters, [ 'forceFrontend' => true, 'object' => $this - ]); + ])); + } + + /** + * Sets additional parameters used to build the link to the media file. + * + * @param array $parameters + */ + public function setLinkParameters(array $parameters) { + $this->linkParameters = $parameters; } /** @@ -113,18 +127,18 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, */ public function getThumbnailLink($size) { if (!isset(self::$thumbnailSizes[$size])) { - throw new SystemException("Unknown thumbnail size '".$size."'"); + throw new \InvalidArgumentException("Unknown thumbnail size '".$size."'"); } if (!$this->{$size.'ThumbnailType'}) { return $this->getLink(); } - return LinkHandler::getInstance()->getLink('Media', [ + return LinkHandler::getInstance()->getLink('Media', array_merge($this->linkParameters, [ 'forceFrontend' => true, 'object' => $this, 'thumbnail' => $size - ]); + ])); } /** @@ -132,11 +146,11 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, * * @param string $size * @return integer - * @throws SystemException + * @throws \InvalidArgumentException */ public function getThumbnailWidth($size) { if (!isset(self::$thumbnailSizes[$size])) { - throw new SystemException("Unknown thumbnail size '".$size."'"); + throw new \InvalidArgumentException("Unknown thumbnail size '".$size."'"); } if ($this->{$size.'ThumbnailType'}) { @@ -151,11 +165,11 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, * * @param string $size * @return integer - * @throws SystemException + * @throws \InvalidArgumentException */ public function getThumbnailHeight($size) { if (!isset(self::$thumbnailSizes[$size])) { - throw new SystemException("Unknown thumbnail size '".$size."'"); + throw new \InvalidArgumentException("Unknown thumbnail size '".$size."'"); } if ($this->{$size.'ThumbnailType'}) { @@ -170,7 +184,7 @@ class Media extends DatabaseObject implements ILinkableObject, IRouteController, */ public function getThumbnailLocation($size) { if (!isset(self::$thumbnailSizes[$size])) { - throw new SystemException("Unknown thumbnail size '".$size."'"); + throw new \InvalidArgumentException("Unknown thumbnail size '".$size."'"); } return self::getStorage().substr($this->fileHash, 0, 2).'/'.$this->mediaID.'-'.$size.'-'.$this->fileHash; diff --git a/wcfsetup/install/files/lib/data/media/ViewableMedia.class.php b/wcfsetup/install/files/lib/data/media/ViewableMedia.class.php index af180a8132..50bfb194ad 100644 --- a/wcfsetup/install/files/lib/data/media/ViewableMedia.class.php +++ b/wcfsetup/install/files/lib/data/media/ViewableMedia.class.php @@ -36,7 +36,7 @@ class ViewableMedia extends DatabaseObjectDecorator { return ''.StringUtil::encodeHTML($this->altText).'title ? 'title="'.StringUtil::encodeHTML($this->title).'" ' : '').'/>'; } - return 'getLink().'">'.StringUtil::encodeHTML($this->getTitle()).''; } /** diff --git a/wcfsetup/install/files/lib/page/MediaPage.class.php b/wcfsetup/install/files/lib/page/MediaPage.class.php index 824581344e..514573882c 100644 --- a/wcfsetup/install/files/lib/page/MediaPage.class.php +++ b/wcfsetup/install/files/lib/page/MediaPage.class.php @@ -1,8 +1,15 @@ getPermission('admin.content.cms.canManageMedia')) { + if ($this->articleID) { + $this->article = new Article($this->articleID); + + if (!$this->article->articleID || !$this->article->canRead()) { + throw new PermissionDeniedException(); + } + } + else if ($this->boxID) { + $this->box = BoxHandler::getInstance()->getBox($this->boxID); + + if ($this->box === null || !$this->box->isAccessible()) { + throw new PermissionDeniedException(); + } + } + else if ($this->messageID) { + MessageEmbeddedObjectManager::getInstance()->loadObjects($this->messageObjectType, [$this->messageID]); + $this->message = MessageEmbeddedObjectManager::getInstance()->getObject($this->messageObjectType, $this->messageID); + if ($this->message === null || !($this->message instanceof IMessage) || !$this->message->isVisible()) { + throw new PermissionDeniedException(); + } + } + else { + $parameters = ['canAccess' => false]; + + EventHandler::getInstance()->fireAction($this, 'checkMediaAccess', $parameters); + + if (empty($parameters['canAccess'])) { + throw new PermissionDeniedException(); + } + } + } } - // TODO: remove the following line once method is implemented - // @codingStandardsIgnoreEnd /** * @inheritDoc @@ -131,17 +207,21 @@ class MediaPage extends AbstractPage { throw new IllegalLinkException(); } - $parameters = [ - 'object' => $this->media - ]; - if ($this->thumbnail && $this->media->{$this->thumbnail.'ThumbnailType'}) { - $parameters['thumbnail'] = $this->thumbnail; - } - else { + if ($this->thumbnail && !$this->media->{$this->thumbnail.'ThumbnailType'}) { $this->thumbnail = ''; } - $this->canonicalURL = LinkHandler::getInstance()->getLink('Media', $parameters); + // read context parameters + if (isset($_REQUEST['articleID'])) { + $this->articleID = intval($_REQUEST['articleID']); + } + else if (isset($_REQUEST['boxID'])) { + $this->boxID = intval($_REQUEST['boxID']); + } + else if (isset($_REQUEST['messageObjectType']) && isset($_REQUEST['messageID'])) { + $this->messageObjectType = StringUtil::trim($_REQUEST['messageObjectType']); + $this->messageID = intval($_REQUEST['messageID']); + } } /** diff --git a/wcfsetup/install/files/lib/system/box/BoxHandler.class.php b/wcfsetup/install/files/lib/system/box/BoxHandler.class.php index 71d03185f7..d198196fff 100644 --- a/wcfsetup/install/files/lib/system/box/BoxHandler.class.php +++ b/wcfsetup/install/files/lib/system/box/BoxHandler.class.php @@ -19,8 +19,8 @@ use wcf\system\WCF; */ class BoxHandler extends SingletonFactory { /** - * boxes grouped by position - * @var Box[][] + * boxes with box id as key + * @var Box[] */ protected $boxes = []; @@ -30,6 +30,12 @@ class BoxHandler extends SingletonFactory { */ protected $boxesByIdentifier = []; + /** + * boxes grouped by position + * @var Box[][] + */ + protected $boxesByPosition = []; + /** * @inheritDoc */ @@ -46,12 +52,15 @@ class BoxHandler extends SingletonFactory { else $boxList->getConditionBuilder()->add('box.visibleEverywhere = ?', [1]); $boxList->sqlOrderBy = 'showOrder'; $boxList->readObjects(); + + $this->boxes = $boxList->getObjects(); foreach ($boxList as $box) { if ($box->isAccessible()) { - if (!isset($this->boxes[$box->position])) $this->boxes[$box->position] = []; - $this->boxes[$box->position][] = $box; + if (!isset($this->boxesByPosition[$box->position])) $this->boxesByPosition[$box->position] = []; + $this->boxesByPosition[$box->position][] = $box; + $this->boxesByIdentifier[$box->identifier] = $box; - } + } } } @@ -96,6 +105,20 @@ class BoxHandler extends SingletonFactory { ]))->executeAction(); } + /** + * Returns the box with the given id or null. + * + * @param integer $boxID + * @return Box|null + */ + public function getBox($boxID) { + if (isset($this->boxes[$boxID])) { + return $this->boxes[$boxID]; + } + + return null; + } + /** * Returns boxes for the given position. * @@ -103,8 +126,8 @@ class BoxHandler extends SingletonFactory { * @return Box[] */ public function getBoxes($position) { - if (isset($this->boxes[$position])) { - return $this->boxes[$position]; + if (isset($this->boxesByPosition[$position])) { + return $this->boxesByPosition[$position]; } return []; diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 979d3f49ed..9cbf443c7c 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -2459,10 +2459,10 @@ Errors are: - - - - + + + +