Add quote support for form builder wysiwyg component
authorMatthias Schmidt <gravatronics@live.com>
Fri, 22 Mar 2019 15:02:27 +0000 (16:02 +0100)
committerMatthias Schmidt <gravatronics@live.com>
Fri, 22 Mar 2019 15:02:27 +0000 (16:02 +0100)
See #2852

com.woltlab.wcf/templates/__wysiwygFormField.tpl
wcfsetup/install/files/acp/templates/__wysiwygFormField.tpl
wcfsetup/install/files/lib/system/form/builder/IFormNode.class.php
wcfsetup/install/files/lib/system/form/builder/TFormParentNode.class.php
wcfsetup/install/files/lib/system/form/builder/container/wysiwyg/WysiwygFormContainer.class.php
wcfsetup/install/files/lib/system/form/builder/field/wysiwyg/WysiwygFormField.class.php

index 9336bc7c7641a143230e10d20a9d5af3ba5bf485..527dc7c99404ddc694b456d8374db7d9b9c6608f 100644 (file)
 
 {include file='wysiwyg' wysiwygSelector=$field->getPrefixedId()}
 
+{if $field->supportsQuotes()}
+       {js application='wcf' file='WCF.Message' bundle='WCF.Combined'}
+       <script data-relocate="true">
+               {* use empty require to ensure that the `Bootstrap` module is loaded and 
+                       `window.__wcf_bc_eventHandler` is available *}
+               require([], function() {
+                       {include file='__messageQuoteManager' wysiwygSelector=$field->getPrefixedId() supportPaste=true}
+                       
+                       {if $field->getQuoteData() !== null}
+                               var quoteHandler = new WCF.Message.Quote.Handler(
+                                       $quoteManager,
+                                       '{$field->getQuoteData('actionClass')|encodeJS}',
+                                       '{$field->getQuoteData('objectType')}',
+                                       '{$field->getQuoteData('selectors')[container]}',
+                                       '{$field->getQuoteData('selectors')[messageBody]}',
+                                       '{$field->getQuoteData('selectors')[messageContent]}',
+                                       true
+                               );
+                               
+                               elData(elById('{@$field->getPrefixedId()}'), 'quote-handler', quoteHandler);
+                       {/if}
+               });
+       </script>
+{/if}
+
 {include file='__formFieldFooter'}
index 9336bc7c7641a143230e10d20a9d5af3ba5bf485..527dc7c99404ddc694b456d8374db7d9b9c6608f 100644 (file)
 
 {include file='wysiwyg' wysiwygSelector=$field->getPrefixedId()}
 
+{if $field->supportsQuotes()}
+       {js application='wcf' file='WCF.Message' bundle='WCF.Combined'}
+       <script data-relocate="true">
+               {* use empty require to ensure that the `Bootstrap` module is loaded and 
+                       `window.__wcf_bc_eventHandler` is available *}
+               require([], function() {
+                       {include file='__messageQuoteManager' wysiwygSelector=$field->getPrefixedId() supportPaste=true}
+                       
+                       {if $field->getQuoteData() !== null}
+                               var quoteHandler = new WCF.Message.Quote.Handler(
+                                       $quoteManager,
+                                       '{$field->getQuoteData('actionClass')|encodeJS}',
+                                       '{$field->getQuoteData('objectType')}',
+                                       '{$field->getQuoteData('selectors')[container]}',
+                                       '{$field->getQuoteData('selectors')[messageBody]}',
+                                       '{$field->getQuoteData('selectors')[messageContent]}',
+                                       true
+                               );
+                               
+                               elData(elById('{@$field->getPrefixedId()}'), 'quote-handler', quoteHandler);
+                       {/if}
+               });
+       </script>
+{/if}
+
 {include file='__formFieldFooter'}
index a10619172e251fdea28e3ef3dd067ef3ed15c456..b5a27314add07bc1c1a5a1b8ea1e9e8d71aa8f72 100644 (file)
@@ -71,7 +71,7 @@ interface IFormNode {
        public function available($available = true);
        
        /**
-        * Cleans up after the whole form is not used anymore.
+        * Cleans up after the form data has been saved and the form is not used anymore.
         * This method has to support being called multiple times.
         * 
         * This form should not clean up input fields. 
index da77389d787c2d1cb8fe29eadb2e5c364e2eafe9..799f6c9a82ede2b949ce31fef364f7a30dd8c739 100644 (file)
@@ -88,7 +88,7 @@ trait TFormParentNode {
        }
        
        /**
-        * Cleans up after the whole form is not used anymore.
+        * Cleans up after the form data has been saved and the form is not used anymore.
         * This method has to support being called multiple times.
         * 
         * This form should not clean up input fields.
index af4c1fe15e76df48e9f6db08d61f9160cbc62686..2052275ee90c124ea8737cc03f57c502d2b7d690 100644 (file)
@@ -72,6 +72,12 @@ class WysiwygFormContainer extends FormContainer {
         */
        protected $pollContainer;
        
+       /**
+        * quote-related data used to create the JavaScript quote manager
+        * @var null|array
+        */
+       protected $quoteData;
+       
        /**
         * settings form container
         * @var FormContainer
@@ -91,11 +97,17 @@ class WysiwygFormContainer extends FormContainer {
        protected $smiliesContainer;
        
        /**
-        * is `true` if the wysiwyg form field should support mentions, otherwise `false`
+        * is `true` if the wysiwyg form field will support mentions, otherwise `false`
         * @var boolean
         */
        protected $supportMentions = false;
        
+       /**
+        * is `true` if quotes are supported for this container, otherwise `false`
+        * @var boolean
+        */
+       protected $supportQuotes = false;
+       
        /**
         * is `true` if smilies are supported for this container, otherwise `false`
         * @var boolean
@@ -359,7 +371,15 @@ class WysiwygFormContainer extends FormContainer {
                $this->wysiwygField = WysiwygFormField::create($this->wysiwygId)
                        ->objectType($this->messageObjectType)
                        ->supportAttachments($this->attachmentData !== null)
-                       ->supportMentions($this->supportMentions);
+                       ->supportMentions($this->supportMentions)
+                       ->supportQuotes($this->supportQuotes);
+               if ($this->quoteData !== null) {
+                       $this->wysiwygField->quoteData(
+                               $this->quoteData['objectType'],
+                               $this->quoteData['actionClass'],
+                               $this->quoteData['selectors']
+                       );
+               }
                $this->smiliesContainer = WysiwygSmileyFormContainer::create($this->wysiwygId . 'SmiliesTab')
                        ->wysiwygId($this->getWysiwygId())
                        ->label('wcf.message.smilies')
@@ -438,6 +458,35 @@ class WysiwygFormContainer extends FormContainer {
                return $this;
        }
        
+       /**
+        * Sets the data required for advanced quote support for when quotable content is present
+        * on the active page and returns this container.
+        * 
+        * Calling this method automatically enables quote support for this container.
+        * 
+        * @param       string          $objectType     name of the relevant `com.woltlab.wcf.message.quote` object type
+        * @param       string          $actionClass    action class implementing `wcf\data\IMessageQuoteAction`
+        * @param       string[]        $selectors      selectors for the quotable content (required keys: `container`, `messageBody`, and `messageContent`)
+        * @return      static
+        */
+       public function quoteData($objectType, $actionClass, array $selectors = []) {
+               if ($this->wysiwygField !== null) {
+                       $this->wysiwygField->quoteData($objectType, $actionClass, $selectors);
+               }
+               else {
+                       $this->supportQuotes();
+                       
+                       // the parameters are validated by `WysiwygFormField`
+                       $this->quoteData = [
+                               'actionClass' => $actionClass,
+                               'objectType' => $objectType,
+                               'selectors' => $selectors
+                       ];
+               }
+               
+               return $this;
+       }
+       
        /**
         * Sets if mentions are supported by the editor field and returns this form container.
         * 
@@ -445,14 +494,33 @@ class WysiwygFormContainer extends FormContainer {
         * 
         * @param       boolean         $supportMention
         * @return      WysiwygFormContainer            this form container
-        * @throws      \BadMethodCallException         if the wysiwyg form field has already been initialized
         */
        public function supportMentions($supportMentions = true) {
                if ($this->wysiwygField !== null) {
-                       throw new \BadMethodCallException("The wysiwyg form field has already been initialized. Use the wysiwyg form field directly to manipulate mention support.");
+                       $this->wysiwygField->supportMentions($supportMentions);
+               }
+               else {
+                       $this->supportMentions = $supportMentions;
                }
                
-               $this->supportMentions = $supportMentions;
+               return $this;
+       }
+       
+       /**
+        * Sets if quotes are supported by the editor field and returns this form container.
+        * 
+        * By default, quotes are supported.
+        * 
+        * @param       boolean         $supportMention
+        * @return      WysiwygFormContainer            this form container
+        */
+       public function supportQuotes($supportQuotes = true) {
+               if ($this->wysiwygField !== null) {
+                       $this->wysiwygField->supportQuotes($supportQuotes);
+               }
+               else {
+                       $this->supportQuotes = $supportQuotes;
+               }
                
                return $this;
        }
@@ -464,14 +532,14 @@ class WysiwygFormContainer extends FormContainer {
         * 
         * @param       boolean         $supportSmilies
         * @return      WysiwygFormContainer            this form container
-        * @throws      \BadMethodCallException         if the poll container has already been initialized
         */
        public function supportSmilies($supportSmilies = true) {
                if ($this->smiliesContainer !== null) {
-                       throw new \BadMethodCallException("The smilies form container has already been initialized. Use the smilies container directly to manipulate smiley support.");
+                       $this->smiliesContainer->available($supportSmilies);
+               }
+               else {
+                       $this->supportSmilies = $supportSmilies;
                }
-               
-               $this->supportSmilies = $supportSmilies;
                
                return $this;
        }
index 3779037b8500d2f847caf9fbc9c0a77cfeb55b06..628118fff3fc5d1eca82a9701dc2802874201426 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 namespace wcf\system\form\builder\field\wysiwyg;
+use wcf\data\IMessageQuoteAction;
+use wcf\data\object\type\ObjectTypeCache;
 use wcf\system\form\builder\field\AbstractFormField;
 use wcf\system\form\builder\field\data\processor\CustomFormFieldDataProcessor;
 use wcf\system\form\builder\field\IMaximumLengthFormField;
@@ -11,6 +13,7 @@ use wcf\system\form\builder\IFormDocument;
 use wcf\system\form\builder\IObjectTypeFormNode;
 use wcf\system\form\builder\TObjectTypeFormNode;
 use wcf\system\html\input\HtmlInputProcessor;
+use wcf\system\message\quote\MessageQuoteManager;
 use wcf\util\StringUtil;
 
 /**
@@ -46,17 +49,29 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
        protected $lastEditTime = 0;
        
        /**
-        * is `true` if this form field should support attachments, otherwise `false`
+        * quote-related data used to create the JavaScript quote manager
+        * @var null|array
+        */
+       protected $quoteData;
+       
+       /**
+        * is `true` if this form field supports attachments, otherwise `false`
         * @var boolean 
         */
        protected $supportAttachments = false;
        
        /**
-        * is `true` if this form field should support mentions, otherwise `false`
+        * is `true` if this form field supports mentions, otherwise `false`
         * @var boolean
         */
        protected $supportMentions = false;
        
+       /**
+        * is `true` if this form field supports quotes, otherwise `false`
+        * @var boolean
+        */
+       protected $supportQuotes = false;
+       
        /**
         * @inheritDoc
         */
@@ -74,6 +89,13 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                return $this;
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function cleanup() {
+               MessageQuoteManager::getInstance()->saved();
+       }
+       
        /**
         * Returns the identifier used to autosave the field value. If autosave is disabled,
         * an empty string is returned.
@@ -84,6 +106,17 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                return $this->autosaveId;
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function getHtml() {
+               if ($this->supportsQuotes()) {
+                       MessageQuoteManager::getInstance()->assignVariables();
+               }
+               
+               return parent::getHtml();
+       }
+       
        /**
         * @inheritDoc
         */
@@ -101,6 +134,31 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                return $this->lastEditTime;
        }
        
+       /**
+        * Returns all quote data or specific quote data if an argument is given.
+        * 
+        * @param       null|string     $index          quote data index
+        * @return      string[]|string
+        * 
+        * @throws      \BadMethodCallException         if quotes are not supported for this field
+        * @throws      \InvalidArgumentException       if unknown quote data is requested
+        */
+       public function getQuoteData($index = null) {
+               if (!$this->supportQuotes()) {
+                       throw new \BadMethodCallException("Quotes are not supported.");
+               }
+               
+               if ($index === null) {
+                       return $this->quoteData;
+               }
+               
+               if (!isset($this->quoteData[$index])) {
+                       throw new \InvalidArgumentException("Unknown quote data '{$index}'.");
+               }
+               
+               return $this->quoteData[$index];
+       }
+       
        /**
         * @inheritDoc
         */
@@ -136,6 +194,50 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                
                return $this;
        }
+
+       /**
+        * Sets the data required for advanced quote support for when quotable content is present
+        * on the active page and returns this field.
+        * 
+        * Calling this method automatically enables quote support for this field.
+        * 
+        * @param       string          $objectType     name of the relevant `com.woltlab.wcf.message.quote` object type
+        * @param       string          $actionClass    action class implementing `wcf\data\IMessageQuoteAction`
+        * @param       string[]        $selectors      selectors for the quotable content (required keys: `container`, `messageBody`, and `messageContent`)
+        * @return      static
+        * 
+        * @throws      \InvalidArgumentException       if any of the given arguments is invalid
+        */
+       public function quoteData($objectType, $actionClass, array $selectors = []) {
+               if (ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.message.quote', $objectType) === null) {
+                       throw new \InvalidArgumentException("Unknown message quote object type '{$objectType}'.");
+               }
+               
+               if (!class_exists($actionClass)) {
+                       throw new \InvalidArgumentException("Unknown class '{$actionClass}'");
+               }
+               if (!is_subclass_of($actionClass, IMessageQuoteAction::class)) {
+                       throw new \InvalidArgumentException("'{$actionClass}' does not implement '" . IMessageQuoteAction::class . "'.");
+               }
+               
+               if (!empty($selectors)) {
+                       foreach (['container', 'messageBody', 'messageContent'] as $selector) {
+                               if (!isset($selectors[$selector])) {
+                                       throw new \InvalidArgumentException("Missing selector '{$selector}'.");
+                               }
+                       }
+               }
+               
+               $this->supportQuotes();
+               
+               $this->quoteData = [
+                       'actionClass' => $actionClass,
+                       'objectType' => $objectType,
+                       'selectors' => $selectors
+               ];
+               
+               return $this;
+       }
        
        /**
         * @inheritDoc
@@ -149,6 +251,10 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                        }
                }
                
+               if ($this->supportsQuotes()) {
+                       MessageQuoteManager::getInstance()->readFormParameters();
+               }
+               
                return $this;
        }
        
@@ -176,6 +282,26 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                return $this;
        }
        
+       /**
+        * Sets if the form field supports quotes and returns this field.
+        * 
+        * @param       boolean         $supportQuotes
+        * @return      WysiwygFormField                this field
+        */
+       public function supportQuotes($supportQuotes = true) {
+               $this->supportQuotes = $supportQuotes;
+               
+               if (!$this->supportsQuotes()) {
+                       // unset previously set quote data
+                       $this->quoteData = null;
+               }
+               else {
+                       MessageQuoteManager::getInstance()->readParameters();
+               }
+               
+               return $this;
+       }
+       
        /**
         * Returns `true` if the form field supports attachments and returns `false` otherwise.
         * 
@@ -203,6 +329,17 @@ class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormFi
                return $this->supportMentions;
        }
        
+       /**
+        * Returns `true` if the form field supports quotes and returns `false` otherwise.
+        * 
+        * By default, quotes are supported.
+        * 
+        * @return      boolean
+        */
+       public function supportsQuotes() {
+               return $this->supportQuotes !== null;
+       }
+       
        /**
         * @inheritDoc
         */