Add localization support for embedded content
authorAlexander Ebert <ebert@woltlab.com>
Thu, 8 Jun 2017 16:33:10 +0000 (18:33 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 8 Jun 2017 16:33:10 +0000 (18:33 +0200)
See #2297

wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php
wcfsetup/install/files/lib/data/article/content/ViewableArticleContentList.class.php
wcfsetup/install/files/lib/data/media/ViewableMedia.class.php
wcfsetup/install/files/lib/data/media/ViewableMediaList.class.php
wcfsetup/install/files/lib/system/bbcode/WoltLabSuiteMediaBBCode.class.php
wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php
wcfsetup/install/files/lib/system/message/embedded/object/MediaMessageEmbeddedObjectHandler.class.php
wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php

index 3ac06205ce52ee7fc36e84acf6852144d757c2b7..f8f8da2611ea689b50fabf1433749b40d22e9d2c 100644 (file)
@@ -4,6 +4,7 @@ use wcf\data\article\Article;
 use wcf\data\language\Language;
 use wcf\data\DatabaseObject;
 use wcf\data\ILinkableObject;
+use wcf\system\bbcode\HtmlBBCodeParser;
 use wcf\system\html\input\HtmlInputProcessor;
 use wcf\system\html\output\AmpHtmlOutputProcessor;
 use wcf\system\html\output\HtmlOutputProcessor;
@@ -93,7 +94,7 @@ class ArticleContent extends DatabaseObject implements ILinkableObject, IRouteCo
         */
        public function getFormattedContent() {
                $processor = new HtmlOutputProcessor();
-               $processor->process($this->content, 'com.woltlab.wcf.article.content', $this->articleContentID);
+               $processor->process($this->content, 'com.woltlab.wcf.article.content', $this->articleContentID, false, $this->languageID);
                
                return $processor->getHtml();
        }
index 70ad0a081eb972ac068f1d12e4d06e938215aaf8..73e175011a05fc65a6cc30b20e7a94d1023b44c6 100644 (file)
@@ -61,7 +61,10 @@ class ViewableArticleContentList extends ArticleContentList {
                
                // load embedded objects
                if (!empty($embeddedObjectPostIDs)) {
-                       MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wcf.article.content', $embeddedObjectPostIDs);
+                       $contentLanguageID = null;
+                       if (count($embeddedObjectPostIDs) === 1) $contentLanguageID = reset($this->objects)->languageID;
+                       
+                       MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wcf.article.content', $embeddedObjectPostIDs, $contentLanguageID);
                }
        }
 }
index 3d9b9f19a6b051f7379e03ee9eb7dbb62eb527ff..ef02f98b3aa05f6b39ad3012e5e582be5fcadbc4 100644 (file)
@@ -22,6 +22,18 @@ use wcf\util\StringUtil;
  * @property-read      string|null     $altText        alternative text of the media file in the active user's language or `null` if object has not been fetched via `ViewableMediaList`
  */
 class ViewableMedia extends DatabaseObjectDecorator {
+       /**
+        * force localized content by language id
+        * @var integer
+        */
+       protected $forceLanguageID;
+       
+       /**
+        * localized content per language id
+        * @var string[][]
+        */
+       protected $localizedContent = [];
+       
        /**
         * user profile of the user who uploaded the media file
         * @var UserProfile
@@ -33,6 +45,53 @@ class ViewableMedia extends DatabaseObjectDecorator {
         */
        protected static $baseClass = Media::class;
        
+       /**
+        * Registers localized content by language id.
+        * 
+        * @param       integer         $languageID
+        * @param       string[]        $content
+        */
+       public function setLocalizedContent($languageID, array $content) {
+               $this->localizedContent[$languageID] = $content;
+       }
+       
+       /**
+        * Returns an instance of this class with localized versions.
+        * 
+        * @param       integer         $languageID
+        * @return      ViewableMedia
+        */
+       public function getLocalizedVersion($languageID) {
+               if (isset($this->localizedContent[$languageID])) {
+                       $localized = clone $this;
+                       $localized->forceLanguageID($languageID);
+                       
+                       return $localized;
+               }
+               
+               return $this;
+       }
+       
+       /**
+        * Forces the localized values by language id.
+        * 
+        * @param       integer         $languageID
+        */
+       protected function forceLanguageID($languageID) {
+               $this->forceLanguageID = $languageID;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function __get($name) {
+               if ($this->forceLanguageID !== null && isset($this->localizedContent[$this->forceLanguageID][$name])) {
+                       return $this->localizedContent[$this->forceLanguageID][$name];
+               }
+               
+               return $this->object->__get($name);
+       }
+       
        /**
         * @inheritDoc
         */
index b625897b57a4f7be82d5e2404fe9257665d4b30e..e8a7be6996266ccebc58385cf92e5f1b6d8f06a0 100644 (file)
@@ -29,7 +29,7 @@ class ViewableMediaList extends MediaList {
                parent::__construct();
                
                // fetch content data
-               $this->sqlSelects .= "media_content.*";
+               $this->sqlSelects .= "media_content.*, COALESCE(media.languageID, ".WCF::getLanguage()->languageID.") AS localizedLanguageID";
                $this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_media_content media_content ON (media_content.mediaID = media.mediaID AND media_content.languageID = COALESCE(media.languageID, ".WCF::getLanguage()->languageID."))";
        }
 }
index 8b083823b730436216934782c95627421fd700f4..1b80b7c89ad12404759c813d7e0143bc0abbc93b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 namespace wcf\system\bbcode;
-use wcf\data\media\Media;
+use wcf\data\media\ViewableMedia;
 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\WCF;
 use wcf\util\StringUtil;
@@ -24,9 +24,8 @@ class WoltLabSuiteMediaBBCode extends AbstractBBCode {
                        return '';
                }
                
-               /** @var Media $media */
+               /** @var ViewableMedia $media */
                $media = MessageEmbeddedObjectManager::getInstance()->getObject('com.woltlab.wcf.media', $mediaID);
-               
                if ($media !== null && $media->isAccessible()) {
                        if ($media->isImage) {
                                $thumbnailSize = (!empty($openingTag['attributes'][1])) ? $openingTag['attributes'][1] : 'original';
@@ -34,7 +33,7 @@ class WoltLabSuiteMediaBBCode extends AbstractBBCode {
                                
                                WCF::getTPL()->assign([
                                        'float' => $float,
-                                       'media' => $media,
+                                       'media' => $media->getLocalizedVersion(MessageEmbeddedObjectManager::getInstance()->getActiveMessageLanguageID()),
                                        'thumbnailSize' => $thumbnailSize
                                ]);
                                
index d23b2a128f1d5ea2fa6de282d81d90088f9fd823..55d1ca9f5aa04a3ab3d4aa5ca00d5e93f54aea22 100644 (file)
@@ -20,6 +20,12 @@ class HtmlOutputProcessor extends AbstractHtmlProcessor {
         */
        protected $htmlOutputNodeProcessor;
        
+       /**
+        * content language id
+        * @var integer
+        */
+       protected $languageID;
+       
        /**
         * desired output type
         * @var string
@@ -32,9 +38,11 @@ class HtmlOutputProcessor extends AbstractHtmlProcessor {
         * @param       string          $html                           html string
         * @param       string          $objectType                     object type identifier
         * @param       integer         $objectID                       object id
-        * @param       boolean         $doKeywordHighlighting                                   
+        * @param       boolean         $doKeywordHighlighting          enable keyword highlighting
+        * @param       integer         $languageID                     content language id
         */
-       public function process($html, $objectType, $objectID, $doKeywordHighlighting = true) {
+       public function process($html, $objectType, $objectID, $doKeywordHighlighting = true, $languageID = null) {
+               $this->languageID = $languageID;
                $this->setContext($objectType, $objectID);
                
                $this->getHtmlOutputNodeProcessor()->setOutputType($this->outputType);
@@ -71,7 +79,7 @@ class HtmlOutputProcessor extends AbstractHtmlProcessor {
        public function setContext($objectType, $objectID) {
                parent::setContext($objectType, $objectID);
                
-               MessageEmbeddedObjectManager::getInstance()->setActiveMessage($objectType, $objectID);
+               MessageEmbeddedObjectManager::getInstance()->setActiveMessage($objectType, $objectID, $this->languageID);
        }
        
        /**
index 3af29d4aacc1ddbda9eaf9337980b99d63f83ce6..ee591d94ddc14103618d86cc9a8c0ffa803d39f5 100644 (file)
@@ -3,7 +3,9 @@ namespace wcf\system\message\embedded\object;
 use wcf\data\media\Media;
 use wcf\data\media\MediaList;
 use wcf\system\cache\runtime\ViewableMediaRuntimeCache;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\html\input\HtmlInputProcessor;
+use wcf\system\WCF;
 use wcf\util\ArrayUtil;
 
 /**
@@ -39,7 +41,33 @@ class MediaMessageEmbeddedObjectHandler extends AbstractSimpleMessageEmbeddedObj
         * @inheritDoc
         */
        public function loadObjects(array $objectIDs) {
-               return ViewableMediaRuntimeCache::getInstance()->getObjects($objectIDs);
+               $viewableMedia = ViewableMediaRuntimeCache::getInstance()->getObjects($objectIDs);
+               $contentLanguageID = MessageEmbeddedObjectManager::getInstance()->getContentLanguageID();
+               if ($contentLanguageID !== null) {
+                       $mediaIDs = [];
+                       foreach ($viewableMedia as $media) {
+                               if ($media->localizedLanguageID != $contentLanguageID) {
+                                       $mediaIDs[] = $media->getDecoratedObject()->mediaID;
+                               }
+                       }
+                       
+                       if (!empty($mediaIDs)) {
+                               $conditions = new PreparedStatementConditionBuilder();
+                               $conditions->add("mediaID IN (?)", [$mediaIDs]);
+                               $conditions->add("languageID = ?", [$contentLanguageID]);
+                               
+                               $sql = "SELECT  *
+                                       FROM    wcf".WCF_N."_media_content
+                                       ".$conditions;
+                               $statement = WCF::getDB()->prepareStatement($sql);
+                               $statement->execute($conditions->getParameters());
+                               while ($row = $statement->fetchArray()) {
+                                       $viewableMedia[$row['mediaID']]->setLocalizedContent($row['languageID'], $row);
+                               }
+                       }
+               }
+               
+               return $viewableMedia;
        }
        
        /**
index 8860f55e22bc8975b76953b9cb207687def1f0c1..7aa3f150ca0cc828a9fc353ed8d782ac4d0c19ef 100644 (file)
@@ -40,12 +40,24 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
         */
        protected $activeMessageID;
        
+       /**
+        * language id of the active message
+        * @var integer
+        */
+       protected $activeMessageLanguageID;
+       
        /**
         * list of embedded object handlers
         * @var array
         */
        protected $embeddedObjectHandlers;
        
+       /**
+        * content language id
+        * @var integer
+        */
+       protected $contentLanguageID;
+       
        /**
         * Registers the embedded objects found in given message.
         * 
@@ -149,9 +161,10 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
         * 
         * @param       string          $messageObjectType
         * @param       integer[]       $messageIDs
+        * @param       integer         $contentLanguageID
         * @throws      InvalidObjectTypeException
         */
-       public function loadObjects($messageObjectType, array $messageIDs) {
+       public function loadObjects($messageObjectType, array $messageIDs, $contentLanguageID = null) {
                $messageObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType);
                if ($messageObjectTypeID === null) {
                        throw new InvalidObjectTypeException($messageObjectType, 'com.woltlab.wcf.message');
@@ -182,6 +195,8 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
                        $this->messageEmbeddedObjects[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']][] = $row['embeddedObjectID'];
                }
                
+               $this->contentLanguageID = $contentLanguageID;
+               
                // load objects
                foreach ($embeddedObjects as $embeddedObjectTypeID => $objectIDs) {
                        if (!isset($this->embeddedObjects[$embeddedObjectTypeID])) $this->embeddedObjects[$embeddedObjectTypeID] = [];
@@ -189,6 +204,17 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
                                $this->embeddedObjects[$embeddedObjectTypeID][$objectID] = $object;
                        }
                }
+               
+               $this->contentLanguageID = null;
+       }
+       
+       /**
+        * Returns the content language id or null.
+        * 
+        * @return      integer
+        */
+       public function getContentLanguageID() {
+               return $this->contentLanguageID;
        }
        
        /**
@@ -197,9 +223,19 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
         * @param       string          $messageObjectType
         * @param       integer         $messageID
         */
-       public function setActiveMessage($messageObjectType, $messageID) {
+       public function setActiveMessage($messageObjectType, $messageID, $languageID = null) {
                $this->activeMessageObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType);
                $this->activeMessageID = $messageID;
+               $this->activeMessageLanguageID = $languageID;
+       }
+       
+       /**
+        * Returns the language id of the active message.
+        * 
+        * @return      integer
+        */
+       public function getActiveMessageLanguageID() {
+               return $this->activeMessageLanguageID;
        }
        
        /**