Remove unused imports
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / data / conversation / message / ConversationMessageAction.class.php
1 <?php
2 namespace wcf\data\conversation\message;
3 use wcf\data\conversation\Conversation;
4 use wcf\data\conversation\ConversationAction;
5 use wcf\data\conversation\ConversationEditor;
6 use wcf\data\smiley\SmileyCache;
7 use wcf\data\AbstractDatabaseObjectAction;
8 use wcf\data\DatabaseObject;
9 use wcf\data\IAttachmentMessageQuickReplyAction;
10 use wcf\data\IMessageInlineEditorAction;
11 use wcf\data\IMessageQuoteAction;
12 use wcf\system\attachment\AttachmentHandler;
13 use wcf\system\bbcode\BBCodeHandler;
14 use wcf\system\exception\PermissionDeniedException;
15 use wcf\system\exception\UserInputException;
16 use wcf\system\html\input\HtmlInputProcessor;
17 use wcf\system\message\censorship\Censorship;
18 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
19 use wcf\system\message\quote\MessageQuoteManager;
20 use wcf\system\message\QuickReplyManager;
21 use wcf\system\moderation\queue\ModerationQueueManager;
22 use wcf\system\request\LinkHandler;
23 use wcf\system\search\SearchIndexManager;
24 use wcf\system\user\notification\object\ConversationMessageUserNotificationObject;
25 use wcf\system\user\notification\UserNotificationHandler;
26 use wcf\system\user\storage\UserStorageHandler;
27 use wcf\system\WCF;
28 use wcf\util\StringUtil;
29
30 /**
31 * Executes conversation message-related actions.
32 *
33 * @author Marcel Werk
34 * @copyright 2001-2016 WoltLab GmbH
35 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
36 * @package WoltLabSuite\Core\Data\Conversation\Message
37 *
38 * @method ConversationMessageEditor[] getObjects()
39 * @method ConversationMessageEditor getSingleObject()
40 */
41 class ConversationMessageAction extends AbstractDatabaseObjectAction implements IAttachmentMessageQuickReplyAction, IMessageInlineEditorAction, IMessageQuoteAction {
42 /**
43 * @inheritDoc
44 */
45 protected $className = ConversationMessageEditor::class;
46
47 /**
48 * conversation object
49 * @var Conversation
50 */
51 public $conversation;
52
53 /**
54 * @var HtmlInputProcessor
55 */
56 public $htmlInputProcessor;
57
58 /**
59 * conversation message object
60 * @var ConversationMessage
61 */
62 public $message;
63
64 /**
65 * @inheritDoc
66 * @return ConversationMessage
67 */
68 public function create() {
69 if (!isset($this->parameters['data']['enableHtml'])) $this->parameters['data']['enableHtml'] = 1;
70
71 // count attachments
72 if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) {
73 $this->parameters['data']['attachments'] = count($this->parameters['attachmentHandler']);
74 }
75
76 if (LOG_IP_ADDRESS) {
77 // add ip address
78 if (!isset($this->parameters['data']['ipAddress'])) {
79 $this->parameters['data']['ipAddress'] = WCF::getSession()->ipAddress;
80 }
81 }
82 else {
83 // do not track ip address
84 if (isset($this->parameters['data']['ipAddress'])) {
85 unset($this->parameters['data']['ipAddress']);
86 }
87 }
88
89 if (!empty($this->parameters['htmlInputProcessor'])) {
90 /** @noinspection PhpUndefinedMethodInspection */
91 $this->parameters['data']['message'] = $this->parameters['htmlInputProcessor']->getHtml();
92 }
93
94 // create message
95 /** @var ConversationMessage $message */
96 $message = parent::create();
97 $messageEditor = new ConversationMessageEditor($message);
98
99 // get conversation
100 $conversation = (isset($this->parameters['conversation']) ? $this->parameters['conversation'] : new Conversation($message->conversationID));
101 $conversationEditor = new ConversationEditor($conversation);
102
103 if (empty($this->parameters['isFirstPost'])) {
104 // update last message
105 $conversationEditor->addMessage($message);
106
107 // fire notification event
108 if (!$conversation->isDraft) {
109 $notificationRecipients = array_diff($conversation->getParticipantIDs(true), [$message->userID]); // don't notify message author
110 if (!empty($notificationRecipients)) {
111 UserNotificationHandler::getInstance()->fireEvent(
112 'conversationMessage',
113 'com.woltlab.wcf.conversation.message.notification',
114 new ConversationMessageUserNotificationObject($message),
115 $notificationRecipients
116 );
117 }
118 }
119
120 $userConversation = Conversation::getUserConversation($conversation->conversationID, $message->userID);
121 if ($userConversation !== null && $userConversation->isInvisible) {
122 // make invisible participant visible
123 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
124 SET isInvisible = 0
125 WHERE participantID = ?
126 AND conversationID = ?";
127 $statement = WCF::getDB()->prepareStatement($sql);
128 $statement->execute([$message->userID, $conversation->conversationID]);
129
130 $conversationEditor->updateParticipantSummary();
131 $conversationEditor->updateParticipantCount();
132 }
133
134 // reset visibility if it was hidden but not left
135 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
136 SET hideConversation = ?
137 WHERE conversationID = ?
138 AND hideConversation = ?";
139 $statement = WCF::getDB()->prepareStatement($sql);
140 $statement->execute([
141 Conversation::STATE_DEFAULT,
142 $conversation->conversationID,
143 Conversation::STATE_HIDDEN
144 ]);
145 }
146
147 // reset storage
148 UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'unreadConversationCount');
149
150 // update search index
151 SearchIndexManager::getInstance()->set(
152 'com.woltlab.wcf.conversation.message',
153 $message->messageID,
154 $message->message,
155 !empty($this->parameters['isFirstPost']) ? $conversation->subject : '',
156 $message->time,
157 $message->userID,
158 $message->username
159 );
160
161 // update attachments
162 if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) {
163 /** @noinspection PhpUndefinedMethodInspection */
164 $this->parameters['attachmentHandler']->updateObjectID($message->messageID);
165 }
166
167 // save embedded objects
168 if (!empty($this->parameters['htmlInputProcessor'])) {
169 /** @noinspection PhpUndefinedMethodInspection */
170 $this->parameters['htmlInputProcessor']->setObjectID($message->messageID);
171
172 if (MessageEmbeddedObjectManager::getInstance()->registerObjects($this->parameters['htmlInputProcessor'])) {
173 $messageEditor->update(['hasEmbeddedObjects' => 1]);
174 }
175 }
176
177 // clear quotes
178 if (isset($this->parameters['removeQuoteIDs']) && !empty($this->parameters['removeQuoteIDs'])) {
179 MessageQuoteManager::getInstance()->markQuotesForRemoval($this->parameters['removeQuoteIDs']);
180 }
181 MessageQuoteManager::getInstance()->removeMarkedQuotes();
182
183 // return new message
184 return $message;
185 }
186
187 /**
188 * @inheritDoc
189 */
190 public function update() {
191 // count attachments
192 if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) {
193 $this->parameters['data']['attachments'] = count($this->parameters['attachmentHandler']);
194 }
195
196 if (!empty($this->parameters['htmlInputProcessor'])) {
197 /** @noinspection PhpUndefinedMethodInspection */
198 $this->parameters['data']['message'] = $this->parameters['htmlInputProcessor']->getHtml();
199 }
200
201 parent::update();
202
203 // update search index / embedded objects
204 if (isset($this->parameters['data']) && isset($this->parameters['data']['message'])) {
205 foreach ($this->getObjects() as $message) {
206 $conversation = $message->getConversation();
207 SearchIndexManager::getInstance()->set(
208 'com.woltlab.wcf.conversation.message',
209 $message->messageID,
210 $this->parameters['data']['message'],
211 $conversation->firstMessageID == $message->messageID ? $conversation->subject : '',
212 $message->time,
213 $message->userID,
214 $message->username
215 );
216
217 if (!empty($this->parameters['htmlInputProcessor'])) {
218 /** @noinspection PhpUndefinedMethodInspection */
219 $this->parameters['htmlInputProcessor']->setObjectID($message->messageID);
220
221 if ($message->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects($this->parameters['htmlInputProcessor'])) {
222 $message->update(['hasEmbeddedObjects' => $message->hasEmbeddedObjects ? 0 : 1]);
223 }
224 }
225 }
226 }
227 }
228
229 /**
230 * @inheritDoc
231 */
232 public function delete() {
233 $count = parent::delete();
234
235 $attachmentMessageIDs = $conversationIDs = [];
236 foreach ($this->getObjects() as $message) {
237 if (!in_array($message->conversationID, $conversationIDs)) {
238 $conversationIDs[] = $message->conversationID;
239 }
240
241 if ($message->attachments) {
242 $attachmentMessageIDs[] = $message->messageID;
243 }
244 }
245
246 // rebuild conversations
247 if (!empty($conversationIDs)) {
248 $conversationAction = new ConversationAction($conversationIDs, 'rebuild');
249 $conversationAction->executeAction();
250 }
251
252 if (!empty($this->objectIDs)) {
253 // delete notifications
254 UserNotificationHandler::getInstance()->removeNotifications('com.woltlab.wcf.conversation.message.notification', $this->objectIDs);
255
256 // update search index
257 SearchIndexManager::getInstance()->delete('com.woltlab.wcf.conversation.message', $this->objectIDs);
258
259 // update embedded objects
260 MessageEmbeddedObjectManager::getInstance()->removeObjects('com.woltlab.wcf.conversation.message', $this->objectIDs);
261
262 // remove moderation queues
263 ModerationQueueManager::getInstance()->removeQueues('com.woltlab.wcf.conversation.message', $this->objectIDs);
264 }
265
266 // remove attachments
267 if (!empty($attachmentMessageIDs)) {
268 AttachmentHandler::removeAttachments('com.woltlab.wcf.conversation.message', $attachmentMessageIDs);
269 }
270
271 return $count;
272 }
273
274 /**
275 * @inheritDoc
276 */
277 public function validateQuickReply() {
278 QuickReplyManager::getInstance()->setDisallowedBBCodes(explode(',', WCF::getSession()->getPermission('user.message.disallowedBBCodes')));
279 QuickReplyManager::getInstance()->validateParameters($this, $this->parameters, Conversation::class);
280 }
281
282 /**
283 * @inheritDoc
284 */
285 public function quickReply() {
286 return QuickReplyManager::getInstance()->createMessage(
287 $this,
288 $this->parameters,
289 ConversationAction::class,
290 CONVERSATION_LIST_DEFAULT_SORT_ORDER,
291 'conversationMessageList'
292 );
293 }
294
295 /**
296 * @inheritDoc
297 */
298 public function validateJumpToExtended() {
299 $this->readInteger('containerID');
300 $this->readString('message', true);
301 $this->readString('tmpHash', true);
302
303 $this->conversation = new Conversation($this->parameters['containerID']);
304 if (!$this->conversation->conversationID) {
305 throw new UserInputException('containerID');
306 }
307 else if ($this->conversation->isClosed || !Conversation::isParticipant([$this->conversation->conversationID])) {
308 throw new PermissionDeniedException();
309 }
310
311 // editing existing message
312 if (isset($this->parameters['messageID'])) {
313 $this->message = new ConversationMessage(intval($this->parameters['messageID']));
314 if (!$this->message->messageID || ($this->message->conversationID != $this->conversation->conversationID)) {
315 throw new UserInputException('messageID');
316 }
317
318 if (!$this->message->canEdit()) {
319 throw new PermissionDeniedException();
320 }
321 }
322 }
323
324 /**
325 * @inheritDoc
326 */
327 public function jumpToExtended() {
328 // quick reply
329 if ($this->message === null) {
330 QuickReplyManager::getInstance()->setMessage('conversation', $this->conversation->conversationID, $this->parameters['message']);
331 $url = LinkHandler::getInstance()->getLink('ConversationMessageAdd', ['id' => $this->conversation->conversationID]);
332 }
333 else {
334 // editing message
335 QuickReplyManager::getInstance()->setMessage('conversationMessage', $this->message->messageID, $this->parameters['message']);
336 $url = LinkHandler::getInstance()->getLink('ConversationMessageEdit', ['id' => $this->message->messageID]);
337 }
338
339 if (!empty($this->parameters['tmpHash'])) {
340 QuickReplyManager::getInstance()->setTmpHash($this->parameters['tmpHash']);
341 }
342
343 // redirect
344 return [
345 'url' => $url
346 ];
347 }
348
349 /**
350 * @inheritDoc
351 */
352 public function validateBeginEdit() {
353 $this->readInteger('containerID');
354 $this->readInteger('objectID');
355
356 $this->conversation = new Conversation($this->parameters['containerID']);
357 if (!$this->conversation->conversationID) {
358 throw new UserInputException('containerID');
359 }
360
361 if ($this->conversation->isClosed || !Conversation::isParticipant([$this->conversation->conversationID])) {
362 throw new PermissionDeniedException();
363 }
364
365 $this->message = new ConversationMessage($this->parameters['objectID']);
366 if (!$this->message->messageID) {
367 throw new UserInputException('objectID');
368 }
369
370 if (!$this->message->canEdit()) {
371 throw new PermissionDeniedException();
372 }
373
374 BBCodeHandler::getInstance()->setDisallowedBBCodes(explode(',', WCF::getSession()->getPermission('user.message.disallowedBBCodes')));
375 }
376
377 /**
378 * @inheritDoc
379 */
380 public function beginEdit() {
381 WCF::getTPL()->assign([
382 'defaultSmilies' => SmileyCache::getInstance()->getCategorySmilies(),
383 'message' => $this->message,
384 'permissionCanUseSmilies' => 'user.message.canUseSmilies',
385 'wysiwygSelector' => 'messageEditor'.$this->message->messageID
386 ]);
387
388 if (MODULE_ATTACHMENT) {
389 $tmpHash = StringUtil::getRandomID();
390 $attachmentHandler = new AttachmentHandler('com.woltlab.wcf.conversation.message', $this->message->messageID, $tmpHash);
391 $attachmentList = $attachmentHandler->getAttachmentList();
392
393 WCF::getTPL()->assign([
394 'attachmentHandler' => $attachmentHandler,
395 'attachmentList' => $attachmentList->getObjects(),
396 'attachmentObjectID' => $this->message->messageID,
397 'attachmentObjectType' => 'com.woltlab.wcf.conversation.message',
398 'attachmentParentObjectID' => 0,
399 'tmpHash' => $tmpHash
400 ]);
401 }
402
403 return [
404 'actionName' => 'beginEdit',
405 'template' => WCF::getTPL()->fetch('conversationMessageInlineEditor')
406 ];
407 }
408
409 /**
410 * @inheritDoc
411 */
412 public function validateSave() {
413 $this->readString('message', true, 'data');
414
415 if (empty($this->parameters['data']['message'])) {
416 throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.global.form.error.empty'));
417 }
418
419 $this->validateBeginEdit();
420
421 $this->validateMessage($this->conversation, $this->getHtmlInputProcessor($this->parameters['data']['message'], $this->message->messageID));
422 }
423
424 /**
425 * @inheritDoc
426 */
427 public function save() {
428 $data = [];
429
430 if (!$this->message->getConversation()->isDraft) {
431 $data['lastEditTime'] = TIME_NOW;
432 $data['editCount'] = $this->message->editCount + 1;
433 }
434 // execute update action
435 $action = new ConversationMessageAction([$this->message], 'update', [
436 'data' => $data,
437 'htmlInputProcessor' => $this->getHtmlInputProcessor()
438 ]);
439 $action->executeAction();
440
441 // load new message
442 $this->message = new ConversationMessage($this->message->messageID);
443 $this->message->getAttachments();
444
445 $attachmentList = null;
446 if (MODULE_ATTACHMENT) {
447 $attachmentList = $this->message->getAttachments(true);
448 $count = 0;
449 if ($attachmentList !== null) {
450 // set permissions
451 $attachmentList->setPermissions([
452 'canDownload' => true,
453 'canViewPreview' => true
454 ]);
455
456 $count = count($attachmentList);
457 }
458
459 // update count to reflect number of attachments after edit
460 if ($count != $this->message->attachments) {
461 $messageEditor = new ConversationMessageEditor($this->message);
462 $messageEditor->update(['attachments' => $count]);
463 }
464 }
465
466 // load embedded objects
467 MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wcf.conversation.message', [$this->message->messageID]);
468
469 $data = [
470 'actionName' => 'save',
471 'message' => $this->message->getFormattedMessage()
472 ];
473
474 if (MODULE_ATTACHMENT) {
475 WCF::getTPL()->assign([
476 'attachmentList' => $attachmentList,
477 'objectID' => $this->message->messageID
478 ]);
479 $data['attachmentList'] = WCF::getTPL()->fetch('attachments');
480 }
481
482 return $data;
483 }
484
485 /**
486 * @inheritDoc
487 */
488 public function validateContainer(DatabaseObject $conversation) {
489 /** @var Conversation $conversation */
490
491 if (!$conversation->conversationID) {
492 throw new UserInputException('objectID');
493 }
494 if ($conversation->isClosed) {
495 throw new PermissionDeniedException();
496 }
497 $conversation->loadUserParticipation();
498 if (!$conversation->canRead()) {
499 throw new PermissionDeniedException();
500 }
501 }
502
503 /**
504 * @inheritDoc
505 */
506 public function validateMessage(DatabaseObject $container, HtmlInputProcessor $htmlInputProcessor) {
507 $message = $htmlInputProcessor->getTextContent();
508 if (mb_strlen($message) > WCF::getSession()->getPermission('user.conversation.maxLength')) {
509 throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.tooLong', ['maxTextLength' => WCF::getSession()->getPermission('user.conversation.maxLength')]));
510 }
511
512 // search for disallowed bbcodes
513 $disallowedBBCodes = $htmlInputProcessor->validate();
514 if (!empty($disallowedBBCodes)) {
515 throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', ['disallowedBBCodes' => $disallowedBBCodes]));
516 }
517
518 // search for censored words
519 if (ENABLE_CENSORSHIP) {
520 $result = Censorship::getInstance()->test($message);
521 if ($result) {
522 throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.censoredWordsFound', ['censoredWords' => $result]));
523 }
524 }
525 }
526
527 /**
528 * @inheritDoc
529 */
530 public function getMessageList(DatabaseObject $conversation, $lastMessageTime) {
531 /** @var Conversation $conversation */
532
533 $messageList = new ViewableConversationMessageList();
534 $messageList->setConversation($conversation);
535 $messageList->getConditionBuilder()->add("conversation_message.conversationID = ?", [$conversation->conversationID]);
536 $messageList->getConditionBuilder()->add("conversation_message.time > ?", [$lastMessageTime]);
537 $messageList->sqlOrderBy = "conversation_message.time ".CONVERSATION_LIST_DEFAULT_SORT_ORDER;
538 $messageList->readObjects();
539
540 return $messageList;
541 }
542
543 /**
544 * @inheritDoc
545 */
546 public function getPageNo(DatabaseObject $conversation) {
547 /** @var Conversation $conversation */
548
549 $sql = "SELECT COUNT(*) AS count
550 FROM wcf".WCF_N."_conversation_message
551 WHERE conversationID = ?";
552 $statement = WCF::getDB()->prepareStatement($sql);
553 $statement->execute([$conversation->conversationID]);
554 $count = $statement->fetchArray();
555
556 return [intval(ceil($count['count'] / CONVERSATION_MESSAGES_PER_PAGE)), $count['count']];
557 }
558
559 /**
560 * @inheritDoc
561 */
562 public function getRedirectUrl(DatabaseObject $conversation, DatabaseObject $message) {
563 /** @var ConversationMessage $message */
564 return LinkHandler::getInstance()->getLink('Conversation', [
565 'object' => $conversation,
566 'messageID' => $message->messageID
567 ]).'#message'.$message->messageID;
568 }
569
570 /**
571 * @inheritDoc
572 */
573 public function validateSaveFullQuote() {
574 $this->message = $this->getSingleObject();
575
576 if (!Conversation::isParticipant([$this->message->conversationID])) {
577 throw new PermissionDeniedException();
578 }
579 }
580
581 /**
582 * @inheritDoc
583 */
584 public function saveFullQuote() {
585 $quoteID = MessageQuoteManager::getInstance()->addQuote(
586 'com.woltlab.wcf.conversation.message',
587 $this->message->conversationID,
588 $this->message->messageID,
589 $this->message->getExcerpt(),
590 $this->message->getMessage()
591 );
592
593 if ($quoteID === false) {
594 $removeQuoteID = MessageQuoteManager::getInstance()->getQuoteID(
595 'com.woltlab.wcf.conversation.message',
596 $this->message->messageID,
597 $this->message->getExcerpt(),
598 $this->message->getMessage()
599 );
600 MessageQuoteManager::getInstance()->removeQuote($removeQuoteID);
601 }
602
603 $returnValues = [
604 'count' => MessageQuoteManager::getInstance()->countQuotes(),
605 'fullQuoteMessageIDs' => MessageQuoteManager::getInstance()->getFullQuoteObjectIDs(['com.woltlab.wcf.conversation.message'])
606 ];
607
608 if ($quoteID) {
609 $returnValues['renderedQuote'] = MessageQuoteManager::getInstance()->getQuoteComponents($quoteID);
610 }
611
612 return $returnValues;
613 }
614
615 /**
616 * @inheritDoc
617 */
618 public function validateSaveQuote() {
619 $this->readString('message');
620 $this->readBoolean('renderQuote', true);
621 $this->message = $this->getSingleObject();
622
623 if (!Conversation::isParticipant([$this->message->conversationID])) {
624 throw new PermissionDeniedException();
625 }
626 }
627
628 /**
629 * @inheritDoc
630 */
631 public function saveQuote() {
632 $quoteID = MessageQuoteManager::getInstance()->addQuote(
633 'com.woltlab.wcf.conversation.message',
634 $this->message->conversationID,
635 $this->message->messageID,
636 $this->parameters['message'],
637 false
638 );
639
640 $returnValues = [
641 'count' => MessageQuoteManager::getInstance()->countQuotes(),
642 'fullQuoteMessageIDs' => MessageQuoteManager::getInstance()->getFullQuoteObjectIDs(['com.woltlab.wcf.conversation.message'])
643 ];
644
645 if ($this->parameters['renderQuote']) {
646 $returnValues['renderedQuote'] = MessageQuoteManager::getInstance()->getQuoteComponents($quoteID);
647 }
648
649 return $returnValues;
650 }
651
652 /**
653 * @inheritDoc
654 */
655 public function validateGetRenderedQuotes() {
656 $this->readInteger('parentObjectID');
657
658 $this->conversation = new Conversation($this->parameters['parentObjectID']);
659 if (!$this->conversation->conversationID) {
660 throw new UserInputException('parentObjectID');
661 }
662 }
663
664 /**
665 * @inheritDoc
666 */
667 public function getRenderedQuotes() {
668 $quotes = MessageQuoteManager::getInstance()->getQuotesByParentObjectID('com.woltlab.wcf.conversation.message', $this->conversation->conversationID);
669
670 return [
671 'template' => implode("\n\n", $quotes)
672 ];
673 }
674
675 /**
676 * @inheritDoc
677 */
678 public function getAttachmentHandler(DatabaseObject $conversation) {
679 return new AttachmentHandler('com.woltlab.wcf.conversation.message', 0, $this->parameters['tmpHash']);
680 }
681
682 /**
683 * @inheritDoc
684 */
685 public function getHtmlInputProcessor($message = null, $objectID = 0) {
686 if ($message === null) {
687 return $this->htmlInputProcessor;
688 }
689
690 $this->htmlInputProcessor = new HtmlInputProcessor();
691 $this->htmlInputProcessor->process($message, 'com.woltlab.wcf.conversation.message', $objectID);
692
693 return $this->htmlInputProcessor;
694 }
695 }