2 namespace wcf\system\message\embedded\
object;
3 use wcf\data\
object\type\ObjectTypeCache
;
4 use wcf\system\database\util\PreparedStatementConditionBuilder
;
5 use wcf\system\exception\InvalidObjectTypeException
;
6 use wcf\system\html\input\HtmlInputProcessor
;
7 use wcf\system\SingletonFactory
;
11 * Default interface of embedded object handler.
14 * @copyright 2001-2019 WoltLab GmbH
15 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
16 * @package WoltLabSuite\Core\System\Message\Embedded\Object
18 class MessageEmbeddedObjectManager
extends SingletonFactory
{
20 * caches message to embedded object assignments
23 protected $messageEmbeddedObjects = [];
26 * caches embedded objects
29 protected $embeddedObjects = [];
32 * object type of the active message
35 protected $activeMessageObjectTypeID;
38 * id of the active message
41 protected $activeMessageID;
44 * language id of the active message
47 protected $activeMessageLanguageID;
50 * list of embedded object handlers
53 protected $embeddedObjectHandlers;
59 protected $contentLanguageID;
62 * local cache for bulk operations
65 protected $bulkData = [
71 * A list of previous active message settings used to restore
72 * the internal state in case of nested message processing.
74 protected $activeMessageHistory = [];
77 * Registers the embedded objects found in given message.
79 * @param HtmlInputProcessor $htmlInputProcessor html input processor instance holding embedded object data
80 * @param boolean $isBulk true for bulk operations
81 * @return boolean true if at least one embedded object was found
83 public function registerObjects(HtmlInputProcessor
$htmlInputProcessor, $isBulk = false) {
84 $context = $htmlInputProcessor->getContext();
86 $messageObjectType = $context['objectType'];
87 $messageObjectTypeID = $context['objectTypeID'];
88 $messageID = $context['objectID'];
90 // delete existing assignments
92 if (!isset($this->bulkData
['remove'][$messageObjectType])) $this->bulkData
['remove'][$messageObjectType] = [];
93 $this->bulkData
['remove'][$messageObjectType][] = $messageID;
96 $this->removeObjects($messageObjectType, [$messageID]);
102 $sql = "INSERT INTO wcf".WCF_N
."_message_embedded_object
103 (messageObjectTypeID, messageID, embeddedObjectTypeID, embeddedObjectID)
104 VALUES (?, ?, ?, ?)";
105 $statement = WCF
::getDB()->prepareStatement($sql);
107 WCF
::getDB()->beginTransaction();
110 $embeddedData = $htmlInputProcessor->getEmbeddedContent();
111 $returnValue = false;
113 /** @var IMessageEmbeddedObjectHandler $handler */
114 foreach ($this->getEmbeddedObjectHandlers() as $handler) {
115 $objectIDs = $handler->parse($htmlInputProcessor, $embeddedData);
117 if (!empty($objectIDs)) {
118 foreach ($objectIDs as $objectID) {
119 $parameters = [$messageObjectTypeID, $messageID, $handler->objectTypeID
, $objectID];
121 $this->bulkData
['insert'][] = $parameters;
124 $statement->execute($parameters);
133 WCF
::getDB()->commitTransaction();
140 * Commits the bulk operation by performing all deletes and inserts
141 * in one big transaction to save performance.
143 public function commitBulkOperation() {
144 // delete existing data
145 WCF
::getDB()->beginTransaction();
146 foreach ($this->bulkData
['remove'] as $objectType => $objectIDs) {
147 $this->removeObjects($objectType, $objectIDs);
149 WCF
::getDB()->commitTransaction();
152 $sql = "INSERT INTO wcf".WCF_N
."_message_embedded_object
153 (messageObjectTypeID, messageID, embeddedObjectTypeID, embeddedObjectID)
154 VALUES (?, ?, ?, ?)";
155 $statement = WCF
::getDB()->prepareStatement($sql);
157 WCF
::getDB()->beginTransaction();
158 foreach ($this->bulkData
['insert'] as $parameters) {
159 $statement->execute($parameters);
161 WCF
::getDB()->commitTransaction();
171 * Registers the embedded objects found in a message using the simplified syntax.
173 * @param string $messageObjectType object type identifier
174 * @param integer $messageID object id
175 * @param integer[][] $embeddedContent list of object ids for embedded objects by object type id
176 * @return boolean true if at least one embedded object was found
178 public function registerSimpleObjects($messageObjectType, $messageID, array $embeddedContent) {
179 $messageObjectTypeID = ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType);
181 // delete existing assignments
182 $this->removeObjects($messageObjectType, [$messageID]);
184 if (empty($embeddedContent)) {
189 $sql = "INSERT INTO wcf".WCF_N
."_message_embedded_object
190 (messageObjectTypeID, messageID, embeddedObjectTypeID, embeddedObjectID)
191 VALUES (?, ?, ?, ?)";
192 $statement = WCF
::getDB()->prepareStatement($sql);
194 // call embedded object handlers
195 WCF
::getDB()->beginTransaction();
196 foreach ($embeddedContent as $objectTypeID => $objectIDs) {
197 foreach ($objectIDs as $objectID) {
198 $statement->execute([$messageObjectTypeID, $messageID, $objectTypeID, $objectID]);
201 WCF
::getDB()->commitTransaction();
207 * Removes embedded object assignments for given messages.
209 * @param string $messageObjectType
210 * @param integer[] $messageIDs
212 public function removeObjects($messageObjectType, array $messageIDs) {
213 $conditionBuilder = new PreparedStatementConditionBuilder();
214 $conditionBuilder->add('messageObjectTypeID = ?', [ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType)]);
215 $conditionBuilder->add('messageID IN (?)', [$messageIDs]);
217 $sql = "DELETE FROM wcf".WCF_N
."_message_embedded_object
219 $statement = WCF
::getDB()->prepareStatement($sql);
220 $statement->execute($conditionBuilder->getParameters());
224 * Loads the embedded objects for given messages.
226 * @param string $messageObjectType
227 * @param integer[] $messageIDs
228 * @param integer $contentLanguageID
229 * @throws InvalidObjectTypeException
231 public function loadObjects($messageObjectType, array $messageIDs, $contentLanguageID = null) {
232 $messageObjectTypeID = ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType);
233 if ($messageObjectTypeID === null) {
234 throw new InvalidObjectTypeException($messageObjectType, 'com.woltlab.wcf.message');
237 $conditionBuilder = new PreparedStatementConditionBuilder();
238 $conditionBuilder->add('messageObjectTypeID = ?', [$messageObjectTypeID]);
239 $conditionBuilder->add('messageID IN (?)', [$messageIDs]);
243 FROM wcf".WCF_N
."_message_embedded_object
245 $statement = WCF
::getDB()->prepareStatement($sql);
246 $statement->execute($conditionBuilder->getParameters());
247 $embeddedObjects = [];
248 while ($row = $statement->fetchArray()) {
249 if (!isset($this->embeddedObjects
[$row['embeddedObjectTypeID']][$row['embeddedObjectID']])) {
250 // group objects by object type
251 if (!isset($embeddedObjects[$row['embeddedObjectTypeID']])) $embeddedObjects[$row['embeddedObjectTypeID']] = [];
252 $embeddedObjects[$row['embeddedObjectTypeID']][] = $row['embeddedObjectID'];
255 // store message to embedded object assignment
256 if (!isset($this->messageEmbeddedObjects
[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']])) {
257 $this->messageEmbeddedObjects
[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']] = [];
259 $this->messageEmbeddedObjects
[$row['messageObjectTypeID']][$row['messageID']][$row['embeddedObjectTypeID']][] = $row['embeddedObjectID'];
262 $this->contentLanguageID
= $contentLanguageID;
265 foreach ($embeddedObjects as $embeddedObjectTypeID => $objectIDs) {
266 if (!isset($this->embeddedObjects
[$embeddedObjectTypeID])) $this->embeddedObjects
[$embeddedObjectTypeID] = [];
267 foreach ($this->getEmbeddedObjectHandler($embeddedObjectTypeID)->loadObjects(array_unique($objectIDs)) as $objectID => $object) {
268 $this->embeddedObjects
[$embeddedObjectTypeID][$objectID] = $object;
272 $this->contentLanguageID
= null;
276 * Returns the content language id or null.
280 public function getContentLanguageID() {
281 return $this->contentLanguageID
;
285 * Sets active message information.
287 * @param string $messageObjectType
288 * @param integer $messageID
289 * @param integer $languageID
291 public function setActiveMessage($messageObjectType, $messageID, $languageID = null) {
292 if ($this->activeMessageObjectTypeID
) {
293 $this->activeMessageHistory
[] = [
294 'activeMessageID' => $this->activeMessageID
,
295 'activeMessageLanguageID' => $this->activeMessageLanguageID
,
296 'activeMessageObjectTypeID' => $this->activeMessageObjectTypeID
,
300 $this->activeMessageObjectTypeID
= ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message', $messageObjectType);
301 $this->activeMessageID
= $messageID;
302 $this->activeMessageLanguageID
= $languageID;
306 * Restores the internal state in case of nested message processing.
308 public function reset() {
309 $newState = \array_pop
($this->activeMessageHistory
);
310 if ($newState === null) {
312 'activeMessageID' => null,
313 'activeMessageLanguageID' => null,
314 'activeMessageObjectTypeID' => null,
318 $this->activeMessageID
= $newState['activeMessageID'];
319 $this->activeMessageLanguageID
= $newState['activeMessageLanguageID'];
320 $this->activeMessageObjectTypeID
= $newState['activeMessageObjectTypeID'];
324 * Returns the language id of the active message.
328 public function getActiveMessageLanguageID() {
329 return $this->activeMessageLanguageID
;
333 * Returns all embedded objects of a specific type.
335 * @param string $embeddedObjectType
338 public function getObjects($embeddedObjectType) {
339 $embeddedObjectTypeID = ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message.embeddedObject', $embeddedObjectType);
341 if (!empty($this->messageEmbeddedObjects
[$this->activeMessageObjectTypeID
][$this->activeMessageID
][$embeddedObjectTypeID])) {
342 foreach ($this->messageEmbeddedObjects
[$this->activeMessageObjectTypeID
][$this->activeMessageID
][$embeddedObjectTypeID] as $embeddedObjectID) {
343 if (isset($this->embeddedObjects
[$embeddedObjectTypeID][$embeddedObjectID])) {
344 $returnValue[] = $this->embeddedObjects
[$embeddedObjectTypeID][$embeddedObjectID];
353 * Returns a specific embedded object.
355 * @param string $embeddedObjectType
356 * @param integer $objectID
357 * @return \wcf\data\DatabaseObject
359 public function getObject($embeddedObjectType, $objectID) {
360 $embeddedObjectTypeID = ObjectTypeCache
::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.message.embeddedObject', $embeddedObjectType);
361 if (!empty($this->messageEmbeddedObjects
[$this->activeMessageObjectTypeID
][$this->activeMessageID
][$embeddedObjectTypeID])) {
362 foreach ($this->messageEmbeddedObjects
[$this->activeMessageObjectTypeID
][$this->activeMessageID
][$embeddedObjectTypeID] as $embeddedObjectID) {
363 if ($embeddedObjectID == $objectID) {
364 if (isset($this->embeddedObjects
[$embeddedObjectTypeID][$embeddedObjectID])) {
365 return $this->embeddedObjects
[$embeddedObjectTypeID][$embeddedObjectID];
375 * Temporarily registers a message, the parsed data will not be stored.
377 * @param HtmlInputProcessor $htmlInputProcessor html input processor
379 public function registerTemporaryMessage(HtmlInputProcessor
$htmlInputProcessor) {
380 $context = $htmlInputProcessor->getContext();
382 // set active message information
383 $this->activeMessageObjectTypeID
= $context['objectTypeID'];
384 $this->activeMessageID
= $context['objectID'];
386 $embeddedData = $htmlInputProcessor->getEmbeddedContent();
388 /** @var IMessageEmbeddedObjectHandler $handler */
389 foreach ($this->getEmbeddedObjectHandlers() as $handler) {
390 $objectIDs = $handler->parse($htmlInputProcessor, $embeddedData);
392 if (!empty($objectIDs)) {
394 $this->messageEmbeddedObjects
[$this->activeMessageObjectTypeID
][$this->activeMessageID
][$handler->objectTypeID
] = $objectIDs;
397 $this->embeddedObjects
[$handler->objectTypeID
] = $handler->loadObjects($objectIDs);
403 * @return ISimpleMessageEmbeddedObjectHandler[];
405 public function getSimpleMessageEmbeddedObjectHandlers() {
407 foreach ($this->getEmbeddedObjectHandlers() as $handler) {
408 if ($handler instanceof ISimpleMessageEmbeddedObjectHandler
) {
409 $name = lcfirst(preg_replace('~^.*\\\\([A-Z][a-zA-Z]+)MessageEmbeddedObjectHandler$~', '$1', get_class($handler)));
410 $handlers[$name] = $handler;
418 * Returns all embedded object handlers.
420 * @return IMessageEmbeddedObjectHandler[]
422 protected function getEmbeddedObjectHandlers() {
423 if ($this->embeddedObjectHandlers
=== null) {
424 $this->embeddedObjectHandlers
= [];
425 foreach (ObjectTypeCache
::getInstance()->getObjectTypes('com.woltlab.wcf.message.embeddedObject') as $objectType) {
426 $this->embeddedObjectHandlers
[$objectType->objectTypeID
] = $objectType->getProcessor();
430 return $this->embeddedObjectHandlers
;
434 * Returns a specific embedded object handler.
436 * @param integer $objectTypeID
437 * @return IMessageEmbeddedObjectHandler
439 protected function getEmbeddedObjectHandler($objectTypeID) {
440 $this->getEmbeddedObjectHandlers();
442 return $this->embeddedObjectHandlers
[$objectTypeID];
448 public function parseTemporaryMessage() {
449 throw new \
BadMethodCallException("parseTemporaryMessage() has been removed, please use registerTemporaryMessage() instead.");