Fieldmanager
  • Package
  • Class
  • Tree
  • Todo

Packages

  • Fieldmanager
    • Context
    • Datasource
    • Field
    • Util
  • None

Classes

  • Fieldmanager_Autocomplete
  • Fieldmanager_Checkbox
  • Fieldmanager_Checkboxes
  • Fieldmanager_Colorpicker
  • Fieldmanager_Datepicker
  • Fieldmanager_DraggablePost
  • Fieldmanager_Field
  • Fieldmanager_Grid
  • Fieldmanager_Group
  • Fieldmanager_Hidden
  • Fieldmanager_Link
  • Fieldmanager_Media
  • Fieldmanager_Options
  • Fieldmanager_Password
  • Fieldmanager_Radios
  • Fieldmanager_RichTextArea
  • Fieldmanager_Select
  • Fieldmanager_TextArea
  • Fieldmanager_TextField
  1 <?php
  2 
  3 /**
  4  * WordPress visual editor (TinyMCE) which submits HTML.
  5  *
  6  * @package Fieldmanager_Field
  7  */
  8 class Fieldmanager_RichTextArea extends Fieldmanager_Field {
  9 
 10     /**
 11      * @var string
 12      * Override field_class
 13      */
 14     public $field_class = 'richtext';
 15 
 16     /**
 17      * @deprecated
 18      */
 19     public $apply_mce_filters = true;
 20 
 21     /**
 22      * @deprecated
 23      * @see Fieldmanager_RichTextArea::$editor_settings
 24      */
 25     public $init_options = array();
 26 
 27     /**
 28      * Arguments passed to wp_editor()'s `$settings` parameter.
 29      * @see http://codex.wordpress.org/Function_Reference/wp_editor#Arguments
 30      * @var array
 31      */
 32     public $editor_settings = array();
 33 
 34     /**
 35      * @deprecated
 36      */
 37     public $add_code_plugin = false;
 38 
 39     /**
 40      * First row of buttons for the tinymce toolbar
 41      * @var array
 42      */
 43     public $buttons_1;
 44 
 45     /**
 46      * Second row of buttons for the tinymce toolbar
 47      * @var array
 48      */
 49     public $buttons_2;
 50 
 51     /**
 52      * Third row of buttons for the tinymce toolbar
 53      * @var array
 54      */
 55     public $buttons_3;
 56 
 57     /**
 58      * Fourth row of buttons for the tinymce toolbar
 59      * @var array
 60      */
 61     public $buttons_4;
 62 
 63     /**
 64      * External stylesheet(s) to include in the editor. Multiple files can be included
 65      * delimited by commas.
 66      * @var string
 67      */
 68     public $stylesheet;
 69 
 70     /**
 71      * Indicates if we should be altering the tinymce config.
 72      * @access protected
 73      * @var boolean
 74      */
 75     protected $edit_config = false;
 76 
 77     /**
 78      * Construct defaults for this field.
 79      *
 80      * @param string $label title of form field
 81      * @param array $options with keys matching vars of the field in use.
 82      */
 83     public function __construct( $label = '', $options = array() ) {
 84         $this->sanitize = array( $this, 'sanitize' );
 85         fm_add_script( 'fm_richtext', 'js/richtext.js', array( 'jquery', 'fieldmanager_script' ), '1.0.7' );
 86 
 87         parent::__construct( $label, $options );
 88     }
 89 
 90     /**
 91      * Default sanitization function for RichTextAreas.
 92      *
 93      * @param  string $value Raw content for this field.
 94      * @return string sanitized content.
 95      */
 96     public function sanitize( $value ) {
 97         return wp_filter_post_kses( wpautop( $value ) ); // run through wpautop first to preserve breaks.
 98     }
 99 
100     /**
101      * Render the form element, which is a textarea by default.
102      *
103      * @param mixed $value
104      * @return string HTML
105      */
106     public function form_element( $value = '' ) {
107         $proto = $this->has_proto();
108         $wrapper_classes = array();
109 
110         $this->prep_editor_config();
111 
112         $settings = $this->array_merge_deep( $this->editor_settings, array(
113             'textarea_name'  => $this->get_form_name(),
114             'editor_class'   => 'fm-element fm-richtext',
115             'tinymce'        => array( 'wp_skip_init' => true ),
116         ) );
117 
118         if ( $proto ) {
119             add_filter( 'the_editor', array( $this, 'add_proto_id' ) );
120         }
121 
122         if ( ! isset( $settings['default_editor'] ) ) {
123             $settings['default_editor'] = 'tinymce';
124         } elseif ( 'cookie' == $settings['default_editor'] ) {
125             if ( $proto ) {
126                 $settings['default_editor'] = 'tinymce';
127             } else {
128                 $cookie_value = '';
129                 if ( $user = wp_get_current_user() ) { // look for cookie
130                     $setting_key = str_replace( '-', '_', $this->get_element_id() );
131                     $setting_key = preg_replace( '/[^a-z0-9_]/i', '', $setting_key );
132                     $cookie_value = get_user_setting( 'editor_' . $setting_key, 'tinymce' );
133                 }
134 
135                 $settings['default_editor'] = in_array( $cookie_value, array( 'tinymce', 'html' ) ) ? $cookie_value : 'tinymce';
136             }
137 
138             $wrapper_classes[] = 'fm-richtext-remember-editor';
139         }
140 
141         $this->add_editor_filters();
142 
143         ob_start();
144         wp_editor( $value, $this->get_element_id(), $settings );
145         $content = ob_get_clean();
146 
147         $this->remove_editor_filters();
148 
149         if ( $proto ) {
150             remove_filter( 'the_editor', array( $this, 'add_proto_id' ) );
151         }
152 
153         // Add classes to the wrapper if needed
154         if ( ! empty( $wrapper_classes ) ) {
155             $content = str_replace( 'wp-core-ui wp-editor-wrap', 'wp-core-ui wp-editor-wrap ' . implode( ' ', $wrapper_classes ), $content );
156         }
157 
158         return $content;
159     }
160 
161     /**
162      * Before generating the editor, manipualte the settings as needed.
163      */
164     protected function prep_editor_config() {
165         // Attempt to maintain some backwards compatibility for $init_options
166         if ( ! empty( $this->init_options ) ) {
167             if ( ! isset( $this->stylesheet ) && ! empty( $this->init_options['content_css'] ) ) {
168                 $this->stylesheet = $this->init_options['content_css'];
169                 unset( $this->init_options['content_css'] );
170             }
171             if ( empty( $this->editor_settings['tinymce'] ) ) {
172                 $this->editor_settings['tinymce'] = array();
173             }
174             $this->editor_settings['tinymce'] = wp_parse_args( $this->editor_settings['tinymce'], $this->init_options );
175         }
176 
177         if ( isset( $this->stylesheet ) ) {
178             $this->edit_config = true;
179         }
180     }
181 
182     /**
183      * Note the ID for the proto so in repeated fields we can dig up the editor settings.
184      *
185      * @param string $editor HTML for the editor.
186      * @return string The editor HTML with a `data-proto-id` attribute added.
187      */
188     public function add_proto_id( $editor ) {
189         return str_replace( '<textarea', '<textarea data-proto-id="' . $this->get_element_id() . '"', $editor );
190     }
191 
192     /**
193      * Filter the editor buttons.
194      *
195      * @param  array $buttons Buttons for the editor toolbar.
196      * @return array Filtered buttons.
197      */
198     public function customize_buttons( $buttons ) {
199         switch ( current_filter() ) {
200             case 'teeny_mce_buttons':
201             case 'mce_buttons'      : return $this->buttons_1;
202             case 'mce_buttons_2'    : return $this->buttons_2;
203             case 'mce_buttons_3'    : return $this->buttons_3;
204             case 'mce_buttons_4'    : return $this->buttons_4;
205         }
206         return $buttons;
207     }
208 
209     /**
210      * Make final tweaks to the editor config.
211      *
212      * @param  array $mceInit The raw settings passed to TinyMCE.
213      * @return array The raw settings passed to TinyMCE.
214      */
215     public function editor_config( $mceInit ) {
216         if ( isset( $this->stylesheet ) ) {
217             $this->stylesheet = explode( ',', $this->stylesheet );
218             $this->stylesheet = array_map( 'esc_url_raw', $this->stylesheet );
219             $mceInit['content_css'] = implode( ',', $this->stylesheet );
220         }
221         return $mceInit;
222     }
223 
224     /**
225      * Add necessary filters before generating the editor.
226      */
227     protected function add_editor_filters() {
228         if ( isset( $this->buttons_1 ) ) {
229             add_filter( 'mce_buttons', array( $this, 'customize_buttons' ) );
230             add_filter( 'teeny_mce_buttons', array( $this, 'customize_buttons' ) );
231         }
232         if ( isset( $this->buttons_2 ) ) {
233             add_filter( 'mce_buttons_2', array( $this, 'customize_buttons' ) );
234         }
235         if ( isset( $this->buttons_3 ) ) {
236             add_filter( 'mce_buttons_3', array( $this, 'customize_buttons' ) );
237         }
238         if ( isset( $this->buttons_4 ) ) {
239             add_filter( 'mce_buttons_4', array( $this, 'customize_buttons' ) );
240         }
241         if ( $this->edit_config ) {
242             if ( ! empty( $this->editor_settings['teeny'] ) ) {
243                 add_filter( 'teeny_mce_before_init', array( $this, 'editor_config' ) );
244             } else {
245                 add_filter( 'tiny_mce_before_init', array( $this, 'editor_config' ) );
246             }
247         }
248 
249         // WordPress < 4.3 assumes there's only one editor on any given page, so
250         // it adds a filter based on the visual vs. text state of that editor.
251         // It will re-add filters for each editor, so there's no harm in
252         // removing whatever it added.
253         remove_filter( 'the_editor_content', 'wp_htmledit_pre' );
254         remove_filter( 'the_editor_content', 'wp_richedit_pre' );
255     }
256 
257     /**
258      * Remove the filters we added before generating the editor so they don't
259      * affect other editors.
260      */
261     protected function remove_editor_filters() {
262         remove_filter( 'mce_buttons', array( $this, 'customize_buttons' ) );
263         remove_filter( 'mce_buttons_2', array( $this, 'customize_buttons' ) );
264         remove_filter( 'mce_buttons_3', array( $this, 'customize_buttons' ) );
265         remove_filter( 'mce_buttons_4', array( $this, 'customize_buttons' ) );
266         remove_filter( 'teeny_mce_before_init', array( $this, 'editor_config' ) );
267         remove_filter( 'tiny_mce_before_init', array( $this, 'editor_config' ) );
268     }
269 
270     /**
271      * array_merge_recursive as it should have been done. Modeled on the
272      * similarly named function in drupal. This differs from
273      * array_merge_recursive in that if it sees two strings, it doesn't merge
274      * them into an array of strings. That's dumb.
275      *
276      * @return array
277      */
278     protected function array_merge_deep() {
279         $result = array();
280         foreach ( func_get_args() as $array ) {
281             foreach ( $array as $key => $value) {
282                 if ( is_integer( $key ) ) {
283                     // Renumber integer keys as array_merge_recursive() does. Note that PHP
284                     // automatically converts array keys that are integer strings (e.g., '1')
285                     // to integers.
286                     $result[] = $value;
287                 } elseif ( isset( $result[ $key ] ) && is_array( $result[ $key ] ) && is_array( $value ) ) {
288                     // Recurse when both values are arrays.
289                     $result[ $key ] = $this->array_merge_deep( $result[ $key ], $value );
290                 } else {
291                     // Otherwise, use the latter value, overriding any previous value.
292                     $result[ $key ] = $value;
293                 }
294             }
295         }
296         return $result;
297     }
298 
299 }
300 
Fieldmanager API documentation generated by ApiGen 2.8.0