Completed
Push — develop ( 85c2ba...5fea9a )
by Marco
04:56
created

Admin::admin_print_scripts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Black Studio TinyMCE Widget - Admin features
4
 *
5
 * @package Black_Studio_TinyMCE_Widget
6
 */
7
8
namespace Black_Studio_TinyMCE_Widget\Admin;
9
10
use WP_Query;
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
if ( ! class_exists( 'Black_Studio_TinyMCE_Widget\\Admin\\Admin', false ) ) {
18
19
	/**
20
	 * Class that provides admin functionalities
21
	 *
22
	 * @package Black_Studio_TinyMCE_Widget
23
	 * @since 2.0.0
24
	 */
25
	final class Admin {
26
27
		/**
28
		 * The single instance of the class
29
		 *
30
		 * @var object
31
		 * @since 2.0.0
32
		 */
33
		protected static $_instance = null;
34
35
		/**
36
		 * Array containing the plugin links
37
		 *
38
		 * @var array
39
		 * @since 2.0.0
40
		 */
41
		protected $links;
42
43
		/**
44
		 * Return the single class instance
45
		 *
46
		 * @return object
47
		 * @since 2.0.0
48
		 */
49
		public static function instance() {
50
			if ( is_null( self::$_instance ) ) {
51
				self::$_instance = new self();
52
			}
53
			return self::$_instance;
54
		}
55
56
		/**
57
		 * Class constructor
58
		 *
59
		 * @uses add_action()
60
		 * @uses add_filter()
61
		 * @uses get_option()
62
		 * @uses get_bloginfo()
63
		 *
64
		 * @global object $wp_embed
65
		 * @since 2.0.0
66
		 */
67
		protected function __construct() {
68
			// Register action and filter hooks.
69
			add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
70
			add_action( 'admin_init', array( $this, 'admin_init' ), 20 );
71
			add_action( 'init', array( $this, 'register_dummy_post_type' ) );
72
		}
73
74
		/**
75
		 * Prevent the class from being cloned
76
		 *
77
		 * @return void
78
		 * @since 2.0.0
79
		 */
80
		protected function __clone() {
81
			_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; uh?' ), '2.0' );
82
		}
83
84
		/**
85
		 * Load language files
86
		 *
87
		 * @uses load_plugin_textdomain()
88
		 *
89
		 * @return void
90
		 * @since 2.0.0
91
		 */
92
		public function load_textdomain() {
93
			load_plugin_textdomain( 'black-studio-tinymce-widget', false, dirname( dirname( dirname( plugin_basename( __FILE__ ) ) ) ) . '/languages/' );
94
		}
95
96
		/**
97
		 * Checks if the plugin admin code should be loaded
98
		 *
99
		 * @uses apply_filters()
100
		 *
101
		 * @global string $pagenow
102
		 * @return boolean
103
		 * @since 2.0.0
104
		 */
105
		public function enabled() {
106
			global $pagenow;
107
			$enabled_pages = apply_filters( 'black_studio_tinymce_enable_pages', array( 'widgets.php', 'customize.php', 'admin-ajax.php' ) );
108
			return apply_filters( 'black_studio_tinymce_enable', in_array( $pagenow, $enabled_pages, true ) );
109
		}
110
111
		/**
112
		 * Add actions and filters (only in widgets admin page)
113
		 *
114
		 * @uses add_action()
115
		 * @uses add_filter()
116
		 * @uses do_action()
117
		 *
118
		 * @return void
119
		 * @since 2.0.0
120
		 */
121
		public function admin_init() {
122
			$this->init_links();
123
			add_action( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
124
			if ( $this->enabled() ) {
125
				add_action( 'admin_head', array( $this, 'enqueue_media' ) );
126
				add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ) );
127
				add_action( 'admin_print_styles', array( $this, 'admin_print_styles' ) );
128
				add_action( 'admin_print_footer_scripts', array( $this, 'admin_print_footer_scripts' ) );
129
				add_action( 'black_studio_tinymce_before_editor', array( $this, 'display_links' ) );
130
				add_action( 'black_studio_tinymce_editor', array( $this, 'editor' ), 10, 4 );
131
				add_action( 'black_studio_tinymce_after_editor', array( $this, 'fix_the_editor_content_filter' ) );
132
				add_action( 'wp_tiny_mce_init', array( $this, 'wp_tiny_mce_init' ) );
133
				add_filter( 'wp_editor_settings', array( $this, 'editor_settings' ), 5, 2 );
134
				add_filter( 'tiny_mce_before_init', array( $this, 'tinymce_fix_rtl' ), 10 );
135
				add_filter( 'tiny_mce_before_init', array( $this, 'tinymce_fullscreen' ), 10, 2 );
136
				add_filter( 'quicktags_settings', array( $this, 'quicktags_fullscreen' ), 10, 2 );
137
				if ( ! user_can_richedit() ) {
138
					add_action( 'admin_notices', array( $this, 'visual_editor_disabled_notice' ) );
139
				}
140
				add_action( 'wp_ajax_bstw_visual_editor_disabled_dismiss_notice', array( $this, 'visual_editor_disabled_dismiss_notice' ) );
141
				do_action( 'black_studio_tinymce_load' );
142
			}
143
		}
144
145
		/**
146
		 * Instantiate tinyMCE editor
147
		 *
148
		 * @uses add_thickbox()
149
		 * @uses wp_enqueue_media()
150
		 *
151
		 * @return void
152
		 * @since 2.0.0
153
		 */
154
		public function enqueue_media() {
155
			// Add support for thickbox media dialog.
156
			add_thickbox();
157
			// New media modal dialog (WP 3.5+).
158
			if ( function_exists( 'wp_enqueue_media' ) ) {
159
				wp_enqueue_media();
160
			}
161
		}
162
163
		/**
164
		 * Enqueue styles
165
		 *
166
		 * @uses wp_enqueue_style()
167
		 *
168
		 * @return void
169
		 * @since 2.0.0
170
		 */
171
		public function admin_print_styles() {
172
			wp_enqueue_style( 'wp-jquery-ui-dialog' );
173
			wp_enqueue_style( 'editor-buttons' );
174
			$this->enqueue_style();
175
		}
176
177
		/**
178
		 * Helper function to enqueue style
179
		 *
180
		 * @uses apply_filters()
181
		 * @uses wp_enqueue_style()
182
		 * @uses plugins_url()
183
		 * @uses SCRIPT_DEBUG
184
		 *
185
		 * @return void
186
		 * @since 2.0.0
187
		 */
188
		public function enqueue_style() {
189
			$style  = apply_filters( 'black_studio_tinymce_widget_style', 'black-studio-tinymce-widget' );
190
			$path   = apply_filters( 'black_studio_tinymce_widget_style_path', 'css/' );
191
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.css' : '.min.css';
192
			wp_enqueue_style(
193
				$style,
194
				plugins_url( $path . $style . $suffix, dirname( dirname( __FILE__ ) ) ),
195
				array(),
196
				bstw()->get_version()
197
			);
198
		}
199
200
		/**
201
		 * Enqueue header scripts
202
		 *
203
		 * @uses wp_enqueue_script()
204
		 * @uses do_action()
205
		 *
206
		 * @return void
207
		 * @since 2.0.0
208
		 */
209
		public function admin_print_scripts() {
210
			wp_enqueue_script( 'media-upload' );
211
			wp_enqueue_script( 'wplink' );
212
			wp_enqueue_script( 'wpdialogs-popup' );
213
			$this->enqueue_script();
214
			$this->localize_script();
215
			do_action( 'wp_enqueue_editor', array( 'tinymce' => true ) );
216
		}
217
218
		/**
219
		 * Helper function to enqueue script
220
		 *
221
		 * @uses apply_filters()
222
		 * @uses wp_enqueue_script()
223
		 * @uses plugins_url()
224
		 * @uses SCRIPT_DEBUG
225
		 *
226
		 * @return void
227
		 * @since 2.0.0
228
		 */
229
		public function enqueue_script() {
230
			$script = apply_filters( 'black_studio_tinymce_widget_script', 'black-studio-tinymce-widget' );
231
			$path   = apply_filters( 'black_studio_tinymce_widget_script_path', 'js/' );
232
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js';
233
			wp_enqueue_script(
234
				$script,
235
				plugins_url( $path . $script . $suffix, dirname( dirname( __FILE__ ) ) ),
236
				array( 'jquery', 'editor', 'quicktags' ),
237
				bstw()->get_version(),
238
				true
239
			);
240
		}
241
242
		/**
243
		 * Helper function to enqueue localized script
244
		 *
245
		 * @uses apply_filters()
246
		 * @uses wp_localize_script()
247
		 *
248
		 * @return void
249
		 * @since 2.0.0
250
		 */
251
		public function localize_script() {
252
			$container_selectors = apply_filters( 'black_studio_tinymce_container_selectors', array( 'div.widget', 'div.widget-inside' ) );
253
			$activate_events     = apply_filters( 'black_studio_tinymce_activate_events', array() );
254
			$deactivate_events   = apply_filters( 'black_studio_tinymce_deactivate_events', array() );
255
			$data                = array(
256
				'dummy_post_id'       => $this->get_dummy_post_id(),
257
				'container_selectors' => implode( ', ', $container_selectors ),
258
				'activate_events'     => $activate_events,
259
				'deactivate_events'   => $deactivate_events,
260
				/* translators: error message shown when a duplicated widget ID is detected */
261
				'error_duplicate_id'  => __( 'ERROR: Duplicate widget ID detected. To avoid content loss, please create a new widget with the same content and then delete this one.', 'black-studio-tinymce-widget' ),
262
			);
263
			wp_localize_script( apply_filters( 'black_studio_tinymce_widget_script', 'black-studio-tinymce-widget' ), 'bstw_data', $data );
264
		}
265
266
		/**
267
		 * Enqueue footer scripts
268
		 *
269
		 * @return void
270
		 * @since 2.0.0
271
		 */
272
		public function admin_print_footer_scripts() {
273
			$this->editor( '', 'black-studio-tinymce-widget', 'black-studio-tinymce-widget' );
274
		}
275
276
		/**
277
		 * Output the visual editor
278
		 *
279
		 * @uses wp_editor()
280
		 *
281
		 * @param string $text      Text inside the editor.
282
		 * @param string $editor_id Editor instance ID.
283
		 * @param string $name      Editor instance name.
284
		 * @param string $type      Editor instance type.
285
		 * @return void
286
		 * @since 2.0.0
287
		 */
288
		public function editor( $text, $editor_id, $name = '', $type = 'visual' ) {
289
			wp_editor( $text, $editor_id, array(
290
				'textarea_name'  => $name,
291
				'default_editor' => 'visual' === $type ? 'tmce' : 'html',
292
			) );
293
		}
294
295
		/**
296
		 * Remove editor content filters for multiple editor instances
297
		 * Workaround for WordPress Core bug #28403 https://core.trac.wordpress.org/ticket/28403
298
		 *
299
		 * @uses remove_filter
300
		 *
301
		 * @return void
302
		 * @since 2.1.7
303
		 */
304
		public function fix_the_editor_content_filter() {
305
			remove_filter( 'the_editor_content', 'wp_htmledit_pre' );
306
			remove_filter( 'the_editor_content', 'wp_richedit_pre' );
307
		}
308
309
		/**
310
		 * Setup editor instance for event handling
311
		 *
312
		 * @uses SCRIPT_DEBUG
313
		 *
314
		 * @return void
315
		 * @since 2.2.1
316
		 */
317
		public function wp_tiny_mce_init() {
318
			$script = 'black-studio-tinymce-widget-setup';
319
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.js' : '.min.js';
320
			echo "\t\t" . '<script type="text/javascript" src="' . plugins_url( 'js/' . $script . $suffix, dirname( dirname( __FILE__ ) ) ) . '"></script>' . "\n"; // xss ok.
321
		}
322
323
		/**
324
		 * Set editor settings
325
		 *
326
		 * @param mixed[] $settings Array of settings.
327
		 * @param string  $editor_id Editor instance ID.
328
		 * @return mixed[]
329
		 * @since 2.0.0
330
		 */
331
		public function editor_settings( $settings, $editor_id ) {
332
			if ( strstr( $editor_id, 'black-studio-tinymce' ) ) {
333
				$settings['tinymce']       = array(
334
					'wp_skip_init'       => 'widget-black-studio-tinymce-__i__-text' === $editor_id,
335
					'add_unload_trigger' => false,
336
					'wp_autoresize_on'   => false,
337
				);
338
				$settings['editor_height'] = 350;
339
				$settings['dfw']           = true;
340
				$settings['editor_class']  = 'black-studio-tinymce';
341
			}
342
			return $settings;
343
		}
344
345
		/**
346
		 * Initialize plugin links
347
		 *
348
		 * @return void
349
		 * @since 2.0.0
350
		 */
351
		public function init_links() {
352
			$this->links = array(
353
				/* translators: text used for plugin home link */
354
				'https://wordpress.org/plugins/black-studio-tinymce-widget/' => __( 'Home', 'black-studio-tinymce-widget' ),
355
				/* translators: text used for support faq link */
356
				'https://wordpress.org/plugins/black-studio-tinymce-widget/faq/' => __( 'FAQ', 'black-studio-tinymce-widget' ),
357
				/* translators: text used for support forum link */
358
				'https://wordpress.org/support/plugin/black-studio-tinymce-widget' => __( 'Support', 'black-studio-tinymce-widget' ),
359
				/* translators: text used for reviews link */
360
				'https://wordpress.org/support/view/plugin-reviews/black-studio-tinymce-widget' => __( 'Rate', 'black-studio-tinymce-widget' ),
361
				/* translators: text used for follow on twitter link */
362
				'https://twitter.com/blackstudioita' => __( 'Follow', 'black-studio-tinymce-widget' ),
363
				/* translators: text used for donation link */
364
				'https://www.blackstudio.it/en/wordpress-plugins/black-studio-tinymce-widget/' => __( 'Donate', 'black-studio-tinymce-widget' ),
365
			);
366
		}
367
368
		/**
369
		 * Display plugin links
370
		 *
371
		 * @return void
372
		 * @since 2.0.0
373
		 */
374
		public function display_links() {
375
			echo "\t<div class='bstw-links'>\n";
376
			echo "\t\t<span class='bstw-links-list'>\n";
377
			$counter = count( $this->links ) - 1;
378
			foreach ( $this->links as $url => $label ) {
379
				$separator = ( $counter-- > 0 ? ' | ' : '' );
380
				echo "\t\t\t<a href='" . esc_url( $url ) . "' target='_blank'>" . esc_html( $label ) . "</a>$separator\n"; // xss ok.
381
			}
382
			echo "\t\t</span>\n";
383
			/* translators: text used for the icon that shows the plugin links */
384
			echo "\t\t<a class='bstw-links-icon icon16 icon-plugins' href='#' title='" . esc_attr( __( 'About Black Studio TinyMCE Widget plugin', 'black-studio-tinymce-widget' ) ) . "'></a>\n";
385
			echo "\t</div>\n";
386
		}
387
388
		/**
389
		 * Show row meta on the plugin screen
390
		 *
391
		 * @uses esc_html()
392
		 * @uses esc_url()
393
		 *
394
		 * @param string[] $links Array of links.
395
		 * @param string   $file  Plugin's filename.
396
		 * @return string[]
397
		 * @since 2.0.0
398
		 */
399
		public function plugin_row_meta( $links, $file ) {
400
			if ( bstw()->get_basename() === $file ) {
401
				foreach ( $this->links as $url => $label ) {
402
					$links[ $label ] = '<a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $label ) . '</a>';
403
				}
404
			}
405
			return $links;
406
		}
407
408
		/**
409
		 * Fix for rtl languages
410
		 *
411
		 * @param mixed[] $settings Array of settings.
412
		 * @return mixed[]
413
		 * @since 2.1.0
414
		 */
415
		public function tinymce_fix_rtl( $settings ) {
416
			// This fix has to be applied to all editor instances (not just BSTW ones).
417
			if ( is_rtl() && isset( $settings['plugins'] ) && ',directionality' === $settings['plugins'] ) {
418
				unset( $settings['plugins'] );
419
			}
420
			return $settings;
421
		}
422
423
		/**
424
		 * Apply TinyMCE default fullscreen
425
		 *
426
		 * @param mixed[]     $settings  Array of settings.
427
		 * @param string|null $editor_id Editor ID.
428
		 * @return mixed[]
429
		 * @since 2.1.2
430
		 */
431
		public function tinymce_fullscreen( $settings, $editor_id = null ) {
432
			if ( strstr( $editor_id, 'black-studio-tinymce' ) ) {
433
				for ( $i = 1; $i <= 4; $i++ ) {
434
					$toolbar = 'toolbar' . $i;
435
					if ( isset( $settings[ $toolbar ] ) ) {
436
						$settings[ $toolbar ] = str_replace( 'wp_fullscreen', 'wp_fullscreen,fullscreen', $settings[ $toolbar ] );
437
					}
438
				}
439
			}
440
			return $settings;
441
		}
442
443
		/**
444
		 * Disable Quicktags default fullscreen
445
		 *
446
		 * @param mixed[] $settings  Array of settings.
447
		 * @param string  $editor_id Editor ID.
448
		 * @return mixed[]
449
		 * @since 2.1.2
450
		 */
451
		public function quicktags_fullscreen( $settings, $editor_id ) {
452
			if ( strstr( $editor_id, 'black-studio-tinymce' ) ) {
453
				$settings['buttons'] = str_replace( ',fullscreen', '', $settings['buttons'] );
454
			}
455
			return $settings;
456
		}
457
458
		/**
459
		 * Show admin notice when visual editor is disabled in current user's profile settings
460
		 *
461
		 * @uses get_user_meta()
462
		 * @uses get_current_user_id()
463
		 *
464
		 * @return void
465
		 * @since 2.4.0
466
		 */
467
		public function visual_editor_disabled_notice() {
468
			global $pagenow;
469
			$dismissed = false;
470
			if ( function_exists( 'get_user_meta' ) ) {
471
				$dismissed = get_user_meta( get_current_user_id(), '_bstw_visual_editor_disabled_notice_dismissed', true );
472
			}
473
			if ( 'widgets.php' === $pagenow && empty( $dismissed ) ) {
474
				echo '<div class="bstw-visual-editor-disabled-notice notice notice-warning is-dismissible">';
475
				/* translators: warning message shown when when visual editor is disabled in current user's profile settings */
476
				echo '<p>' . esc_html( __( 'Visual Editor is disabled in your Profile settings. You need to enable it in order to use the Visual Editor widget at its full potential.', 'black-studio-tinymce-widget' ) ) . '</p>';
477
				echo '</div>';
478
			}
479
		}
480
481
		/**
482
		 * Store dismission of the "Visual Editor disabled" notice for the current user
483
		 *
484
		 * @uses add_user_meta()
485
		 * @uses get_current_user_id()
486
		 *
487
		 * @return void
488
		 * @since 2.4.0
489
		 */
490
		public function visual_editor_disabled_dismiss_notice() {
491
			if ( function_exists( 'add_user_meta' ) ) {
492
				add_user_meta( get_current_user_id(), '_bstw_visual_editor_disabled_notice_dismissed', true );
493
			}
494
		}
495
496
		/**
497
		 * Register a private custom post type to be used for link embed previews
498
		 *
499
		 * @uses register_post_type()
500
		 *
501
		 * @return void
502
		 * @since 3.0.0
503
		 */
504
		public function register_dummy_post_type() {
505
			$args = array(
506
				'public'             => false,
507
				'publicly_queryable' => false,
508
				'show_ui'            => false,
509
				'query_var'          => false,
510
				'rewrite'            => false,
511
				'capability_type'    => 'post',
512
				'hierarchical'       => false,
513
				'menu_position'      => null,
514
				'show_in_nav_menus'  => false,
515
				'has_archive'        => false,
516
			);
517
			register_post_type( 'bstw_dummy', $args );
518
		}
519
520
		/**
521
		 * Get dummy post ID for link embed previews
522
		 *
523
		 * @uses WP_Query()
524
		 * @uses wp_insert_post()
525
		 * @uses update_option()
526
		 * @uses get_option()
527
		 *
528
		 * @return int
529
		 * @since 3.0.0
530
		 */
531
		public function get_dummy_post_id() {
532
			$query_post = new WP_Query( 'post_type=bstw_dummy' );
533
			if ( $query_post->post_count > 0 ) {
534
				$dummy_post_id = $query_post->post->ID;
535
			} else {
536
				$dummy_post_id = wp_insert_post( array( 'post_type' => 'bstw_dummy' ) );
537
			}
538
			return $dummy_post_id;
539
		}
540
541
	} // END class Black_Studio_TinyMCE_Admin
542
543
} // END class_exists check
544