<?php class SiteOrigin_Widget_Field_TinyMCE extends SiteOrigin_Widget_Field_Text_Input_Base { /** * The number of visible rows in the textarea. * * @var int */ protected $rows = 10; /** * The editor initial height. Overrides rows if it is set. * * @var int */ protected $editor_height; /** * The editor to be displayed initially. * * @var string */ protected $default_editor = 'tinymce'; /** * The last editor selected by the user. * * @var string */ protected $selected_editor; /** * An array of the buttons which will be rendered for the first toolbar of the TinyMCE editor. * * @var array */ protected $mce_buttons; /** * An array of the buttons which will be rendered for the second toolbar of the TinyMCE editor. * * @var array */ protected $mce_buttons_2; /** * An array of the buttons which will be rendered for the third toolbar of the TinyMCE editor. * * @var array */ protected $mce_buttons_3; /** * An array of the buttons which will be rendered for the fourth toolbar of the TinyMCE editor. * * @var array */ protected $mce_buttons_4; /** * An array of the buttons which will be rendered for the QuickTags editor. * * @var array */ protected $quicktags_buttons; /** * Whether to apply `wpautop` processing. (Adds paragraphs for double linebreaks) Default is true. * * @var array */ protected $wpautop; /** * An array of filter callbacks to apply to the set of buttons which will be rendered for the editor. * * @var array */ protected $button_filters; /** * An array of the included plugins to enable for the TinyMCE editor. * * @var array */ protected $mce_plugins; /** * An array of external plugins for the TinyMCE editor. * * @var array */ protected $mce_external_plugins; /** * Updated Editor JS API was introduced in WP 4.8 so need to check for compatibility with older versions. * * @var bool */ private $wp_version_lt_4_8; /** * Whether to add the Add Media button. * * @var bool */ protected $media_buttons = true; protected function get_default_options() { return array( 'wpautop' => true, 'mce_buttons' => array( 'formatselect', 'bold', 'italic', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'wp_adv', ), 'mce_buttons_2' => array( 'strikethrough', 'hr', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help', ), 'quicktags_buttons' => array( 'strong', 'em', 'link', 'block', 'del', 'ins', 'img', 'ul', 'ol', 'li', 'code', 'more', 'close', ), 'mce_plugins' => array( 'charmap', 'colorpicker', 'hr', 'lists', 'media', 'paste', 'tabfocus', 'textcolor', 'fullscreen', 'wordpress', 'wpautoresize', 'wpeditimage', 'wpemoji', 'wpgallery', 'wplink', 'wpdialogs', 'wptextpattern', 'wpview', ), 'mce_external_plugins' => array(), ); } protected function initialize() { if ( ! is_admin() ) { return; } global $wp_version; $this->wp_version_lt_4_8 = version_compare( $wp_version, '4.8', '<' ); if ( ! empty( $this->wp_version_lt_4_8 ) ) { add_filter( 'mce_buttons', array( $this, 'mce_buttons_filter' ), 10, 2 ); add_filter( 'quicktags_settings', array( $this, 'quicktags_settings' ), 10, 2 ); } if ( ! empty( $this->button_filters ) ) { foreach ( $this->button_filters as $filter_name => $filter ) { $is_valid_filter = preg_match( '/mce_buttons(?:_[1-4])?|quicktags_settings/', $filter_name ) && ! empty( $filter ) && is_callable( $filter ); if ( $is_valid_filter ) { add_filter( $filter_name, array( $this, $filter_name ), 10, 2 ); } } } if ( class_exists( 'WC_Shortcodes_TinyMCE_Buttons' ) ) { if ( ! empty( $this->wp_version_lt_4_8 ) ) { $screen = get_current_screen(); if ( ! is_null( $screen ) && $screen->id != 'widgets' ) { add_filter( 'mce_external_plugins', array( $this, 'add_wpc_shortcodes_plugin' ), 15 ); add_filter( 'mce_buttons', array( $this, 'register_wpc_shortcodes_button' ), 15 ); } } else { $this->mce_external_plugins = $this->add_wpc_shortcodes_plugin( $this->mce_external_plugins ); $this->mce_buttons = $this->register_wpc_shortcodes_button( $this->mce_buttons ); } } if ( class_exists( 'WC_Shortcodes_Admin' ) ) { if ( ! empty( $this->wp_version_lt_4_8 ) ) { $screen = get_current_screen(); if ( ! is_null( $screen ) && $screen->id != 'widgets' ) { add_filter( 'mce_external_plugins', array( $this, 'add_wc_shortcodes_plugin' ), 15 ); add_filter( 'mce_buttons', array( $this, 'register_wc_shortcodes_button' ), 15 ); } } else { $this->mce_external_plugins = $this->add_wc_shortcodes_plugin( $this->mce_external_plugins ); $this->mce_buttons = $this->register_wc_shortcodes_button( $this->mce_buttons ); } } } public function add_wc_shortcodes_plugin( $plugins ) { if ( isset( $plugins['woocommerce_shortcodes'] ) ) { return $plugins; } $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; $editor_path = 'woocommerce-shortcodes/assets/js/editor' . $suffix . '.js'; if ( file_exists( WP_PLUGIN_DIR . '/' . $editor_path ) ) { $plugins['woocommerce_shortcodes'] = plugins_url( $editor_path ); } return $plugins; } public function register_wc_shortcodes_button( $buttons ) { if ( ! in_array( 'woocommerce_shortcodes', $buttons ) ) { array_push( $buttons, '|', 'woocommerce_shortcodes' ); } return $buttons; } public function add_wpc_shortcodes_plugin( $plugins ) { if ( ! isset( $plugins['wpc_shortcodes'] ) ) { $shortcodes_path = 'wc-shortcodes/includes/mce/js/shortcodes-tinymce-4.js'; if ( file_exists( WP_PLUGIN_DIR . '/' . $shortcodes_path ) ) { $plugins['wpc_shortcodes'] = plugins_url( $shortcodes_path . '?ver=' . WC_SHORTCODES_VERSION ); } } if ( ! isset( $plugins['wpc_font_awesome'] ) ) { $fontawesome_path = 'wc-shortcodes/includes/mce/js/font-awesome-tinymce-4.js'; if ( file_exists( WP_PLUGIN_DIR . '/' . $fontawesome_path ) ) { $plugins['wpc_font_awesome'] = plugins_url( $fontawesome_path . '?ver=' . WC_SHORTCODES_VERSION ); } } return $plugins; } public function register_wpc_shortcodes_button( $buttons ) { if ( ! in_array( 'wpc_shortcodes_button', $buttons ) ) { array_push( $buttons, 'wpc_shortcodes_button' ); } if ( ! in_array( 'wpcfontAwesomeGlyphSelect', $buttons ) ) { array_push( $buttons, 'wpcfontAwesomeGlyphSelect' ); } return $buttons; } /** * @return array|mixed */ public function __call( $name, $arguments ) { if ( preg_match( '/mce_buttons(?:_[1-4])?|quicktags_settings/', $name ) && ! empty( $this->button_filters[$name] ) ) { $filter = $this->button_filters[$name]; if ( ! empty( $filter[0] ) && is_a( $filter[0], 'SiteOrigin_Widget' ) ) { $widget = $filter[0]; $settings = ! empty( $arguments[0] ) ? $arguments[0] : array(); $editor_id = ! empty( $arguments[1] ) ? $arguments[1] : ''; if ( preg_match( '/widget-' . $widget->id_base . '-.*-' . $this->base_name . '/', $editor_id ) ) { return call_user_func( $filter, $settings, $editor_id ); } else { return $settings; } } } } public function mce_buttons_filter( $buttons, $editor_id ) { if ( ( $key = array_search( 'fullscreen', $buttons ) ) !== false ) { unset( $buttons[$key] ); } return $buttons; } public function quicktags_settings( $settings, $editor_id ) { $settings['buttons'] = preg_replace( '/,fullscreen/', '', $settings['buttons'] ); $settings['buttons'] = preg_replace( '/,dfw/', '', $settings['buttons'] ); return $settings; } protected function get_input_classes() { $classes = parent::get_input_classes(); $classes[] = 'wp-editor-area'; $classes[] = 'wp-exclude-emoji'; return $classes; } protected function render_before_field( $value, $instance ) { $selected_editor = $this->get_selected_editor( $instance ); if ( ! empty( $selected_editor ) ) { $this->selected_editor = $selected_editor; } else { $this->selected_editor = $this->default_editor; } parent::render_before_field( $value, $instance ); } protected function render_field( $value, $instance ) { if ( $this->wp_version_lt_4_8 ) { $this->render_field_pre48( $value, $instance ); return; } $user_can_richedit = user_can_richedit(); $selected_editor = $user_can_richedit && in_array( $this->selected_editor, array( 'tinymce', 'tmce' ) ) ? 'tmce' : 'html'; $settings = array( 'selectedEditor' => $selected_editor, 'media_buttons' => $this->media_buttons, ); if ( $user_can_richedit ) { $tiny_mce_plugins = apply_filters( 'tiny_mce_plugins', $this->mce_plugins ); $tmce_settings = array( 'toolbar1' => apply_filters( 'mce_buttons', $this->mce_buttons, $this->element_id ), 'toolbar2' => apply_filters( 'mce_buttons_2', $this->mce_buttons_2, $this->element_id ), 'toolbar3' => apply_filters( 'mce_buttons_3', $this->mce_buttons_3, $this->element_id ), 'toolbar4' => apply_filters( 'mce_buttons_4', $this->mce_buttons_4, $this->element_id ), 'plugins' => ! empty( $tiny_mce_plugins ) && is_array( $tiny_mce_plugins ) ? array_unique( $tiny_mce_plugins ) : array(), ); foreach ( $tmce_settings as $name => $setting ) { $tmce_settings[ $name ] = is_array( $setting ) ? implode( ',', $setting ) : ''; } $mce_external_plugins = apply_filters( 'mce_external_plugins', $this->mce_external_plugins, $this->element_id ); $tmce_settings['external_plugins'] = ! empty( $mce_external_plugins ) && is_array( $mce_external_plugins ) ? array_unique( $mce_external_plugins ) : array(); $suffix = SCRIPT_DEBUG ? '' : '.min'; $version = 'ver=' . get_bloginfo( 'version' ); // Default stylesheets $mce_css = includes_url( "css/dashicons$suffix.css?$version" ) . ',' . includes_url( "js/tinymce/skins/wordpress/wp-content.css?$version" ); $editor_styles = get_editor_stylesheets(); if ( ! empty( $editor_styles ) ) { // Force urlencoding of commas. foreach ( $editor_styles as $key => $url ) { if ( strpos( $url, ',' ) !== false ) { $editor_styles[ $key ] = str_replace( ',', '%2C', $url ); } } $mce_css .= ',' . implode( ',', $editor_styles ); } $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' ); $tmce_settings['content_css'] = $mce_css; $settings['tinymce'] = array( 'wp_skip_init' => strpos( $this->element_id, '__i__' ) != false || strpos( $this->element_id, '_id_' ) != false, 'wpautop' => ! empty( $this->wpautop ), ); $tmce_settings = apply_filters( 'tiny_mce_before_init', $tmce_settings, $this->element_id ); foreach ( $tmce_settings as $name => $setting ) { unset( $jdec ); if ( ! empty( $tmce_settings[ $name ] ) ) { // Attempt to decode setting as JSON. For back compat with filters used by WP editor. if ( is_string( $setting ) ) { $jdec = json_decode( $setting, true ); } $settings['tinymce'][ $name ] = empty( $jdec ) ? $setting : $jdec; } } if ( $this->media_buttons ) { $media_buttons_html = $this->render_media_buttons( $this->element_id ); } } $qt_settings = apply_filters( 'quicktags_settings', array( 'buttons' => $this->quicktags_buttons ), $this->element_id ); $qt_settings['buttons'] = ! empty( $qt_settings['buttons'] ) ? $qt_settings['buttons'] : array(); $qt_settings['buttons'] = is_array( $qt_settings['buttons'] ) ? implode( ',', $qt_settings['buttons'] ) : ''; $settings['quicktags'] = array( 'buttons' => $qt_settings['buttons'], ); $value = apply_filters( 'the_editor_content', $value, $this->selected_editor ); if ( ! empty( $value ) && stripos( $value, 'textarea' ) !== false ) { $value = preg_replace( '%</textarea%i', '</textarea', $value ); } if ( $this->media_buttons ) { $media_buttons_html = $this->render_media_buttons( $this->element_id ); } $settings['baseURL'] = includes_url( 'js/tinymce' ); $settings['suffix'] = SCRIPT_DEBUG ? '' : '.min'; ?> <div class="siteorigin-widget-tinymce-container" <?php if ( $this->media_buttons && ! empty( $media_buttons_html ) ) { ?> data-media-buttons="<?php echo esc_attr( json_encode( array( 'html' => $media_buttons_html ) ) ); ?>" <?php } ?> data-editor-settings="<?php echo esc_attr( json_encode( $settings ) ); ?>" > <textarea id="<?php echo esc_attr( $this->element_id ); ?>" name="<?php echo esc_attr( $this->element_name ); ?>" <?php if ( isset( $this->editor_height ) ) { ?> style="height: <?php echo (int) $this->editor_height; ?>px" <?php } else { ?> rows="<?php echo esc_attr( $this->rows ); ?>" <?php } ?> <?php $this->render_data_attributes( $this->get_input_data_attributes() ); ?> <?php $this->render_CSS_classes( $this->get_input_classes() ); ?> <?php if ( ! empty( $this->placeholder ) ) { echo 'placeholder="' . esc_attr( $this->placeholder ) . '"'; } ?> <?php if ( ! empty( $this->readonly ) ) { echo 'readonly'; } ?>><?php echo ! empty( $value ) ? esc_textarea( $value ) : ''; ?></textarea> </div> <input type="hidden" name="<?php echo esc_attr( $this->for_widget->so_get_field_name( $this->base_name . '_selected_editor', $this->parent_container ) ); ?>" class="siteorigin-widget-input siteorigin-widget-tinymce-selected-editor" value="<?php echo esc_attr( $this->selected_editor ); ?>" /> <?php } private function render_field_pre48( $value, $instance ) { $settings = array( 'textarea_name' => esc_attr( $this->element_name ), 'default_editor' => $this->selected_editor, 'textarea_rows' => $this->rows, 'editor_class' => 'siteorigin-widget-input', 'media_buttons' => $this->media_buttons, 'tinymce' => array( 'wp_skip_init' => strpos( $this->element_id, '__i__' ) != false || strpos( $this->element_id, '_id_' ) != false, ), ); if ( isset( $this->editor_height ) ) { $settings['editor_height'] = $this->editor_height; } preg_match( '/widget-(.+?)\[/', $this->element_name, $id_base_matches ); $widget_id_base = empty( $id_base_matches ) || count( $id_base_matches ) < 2 ? '' : $id_base_matches[1]; ?> <div class="siteorigin-widget-tinymce-container" data-mce-settings="<?php echo esc_attr( json_encode( $settings['tinymce'] ) ); ?>" data-qt-settings="<?php echo esc_attr( json_encode( array() ) ); ?>" data-widget-id-base="<?php echo esc_attr( $widget_id_base ); ?>" > <?php wp_editor( $value, esc_attr( $this->element_id ), $settings ); ?> </div> <input type="hidden" name="<?php echo esc_attr( $this->for_widget->so_get_field_name( $this->base_name . '_selected_editor', $this->parent_container ) ); ?>" class="siteorigin-widget-input siteorigin-widget-tinymce-selected-editor" value="<?php echo esc_attr( $this->selected_editor ); ?>" /> <?php if ( $this->selected_editor == 'html' ) { remove_filter( 'the_editor_content', 'wp_htmledit_pre' ); } if ( $this->selected_editor == 'tinymce' ) { remove_filter( 'the_editor_content', 'wp_richedit_pre' ); } } public function enqueue_scripts() { if ( $this->wp_version_lt_4_8 ) { $src = plugin_dir_url( __FILE__ ) . 'js/tinymce-field-pre48' . SOW_BUNDLE_JS_SUFFIX . '.js'; $deps = array( 'jquery', 'editor', 'quicktags' ); } else { wp_enqueue_editor(); $src = plugin_dir_url( __FILE__ ) . 'js/tinymce-field' . SOW_BUNDLE_JS_SUFFIX . '.js'; $deps = array( 'jquery' ); // Ensure the editor buttons CSS has loaded. // This is required for the new widget area, but in general, we want to ensure it's loaded. wp_enqueue_style( 'editor-buttons' ); } wp_enqueue_script( 'so-tinymce-field', $src, $deps, SOW_BUNDLE_VERSION ); wp_enqueue_style( 'so-tinymce-field', plugin_dir_url( __FILE__ ) . 'css/tinymce-field.css', array(), SOW_BUNDLE_VERSION ); } protected function sanitize_field_input( $value, $instance ) { $selected_editor = $this->get_selected_editor( $instance ); if ( in_array( $selected_editor, array( 'tinymce', 'tmce' ) ) && ! empty( $this->wpautop ) ) { $value = wpautop( $value ); } if ( current_user_can( 'unfiltered_html' ) ) { $sanitized_value = $value; } else { $sanitized_value = wp_kses_post( $value ); } $sanitized_value = balanceTags( $sanitized_value, true ); return $sanitized_value; } public function sanitize_instance( $instance ) { $selected_editor_name = $this->get_selected_editor_field_name( $this->base_name ); if ( ! empty( $instance[ $selected_editor_name ] ) ) { $selected_editor = $instance[ $selected_editor_name ]; $instance[ $selected_editor_name ] = in_array( $selected_editor, array( 'tinymce', 'tmce', 'quicktags', 'html' ) ) ? $selected_editor : $this->default_editor; } return $instance; } public function get_selected_editor( $instance ) { $selected_editor = null; $selected_editor_name = $this->get_selected_editor_field_name( $this->base_name ); if ( ! empty( $instance[ $selected_editor_name ] ) ) { $selected_editor = $instance[ $selected_editor_name ]; } return $selected_editor; } public function get_selected_editor_field_name( $base_name ) { $v_name = $base_name; if ( strpos( $v_name, '][' ) !== false ) { // Remove this splitter $v_name = substr( $v_name, strrpos( $v_name, '][' ) + 2 ); } return $v_name . '_selected_editor'; } private function render_media_buttons( $editor_id ) { ob_start(); if ( ! function_exists( 'media_buttons' ) ) { include ABSPATH . 'wp-admin/includes/media.php'; } echo '<div id="wp-' . esc_attr( $editor_id ) . '-media-buttons" class="wp-media-buttons">'; do_action( 'media_buttons', $editor_id ); echo "</div>\n"; return ob_get_clean(); } }