Reworked the rewrite rule generator
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / form / builder / field / wysiwyg / WysiwygFormField.class.php
CommitLineData
5158aa21 1<?php
71212588 2namespace wcf\system\form\builder\field\wysiwyg;
0dc68d99
MS
3use wcf\data\IMessageQuoteAction;
4use wcf\data\object\type\ObjectTypeCache;
71212588 5use wcf\system\form\builder\field\AbstractFormField;
76d761f6 6use wcf\system\form\builder\field\data\processor\CustomFormFieldDataProcessor;
71212588
MS
7use wcf\system\form\builder\field\IMaximumLengthFormField;
8use wcf\system\form\builder\field\IMinimumLengthFormField;
71212588
MS
9use wcf\system\form\builder\field\TMaximumLengthFormField;
10use wcf\system\form\builder\field\TMinimumLengthFormField;
5158aa21
MS
11use wcf\system\form\builder\field\validation\FormFieldValidationError;
12use wcf\system\form\builder\IFormDocument;
26e502ba
MS
13use wcf\system\form\builder\IObjectTypeFormNode;
14use wcf\system\form\builder\TObjectTypeFormNode;
5158aa21 15use wcf\system\html\input\HtmlInputProcessor;
0dc68d99 16use wcf\system\message\quote\MessageQuoteManager;
5158aa21
MS
17use wcf\util\StringUtil;
18
19/**
20 * Implementation of a form field for wysiwyg editors.
21 *
22 * @author Matthias Schmidt
7b7b9764 23 * @copyright 2001-2019 WoltLab GmbH
5158aa21
MS
24 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
25 * @package WoltLabSuite\Core\System\Form\Builder\Field
dd2d8c0c 26 * @since 5.2
5158aa21 27 */
71212588 28class WysiwygFormField extends AbstractFormField implements IMaximumLengthFormField, IMinimumLengthFormField, IObjectTypeFormNode {
5158aa21
MS
29 use TMaximumLengthFormField;
30 use TMinimumLengthFormField;
71212588 31 use TObjectTypeFormNode;
5158aa21
MS
32
33 /**
34 * identifier used to autosave the field value; if empty, autosave is disabled
35 * @var string
36 */
9299c8e5 37 protected $autosaveId = '';
5158aa21 38
71212588
MS
39 /**
40 * input processor containing the wysiwyg text
41 * @var HtmlInputProcessor
42 */
43 protected $htmlInputProcessor;
44
5158aa21
MS
45 /**
46 * last time the field has been edited; if `0`, the last edit time is unknown
47 * @var int
48 */
9299c8e5 49 protected $lastEditTime = 0;
5158aa21
MS
50
51 /**
0dc68d99
MS
52 * quote-related data used to create the JavaScript quote manager
53 * @var null|array
54 */
55 protected $quoteData;
56
57 /**
58 * is `true` if this form field supports attachments, otherwise `false`
71212588 59 * @var boolean
5158aa21 60 */
71212588
MS
61 protected $supportAttachments = false;
62
63 /**
0dc68d99 64 * is `true` if this form field supports mentions, otherwise `false`
71212588
MS
65 * @var boolean
66 */
67 protected $supportMentions = false;
5158aa21 68
0dc68d99
MS
69 /**
70 * is `true` if this form field supports quotes, otherwise `false`
71 * @var boolean
72 */
73 protected $supportQuotes = false;
74
5158aa21
MS
75 /**
76 * @inheritDoc
77 */
78 protected $templateName = '__wysiwygFormField';
79
80 /**
81 * Sets the identifier used to autosave the field value and returns this field.
82 *
83 * @param string $autosaveId identifier used to autosave field value
26e502ba 84 * @return WysiwygFormField this field
5158aa21 85 */
020e6570 86 public function autosaveId($autosaveId) {
9299c8e5 87 $this->autosaveId = $autosaveId;
5158aa21
MS
88
89 return $this;
90 }
91
0dc68d99
MS
92 /**
93 * @inheritDoc
94 */
95 public function cleanup() {
96 MessageQuoteManager::getInstance()->saved();
97 }
98
5158aa21
MS
99 /**
100 * Returns the identifier used to autosave the field value. If autosave is disabled,
9d68d5b2 101 * an empty string is returned.
5158aa21
MS
102 *
103 * @return string
104 */
7b43decd 105 public function getAutosaveId() {
9299c8e5 106 return $this->autosaveId;
5158aa21
MS
107 }
108
0dc68d99
MS
109 /**
110 * @inheritDoc
111 */
112 public function getHtml() {
113 if ($this->supportsQuotes()) {
114 MessageQuoteManager::getInstance()->assignVariables();
115 }
116
117 return parent::getHtml();
118 }
119
5158aa21
MS
120 /**
121 * @inheritDoc
122 */
7b43decd 123 public function getObjectTypeDefinition() {
5158aa21
MS
124 return 'com.woltlab.wcf.message';
125 }
126
127 /**
128 * Returns the last time the field has been edited. If no last edit time has
129 * been set, `0` is returned.
130 *
131 * @return int
132 */
7b43decd 133 public function getLastEditTime() {
9299c8e5 134 return $this->lastEditTime;
5158aa21
MS
135 }
136
0dc68d99
MS
137 /**
138 * Returns all quote data or specific quote data if an argument is given.
139 *
140 * @param null|string $index quote data index
141 * @return string[]|string
142 *
143 * @throws \BadMethodCallException if quotes are not supported for this field
144 * @throws \InvalidArgumentException if unknown quote data is requested
145 */
146 public function getQuoteData($index = null) {
147 if (!$this->supportQuotes()) {
148 throw new \BadMethodCallException("Quotes are not supported.");
149 }
150
151 if ($index === null) {
152 return $this->quoteData;
153 }
154
155 if (!isset($this->quoteData[$index])) {
156 throw new \InvalidArgumentException("Unknown quote data '{$index}'.");
157 }
158
159 return $this->quoteData[$index];
160 }
161
5158aa21
MS
162 /**
163 * @inheritDoc
164 */
7b43decd 165 public function hasSaveValue() {
071fff89 166 return false;
5158aa21
MS
167 }
168
169 /**
170 * Sets the last time this field has been edited and returns this field.
171 *
172 * @param int $lastEditTime last time field has been edited
26e502ba 173 * @return WysiwygFormField this field
5158aa21 174 */
020e6570 175 public function lastEditTime($lastEditTime) {
9299c8e5 176 $this->lastEditTime = $lastEditTime;
5158aa21
MS
177
178 return $this;
179 }
180
181 /**
182 * @inheritDoc
183 */
7b43decd 184 public function populate() {
5158aa21
MS
185 parent::populate();
186
187 $this->getDocument()->getDataHandler()->add(new CustomFormFieldDataProcessor('wysiwyg', function(IFormDocument $document, array $parameters) {
8bdbb251 188 if ($this->checkDependencies()) {
f3a825e9 189 $parameters[$this->getObjectProperty() . '_htmlInputProcessor'] = $this->htmlInputProcessor;
8bdbb251 190 }
5158aa21
MS
191
192 return $parameters;
193 }));
194
195 return $this;
196 }
0dc68d99
MS
197
198 /**
199 * Sets the data required for advanced quote support for when quotable content is present
200 * on the active page and returns this field.
201 *
202 * Calling this method automatically enables quote support for this field.
203 *
204 * @param string $objectType name of the relevant `com.woltlab.wcf.message.quote` object type
205 * @param string $actionClass action class implementing `wcf\data\IMessageQuoteAction`
206 * @param string[] $selectors selectors for the quotable content (required keys: `container`, `messageBody`, and `messageContent`)
207 * @return static
208 *
209 * @throws \InvalidArgumentException if any of the given arguments is invalid
210 */
211 public function quoteData($objectType, $actionClass, array $selectors = []) {
212 if (ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.message.quote', $objectType) === null) {
213 throw new \InvalidArgumentException("Unknown message quote object type '{$objectType}'.");
214 }
215
216 if (!class_exists($actionClass)) {
217 throw new \InvalidArgumentException("Unknown class '{$actionClass}'");
218 }
219 if (!is_subclass_of($actionClass, IMessageQuoteAction::class)) {
220 throw new \InvalidArgumentException("'{$actionClass}' does not implement '" . IMessageQuoteAction::class . "'.");
221 }
222
223 if (!empty($selectors)) {
224 foreach (['container', 'messageBody', 'messageContent'] as $selector) {
225 if (!isset($selectors[$selector])) {
226 throw new \InvalidArgumentException("Missing selector '{$selector}'.");
227 }
228 }
229 }
230
231 $this->supportQuotes();
232
233 $this->quoteData = [
234 'actionClass' => $actionClass,
235 'objectType' => $objectType,
236 'selectors' => $selectors
237 ];
238
239 return $this;
240 }
5158aa21
MS
241
242 /**
243 * @inheritDoc
244 */
7b43decd 245 public function readValue() {
4b683ecd
MS
246 if ($this->getDocument()->hasRequestData($this->getPrefixedId())) {
247 $value = $this->getDocument()->getRequestData($this->getPrefixedId());
248
249 if (is_string($value)) {
9299c8e5 250 $this->value = StringUtil::trim($value);
4b683ecd 251 }
5158aa21
MS
252 }
253
0dc68d99
MS
254 if ($this->supportsQuotes()) {
255 MessageQuoteManager::getInstance()->readFormParameters();
256 }
257
5158aa21
MS
258 return $this;
259 }
260
71212588
MS
261 /**
262 * Sets if the form field supports attachments and returns this field.
263 *
264 * @param boolean $supportAttachments
26e502ba 265 * @return WysiwygFormField this field
71212588
MS
266 */
267 public function supportAttachments($supportAttachments = true) {
268 $this->supportAttachments = $supportAttachments;
269
270 return $this;
271 }
272
273 /**
274 * Sets if the form field supports mentions and returns this field.
275 *
276 * @param boolean $supportMentions
26e502ba 277 * @return WysiwygFormField this field
71212588
MS
278 */
279 public function supportMentions($supportMentions = true) {
280 $this->supportMentions = $supportMentions;
281
282 return $this;
283 }
284
0dc68d99
MS
285 /**
286 * Sets if the form field supports quotes and returns this field.
287 *
288 * @param boolean $supportQuotes
289 * @return WysiwygFormField this field
290 */
291 public function supportQuotes($supportQuotes = true) {
292 $this->supportQuotes = $supportQuotes;
293
294 if (!$this->supportsQuotes()) {
295 // unset previously set quote data
296 $this->quoteData = null;
297 }
298 else {
299 MessageQuoteManager::getInstance()->readParameters();
300 }
301
302 return $this;
303 }
304
71212588
MS
305 /**
306 * Returns `true` if the form field supports attachments and returns `false` otherwise.
307 *
308 * Important: If this method returns `true`, it does not necessarily mean that attachment
309 * support will also work as that is the task of `WysiwygAttachmentFormField`. This method
310 * is primarily relevant to inform the JavaScript API that the field supports attachments
311 * so that the relevant editor plugin is loaded.
312 *
313 * By default, attachments are not supported.
314 *
315 * @return boolean
316 */
317 public function supportsAttachments() {
318 return $this->supportAttachments;
319 }
320
321 /**
322 * Returns `true` if the form field supports mentions and returns `false` otherwise.
323 *
324 * By default, mentions are not supported.
325 *
326 * @return boolean
327 */
328 public function supportsMentions() {
329 return $this->supportMentions;
330 }
331
0dc68d99
MS
332 /**
333 * Returns `true` if the form field supports quotes and returns `false` otherwise.
334 *
a11099b7 335 * By default, quotes are not supported.
0dc68d99
MS
336 *
337 * @return boolean
338 */
339 public function supportsQuotes() {
477f6668 340 return $this->supportQuotes;
0dc68d99
MS
341 }
342
5158aa21
MS
343 /**
344 * @inheritDoc
345 */
346 public function validate() {
6fc348d5
MS
347 $this->htmlInputProcessor = new HtmlInputProcessor();
348 $this->htmlInputProcessor->process($this->getValue(), $this->getObjectType()->objectType);
349
350 if ($this->isRequired() && $this->htmlInputProcessor->appearsToBeEmpty()) {
5158aa21
MS
351 $this->addValidationError(new FormFieldValidationError('empty'));
352 }
353 else {
6fc348d5
MS
354 $message = $this->htmlInputProcessor->getTextContent();
355 $this->validateMinimumLength($message);
356 $this->validateMaximumLength($message);
5158aa21
MS
357 }
358
5158aa21
MS
359 parent::validate();
360 }
361}