2 namespace wcf\system\form\builder\field\wysiwyg
;
3 use wcf\data\IMessageQuoteAction
;
4 use wcf\data\
object\type\ObjectTypeCache
;
5 use wcf\system\bbcode\BBCodeHandler
;
6 use wcf\system\form\builder\data\processor\CustomFormDataProcessor
;
7 use wcf\system\form\builder\field\AbstractFormField
;
8 use wcf\system\form\builder\field\IAttributeFormField
;
9 use wcf\system\form\builder\field\IMaximumLengthFormField
;
10 use wcf\system\form\builder\field\IMinimumLengthFormField
;
11 use wcf\system\form\builder\field\TInputAttributeFormField
;
12 use wcf\system\form\builder\field\TMaximumLengthFormField
;
13 use wcf\system\form\builder\field\TMinimumLengthFormField
;
14 use wcf\system\form\builder\field\validation\FormFieldValidationError
;
15 use wcf\system\form\builder\IFormDocument
;
16 use wcf\system\form\builder\IObjectTypeFormNode
;
17 use wcf\system\form\builder\TObjectTypeFormNode
;
18 use wcf\system\html\input\HtmlInputProcessor
;
19 use wcf\system\message\censorship\Censorship
;
20 use wcf\system\message\quote\MessageQuoteManager
;
22 use wcf\util\StringUtil
;
25 * Implementation of a form field for wysiwyg editors.
27 * @author Matthias Schmidt
28 * @copyright 2001-2019 WoltLab GmbH
29 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
30 * @package WoltLabSuite\Core\System\Form\Builder\Field
33 class WysiwygFormField
extends AbstractFormField
implements IAttributeFormField
, IMaximumLengthFormField
, IMinimumLengthFormField
, IObjectTypeFormNode
{
34 use TInputAttributeFormField
{
35 getReservedFieldAttributes
as private inputGetReservedFieldAttributes
;
37 use TMaximumLengthFormField
;
38 use TMinimumLengthFormField
;
39 use TObjectTypeFormNode
;
42 * identifier used to autosave the field value; if empty, autosave is disabled
45 protected $autosaveId = '';
48 * input processor containing the wysiwyg text
49 * @var HtmlInputProcessor
51 protected $htmlInputProcessor;
54 * last time the field has been edited; if `0`, the last edit time is unknown
57 protected $lastEditTime = 0;
60 * quote-related data used to create the JavaScript quote manager
66 * is `true` if this form field supports attachments, otherwise `false`
69 protected $supportAttachments = false;
72 * is `true` if this form field supports mentions, otherwise `false`
75 protected $supportMentions = false;
78 * is `true` if this form field supports quotes, otherwise `false`
81 protected $supportQuotes = false;
86 protected $javaScriptDataHandlerModule = 'WoltLabSuite/Core/Form/Builder/Field/Value';
91 protected $templateName = '__wysiwygFormField';
94 * Sets the identifier used to autosave the field value and returns this field.
96 * @param string $autosaveId identifier used to autosave field value
97 * @return WysiwygFormField this field
99 public function autosaveId($autosaveId) {
100 $this->autosaveId
= $autosaveId;
108 public function cleanup() {
109 MessageQuoteManager
::getInstance()->saved();
113 * Returns the identifier used to autosave the field value. If autosave is disabled,
114 * an empty string is returned.
118 public function getAutosaveId() {
119 return $this->autosaveId
;
125 public function getFieldHtml() {
126 if ($this->supportsQuotes()) {
127 MessageQuoteManager
::getInstance()->assignVariables();
130 /** @noinspection PhpUndefinedFieldInspection */
131 $disallowedBBCodesPermission = $this->getObjectType()->disallowedBBCodesPermission
;
132 if ($disallowedBBCodesPermission === null) {
133 $disallowedBBCodesPermission = 'user.message.disallowedBBCodes';
136 BBCodeHandler
::getInstance()->setDisallowedBBCodes(explode(
138 WCF
::getSession()->getPermission($disallowedBBCodesPermission)
141 return parent
::getFieldHtml();
147 public function getObjectTypeDefinition() {
148 return 'com.woltlab.wcf.message';
152 * Returns the last time the field has been edited. If no last edit time has
153 * been set, `0` is returned.
157 public function getLastEditTime() {
158 return $this->lastEditTime
;
162 * Returns all quote data or specific quote data if an argument is given.
164 * @param null|string $index quote data index
165 * @return string[]|string
167 * @throws \BadMethodCallException if quotes are not supported for this field
168 * @throws \InvalidArgumentException if unknown quote data is requested
170 public function getQuoteData($index = null) {
171 if (!$this->supportQuotes()) {
172 throw new \
BadMethodCallException("Quotes are not supported.");
175 if ($index === null) {
176 return $this->quoteData
;
179 if (!isset($this->quoteData
[$index])) {
180 throw new \
InvalidArgumentException("Unknown quote data '{$index}'.");
183 return $this->quoteData
[$index];
189 public function hasSaveValue() {
194 * Sets the last time this field has been edited and returns this field.
196 * @param int $lastEditTime last time field has been edited
197 * @return WysiwygFormField this field
199 public function lastEditTime($lastEditTime) {
200 $this->lastEditTime
= $lastEditTime;
208 public function populate() {
211 $this->getDocument()->getDataHandler()->addProcessor(new CustomFormDataProcessor('wysiwyg', function(IFormDocument
$document, array $parameters) {
212 if ($this->checkDependencies()) {
213 $parameters[$this->getObjectProperty() . '_htmlInputProcessor'] = $this->htmlInputProcessor
;
223 * Sets the data required for advanced quote support for when quotable content is present
224 * on the active page and returns this field.
226 * Calling this method automatically enables quote support for this field.
228 * @param string $objectType name of the relevant `com.woltlab.wcf.message.quote` object type
229 * @param string $actionClass action class implementing `wcf\data\IMessageQuoteAction`
230 * @param string[] $selectors selectors for the quotable content (required keys: `container`, `messageBody`, and `messageContent`)
233 * @throws \InvalidArgumentException if any of the given arguments is invalid
235 public function quoteData($objectType, $actionClass, array $selectors = []) {
236 if (ObjectTypeCache
::getInstance()->getObjectTypeByName('com.woltlab.wcf.message.quote', $objectType) === null) {
237 throw new \
InvalidArgumentException("Unknown message quote object type '{$objectType}'.");
240 if (!class_exists($actionClass)) {
241 throw new \
InvalidArgumentException("Unknown class '{$actionClass}'");
243 if (!is_subclass_of($actionClass, IMessageQuoteAction
::class)) {
244 throw new \
InvalidArgumentException("'{$actionClass}' does not implement '" . IMessageQuoteAction
::class . "'.");
247 if (!empty($selectors)) {
248 foreach (['container', 'messageBody', 'messageContent'] as $selector) {
249 if (!isset($selectors[$selector])) {
250 throw new \
InvalidArgumentException("Missing selector '{$selector}'.");
255 $this->supportQuotes();
258 'actionClass' => $actionClass,
259 'objectType' => $objectType,
260 'selectors' => $selectors
269 public function readValue() {
270 if ($this->getDocument()->hasRequestData($this->getPrefixedId())) {
271 $value = $this->getDocument()->getRequestData($this->getPrefixedId());
273 if (is_string($value)) {
274 $this->value
= StringUtil
::trim($value);
278 if ($this->supportsQuotes()) {
279 MessageQuoteManager
::getInstance()->readFormParameters();
286 * Sets if the form field supports attachments and returns this field.
288 * @param boolean $supportAttachments
289 * @return WysiwygFormField this field
291 public function supportAttachments($supportAttachments = true) {
292 $this->supportAttachments
= $supportAttachments;
298 * Sets if the form field supports mentions and returns this field.
300 * @param boolean $supportMentions
301 * @return WysiwygFormField this field
303 public function supportMentions($supportMentions = true) {
304 $this->supportMentions
= $supportMentions;
310 * Sets if the form field supports quotes and returns this field.
312 * @param boolean $supportQuotes
313 * @return WysiwygFormField this field
315 public function supportQuotes($supportQuotes = true) {
316 $this->supportQuotes
= $supportQuotes;
318 if (!$this->supportsQuotes()) {
319 // unset previously set quote data
320 $this->quoteData
= null;
323 MessageQuoteManager
::getInstance()->readParameters();
330 * Returns `true` if the form field supports attachments and returns `false` otherwise.
332 * Important: If this method returns `true`, it does not necessarily mean that attachment
333 * support will also work as that is the task of `WysiwygAttachmentFormField`. This method
334 * is primarily relevant to inform the JavaScript API that the field supports attachments
335 * so that the relevant editor plugin is loaded.
337 * By default, attachments are not supported.
341 public function supportsAttachments() {
342 return $this->supportAttachments
;
346 * Returns `true` if the form field supports mentions and returns `false` otherwise.
348 * By default, mentions are not supported.
352 public function supportsMentions() {
353 return $this->supportMentions
;
357 * Returns `true` if the form field supports quotes and returns `false` otherwise.
359 * By default, quotes are not supported.
363 public function supportsQuotes() {
364 return $this->supportQuotes
;
370 public function validate() {
371 /** @noinspection PhpUndefinedFieldInspection */
372 $disallowedBBCodesPermission = $this->getObjectType()->disallowedBBCodesPermission
;
373 if ($disallowedBBCodesPermission === null) {
374 $disallowedBBCodesPermission = 'user.message.disallowedBBCodes';
377 BBCodeHandler
::getInstance()->setDisallowedBBCodes(explode(
379 WCF
::getSession()->getPermission($disallowedBBCodesPermission)
382 $this->htmlInputProcessor
= new HtmlInputProcessor();
383 $this->htmlInputProcessor
->process($this->getValue(), $this->getObjectType()->objectType
);
385 if ($this->isRequired() && $this->htmlInputProcessor
->appearsToBeEmpty()) {
386 $this->addValidationError(new FormFieldValidationError('empty'));
389 $disallowedBBCodes = $this->htmlInputProcessor
->validate();
390 if (!empty($disallowedBBCodes)) {
391 $this->addValidationError(new FormFieldValidationError(
393 'wcf.message.error.disallowedBBCodes',
394 ['disallowedBBCodes' => $disallowedBBCodes]
398 $message = $this->htmlInputProcessor
->getTextContent();
399 $this->validateMinimumLength($message);
400 $this->validateMaximumLength($message);
402 if (empty($this->getValidationErrors()) && ENABLE_CENSORSHIP
) {
403 $result = Censorship
::getInstance()->test($message);
405 $this->addValidationError(new FormFieldValidationError(
407 'wcf.message.error.censoredWordsFound',
408 ['censoredWords' => $result]
422 protected static function getReservedFieldAttributes(): array {
424 static::inputGetReservedFieldAttributes(),
427 'data-autosave-last-edit-time',
428 'data-disable-attachments',
429 'data-support-mention',