Commit | Line | Data |
---|---|---|
9544b6b4 MW |
1 | <?php |
2 | namespace wcf\data\conversation\message; | |
3 | use wcf\data\conversation\Conversation; | |
4 | use wcf\data\conversation\ConversationEditor; | |
b2de9161 | 5 | use wcf\data\smiley\SmileyCache; |
9544b6b4 | 6 | use wcf\data\AbstractDatabaseObjectAction; |
52971287 | 7 | use wcf\data\DatabaseObject; |
4a2ee5bb AE |
8 | use wcf\data\IExtendedMessageQuickReplyAction; |
9 | use wcf\data\IMessageInlineEditorAction; | |
5fa9d104 | 10 | use wcf\data\IMessageQuoteAction; |
5d7f0df0 | 11 | use wcf\system\attachment\AttachmentHandler; |
027c57f4 | 12 | use wcf\system\bbcode\BBCodeParser; |
0a0e61bb | 13 | use wcf\system\bbcode\PreParser; |
5e52978f AE |
14 | use wcf\system\exception\PermissionDeniedException; |
15 | use wcf\system\exception\UserInputException; | |
d8671029 | 16 | use wcf\system\message\censorship\Censorship; |
5fa9d104 | 17 | use wcf\system\message\quote\MessageQuoteManager; |
5e52978f | 18 | use wcf\system\message\QuickReplyManager; |
5e52978f | 19 | use wcf\system\request\LinkHandler; |
80a0761e | 20 | use wcf\system\search\SearchIndexManager; |
b5ffaf76 AE |
21 | use wcf\system\user\notification\object\ConversationMessageUserNotificationObject; |
22 | use wcf\system\user\notification\UserNotificationHandler; | |
df8f8628 | 23 | use wcf\system\user\storage\UserStorageHandler; |
9544b6b4 | 24 | use wcf\system\WCF; |
8a2fa588 | 25 | use wcf\util\StringUtil; |
9544b6b4 MW |
26 | |
27 | /** | |
28 | * Executes message-related actions. | |
29 | * | |
30 | * @author Marcel Werk | |
31 | * @copyright 2001-2012 WoltLab GmbH | |
32 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
33 | * @package com.woltlab.wcf.conversation | |
34 | * @subpackage data.conversation.message | |
61f754e0 | 35 | * @category Community Framework |
9544b6b4 | 36 | */ |
5fa9d104 | 37 | class ConversationMessageAction extends AbstractDatabaseObjectAction implements IExtendedMessageQuickReplyAction, IMessageInlineEditorAction, IMessageQuoteAction { |
9544b6b4 | 38 | /** |
61f754e0 | 39 | * @see wcf\data\AbstractDatabaseObjectAction::$className |
9544b6b4 MW |
40 | */ |
41 | protected $className = 'wcf\data\conversation\message\ConversationMessageEditor'; | |
42 | ||
5e52978f AE |
43 | /** |
44 | * conversation object | |
45 | * @var wcf\data\conversation\Conversation | |
46 | */ | |
47 | public $conversation = null; | |
48 | ||
a5bacc02 AE |
49 | /** |
50 | * conversation message object | |
51 | * @var wcf\data\conversation\message\ConversationMessage | |
52 | */ | |
53 | public $message = null; | |
54 | ||
9544b6b4 | 55 | /** |
61f754e0 | 56 | * @see wcf\data\AbstractDatabaseObjectAction::create() |
9544b6b4 MW |
57 | */ |
58 | public function create() { | |
59 | // count attachments | |
60 | if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) { | |
61 | $this->parameters['data']['attachments'] = count($this->parameters['attachmentHandler']); | |
62 | } | |
63 | ||
64 | if (LOG_IP_ADDRESS) { | |
65 | // add ip address | |
66 | if (!isset($this->parameters['data']['ipAddress'])) { | |
67 | $this->parameters['data']['ipAddress'] = WCF::getSession()->ipAddress; | |
68 | } | |
69 | } | |
70 | else { | |
71 | // do not track ip address | |
72 | if (isset($this->parameters['data']['ipAddress'])) { | |
73 | unset($this->parameters['data']['ipAddress']); | |
74 | } | |
75 | } | |
76 | ||
77 | // create message | |
78 | $message = parent::create(); | |
79 | ||
fcf27f27 | 80 | // get conversation |
6f1cd040 MW |
81 | $conversation = (isset($this->parameters['converation']) ? $this->parameters['converation'] : new Conversation($message->conversationID)); |
82 | $conversationEditor = new ConversationEditor($conversation); | |
61f754e0 | 83 | |
4b541146 | 84 | if (empty($this->parameters['isFirstPost'])) { |
9544b6b4 MW |
85 | // update last message |
86 | $conversationEditor->addMessage($message); | |
64ac54a1 MW |
87 | |
88 | // fire notification event | |
6f1cd040 | 89 | if (!$conversation->isDraft) { |
0ff992a3 | 90 | $notificationRecipients = array_diff($conversation->getParticipantIDs(true), array($message->userID)); // don't notify message author |
566eb43f MW |
91 | if (!empty($notificationRecipients)) { |
92 | UserNotificationHandler::getInstance()->fireEvent('conversationMessage', 'com.woltlab.wcf.conversation.message.notification', new ConversationMessageUserNotificationObject($message), $notificationRecipients); | |
93 | } | |
5fa9d104 | 94 | } |
fcf27f27 MW |
95 | |
96 | // make invisible participant visible | |
97 | $sql = "UPDATE wcf".WCF_N."_conversation_to_user | |
98 | SET isInvisible = 0 | |
99 | WHERE participantID = ? | |
100 | AND conversationID = ?"; | |
101 | $statement = WCF::getDB()->prepareStatement($sql); | |
102 | $statement->execute(array($message->userID, $conversation->conversationID)); | |
0ff992a3 AE |
103 | |
104 | // reset visibility if it was hidden but not left | |
105 | $sql = "UPDATE wcf".WCF_N."_conversation_to_user | |
106 | SET hideConversation = ? | |
107 | WHERE conversationID = ? | |
108 | AND hideConversation = ?"; | |
109 | $statement = WCF::getDB()->prepareStatement($sql); | |
110 | $statement->execute(array( | |
06695793 | 111 | Conversation::STATE_DEFAULT, |
0ff992a3 | 112 | $conversation->conversationID, |
06695793 | 113 | Conversation::STATE_HIDDEN |
0ff992a3 | 114 | )); |
9544b6b4 MW |
115 | } |
116 | ||
df8f8628 | 117 | // reset storage |
462d532a | 118 | UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'unreadConversationCount'); |
9544b6b4 | 119 | |
80a0761e | 120 | // update search index |
6f1cd040 | 121 | SearchIndexManager::getInstance()->add('com.woltlab.wcf.conversation.message', $message->messageID, $message->message, (!empty($this->parameters['isFirstPost']) ? $conversation->subject : ''), $message->time, $message->userID, $message->username); |
9544b6b4 MW |
122 | |
123 | // update attachments | |
124 | if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) { | |
125 | $this->parameters['attachmentHandler']->updateObjectID($message->messageID); | |
126 | } | |
127 | ||
5fa9d104 | 128 | // clear quotes |
f1781bd0 AE |
129 | if (isset($this->parameters['removeQuoteIDs']) && !empty($this->parameters['removeQuoteIDs'])) { |
130 | MessageQuoteManager::getInstance()->markQuotesForRemoval($this->parameters['removeQuoteIDs']); | |
131 | } | |
5fa9d104 AE |
132 | MessageQuoteManager::getInstance()->removeMarkedQuotes(); |
133 | ||
9544b6b4 MW |
134 | // return new message |
135 | return $message; | |
136 | } | |
c764783c MW |
137 | |
138 | /** | |
61f754e0 | 139 | * @see wcf\data\AbstractDatabaseObjectAction::update() |
c764783c MW |
140 | */ |
141 | public function update() { | |
142 | // count attachments | |
143 | if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) { | |
144 | $this->parameters['data']['attachments'] = count($this->parameters['attachmentHandler']); | |
145 | } | |
146 | ||
147 | parent::update(); | |
80a0761e | 148 | |
fcf27f27 MW |
149 | // update search index |
150 | foreach ($this->objects as $message) { | |
151 | $conversation = $message->getConversation(); | |
152 | SearchIndexManager::getInstance()->update('com.woltlab.wcf.conversation.message', $message->messageID, $message->message, ($conversation->firstMessageID == $message->messageID ? $conversation->subject : ''), $message->time, $message->userID, $message->username); | |
153 | } | |
c764783c | 154 | } |
5e52978f | 155 | |
db07cb2d AE |
156 | /** |
157 | * @see wcf\data\AbstractDatabaseObjectAction::delete() | |
158 | */ | |
159 | public function delete() { | |
160 | $count = parent::delete(); | |
161 | ||
5d7f0df0 MW |
162 | $attachmentMessageIDs = array(); |
163 | foreach ($this->objects as $message) { | |
164 | if ($message->attachments) { | |
165 | $attachmentMessageIDs[] = $message->messageID; | |
db07cb2d | 166 | |
db07cb2d AE |
167 | } |
168 | } | |
169 | ||
5d7f0df0 MW |
170 | // @todo: modification log, reports |
171 | ||
172 | if (!empty($this->objectIDs)) { | |
173 | // delete notifications | |
174 | UserNotificationHandler::getInstance()->deleteNotifications('conversationMessage', 'com.woltlab.wcf.conversation.message.notification', array(), $this->objectIDs); | |
175 | ||
176 | // update search index | |
177 | SearchIndexManager::getInstance()->delete('com.woltlab.wcf.conversation.message', $this->objectIDs); | |
178 | } | |
179 | ||
180 | // remove attachments | |
181 | if (!empty($attachmentMessageIDs)) { | |
182 | AttachmentHandler::removeAttachments('com.woltlab.wcf.conversation.message', $attachmentMessageIDs); | |
183 | } | |
184 | ||
db07cb2d AE |
185 | return $count; |
186 | } | |
187 | ||
5e52978f | 188 | /** |
4a2ee5bb | 189 | * @see wcf\data\IMessageQuickReply::validateQuickReply() |
5e52978f AE |
190 | */ |
191 | public function validateQuickReply() { | |
0a0e61bb | 192 | QuickReplyManager::getInstance()->setAllowedBBCodes(explode(',', WCF::getSession()->getPermission('user.message.allowedBBCodes'))); |
5e52978f AE |
193 | QuickReplyManager::getInstance()->validateParameters($this, $this->parameters, 'wcf\data\conversation\Conversation'); |
194 | } | |
195 | ||
196 | /** | |
4a2ee5bb | 197 | * @see wcf\data\IMessageQuickReply::quickReply() |
5e52978f AE |
198 | */ |
199 | public function quickReply() { | |
200 | return QuickReplyManager::getInstance()->createMessage( | |
201 | $this, | |
202 | $this->parameters, | |
203 | 'wcf\data\conversation\ConversationAction', | |
204 | 'wcf\data\conversation\message\ViewableConversationMessageList', | |
205 | 'conversationMessageList', | |
206 | CONVERSATION_LIST_DEFAULT_SORT_ORDER | |
207 | ); | |
208 | } | |
209 | ||
a5bacc02 | 210 | /** |
4a2ee5bb | 211 | * @see wcf\data\IExtendedMessageQuickReplyAction::validateJumpToExtended() |
a5bacc02 AE |
212 | */ |
213 | public function validateJumpToExtended() { | |
c2b8907c AE |
214 | $this->readInteger('containerID'); |
215 | $this->readString('message', true); | |
a5bacc02 | 216 | |
c2b8907c AE |
217 | $this->conversation = new Conversation($this->parameters['containerID']); |
218 | if (!$this->conversation->conversationID) { | |
86a5431b | 219 | throw new UserInputException('containerID'); |
a5bacc02 | 220 | } |
c2b8907c AE |
221 | else if ($this->conversation->isClosed || !Conversation::isParticipant(array($this->conversation->conversationID))) { |
222 | throw new PermissionDeniedException(); | |
a5bacc02 AE |
223 | } |
224 | ||
225 | // editing existing message | |
226 | if (isset($this->parameters['messageID'])) { | |
227 | $this->message = new ConversationMessage(intval($this->parameters['messageID'])); | |
228 | if (!$this->message->messageID || ($this->message->conversationID != $this->conversation->conversationID)) { | |
229 | throw new UserInputException('messageID'); | |
230 | } | |
61f754e0 | 231 | |
a5bacc02 AE |
232 | if (!$this->message->canEdit()) { |
233 | throw new PermissionDeniedException(); | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | /** | |
4a2ee5bb | 239 | * @see wcf\data\IExtendedMessageQuickReplyAction::jumpToExtended() |
a5bacc02 AE |
240 | */ |
241 | public function jumpToExtended() { | |
242 | // quick reply | |
243 | if ($this->message === null) { | |
244 | QuickReplyManager::getInstance()->setMessage('conversation', $this->conversation->conversationID, $this->parameters['message']); | |
245 | $url = LinkHandler::getInstance()->getLink('ConversationMessageAdd', array('id' => $this->conversation->conversationID)); | |
246 | } | |
247 | else { | |
248 | // editing message | |
249 | QuickReplyManager::getInstance()->setMessage('conversationMessage', $this->message->messageID, $this->parameters['message']); | |
250 | $url = LinkHandler::getInstance()->getLink('ConversationMessageEdit', array('id' => $this->message->messageID)); | |
251 | } | |
252 | ||
253 | // redirect | |
254 | return array( | |
255 | 'url' => $url | |
256 | ); | |
257 | } | |
258 | ||
b5ffaf76 | 259 | /** |
4a2ee5bb | 260 | * @see wcf\data\IMessageInlineEditorAction::validateBeginEdit() |
b5ffaf76 AE |
261 | */ |
262 | public function validateBeginEdit() { | |
c2b8907c AE |
263 | $this->readInteger('containerID'); |
264 | $this->readInteger('objectID'); | |
265 | ||
266 | $this->conversation = new Conversation($this->parameters['containerID']); | |
267 | if (!$this->conversation->conversationID) { | |
b5ffaf76 AE |
268 | throw new UserInputException('containerID'); |
269 | } | |
c2b8907c AE |
270 | |
271 | if ($this->conversation->isClosed || !Conversation::isParticipant(array($this->conversation->conversationID))) { | |
272 | throw new PermissionDeniedException(); | |
b5ffaf76 AE |
273 | } |
274 | ||
c2b8907c AE |
275 | $this->message = new ConversationMessage($this->parameters['objectID']); |
276 | if (!$this->message->messageID) { | |
b5ffaf76 AE |
277 | throw new UserInputException('objectID'); |
278 | } | |
c2b8907c AE |
279 | |
280 | if (!$this->message->canEdit()) { | |
281 | throw new PermissionDeniedException(); | |
b5ffaf76 AE |
282 | } |
283 | } | |
284 | ||
285 | /** | |
4a2ee5bb | 286 | * @see wcf\data\IMessageInlineEditorAction::beginEdit() |
b5ffaf76 AE |
287 | */ |
288 | public function beginEdit() { | |
289 | WCF::getTPL()->assign(array( | |
b2de9161 | 290 | 'defaultSmilies' => SmileyCache::getInstance()->getCategorySmilies(), |
b5ffaf76 AE |
291 | 'message' => $this->message, |
292 | 'wysiwygSelector' => 'messageEditor'.$this->message->messageID | |
293 | )); | |
294 | ||
295 | return array( | |
296 | 'actionName' => 'beginEdit', | |
297 | 'template' => WCF::getTPL()->fetch('conversationMessageInlineEditor') | |
298 | ); | |
299 | } | |
300 | ||
301 | /** | |
4a2ee5bb | 302 | * @see wcf\data\IMessageInlineEditorAction::validateSave() |
b5ffaf76 AE |
303 | */ |
304 | public function validateSave() { | |
c2b8907c | 305 | $this->readString('message', false, 'data'); |
b5ffaf76 AE |
306 | |
307 | $this->validateBeginEdit(); | |
271c06ce | 308 | $this->validateMessage($this->conversation, $this->parameters['data']['message']); |
b5ffaf76 AE |
309 | } |
310 | ||
311 | /** | |
4a2ee5bb | 312 | * @see wcf\data\IMessageInlineEditorAction::save() |
b5ffaf76 AE |
313 | */ |
314 | public function save() { | |
315 | $messageEditor = new ConversationMessageEditor($this->message); | |
316 | $messageEditor->update(array( | |
0a0e61bb | 317 | 'message' => PreParser::getInstance()->parse($this->parameters['data']['message'], explode(',', WCF::getSession()->getPermission('user.message.allowedBBCodes'))) |
b5ffaf76 AE |
318 | )); |
319 | ||
320 | // load new message | |
321 | $this->message = new ConversationMessage($this->message->messageID); | |
f7b9b1f3 | 322 | $this->message->getAttachments(); |
b5ffaf76 AE |
323 | |
324 | return array( | |
325 | 'actionName' => 'save', | |
326 | 'message' => $this->message->getFormattedMessage() | |
327 | ); | |
328 | } | |
329 | ||
5e52978f | 330 | /** |
4a2ee5bb | 331 | * @see wcf\data\IMessageQuickReply::validateContainer() |
5e52978f | 332 | */ |
52971287 | 333 | public function validateContainer(DatabaseObject $conversation) { |
5e52978f AE |
334 | if (!$conversation->conversationID) { |
335 | throw new UserInputException('objectID'); | |
336 | } | |
337 | else if ($conversation->isClosed || !Conversation::isParticipant(array($conversation->conversationID))) { | |
338 | throw new PermissionDeniedException(); | |
339 | } | |
340 | } | |
341 | ||
b6c2b36c AE |
342 | /** |
343 | * @see wcf\data\IMessageQuickReplyAction::validateMessage() | |
344 | */ | |
d8671029 | 345 | public function validateMessage(DatabaseObject $container, $message) { |
84cebd68 MW |
346 | if (StringUtil::length($message) > WCF::getSession()->getPermission('user.conversation.maxLength')) { |
347 | throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.tooLong', array('maxTextLength' => WCF::getSession()->getPermission('user.conversation.maxLength')))); | |
348 | } | |
349 | ||
027c57f4 MS |
350 | // search for disallowed bbcodes |
351 | $disallowedBBCodes = BBCodeParser::getInstance()->validateBBCodes($message, explode(',', WCF::getSession()->getPermission('user.message.allowedBBCodes'))); | |
352 | if (!empty($disallowedBBCodes)) { | |
353 | throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', array('disallowedBBCodes' => $disallowedBBCodes))); | |
354 | } | |
355 | ||
d8671029 MW |
356 | // search for censored words |
357 | if (ENABLE_CENSORSHIP) { | |
358 | $result = Censorship::getInstance()->test($message); | |
359 | if ($result) { | |
e86562df | 360 | throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.censoredWordsFound', array('censoredWords' => $result))); |
d8671029 MW |
361 | } |
362 | } | |
363 | } | |
b6c2b36c | 364 | |
5e52978f | 365 | /** |
4a2ee5bb | 366 | * @see wcf\data\IMessageQuickReply::getPageNo() |
5e52978f | 367 | */ |
52971287 | 368 | public function getPageNo(DatabaseObject $conversation) { |
5e52978f AE |
369 | $sql = "SELECT COUNT(*) AS count |
370 | FROM wcf".WCF_N."_conversation_message | |
371 | WHERE conversationID = ?"; | |
372 | $statement = WCF::getDB()->prepareStatement($sql); | |
373 | $statement->execute(array($conversation->conversationID)); | |
374 | $count = $statement->fetchArray(); | |
375 | ||
a3157f71 | 376 | return array(intval(ceil($count['count'] / CONVERSATION_MESSAGES_PER_PAGE)), $count['count']); |
5e52978f AE |
377 | } |
378 | ||
379 | /** | |
4a2ee5bb | 380 | * @see wcf\data\IMessageQuickReply::getRedirectUrl() |
5e52978f | 381 | */ |
52971287 | 382 | public function getRedirectUrl(DatabaseObject $conversation, DatabaseObject $message) { |
5e52978f AE |
383 | return LinkHandler::getInstance()->getLink('Conversation', array( |
384 | 'object' => $conversation, | |
385 | 'messageID' => $message->messageID | |
386 | )).'#message'.$message->messageID; | |
387 | } | |
5fa9d104 | 388 | |
42335f61 | 389 | /** |
09f53f03 | 390 | * @see wcf\data\IMessageQuoteAction::validateSaveFullQuote() |
42335f61 | 391 | */ |
09f53f03 | 392 | public function validateSaveFullQuote() { |
c2b8907c | 393 | $this->message = $this->getSingleObject(); |
61f754e0 | 394 | |
42335f61 AE |
395 | if (!Conversation::isParticipant(array($this->message->conversationID))) { |
396 | throw new PermissionDeniedException(); | |
397 | } | |
398 | } | |
399 | ||
400 | /** | |
401 | * @see wcf\data\IMessageQuoteAction::saveFullQuote() | |
402 | */ | |
403 | public function saveFullQuote() { | |
404 | if (!MessageQuoteManager::getInstance()->addQuote('com.woltlab.wcf.conversation.message', $this->message->messageID, $this->message->getExcerpt(), $this->message->getMessage())) { | |
626fa1ff | 405 | $quoteID = MessageQuoteManager::getInstance()->getQuoteID('com.woltlab.wcf.conversation.message', $this->message->conversationID, $this->message->messageID, $this->message->getExcerpt(), $this->message->getMessage()); |
42335f61 AE |
406 | MessageQuoteManager::getInstance()->removeQuote($quoteID); |
407 | } | |
db864366 | 408 | |
42335f61 | 409 | return array( |
ce6fa919 AE |
410 | 'count' => MessageQuoteManager::getInstance()->countQuotes(), |
411 | 'fullQuoteMessageIDs' => MessageQuoteManager::getInstance()->getFullQuoteObjectIDs(array('com.woltlab.wcf.conversation.message')) | |
42335f61 AE |
412 | ); |
413 | } | |
414 | ||
5fa9d104 AE |
415 | /** |
416 | * @see wcf\data\IMessageQuoteAction::validateSaveQuote() | |
417 | */ | |
418 | public function validateSaveQuote() { | |
c2b8907c AE |
419 | $this->readString('message'); |
420 | $this->message = $this->getSingleObject(); | |
5fa9d104 | 421 | |
5fa9d104 AE |
422 | if (!Conversation::isParticipant(array($this->message->conversationID))) { |
423 | throw new PermissionDeniedException(); | |
424 | } | |
425 | } | |
426 | ||
427 | /** | |
428 | * @see wcf\data\IMessageQuoteAction::saveQuote() | |
429 | */ | |
430 | public function saveQuote() { | |
626fa1ff | 431 | MessageQuoteManager::getInstance()->addQuote('com.woltlab.wcf.conversation.message', $this->message->conversationID, $this->message->messageID, $this->parameters['message']); |
5fa9d104 AE |
432 | |
433 | return array( | |
ce6fa919 AE |
434 | 'count' => MessageQuoteManager::getInstance()->countQuotes(), |
435 | 'fullQuoteMessageIDs' => MessageQuoteManager::getInstance()->getFullQuoteObjectIDs(array('com.woltlab.wcf.conversation.message')) | |
5fa9d104 AE |
436 | ); |
437 | } | |
1ffd7648 AE |
438 | |
439 | /** | |
440 | * @see wcf\data\IMessageQuoteAction::validateGetRenderedQuotes() | |
441 | */ | |
442 | public function validateGetRenderedQuotes() { | |
443 | $this->readInteger('parentObjectID'); | |
444 | ||
445 | $this->conversation = new Conversation($this->parameters['parentObjectID']); | |
446 | if (!$this->conversation->conversationID) { | |
447 | throw new UserInputException('parentObjectID'); | |
448 | } | |
449 | } | |
450 | ||
451 | /** | |
452 | * @see wcf\data\IMessageQuoteAction::getRenderedQuotes() | |
453 | */ | |
454 | public function getRenderedQuotes() { | |
455 | $quotes = MessageQuoteManager::getInstance()->getQuotesByParentObjectID('com.woltlab.wbb.conversation.message', $this->conversation->conversationID); | |
456 | ||
457 | return array( | |
458 | 'template' => implode("\n\n", $quotes) | |
459 | ); | |
460 | } | |
9544b6b4 | 461 | } |