PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.4
Pods – Custom Content Types and Fields v3.3.4
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / classes / fields / wysiwyg.php
pods / classes / fields Last commit date
avatar.php 3 years ago boolean.php 3 years ago code.php 2 years ago color.php 1 year ago comment.php 4 years ago currency.php 10 months ago date.php 2 years ago datetime.php 1 year ago email.php 1 year ago file.php 11 months ago heading.php 1 year ago html.php 2 years ago link.php 1 year ago number.php 10 months ago oembed.php 1 year ago paragraph.php 2 years ago password.php 1 year ago phone.php 1 year ago pick.php 11 months ago slug.php 3 years ago taxonomy.php 4 years ago text.php 2 years ago time.php 2 years ago website.php 11 months ago wysiwyg.php 1 year ago
wysiwyg.php
443 lines
1 <?php
2
3 /**
4 * @package Pods\Fields
5 */
6 class PodsField_WYSIWYG extends PodsField {
7
8 /**
9 * {@inheritdoc}
10 */
11 public static $group = 'Paragraph';
12
13 /**
14 * {@inheritdoc}
15 */
16 public static $type = 'wysiwyg';
17
18 /**
19 * {@inheritdoc}
20 */
21 public static $label = 'WYSIWYG (Visual Editor)';
22
23 /**
24 * {@inheritdoc}
25 */
26 public static $prepare = '%s';
27
28 /**
29 * {@inheritdoc}
30 */
31 public function setup() {
32
33 static::$group = __( 'Paragraph', 'pods' );
34 static::$label = __( 'WYSIWYG (Visual Editor)', 'pods' );
35 }
36
37 /**
38 * {@inheritdoc}
39 */
40 public function options() {
41 $options = [
42 static::$type . '_editor' => [
43 'label' => __( 'Editor', 'pods' ),
44 'default' => 'tinymce',
45 'type' => 'pick',
46 'data' => apply_filters(
47 'pods_form_ui_field_wysiwyg_editors', [
48 'tinymce' => __( 'TinyMCE (WP Default, cannot be used with repeatable fields)', 'pods' ),
49 'quill' => __( 'Quill Editor (Limited line break functionality)', 'pods' ),
50 'cleditor' => __( 'CLEditor (No longer available, the fallback uses Quill Editor)', 'pods' ),
51 ]
52 ),
53 'pick_format_single' => 'dropdown',
54 'pick_show_select_text' => 0,
55 'dependency' => true,
56 ],
57 'editor_options' => [
58 'label' => __( 'Editor Options', 'pods' ),
59 'type' => 'boolean_group',
60 'depends-on' => [ static::$type . '_editor' => 'tinymce' ],
61 'boolean_group' => [
62 static::$type . '_media_buttons' => [
63 'label' => __( 'Enable Media Buttons', 'pods' ),
64 'default' => 1,
65 'type' => 'boolean',
66 ],
67 static::$type . '_delay_init' => [
68 'label' => __( 'Delay TinyMCE Initialization', 'pods' ),
69 'default' => 0,
70 'type' => 'boolean',
71 'help' => __( 'Delay the initialization of the TinyMCE editor until you click to initialize it.', 'pods' ),
72 ],
73 ],
74 ],
75 static::$type . '_editor_height' => [
76 'label' => __( 'Editor Height', 'pods' ),
77 'help' => __( 'Height in pixels', 'pods' ),
78 'default' => '',
79 'type' => 'number',
80 'depends-on' => [ static::$type . '_editor' => 'tinymce' ],
81 'number_decimals' => 0,
82 ],
83 'output_options' => [
84 'label' => __( 'Output Options', 'pods' ),
85 'type' => 'boolean_group',
86 'boolean_group' => [
87 static::$type . '_trim' => [
88 'label' => __( 'Trim extra whitespace before/after contents', 'pods' ),
89 'default' => 1,
90 'type' => 'boolean',
91 ],
92 static::$type . '_trim_lines' => [
93 'label' => __( 'Trim whitespace at the end of lines', 'pods' ),
94 'default' => 0,
95 'type' => 'boolean',
96 ],
97 static::$type . '_trim_p_brs' => [
98 'label' => __( 'Remove blank lines including empty "p" tags and "br" tags', 'pods' ),
99 'default' => 0,
100 'type' => 'boolean',
101 ],
102 static::$type . '_trim_extra_lines' => [
103 'label' => __( 'Remove extra blank lines (when there are 3+ blank lines, replace with a maximum of 2)', 'pods' ),
104 'default' => 0,
105 'type' => 'boolean',
106 ],
107 static::$type . '_sanitize_html' => [
108 'label' => __( 'Sanitize HTML', 'pods' ),
109 'default' => 1,
110 'help' => __( 'This sanitizes things like script tags and other content not normally allowed in WordPress content. Disable this only if you trust users who will have access to enter content into this field.', 'pods' ),
111 'type' => 'boolean',
112 'dependency' => true,
113 ],
114 static::$type . '_oembed' => [
115 'label' => __( 'Enable oEmbed', 'pods' ),
116 'default' => 0,
117 'type' => 'boolean',
118 'help' => [
119 __( 'Embed videos, images, tweets, and other content.', 'pods' ),
120 'https://wordpress.org/support/article/embeds/',
121 ],
122 ],
123 static::$type . '_wptexturize' => [
124 'label' => __( 'Enable wptexturize', 'pods' ),
125 'default' => 1,
126 'type' => 'boolean',
127 'help' => [
128 __( 'Transforms less-beautiful text characters into stylized equivalents.', 'pods' ),
129 'https://developer.wordpress.org/reference/functions/wptexturize/',
130 ],
131 ],
132 static::$type . '_convert_chars' => [
133 'label' => __( 'Enable convert_chars', 'pods' ),
134 'default' => 1,
135 'type' => 'boolean',
136 'help' => [
137 __( 'Converts text into valid XHTML and Unicode', 'pods' ),
138 'https://developer.wordpress.org/reference/functions/convert_chars/',
139 ],
140 ],
141 static::$type . '_wpautop' => [
142 'label' => __( 'Enable wpautop', 'pods' ),
143 'default' => 1,
144 'type' => 'boolean',
145 'help' => [
146 __( 'Changes double line-breaks in the text into HTML paragraphs', 'pods' ),
147 'https://developer.wordpress.org/reference/functions/wpautop/',
148 ],
149 ],
150 static::$type . '_allow_shortcode' => [
151 'label' => __( 'Allow Shortcodes', 'pods' ),
152 'default' => 0,
153 'type' => 'boolean',
154 'dependency' => true,
155 'help' => [
156 __( 'Embed [shortcodes] that help transform your static content into dynamic content.', 'pods' ),
157 'https://codex.wordpress.org/Shortcode_API',
158 ],
159 ],
160 ],
161 ],
162 static::$type . '_allowed_html_tags' => [
163 'label' => __( 'Allowed HTML Tags', 'pods' ),
164 'default' => '',
165 'type' => 'text',
166 'help' => __( 'Format: strong em a ul ol li b i', 'pods' ),
167 ],
168 ];
169
170 return $options;
171 }
172
173 /**
174 * {@inheritdoc}
175 */
176 public function schema( $options = null ) {
177
178 $schema = 'LONGTEXT';
179
180 return $schema;
181 }
182
183 /**
184 * {@inheritdoc}
185 */
186 public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
187 $value = $this->strip_html( $value, $options );
188
189 /**
190 * Allow filtering of the display value for the WYSIWYG field type before it's processed.
191 *
192 * NOTE: HTML has already been stripped at this point.
193 *
194 * @since 3.1.0
195 *
196 * @param mixed|null $value Current value.
197 * @param string $type Field type.
198 * @param string|null $name Field name.
199 * @param array|null $options Field options.
200 * @param array|null $pod Pod information.
201 * @param int|string|null $id Current item ID.
202 */
203 $value = apply_filters( 'pods_form_ui_field_wysiwyg_display_value_pre_process', $value, static::$type, $name, $options, $pod, $id );
204
205 $value = $this->strip_shortcodes( $value, $options );
206 $value = $this->trim_whitespace( $value, $options );
207
208 if ( 1 === (int) pods_v( static::$type . '_oembed', $options, 0 ) ) {
209 $post_temp = false;
210
211 // Workaround for WP_Embed since it needs a $post to work from
212 if ( 'post_type' === pods_v( 'type', $pod ) && 0 < $id && ( ! isset( $GLOBALS['post'] ) || empty( $GLOBALS['post'] ) ) ) {
213 $post_temp = true;
214
215 // @codingStandardsIgnoreLine
216 $GLOBALS['post'] = get_post( $id );
217 }
218
219 /**
220 * @var $embed WP_Embed
221 */
222 $embed = $GLOBALS['wp_embed'];
223 $value = $embed->run_shortcode( $value );
224 $value = $embed->autoembed( $value );
225
226 // Cleanup after ourselves
227 if ( $post_temp ) {
228 // @codingStandardsIgnoreLine
229 $GLOBALS['post'] = null;
230 }
231 }//end if
232
233 if ( 1 === (int) pods_v( static::$type . '_wptexturize', $options, 1 ) ) {
234 $value = wptexturize( $value );
235 }
236
237 if ( 1 === (int) pods_v( static::$type . '_convert_chars', $options, 1 ) ) {
238 $value = convert_chars( $value );
239 }
240
241 if ( 1 === (int) pods_v( static::$type . '_wpautop', $options, 1 ) ) {
242 $value = wpautop( $value );
243 }
244
245 if ( 1 === (int) pods_v( static::$type . '_allow_shortcode', $options, 0 ) ) {
246 if ( 1 === (int) pods_v( static::$type . '_wpautop', $options, 1 ) ) {
247 $value = shortcode_unautop( $value );
248 }
249
250 $value = do_shortcode( $value );
251 }
252
253 /**
254 * Allow filtering of the display value for the WYSIWYG field type.
255 *
256 * @since 3.1.0
257 *
258 * @param mixed|null $value Current value.
259 * @param string $type Field type.
260 * @param string|null $name Field name.
261 * @param array|null $options Field options.
262 * @param array|null $pod Pod information.
263 * @param int|string|null $id Current item ID.
264 */
265 return apply_filters( 'pods_form_ui_field_wysiwyg_display_value', $value, static::$type, $name, $options, $pod, $id );
266 }
267
268 /**
269 * {@inheritdoc}
270 */
271 public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
272
273 $options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;
274 $form_field_type = PodsForm::$field_type;
275
276 // Force TinyMCE repeatable fields to not be repeatable because it lacks compatibility.
277 if ( 1 === (int) pods_v( 'repeatable', $options ) && 'tinymce' === pods_v( static::$type . '_editor', $options, 'tinymce', true ) ) {
278 $options['repeatable'] = 0;
279 }
280
281 $value = $this->maybe_sanitize_output( $value, $options );
282 $value = $this->normalize_value_for_input( $value, $options, "\n" );
283
284 // Normalize the line breaks for React.
285 if ( null !== $value ) {
286 $value = str_replace( "\r\n", "\n", $value );
287 $value = str_replace( "\r", "\n", $value );
288 }
289
290 if ( isset( $options['name'] ) && ! pods_permission( $options ) ) {
291 if ( pods_v( 'read_only', $options, false ) ) {
292 $options['readonly'] = true;
293
294 $field_type = 'textarea';
295 } else {
296 return;
297 }
298 } elseif ( ! pods_has_permissions( $options ) && pods_v( 'read_only', $options, false ) ) {
299 $options['readonly'] = true;
300
301 $field_type = 'textarea';
302 } elseif ( 'tinymce' === pods_v( static::$type . '_editor', $options ) ) {
303 // TinyMCE does not support repeatable.
304 if ( is_array( $value ) ) {
305 $value = implode( "\n", $value );
306 }
307
308 $field_type = 'tinymce';
309
310 // Enforce boolean.
311 $options[ static::$type . '_media_buttons' ] = filter_var( pods_v( static::$type . '_media_buttons', $options, true ), FILTER_VALIDATE_BOOLEAN );
312
313 // Set up default editor.
314 // @todo Support this properly in React, which will be a challenge.
315 $options[ static::$type . '_default_editor' ] = pods_v( static::$type . '_default_editor', $options, wp_default_editor(), true );
316
317 if ( in_array( $options[ static::$type . '_default_editor' ], [ 'html', 'tinymce' ], true ) ) {
318 $options[ static::$type . '_default_editor' ] = 'tinymce';
319 }
320
321 if (
322 (
323 function_exists( 'did_filter' )
324 && ! did_filter( 'tiny_mce_before_init' )
325 )
326 || ! did_action( 'enqueue_block_editor_assets' )
327 ) {
328 wp_tinymce_inline_scripts();
329 }
330
331 wp_enqueue_editor();
332
333 $settings = [];
334 $settings['textarea_name'] = $name;
335 $settings['media_buttons'] = false;
336
337 if ( ! ( defined( 'PODS_DISABLE_FILE_UPLOAD' ) && true === PODS_DISABLE_FILE_UPLOAD ) && ! ( defined( 'PODS_UPLOAD_REQUIRE_LOGIN' ) && is_bool( PODS_UPLOAD_REQUIRE_LOGIN ) && true === PODS_UPLOAD_REQUIRE_LOGIN && ! is_user_logged_in() ) && ! ( defined( 'PODS_UPLOAD_REQUIRE_LOGIN' ) && ! is_bool( PODS_UPLOAD_REQUIRE_LOGIN ) && ( ! is_user_logged_in() || ! current_user_can( PODS_UPLOAD_REQUIRE_LOGIN ) ) ) ) {
338 $settings['media_buttons'] = $options[ static::$type . '_media_buttons' ];
339 } else {
340 $options[ static::$type . '_media_buttons' ] = false;
341 }
342
343 $editor_height = (int) pods_v( static::$type . '_editor_height', $options, false );
344
345 if ( $editor_height ) {
346 $settings['editor_height'] = $editor_height;
347 }
348
349 if ( ! empty( $options[ static::$type . '_tinymce_settings' ] ) ) {
350 $settings = array_merge( $settings, $options[ static::$type . '_tinymce_settings' ] );
351 }
352
353 // WP will handle the scripting needed, but we won't output the textarea here.
354 ob_start();
355 wp_editor( (string) $value, '_pods_dfv_' . $name, $settings );
356 $unused_output = ob_get_clean();
357
358 // Workaround because the above already outputs the style we need.
359 $styles = wp_styles();
360
361 $found_editor_buttons = array_search( 'editor-buttons', $styles->done, true );
362
363 if ( false !== $found_editor_buttons ) {
364 unset( $styles->done[ $found_editor_buttons ] );
365 }
366
367 $found_dashicons = array_search( 'dashicons', $styles->done, true );
368
369 if ( false !== $found_dashicons ) {
370 unset( $styles->done[ $found_dashicons ] );
371 }
372
373 wp_print_styles( 'editor-buttons' );
374 wp_print_styles( 'dashicons' );
375 } elseif ( 'quill' === pods_v( static::$type . '_editor', $options ) ) {
376 $field_type = 'quill';
377 } elseif ( 'cleditor' === pods_v( static::$type . '_editor', $options ) ) {
378 $field_type = 'quill';
379 } else {
380 // Support custom WYSIWYG integration
381 $editor_type = pods_v( static::$type . '_editor', $options );
382 do_action( "pods_form_ui_field_wysiwyg_{$editor_type}", $name, $value, $options, $pod, $id );
383 do_action( 'pods_form_ui_field_wysiwyg', pods_v( static::$type . '_editor', $options ), $name, $value, $options, $pod, $id );
384
385 return;
386 }//end if
387
388 if ( ! empty( $options['disable_dfv'] ) ) {
389 return pods_view( PODS_DIR . 'ui/fields/' . $field_type . '.php', compact( array_keys( get_defined_vars() ) ) );
390 }
391
392 $type = pods_v( 'type', $options, static::$type );
393
394 $args = compact( array_keys( get_defined_vars() ) );
395 $args = (object) $args;
396
397 $this->render_input_script( $args );
398 }
399
400 /**
401 * {@inheritdoc}
402 */
403 public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {
404 $value = $this->strip_html( $value, $options );
405 $value = $this->strip_shortcodes( $value, $options );
406 $value = $this->trim_whitespace( $value, $options );
407
408 $length = (int) pods_v( static::$type . '_max_length', $options, 0 );
409
410 if ( 0 < $length && $length < pods_mb_strlen( $value ) ) {
411 $value = pods_mb_substr( $value, 0, $length );
412 }
413
414 return $value;
415 }
416
417 /**
418 * {@inheritdoc}
419 */
420 public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) {
421 $value = $this->strip_html( $value, $options );
422 $value = $this->strip_shortcodes( $value, $options );
423 $value = $this->trim_whitespace( $value, $options );
424
425 $value = wp_trim_words( $value );
426
427 return $value;
428 }
429
430 /**
431 * {@inheritdoc}
432 */
433 public function strip_html( $value, $options = null ) {
434
435 $options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;
436
437 // Allow HTML tags.
438 $options[ static::$type . '_allow_html' ] = 1;
439
440 return parent::strip_html( $value, $options );
441 }
442 }
443