From 0a2b09a2d0d914887eedf43d02328add19ec4216 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 1 Jul 2014 19:55:16 +0200 Subject: [PATCH] Added message embedded object system --- com.woltlab.wcf/objectType.xml | 13 + com.woltlab.wcf/objectTypeDefinition.xml | 9 + com.woltlab.wcf/templates/quoteBBCodeTag.tpl | 32 ++- .../GroupedAttachmentList.class.php | 5 + .../bbcode/MessagePreviewAction.class.php | 40 +-- .../AbstractAttachmentObjectType.class.php | 29 ++ .../IAttachmentObjectType.class.php | 7 + .../system/bbcode/AttachmentBBCode.class.php | 28 +- .../lib/system/bbcode/QuoteBBCode.class.php | 15 +- ...ractMessageEmbeddedObjectHandler.class.php | 63 +++++ ...mentMessageEmbeddedObjectHandler.class.php | 63 +++++ .../IMessageEmbeddedObjectHandler.class.php | 31 ++ .../MessageEmbeddedObjectManager.class.php | 265 ++++++++++++++++++ ...uoteMessageEmbeddedObjectHandler.class.php | 38 +++ wcfsetup/install/files/style/bbcode.less | 146 +++++++--- wcfsetup/setup/db/install.sql | 13 + 16 files changed, 704 insertions(+), 93 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/message/embedded/object/AbstractMessageEmbeddedObjectHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/message/embedded/object/AttachmentMessageEmbeddedObjectHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/message/embedded/object/IMessageEmbeddedObjectHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php create mode 100644 wcfsetup/install/files/lib/system/message/embedded/object/QuoteMessageEmbeddedObjectHandler.class.php diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index 5609043636..ef5f327891 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -888,5 +888,18 @@ + + + + com.woltlab.wcf.quote + com.woltlab.wcf.message.embeddedObject + + + + com.woltlab.wcf.attachment + com.woltlab.wcf.message.embeddedObject + + + diff --git a/com.woltlab.wcf/objectTypeDefinition.xml b/com.woltlab.wcf/objectTypeDefinition.xml index daebce74ab..f1614aa7b3 100644 --- a/com.woltlab.wcf/objectTypeDefinition.xml +++ b/com.woltlab.wcf/objectTypeDefinition.xml @@ -176,5 +176,14 @@ com.woltlab.wcf.captcha + + + com.woltlab.wcf.message.embeddedObject + + + + + com.woltlab.wcf.message + diff --git a/com.woltlab.wcf/templates/quoteBBCodeTag.tpl b/com.woltlab.wcf/templates/quoteBBCodeTag.tpl index d1001b3604..b655fccf42 100644 --- a/com.woltlab.wcf/templates/quoteBBCodeTag.tpl +++ b/com.woltlab.wcf/templates/quoteBBCodeTag.tpl @@ -1,17 +1,23 @@ -
- {if $quoteAuthor} -
-

- {if $quoteLink} - {lang}wcf.bbcode.quote.title{/lang} - {else} - {lang}wcf.bbcode.quote.title{/lang} - {/if} -

-
+
+ {if $quoteAuthorObject} + {/if} -
- {@$content} +
+ {if $quoteAuthor} +
+

+ {if $quoteLink} + {lang}wcf.bbcode.quote.title{/lang} + {else} + {lang}wcf.bbcode.quote.title{/lang} + {/if} +

+
+ {/if} + +
+ {@$content} +
\ No newline at end of file diff --git a/wcfsetup/install/files/lib/data/attachment/GroupedAttachmentList.class.php b/wcfsetup/install/files/lib/data/attachment/GroupedAttachmentList.class.php index 386faf6147..a53237d21e 100644 --- a/wcfsetup/install/files/lib/data/attachment/GroupedAttachmentList.class.php +++ b/wcfsetup/install/files/lib/data/attachment/GroupedAttachmentList.class.php @@ -45,6 +45,11 @@ class GroupedAttachmentList extends AttachmentList { $this->objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.attachment.objectType', $objectType); $this->getConditionBuilder()->add('attachment.objectTypeID = ?', array($this->objectType->objectTypeID)); + + $this->getConditionBuilder()->add('(SELECT embeddedObjectID FROM wcf'.WCF_N.'_message_embedded_object WHERE messageObjectTypeID = ? AND messageID = attachment.objectID AND embeddedObjectTypeID = ? AND embeddedObjectID = attachment.attachmentID) IS NULL', array( + ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $objectType), + ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message.embeddedObject', 'com.woltlab.wcf.attachment') + )); } /** diff --git a/wcfsetup/install/files/lib/data/bbcode/MessagePreviewAction.class.php b/wcfsetup/install/files/lib/data/bbcode/MessagePreviewAction.class.php index 3170dd997d..dd7e6ce5f0 100644 --- a/wcfsetup/install/files/lib/data/bbcode/MessagePreviewAction.class.php +++ b/wcfsetup/install/files/lib/data/bbcode/MessagePreviewAction.class.php @@ -1,11 +1,9 @@ parameters['attachmentObjectType'])) { - $attachmentList = new GroupedAttachmentList($this->parameters['attachmentObjectType']); - if (!empty($this->parameters['attachmentObjectID'])) { - $attachmentList->getConditionBuilder()->add('attachment.objectID = ?', array($this->parameters['attachmentObjectID'])); - AttachmentBBCode::setObjectID($this->parameters['attachmentObjectID']); - - $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.attachment.objectType', $this->parameters['attachmentObjectType']); - $processor = $objectType->getProcessor(); - if (!$processor->canDownload($this->parameters['attachmentObjectID']) && !$processor->canViewPreview($this->parameters['attachmentObjectID'])) { - if (WCF::getUser()->userID) { - $attachmentList->getConditionBuilder()->add('attachment.userID = ?', array(WCF::getUser()->userID)); - } - else { - $attachmentList->getConditionBuilder()->add('attachment.userID IS NULL'); - } - } - } - else { - $attachmentList->getConditionBuilder()->add('attachment.tmpHash = ?', array($this->parameters['tmpHash'])); - - if (WCF::getUser()->userID) { - $attachmentList->getConditionBuilder()->add('attachment.userID = ?', array(WCF::getUser()->userID)); - } - else { - $attachmentList->getConditionBuilder()->add('attachment.userID IS NULL'); - } - } - - $attachmentList->readObjects(); - AttachmentBBCode::setAttachmentList($attachmentList); - } - // get message $message = StringUtil::trim($this->parameters['data']['message']); + // get embedded objects + MessageEmbeddedObjectManager::getInstance()->parseTemporaryMessage($message); + // parse URLs if ($preParse && $enableBBCodes) { if ($allowedBBCodesPermission) { diff --git a/wcfsetup/install/files/lib/system/attachment/AbstractAttachmentObjectType.class.php b/wcfsetup/install/files/lib/system/attachment/AbstractAttachmentObjectType.class.php index 6b60e8e0ea..d99221c632 100644 --- a/wcfsetup/install/files/lib/system/attachment/AbstractAttachmentObjectType.class.php +++ b/wcfsetup/install/files/lib/system/attachment/AbstractAttachmentObjectType.class.php @@ -15,6 +15,12 @@ use wcf\util\ArrayUtil; * @category Community Framework */ abstract class AbstractAttachmentObjectType implements IAttachmentObjectType { + /** + * cached objects + * @var array<\wcf\data\DatabaseObject> + */ + protected $cachedObjects = array(); + /** * @see \wcf\system\attachment\IAttachmentObjectType::getMaxSize() */ @@ -47,11 +53,34 @@ abstract class AbstractAttachmentObjectType implements IAttachmentObjectType { * @see \wcf\system\attachment\IAttachmentObjectType::getObject() */ public function getObject($objectID) { + if (isset($this->cachedObjects[$objectID])) return $this->cachedObjects[$objectID]; + return null; } + /** + * @see \wcf\system\attachment\IAttachmentObjectType::setCachedObjects() + */ + public function setCachedObjects(array $objects) { + foreach ($objects as $id => $object) { + $this->cachedObjects[$id] = $object; + } + } + /** * @see \wcf\system\attachment\IAttachmentObjectType::getObject() */ public function cacheObjects(array $objectIDs) {} + + /** + * @see \wcf\system\attachment\IAttachmentObjectType::setPermissions() + */ + public function setPermissions(array $attachments) { + foreach ($attachments as $attachment) { + $attachment->setPermissions(array( + 'canDownload' => $this->canDownload($attachment->objectID), + 'canViewPreview' => $this->canViewPreview($attachment->objectID) + )); + } + } } diff --git a/wcfsetup/install/files/lib/system/attachment/IAttachmentObjectType.class.php b/wcfsetup/install/files/lib/system/attachment/IAttachmentObjectType.class.php index 3e61ae7863..f257dfda16 100644 --- a/wcfsetup/install/files/lib/system/attachment/IAttachmentObjectType.class.php +++ b/wcfsetup/install/files/lib/system/attachment/IAttachmentObjectType.class.php @@ -81,4 +81,11 @@ interface IAttachmentObjectType { * @param array $objectIDs */ public function cacheObjects(array $objectIDs); + + /** + * Loads the permissions for given attachments. + * + * @param array<\wcf\data\attachment\Attachment> $attachments + */ + public function setPermissions(array $attachments); } diff --git a/wcfsetup/install/files/lib/system/bbcode/AttachmentBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/AttachmentBBCode.class.php index dd082d799f..399fa51ded 100644 --- a/wcfsetup/install/files/lib/system/bbcode/AttachmentBBCode.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/AttachmentBBCode.class.php @@ -3,6 +3,7 @@ namespace wcf\system\bbcode; use wcf\data\attachment\GroupedAttachmentList; use wcf\system\request\LinkHandler; use wcf\util\StringUtil; +use wcf\system\message\embedded\object\MessageEmbeddedObjectManager; /** * Parses the [attach] bbcode tag. @@ -18,12 +19,14 @@ class AttachmentBBCode extends AbstractBBCode { /** * list of attachments * @var \wcf\data\attachment\GroupedAttachmentList + * @deprecated */ protected static $attachmentList = null; /** * active object id * @var integer + * @deprecated */ protected static $objectID = 0; @@ -37,18 +40,21 @@ class AttachmentBBCode extends AbstractBBCode { $attachmentID = $openingTag['attributes'][0]; } - // get attachment for active object - $attachments = array(); - if (self::$attachmentList !== null) { - $attachments = self::$attachmentList->getGroupedObjects(self::$objectID); + // get embedded object + $attachment = MessageEmbeddedObjectManager::getInstance()->getObject('com.woltlab.wcf.attachment', $attachmentID); + if ($attachment === null) { + if (self::$attachmentList !== null) { + $attachments = self::$attachmentList->getGroupedObjects(self::$objectID); + if (isset($attachments[$attachmentID])) { + $attachment = $attachments[$attachmentID]; + + // mark attachment as embedded + $attachment->markAsEmbedded(); + } + } } - if (isset($attachments[$attachmentID])) { - $attachment = $attachments[$attachmentID]; - - // mark attachment as embedded - $attachment->markAsEmbedded(); - + if ($attachment !== null) { if ($attachment->showAsImage() && $parser->getOutputType() == 'text/html') { // image $linkParameters = array( @@ -84,6 +90,7 @@ class AttachmentBBCode extends AbstractBBCode { * Sets the attachment list. * * @param \wcf\data\attachment\GroupedAttachmentList $attachments + * @deprecated */ public static function setAttachmentList(GroupedAttachmentList $attachmentList) { self::$attachmentList = $attachmentList; @@ -93,6 +100,7 @@ class AttachmentBBCode extends AbstractBBCode { * Sets the active object id. * * @param integer $objectID + * @deprecated */ public static function setObjectID($objectID) { self::$objectID = $objectID; diff --git a/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php index d38acb9b5a..6b6b6c161e 100644 --- a/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php @@ -3,6 +3,7 @@ namespace wcf\system\bbcode; use wcf\system\application\ApplicationHandler; use wcf\system\request\RouteHandler; use wcf\system\WCF; +use wcf\system\message\embedded\object\MessageEmbeddedObjectManager; /** * Parses the [quote] bbcode tag. @@ -25,11 +26,23 @@ class QuoteBBCode extends AbstractBBCode { if (!$externalQuoteLink) { $quoteLink = preg_replace('~^https?://~', RouteHandler::getProtocol(), $quoteLink); } + $quoteAuthor = (!empty($openingTag['attributes'][0]) ? $openingTag['attributes'][0] : ''); + $quoteAuthorObject = null; + if ($quoteAuthor && !$externalQuoteLink) { + $quoteAuthorLC = mb_strtolower($quoteAuthor); + foreach (MessageEmbeddedObjectManager::getInstance()->getObjects('com.woltlab.wcf.quote') as $user) { + if (mb_strtolower($user->username) == $quoteAuthorLC) { + $quoteAuthorObject = $user; + break; + } + } + } WCF::getTPL()->assign(array( 'content' => $content, 'quoteLink' => $quoteLink, - 'quoteAuthor' => (!empty($openingTag['attributes'][0]) ? $openingTag['attributes'][0] : ''), + 'quoteAuthor' => $quoteAuthor, + 'quoteAuthorObject' => $quoteAuthorObject, 'isExternalQuoteLink' => $externalQuoteLink )); return WCF::getTPL()->fetch('quoteBBCodeTag'); diff --git a/wcfsetup/install/files/lib/system/message/embedded/object/AbstractMessageEmbeddedObjectHandler.class.php b/wcfsetup/install/files/lib/system/message/embedded/object/AbstractMessageEmbeddedObjectHandler.class.php new file mode 100644 index 0000000000..9b63f0609f --- /dev/null +++ b/wcfsetup/install/files/lib/system/message/embedded/object/AbstractMessageEmbeddedObjectHandler.class.php @@ -0,0 +1,63 @@ + + * @package com.woltlab.wcf + * @subpackage system.message.embedded.object + * @category Community Framework + */ +abstract class AbstractMessageEmbeddedObjectHandler extends DatabaseObjectDecorator implements IMessageEmbeddedObjectHandler { + /** + * @see \wcf\data\DatabaseObjectDecorator::$baseClass + */ + protected static $baseClass = 'wcf\data\object\type\ObjectType'; + + /** + * Parses given message for specific bbcode parameters. + * + * @param string $message + * @param string $bbcode bbcode name + * @return array + */ + public static function getTextParameters($message, $bbcode) { + if (preg_match_all('~\['.$bbcode.'\](.*?)\[/'.$bbcode.'\]~i', $message, $matches)) { + $results = ArrayUtil::trim($matches[1]); + $results = array_unique($results); + + return $results; + } + + return array(); + } + + /** + * Parses given message for specific bbcode parameters. + * + * @param string $message + * @param string $bbcode bbcode name + * @return array + */ + public static function getFirstParameters($message, $bbcode) { + $pattern = '~\['.$bbcode.'= + (?:\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|([^,\]]*)) + (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))* + \]~ix'; + + if (preg_match_all($pattern, $message, $matches)) { + $results = ArrayUtil::trim($matches[1]); + $results = array_merge($results, ArrayUtil::trim($matches[2])); + $results = array_unique($results); + + return $results; + } + + return array(); + } +} diff --git a/wcfsetup/install/files/lib/system/message/embedded/object/AttachmentMessageEmbeddedObjectHandler.class.php b/wcfsetup/install/files/lib/system/message/embedded/object/AttachmentMessageEmbeddedObjectHandler.class.php new file mode 100644 index 0000000000..d23abf9aca --- /dev/null +++ b/wcfsetup/install/files/lib/system/message/embedded/object/AttachmentMessageEmbeddedObjectHandler.class.php @@ -0,0 +1,63 @@ + + * @package com.woltlab.wcf + * @subpackage system.message.embedded.object + * @category Community Framework + */ +class AttachmentMessageEmbeddedObjectHandler extends AbstractMessageEmbeddedObjectHandler { + /** + * @see \wcf\system\message\embedded\object\IMessageEmbeddedObjectHandler::parseMessage() + */ + public function parseMessage($message) { + $parsedAttachmentIDs = array_unique(ArrayUtil::toIntegerArray(array_merge(self::getFirstParameters($message, 'attach'), self::getTextParameters($message, 'attach')))); + if (!empty($parsedAttachmentIDs)) { + $attachmentIDs = array(); + foreach ($parsedAttachmentIDs as $parsedAttachmentID) { + if ($parsedAttachmentID) $attachmentIDs[] = $parsedAttachmentID; + } + + $attachmentList = new AttachmentList(); + $attachmentList->getConditionBuilder()->add("attachment.attachmentID IN (?)", array($attachmentIDs)); + $attachmentList->readObjectIDs(); + return $attachmentList->getObjectIDs(); + } + + return false; + } + + /** + * @see \wcf\system\message\embedded\object\IMessageEmbeddedObjectHandler::loadObjects() + */ + public function loadObjects(array $objectIDs) { + $attachmentList = new AttachmentList(); + $attachmentList->setObjectIDs($objectIDs); + $attachmentList->readObjects(); + + // group attachments by object type + $groupedAttachments = array(); + foreach ($attachmentList->getObjects() as $attachment) { + if (!isset($groupedAttachments[$attachment->objectTypeID])) $groupedAttachments[$attachment->objectTypeID] = array(); + $groupedAttachments[$attachment->objectTypeID][] = $attachment; + } + + // check permissions + foreach ($groupedAttachments as $objectTypeID => $attachments) { + $processor = ObjectTypeCache::getInstance()->getObjectType($objectTypeID)->getProcessor(); + if ($processor !== null) { + $processor->setPermissions($attachments); + } + } + + return $attachmentList->getObjects(); + } +} diff --git a/wcfsetup/install/files/lib/system/message/embedded/object/IMessageEmbeddedObjectHandler.class.php b/wcfsetup/install/files/lib/system/message/embedded/object/IMessageEmbeddedObjectHandler.class.php new file mode 100644 index 0000000000..607c90b881 --- /dev/null +++ b/wcfsetup/install/files/lib/system/message/embedded/object/IMessageEmbeddedObjectHandler.class.php @@ -0,0 +1,31 @@ + + * @package com.woltlab.wcf + * @subpackage system.message.embedded.object + * @category Community Framework + */ +interface IMessageEmbeddedObjectHandler { + /** + * Parses the given message to extract embedded objects. + * Returns the IDs of found embedded objects. + * + * @param string $message + * @return array + */ + public function parseMessage($message); + + /** + * Loads and returns embedded objects. + * + * @param array $objectIDs + * @return array<\wcf\data\DatabaseObject> + */ + public function loadObjects(array $objectIDs); +} diff --git a/wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php b/wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php new file mode 100644 index 0000000000..ad81560582 --- /dev/null +++ b/wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php @@ -0,0 +1,265 @@ + + * @package com.woltlab.wcf + * @subpackage system.message.embedded.object + * @category Community Framework + */ +class MessageEmbeddedObjectManager extends SingletonFactory { + /** + * caches message to embedded object assignments + * @var array + */ + protected $messageEmbeddedObjects = array(); + + /** + * caches embedded objects + * @var array + */ + protected $embeddedObjects = array(); + + /** + * object type of the active message + * @var integer + */ + protected $activeMessageObjectTypeID = null; + + /** + * id of the active message + * @var integer + */ + protected $activeMessageID = null; + + /** + * list of embedded object handlers + * @var array + */ + protected $embeddedObjectHandlers = null; + + /** + * Registers the embedded objects found in given message. + * + * @param string $messageObjectType + * @param integer $messageID + * @param string $message + * @return boolean + */ + public function registerObjects($messageObjectType, $messageID, $message) { + // remove [code] tags + $message = self::removeCodeTags($message); + + // delete existing assignments + $this->removeObjects($messageObjectType, array($messageID)); + + // get object type id + $messageObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType); + + // prepare statement + $sql = "INSERT INTO wcf".WCF_N."_message_embedded_object + (messageObjectTypeID, messageID, embeddedObjectTypeID, embeddedObjectID) + VALUES (?, ?, ?, ?)"; + $statement = WCF::getDB()->prepareStatement($sql); + + // call embedded object handlers + WCF::getDB()->beginTransaction(); + $returnValue = false; + foreach ($this->getEmbeddedObjectHandlers() as $handler) { + $objectIDs = $handler->parseMessage($message); + if (!empty($objectIDs)) { + $returnValue = true; + foreach ($objectIDs as $objectID) { + $statement->execute(array($messageObjectTypeID, $messageID, $handler->objectTypeID, $objectID)); + } + } + } + WCF::getDB()->commitTransaction(); + + return $returnValue; + } + + /** + * Removes embedded object assigments for given messages. + * + * @param string $messageObjectType + * @param array $messageIDs + */ + public function removeObjects($messageObjectType, array $messageIDs) { + $conditionBuilder = new PreparedStatementConditionBuilder(); + $conditionBuilder->add('messageObjectTypeID = ?', array(ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType))); + $conditionBuilder->add('messageID IN (?)', array($messageIDs)); + + $sql = "DELETE FROM wcf".WCF_N."_message_embedded_object + ".$conditionBuilder; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditionBuilder->getParameters()); + } + + /** + * Loads the embedded objects for given messages. + * + * @param string $messageObjectType + * @param array $messageIDs + */ + public function loadObjects($messageObjectType, array $messageIDs) { + $conditionBuilder = new PreparedStatementConditionBuilder(); + $conditionBuilder->add('messageObjectTypeID = ?', array(ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType))); + $conditionBuilder->add('messageID IN (?)', array($messageIDs)); + + $sql = "SELECT * + FROM wcf".WCF_N."_message_embedded_object + ".$conditionBuilder; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditionBuilder->getParameters()); + $embeddedObjects = array(); + while ($row = $statement->fetchArray()) { + if (!isset($embeddedObjects[$row['embeddedObjectTypeID']])) $embeddedObjects[$row['embeddedObjectTypeID']] = array(); + $embeddedObjects[$row['embeddedObjectTypeID']][] = $row['embeddedObjectID']; + + if (!isset($this->messageEmbeddedObjects[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']])) { + $this->messageEmbeddedObjects[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']] = array(); + } + $this->messageEmbeddedObjects[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']][] = $row['embeddedObjectID']; + } + + foreach ($embeddedObjects as $embeddedObjectTypeID => $objectIDs) { + if (!isset($this->embeddedObjects[$embeddedObjectTypeID])) $this->embeddedObjects[$embeddedObjectTypeID] = array(); + foreach ($this->getEmbeddedObjectHandler($embeddedObjectTypeID)->loadObjects(array_unique($objectIDs)) as $objectID => $object) { + $this->embeddedObjects[$embeddedObjectTypeID][$objectID] = $object; + } + } + } + + /** + * Sets active message information. + * + * @param string $messageObjectType + * @param integer $messageID + */ + public function setActiveMessage($messageObjectType, $messageID) { + $this->activeMessageObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType); + $this->activeMessageID = $messageID; + } + + /** + * Gets all embedded objects of a specific type. + * + * @param string $embeddedObjectType + * @return array + */ + public function getObjects($embeddedObjectType) { + $embeddedObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message.embeddedObject', $embeddedObjectType); + $returnValue = array(); + if (!empty($this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$embeddedObjectTypeID])) { + foreach ($this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$embeddedObjectTypeID] as $embeddedObjectID) { + if (isset($this->embeddedObjects[$embeddedObjectTypeID][$embeddedObjectID])) { + $returnValue[] = $this->embeddedObjects[$embeddedObjectTypeID][$embeddedObjectID]; + } + } + } + + return $returnValue; + } + + /** + * Returns a specific embedded object. + * + * @param string $embeddedObjectType + * @param integer $objectID + * @return \wcf\data\DatabaseObject + */ + public function getObject($embeddedObjectType, $objectID) { + $embeddedObjectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message.embeddedObject', $embeddedObjectType); + $returnValue = array(); + if (!empty($this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$embeddedObjectTypeID])) { + foreach ($this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$embeddedObjectTypeID] as $embeddedObjectID) { + if ($embeddedObjectID == $objectID) { + if (isset($this->embeddedObjects[$embeddedObjectTypeID][$embeddedObjectID])) { + return $this->embeddedObjects[$embeddedObjectTypeID][$embeddedObjectID]; + } + } + } + } + + return null; + } + + /** + * Parses a temporary message and loads found embedded objects. + * + * @param string $message + */ + public function parseTemporaryMessage($message) { + // remove [code] tags + $message = self::removeCodeTags($message); + + // set active message information + $this->activeMessageObjectTypeID = -1; + $this->activeMessageID = -1; + + // get embedded objects + foreach ($this->getEmbeddedObjectHandlers() as $handler) { + $objectIDs = $handler->parseMessage($message); + if (!empty($objectIDs)) { + // save assignments + $this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$handler->objectTypeID] = $objectIDs; + + // loads objects + $this->embeddedObjects[$handler->objectTypeID] = $handler->loadObjects($objectIDs); + } + } + } + + /** + * Returns all embedded object handlers. + * + * @return array + */ + protected function getEmbeddedObjectHandlers() { + if ($this->embeddedObjectHandlers === null) { + $this->embeddedObjectHandlers = array(); + foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.message.embeddedObject') as $objectType) { + $this->embeddedObjectHandlers[$objectType->objectTypeID] = $objectType->getProcessor(); + } + } + + return $this->embeddedObjectHandlers; + } + + /** + * Returns a specific embedded object handler. + * + * @param integer $objectTypeID + * @return object + */ + protected function getEmbeddedObjectHandler($objectTypeID) { + $this->getEmbeddedObjectHandlers(); + + return $this->embeddedObjectHandlers[$objectTypeID]; + } + + /** + * Removes code bbcode occurrences in given message. + * + * @param string $message + * @return string + */ + protected static function removeCodeTags($message) { + return preg_replace("~(\[code + (?:= + (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*) + (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))* + )?\]) + (.*?) + (?:\[/code\])~six", '', $message); + } +} \ No newline at end of file diff --git a/wcfsetup/install/files/lib/system/message/embedded/object/QuoteMessageEmbeddedObjectHandler.class.php b/wcfsetup/install/files/lib/system/message/embedded/object/QuoteMessageEmbeddedObjectHandler.class.php new file mode 100644 index 0000000000..f1dd4e184d --- /dev/null +++ b/wcfsetup/install/files/lib/system/message/embedded/object/QuoteMessageEmbeddedObjectHandler.class.php @@ -0,0 +1,38 @@ + + * @package com.woltlab.wcf + * @subpackage system.message.embedded.object + * @category Community Framework + */ +class QuoteMessageEmbeddedObjectHandler extends AbstractMessageEmbeddedObjectHandler { + /** + * @see \wcf\system\message\embedded\object\IMessageEmbeddedObjectHandler::parseMessage() + */ + public function parseMessage($message) { + $usernames = self::getFirstParameters($message, 'quote'); + if (!empty($usernames)) { + $userList = new UserList(); + $userList->getConditionBuilder()->add("user_table.username IN (?)", array($usernames)); + $userList->readObjectIDs(); + return $userList->getObjectIDs(); + } + + return false; + } + + /** + * @see \wcf\system\message\embedded\object\IMessageEmbeddedObjectHandler::loadObjects() + */ + public function loadObjects(array $objectIDs) { + return UserProfile::getUserProfiles($objectIDs); + } +} diff --git a/wcfsetup/install/files/style/bbcode.less b/wcfsetup/install/files/style/bbcode.less index 0c4ef91974..ee1b57aaaa 100644 --- a/wcfsetup/install/files/style/bbcode.less +++ b/wcfsetup/install/files/style/bbcode.less @@ -219,61 +219,141 @@ html[dir='rtl'] { /* Quote Box */ .quoteBox { - background-color: @wcfContentBackgroundColor; clear: both; - min-height: 28px; - margin-bottom: @wcfGapTiny; - position: relative; - - &.containerPadding { - padding-left: 54px; - } - &::before { - content: "\f10d"; - color: @wcfDimmedColor; - font-family: FontAwesome; - font-size: 28px; - position: absolute; - left: @wcfGapMedium; - top: @wcfGapSmall; + > .container { + background-color: @wcfContentBackgroundColor; + min-height: 28px; + margin-bottom: @wcfGapTiny; + position: relative; + + &.containerPadding { + padding-left: 54px; + } + + > header { + padding-bottom: @wcfGapTiny; + border-bottom: 1px dotted @wcfContainerBorderColor; + margin-bottom: @wcfGapSmall; + + > h3 { + font-weight: bold; + } + } + + > div { + &::before { + content: "\f10d"; + color: @wcfDimmedColor; + font-family: FontAwesome; + font-size: 28px; + position: absolute; + left: @wcfGapMedium; + top: @wcfGapSmall; + } + } } - > header { - padding-bottom: @wcfGapTiny; - border-bottom: 1px dotted @wcfContainerBorderColor; - margin-bottom: @wcfGapSmall; + > .quoteAuthorAvatar { + float: left; + + + .container { + margin-left: 90px; + padding-left: @wcfGapLarge; + + &::before, + &::after { + border-style: inset solid inset none; + border-width: 15px; + content: ""; + display: block; + height: 0; + position: absolute; + top: 15px; + width: 0; + } + + &::before { + border-color: transparent @wcfContainerBorderColor transparent transparent; + left: -15px; + z-index: 100; + } + + &::after { + border-color: transparent @wcfContentBackgroundColor transparent transparent; + left: -14px; + z-index: 101; + } + + > div { + &::before { + display: none; + } + } + } - > h3 { - font-weight: bold; + > a { + display: inline-block; } } /* nested quotes */ .quoteBox { - background-image: none; - padding-left: @wcfGapLarge; - min-height: 0; - - &::before { + > .quoteAuthorAvatar { display: none; } + + > .container { + margin-left: 0; + padding-left: @wcfGapLarge; + min-height: 0; + + &::before, + &::after { + display: none; + } + + > div { + &::before { + display: none; + } + } + } } } @media only screen and (max-width: 800px) { .quoteBox { - &::before { - font-size: 14px; - left: @wcfGapSmall; + > .quoteAuthorAvatar { + display: none; } - &.containerPadding { - padding-left: 28px; + > .container { + margin-left: 0 !important; + + &.containerPadding { + padding-left: 28px; + } + + > div { + &::before { + display: block !important; + font-size: 14px; + left: @wcfGapSmall; + } + } } .quoteBox { - padding-left: @wcfGapSmall; + > .container { + padding-left: @wcfGapSmall; + + > div { + &::before { + display: none !important; + } + } + } } } } diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index c67034eb14..2a891c8b66 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -470,6 +470,16 @@ CREATE TABLE wcf1_like_object ( UNIQUE KEY (objectTypeID, objectID) ); +DROP TABLE IF EXISTS wcf1_message_embedded_object; +CREATE TABLE wcf1_message_embedded_object ( + messageObjectTypeID INT(10) NOT NULL, + messageID INT(10) NOT NULL, + embeddedObjectTypeID INT(10) NOT NULL, + embeddedObjectID INT(10) NOT NULL, + + KEY (messageObjectTypeID, messageID) +); + DROP TABLE IF EXISTS wcf1_moderation_queue; CREATE TABLE wcf1_moderation_queue ( queueID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, @@ -1620,6 +1630,9 @@ ALTER TABLE wcf1_user_profile_visitor ADD FOREIGN KEY (userID) REFERENCES wcf1_u ALTER TABLE wcf1_user_object_watch ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE; ALTER TABLE wcf1_user_object_watch ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE; +ALTER TABLE wcf1_message_embedded_object ADD FOREIGN KEY (messageObjectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE; +ALTER TABLE wcf1_message_embedded_object ADD FOREIGN KEY (embeddedObjectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE; + ALTER TABLE wcf1_moderation_queue ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE; ALTER TABLE wcf1_moderation_queue ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL; ALTER TABLE wcf1_moderation_queue ADD FOREIGN KEY (assignedUserID) REFERENCES wcf1_user (userID) ON DELETE SET NULL; -- 2.20.1