PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/class-wp-editor.php

https://bitbucket.org/acipriani/madeinapulia.com
PHP | 1499 lines | 929 code | 212 blank | 358 comment | 150 complexity | c8768695026080313465cdbd0297b725 MD5 | raw file
Possible License(s): GPL-3.0, MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0, Apache-2.0
  1. <?php
  2. /**
  3. * Facilitates adding of the WordPress editor as used on the Write and Edit screens.
  4. *
  5. * @package WordPress
  6. * @since 3.3.0
  7. *
  8. * Private, not included by default. See wp_editor() in wp-includes/general-template.php.
  9. */
  10. final class _WP_Editors {
  11. public static $mce_locale;
  12. private static $mce_settings = array();
  13. private static $qt_settings = array();
  14. private static $plugins = array();
  15. private static $qt_buttons = array();
  16. private static $ext_plugins;
  17. private static $baseurl;
  18. private static $first_init;
  19. private static $this_tinymce = false;
  20. private static $this_quicktags = false;
  21. private static $has_tinymce = false;
  22. private static $has_quicktags = false;
  23. private static $has_medialib = false;
  24. private static $editor_buttons_css = true;
  25. private static $drag_drop_upload = false;
  26. private function __construct() {}
  27. /**
  28. * Parse default arguments for the editor instance.
  29. *
  30. * @param string $editor_id ID for the current editor instance.
  31. * @param array $settings {
  32. * Array of editor arguments.
  33. *
  34. * @type bool $wpautop Whether to use wpautop(). Default true.
  35. * @type bool $media_buttons Whether to show the Add Media/other media buttons.
  36. * @type string $default_editor When both TinyMCE and Quicktags are used, set which
  37. * editor is shown on page load. Default empty.
  38. * @type bool $drag_drop_upload Whether to enable drag & drop on the editor uploading. Default false.
  39. * Requires the media modal.
  40. * @type string $textarea_name Give the textarea a unique name here. Square brackets
  41. * can be used here. Default $editor_id.
  42. * @type int $textarea_rows Number rows in the editor textarea. Default 20.
  43. * @type string|int $tabindex Tabindex value to use. Default empty.
  44. * @type string $tabfocus_elements The previous and next element ID to move the focus to
  45. * when pressing the Tab key in TinyMCE. Defualt ':prev,:next'.
  46. * @type string $editor_css Intended for extra styles for both Visual and Text editors.
  47. * Should include `<style>` tags, and can use "scoped". Default empty.
  48. * @type string $editor_class Extra classes to add to the editor textarea elemen. Default empty.
  49. * @type bool $teeny Whether to output the minimal editor config. Examples include
  50. * Press This and the Comment editor. Default false.
  51. * @type bool $dfw Whether to replace the default fullscreen with "Distraction Free
  52. * Writing". DFW requires specific DOM elements and css). Default false.
  53. * @type bool|array $tinymce Whether to load TinyMCE. Can be used to pass settings directly to
  54. * TinyMCE using an array. Default true.
  55. * @type bool|array $quicktags Whether to load Quicktags. Can be used to pass settings directly to
  56. * Quicktags using an array. Default true.
  57. * }
  58. * @return array Parsed arguments array.
  59. */
  60. public static function parse_settings( $editor_id, $settings ) {
  61. /**
  62. * Filter the wp_editor() settings.
  63. *
  64. * @since 4.0.0
  65. *
  66. * @see _WP_Editors()::parse_settings()
  67. *
  68. * @param array $settings Array of editor arguments.
  69. * @param string $editor_id ID for the current editor instance.
  70. */
  71. $settings = apply_filters( 'wp_editor_settings', $settings, $editor_id );
  72. $set = wp_parse_args( $settings, array(
  73. 'wpautop' => true,
  74. 'media_buttons' => true,
  75. 'default_editor' => '',
  76. 'drag_drop_upload' => false,
  77. 'textarea_name' => $editor_id,
  78. 'textarea_rows' => 20,
  79. 'tabindex' => '',
  80. 'tabfocus_elements' => ':prev,:next',
  81. 'editor_css' => '',
  82. 'editor_class' => '',
  83. 'teeny' => false,
  84. 'dfw' => false,
  85. '_content_editor_dfw' => false,
  86. 'tinymce' => true,
  87. 'quicktags' => true
  88. ) );
  89. self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() );
  90. if ( self::$this_tinymce ) {
  91. if ( false !== strpos( $editor_id, '[' ) ) {
  92. self::$this_tinymce = false;
  93. _deprecated_argument( 'wp_editor()', '3.9', 'TinyMCE editor IDs cannot have brackets.' );
  94. }
  95. }
  96. self::$this_quicktags = (bool) $set['quicktags'];
  97. if ( self::$this_tinymce )
  98. self::$has_tinymce = true;
  99. if ( self::$this_quicktags )
  100. self::$has_quicktags = true;
  101. if ( empty( $set['editor_height'] ) )
  102. return $set;
  103. if ( 'content' === $editor_id && empty( $set['tinymce']['wp_autoresize_on'] ) ) {
  104. // A cookie (set when a user resizes the editor) overrides the height.
  105. $cookie = (int) get_user_setting( 'ed_size' );
  106. // Upgrade an old TinyMCE cookie if it is still around, and the new one isn't.
  107. if ( ! $cookie && isset( $_COOKIE['TinyMCE_content_size'] ) ) {
  108. parse_str( $_COOKIE['TinyMCE_content_size'], $cookie );
  109. $cookie = $cookie['ch'];
  110. }
  111. if ( $cookie )
  112. $set['editor_height'] = $cookie;
  113. }
  114. if ( $set['editor_height'] < 50 )
  115. $set['editor_height'] = 50;
  116. elseif ( $set['editor_height'] > 5000 )
  117. $set['editor_height'] = 5000;
  118. return $set;
  119. }
  120. /**
  121. * Outputs the HTML for a single instance of the editor.
  122. *
  123. * @param string $content The initial content of the editor.
  124. * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers).
  125. * @param array $settings See the _parse_settings() method for description.
  126. */
  127. public static function editor( $content, $editor_id, $settings = array() ) {
  128. $set = self::parse_settings( $editor_id, $settings );
  129. $editor_class = ' class="' . trim( $set['editor_class'] . ' wp-editor-area' ) . '"';
  130. $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
  131. $switch_class = 'html-active';
  132. $toolbar = $buttons = $autocomplete = '';
  133. if ( $set['drag_drop_upload'] ) {
  134. self::$drag_drop_upload = true;
  135. }
  136. if ( ! empty( $set['editor_height'] ) )
  137. $height = ' style="height: ' . $set['editor_height'] . 'px"';
  138. else
  139. $height = ' rows="' . $set['textarea_rows'] . '"';
  140. if ( !current_user_can( 'upload_files' ) )
  141. $set['media_buttons'] = false;
  142. if ( ! self::$this_quicktags && self::$this_tinymce ) {
  143. $switch_class = 'tmce-active';
  144. $autocomplete = ' autocomplete="off"';
  145. } elseif ( self::$this_quicktags && self::$this_tinymce ) {
  146. $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
  147. $autocomplete = ' autocomplete="off"';
  148. // 'html' is used for the "Text" editor tab.
  149. if ( 'html' === $default_editor ) {
  150. add_filter('the_editor_content', 'wp_htmledit_pre');
  151. $switch_class = 'html-active';
  152. } else {
  153. add_filter('the_editor_content', 'wp_richedit_pre');
  154. $switch_class = 'tmce-active';
  155. }
  156. $buttons .= '<button type="button" id="' . $editor_id . '-tmce" class="wp-switch-editor switch-tmce" onclick="switchEditors.switchto(this);">' . __('Visual') . "</button>\n";
  157. $buttons .= '<button type="button" id="' . $editor_id . '-html" class="wp-switch-editor switch-html" onclick="switchEditors.switchto(this);">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
  158. }
  159. $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;
  160. if ( $set['_content_editor_dfw'] ) {
  161. $wrap_class .= ' has-dfw';
  162. }
  163. echo '<div id="wp-' . $editor_id . '-wrap" class="' . $wrap_class . '">';
  164. if ( self::$editor_buttons_css ) {
  165. wp_print_styles('editor-buttons');
  166. self::$editor_buttons_css = false;
  167. }
  168. if ( !empty($set['editor_css']) )
  169. echo $set['editor_css'] . "\n";
  170. if ( !empty($buttons) || $set['media_buttons'] ) {
  171. echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
  172. if ( $set['media_buttons'] ) {
  173. self::$has_medialib = true;
  174. if ( !function_exists('media_buttons') )
  175. include(ABSPATH . 'wp-admin/includes/media.php');
  176. echo '<div id="wp-' . $editor_id . '-media-buttons" class="wp-media-buttons">';
  177. /**
  178. * Fires after the default media button(s) are displayed.
  179. *
  180. * @since 2.5.0
  181. *
  182. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  183. */
  184. do_action( 'media_buttons', $editor_id );
  185. echo "</div>\n";
  186. }
  187. echo '<div class="wp-editor-tabs">' . $buttons . "</div>\n";
  188. echo "</div>\n";
  189. }
  190. /**
  191. * Filter the HTML markup output that displays the editor.
  192. *
  193. * @since 2.1.0
  194. *
  195. * @param string $output Editor's HTML markup.
  196. */
  197. $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id . '-editor-container" class="wp-editor-container">' .
  198. '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . $set['textarea_name'] . '" ' .
  199. 'id="' . $editor_id . '">%s</textarea></div>' );
  200. /**
  201. * Filter the default editor content.
  202. *
  203. * @since 2.1.0
  204. *
  205. * @param string $content Default editor content.
  206. */
  207. $content = apply_filters( 'the_editor_content', $content );
  208. printf( $the_editor, $content );
  209. echo "\n</div>\n\n";
  210. self::editor_settings($editor_id, $set);
  211. }
  212. /**
  213. * @param string $editor_id
  214. * @param array $set
  215. */
  216. public static function editor_settings($editor_id, $set) {
  217. $first_run = false;
  218. if ( empty(self::$first_init) ) {
  219. if ( is_admin() ) {
  220. add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
  221. add_action( 'admin_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
  222. } else {
  223. add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 );
  224. add_action( 'wp_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 );
  225. }
  226. }
  227. if ( self::$this_quicktags ) {
  228. $qtInit = array(
  229. 'id' => $editor_id,
  230. 'buttons' => ''
  231. );
  232. if ( is_array($set['quicktags']) )
  233. $qtInit = array_merge($qtInit, $set['quicktags']);
  234. if ( empty($qtInit['buttons']) )
  235. $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close';
  236. if ( $set['dfw'] )
  237. $qtInit['buttons'] .= ',fullscreen';
  238. if ( $set['_content_editor_dfw'] ) {
  239. $qtInit['buttons'] .= ',dfw';
  240. }
  241. /**
  242. * Filter the Quicktags settings.
  243. *
  244. * @since 3.3.0
  245. *
  246. * @param array $qtInit Quicktags settings.
  247. * @param string $editor_id The unique editor ID, e.g. 'content'.
  248. */
  249. $qtInit = apply_filters( 'quicktags_settings', $qtInit, $editor_id );
  250. self::$qt_settings[$editor_id] = $qtInit;
  251. self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) );
  252. }
  253. if ( self::$this_tinymce ) {
  254. if ( empty( self::$first_init ) ) {
  255. self::$baseurl = includes_url( 'js/tinymce' );
  256. $mce_locale = get_locale();
  257. self::$mce_locale = $mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
  258. /** This filter is documented in wp-admin/includes/media.php */
  259. $no_captions = (bool) apply_filters( 'disable_captions', '' );
  260. $first_run = true;
  261. $ext_plugins = '';
  262. if ( $set['teeny'] ) {
  263. /**
  264. * Filter the list of teenyMCE plugins.
  265. *
  266. * @since 2.7.0
  267. *
  268. * @param array $plugins An array of teenyMCE plugins.
  269. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  270. */
  271. self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
  272. } else {
  273. /**
  274. * Filter the list of TinyMCE external plugins.
  275. *
  276. * The filter takes an associative array of external plugins for
  277. * TinyMCE in the form 'plugin_name' => 'url'.
  278. *
  279. * The url should be absolute, and should include the js filename
  280. * to be loaded. For example:
  281. * 'myplugin' => 'http://mysite.com/wp-content/plugins/myfolder/mce_plugin.js'.
  282. *
  283. * If the external plugin adds a button, it should be added with
  284. * one of the 'mce_buttons' filters.
  285. *
  286. * @since 2.5.0
  287. *
  288. * @param array $external_plugins An array of external TinyMCE plugins.
  289. */
  290. $mce_external_plugins = apply_filters( 'mce_external_plugins', array() );
  291. $plugins = array(
  292. 'charmap',
  293. 'colorpicker',
  294. 'hr',
  295. 'lists',
  296. 'media',
  297. 'paste',
  298. 'tabfocus',
  299. 'textcolor',
  300. 'fullscreen',
  301. 'wordpress',
  302. 'wpautoresize',
  303. 'wpeditimage',
  304. 'wpgallery',
  305. 'wplink',
  306. 'wpdialogs',
  307. 'wpview',
  308. );
  309. if ( ! self::$has_medialib ) {
  310. $plugins[] = 'image';
  311. }
  312. /**
  313. * Filter the list of default TinyMCE plugins.
  314. *
  315. * The filter specifies which of the default plugins included
  316. * in WordPress should be added to the TinyMCE instance.
  317. *
  318. * @since 3.3.0
  319. *
  320. * @param array $plugins An array of default TinyMCE plugins.
  321. */
  322. $plugins = array_unique( apply_filters( 'tiny_mce_plugins', $plugins ) );
  323. if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) {
  324. // Remove 'spellchecker' from the internal plugins if added with 'tiny_mce_plugins' filter to prevent errors.
  325. // It can be added with 'mce_external_plugins'.
  326. unset( $plugins[$key] );
  327. }
  328. if ( ! empty( $mce_external_plugins ) ) {
  329. /**
  330. * Filter the translations loaded for external TinyMCE 3.x plugins.
  331. *
  332. * The filter takes an associative array ('plugin_name' => 'path')
  333. * where 'path' is the include path to the file.
  334. *
  335. * The language file should follow the same format as wp_mce_translation(),
  336. * and should define a variable ($strings) that holds all translated strings.
  337. *
  338. * @since 2.5.0
  339. *
  340. * @param array $translations Translations for external TinyMCE plugins.
  341. */
  342. $mce_external_languages = apply_filters( 'mce_external_languages', array() );
  343. $loaded_langs = array();
  344. $strings = '';
  345. if ( ! empty( $mce_external_languages ) ) {
  346. foreach ( $mce_external_languages as $name => $path ) {
  347. if ( @is_file( $path ) && @is_readable( $path ) ) {
  348. include_once( $path );
  349. $ext_plugins .= $strings . "\n";
  350. $loaded_langs[] = $name;
  351. }
  352. }
  353. }
  354. foreach ( $mce_external_plugins as $name => $url ) {
  355. if ( in_array( $name, $plugins, true ) ) {
  356. unset( $mce_external_plugins[ $name ] );
  357. continue;
  358. }
  359. $url = set_url_scheme( $url );
  360. $mce_external_plugins[ $name ] = $url;
  361. $plugurl = dirname( $url );
  362. $strings = '';
  363. // Try to load langs/[locale].js and langs/[locale]_dlg.js
  364. if ( ! in_array( $name, $loaded_langs, true ) ) {
  365. $path = str_replace( content_url(), '', $plugurl );
  366. $path = WP_CONTENT_DIR . $path . '/langs/';
  367. if ( function_exists('realpath') )
  368. $path = trailingslashit( realpath($path) );
  369. if ( @is_file( $path . $mce_locale . '.js' ) )
  370. $strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n";
  371. if ( @is_file( $path . $mce_locale . '_dlg.js' ) )
  372. $strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n";
  373. if ( 'en' != $mce_locale && empty( $strings ) ) {
  374. if ( @is_file( $path . 'en.js' ) ) {
  375. $str1 = @file_get_contents( $path . 'en.js' );
  376. $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
  377. }
  378. if ( @is_file( $path . 'en_dlg.js' ) ) {
  379. $str2 = @file_get_contents( $path . 'en_dlg.js' );
  380. $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
  381. }
  382. }
  383. if ( ! empty( $strings ) )
  384. $ext_plugins .= "\n" . $strings . "\n";
  385. }
  386. $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
  387. $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
  388. }
  389. }
  390. }
  391. if ( $set['dfw'] )
  392. $plugins[] = 'wpfullscreen';
  393. self::$plugins = $plugins;
  394. self::$ext_plugins = $ext_plugins;
  395. self::$first_init = array(
  396. 'theme' => 'modern',
  397. 'skin' => 'lightgray',
  398. 'language' => self::$mce_locale,
  399. 'formats' => "{
  400. alignleft: [
  401. {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}},
  402. {selector: 'img,table,dl.wp-caption', classes: 'alignleft'}
  403. ],
  404. aligncenter: [
  405. {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}},
  406. {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'}
  407. ],
  408. alignright: [
  409. {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}},
  410. {selector: 'img,table,dl.wp-caption', classes: 'alignright'}
  411. ],
  412. strikethrough: {inline: 'del'}
  413. }",
  414. 'block_formats' =>
  415. 'Paragraph=p;' .
  416. 'Pre=pre;' .
  417. 'Heading 1=h1;' .
  418. 'Heading 2=h2;' .
  419. 'Heading 3=h3;' .
  420. 'Heading 4=h4;' .
  421. 'Heading 5=h5;' .
  422. 'Heading 6=h6',
  423. 'relative_urls' => false,
  424. 'remove_script_host' => false,
  425. 'convert_urls' => false,
  426. 'browser_spellcheck' => true,
  427. 'fix_list_elements' => true,
  428. 'entities' => '38,amp,60,lt,62,gt',
  429. 'entity_encoding' => 'raw',
  430. 'keep_styles' => false,
  431. 'cache_suffix' => 'wp-mce-' . $GLOBALS['tinymce_version'],
  432. // Limit the preview styles in the menu/toolbar
  433. 'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
  434. 'wpeditimage_disable_captions' => $no_captions,
  435. 'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ),
  436. 'plugins' => implode( ',', $plugins ),
  437. );
  438. if ( ! empty( $mce_external_plugins ) ) {
  439. self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins );
  440. }
  441. $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
  442. $version = 'ver=' . $GLOBALS['wp_version'];
  443. $dashicons = includes_url( "css/dashicons$suffix.css?$version" );
  444. // WordPress default stylesheet and dashicons
  445. $mce_css = array(
  446. $dashicons,
  447. self::$baseurl . '/skins/wordpress/wp-content.css?' . $version
  448. );
  449. $editor_styles = get_editor_stylesheets();
  450. if ( ! empty( $editor_styles ) ) {
  451. foreach ( $editor_styles as $style ) {
  452. $mce_css[] = $style;
  453. }
  454. }
  455. /**
  456. * Filter the comma-delimited list of stylesheets to load in TinyMCE.
  457. *
  458. * @since 2.1.0
  459. *
  460. * @param array $stylesheets Comma-delimited list of stylesheets.
  461. */
  462. $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
  463. if ( ! empty($mce_css) )
  464. self::$first_init['content_css'] = $mce_css;
  465. }
  466. if ( $set['teeny'] ) {
  467. /**
  468. * Filter the list of teenyMCE buttons (Text tab).
  469. *
  470. * @since 2.7.0
  471. *
  472. * @param array $buttons An array of teenyMCE buttons.
  473. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  474. */
  475. $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
  476. $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
  477. } else {
  478. $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker' );
  479. if ( $set['_content_editor_dfw'] ) {
  480. $mce_buttons[] = 'dfw';
  481. } else {
  482. $mce_buttons[] = 'fullscreen';
  483. }
  484. $mce_buttons[] = 'wp_adv';
  485. /**
  486. * Filter the first-row list of TinyMCE buttons (Visual tab).
  487. *
  488. * @since 2.0.0
  489. *
  490. * @param array $buttons First-row list of buttons.
  491. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  492. */
  493. $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
  494. /**
  495. * Filter the second-row list of TinyMCE buttons (Visual tab).
  496. *
  497. * @since 2.0.0
  498. *
  499. * @param array $buttons Second-row list of buttons.
  500. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  501. */
  502. $mce_buttons_2 = apply_filters( 'mce_buttons_2', array( 'formatselect', 'underline', 'alignjustify', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' ), $editor_id );
  503. /**
  504. * Filter the third-row list of TinyMCE buttons (Visual tab).
  505. *
  506. * @since 2.0.0
  507. *
  508. * @param array $buttons Third-row list of buttons.
  509. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  510. */
  511. $mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id );
  512. /**
  513. * Filter the fourth-row list of TinyMCE buttons (Visual tab).
  514. *
  515. * @since 2.5.0
  516. *
  517. * @param array $buttons Fourth-row list of buttons.
  518. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  519. */
  520. $mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id );
  521. }
  522. $body_class = $editor_id;
  523. if ( $post = get_post() ) {
  524. $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
  525. if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
  526. $post_format = get_post_format( $post );
  527. if ( $post_format && ! is_wp_error( $post_format ) )
  528. $body_class .= ' post-format-' . sanitize_html_class( $post_format );
  529. else
  530. $body_class .= ' post-format-standard';
  531. }
  532. }
  533. $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
  534. if ( !empty($set['tinymce']['body_class']) ) {
  535. $body_class .= ' ' . $set['tinymce']['body_class'];
  536. unset($set['tinymce']['body_class']);
  537. }
  538. if ( $set['dfw'] ) {
  539. // replace the first 'fullscreen' with 'wp_fullscreen'
  540. if ( ($key = array_search('fullscreen', $mce_buttons)) !== false )
  541. $mce_buttons[$key] = 'wp_fullscreen';
  542. elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false )
  543. $mce_buttons_2[$key] = 'wp_fullscreen';
  544. elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false )
  545. $mce_buttons_3[$key] = 'wp_fullscreen';
  546. elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false )
  547. $mce_buttons_4[$key] = 'wp_fullscreen';
  548. }
  549. $mceInit = array (
  550. 'selector' => "#$editor_id",
  551. 'resize' => 'vertical',
  552. 'menubar' => false,
  553. 'wpautop' => (bool) $set['wpautop'],
  554. 'indent' => ! $set['wpautop'],
  555. 'toolbar1' => implode($mce_buttons, ','),
  556. 'toolbar2' => implode($mce_buttons_2, ','),
  557. 'toolbar3' => implode($mce_buttons_3, ','),
  558. 'toolbar4' => implode($mce_buttons_4, ','),
  559. 'tabfocus_elements' => $set['tabfocus_elements'],
  560. 'body_class' => $body_class
  561. );
  562. if ( $first_run )
  563. $mceInit = array_merge( self::$first_init, $mceInit );
  564. if ( is_array( $set['tinymce'] ) )
  565. $mceInit = array_merge( $mceInit, $set['tinymce'] );
  566. /*
  567. * For people who really REALLY know what they're doing with TinyMCE
  568. * You can modify $mceInit to add, remove, change elements of the config
  569. * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
  570. * and "extended_valid_elements" can be done through this filter. Best
  571. * is to use the default cleanup by not specifying valid_elements,
  572. * as TinyMCE contains full set of XHTML 1.0.
  573. */
  574. if ( $set['teeny'] ) {
  575. /**
  576. * Filter the teenyMCE config before init.
  577. *
  578. * @since 2.7.0
  579. *
  580. * @param array $mceInit An array with teenyMCE config.
  581. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  582. */
  583. $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id );
  584. } else {
  585. /**
  586. * Filter the TinyMCE config before init.
  587. *
  588. * @since 2.5.0
  589. *
  590. * @param array $mceInit An array with TinyMCE config.
  591. * @param string $editor_id Unique editor identifier, e.g. 'content'.
  592. */
  593. $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
  594. }
  595. if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) {
  596. $mceInit['toolbar3'] = $mceInit['toolbar4'];
  597. $mceInit['toolbar4'] = '';
  598. }
  599. self::$mce_settings[$editor_id] = $mceInit;
  600. } // end if self::$this_tinymce
  601. }
  602. private static function _parse_init($init) {
  603. $options = '';
  604. foreach ( $init as $k => $v ) {
  605. if ( is_bool($v) ) {
  606. $val = $v ? 'true' : 'false';
  607. $options .= $k . ':' . $val . ',';
  608. continue;
  609. } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
  610. $options .= $k . ':' . $v . ',';
  611. continue;
  612. }
  613. $options .= $k . ':"' . $v . '",';
  614. }
  615. return '{' . trim( $options, ' ,' ) . '}';
  616. }
  617. public static function enqueue_scripts() {
  618. wp_enqueue_script('word-count');
  619. if ( self::$has_tinymce )
  620. wp_enqueue_script('editor');
  621. if ( self::$has_quicktags ) {
  622. wp_enqueue_script( 'quicktags' );
  623. wp_enqueue_style( 'buttons' );
  624. }
  625. if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
  626. wp_enqueue_script('wplink');
  627. }
  628. if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) )
  629. wp_enqueue_script('wp-fullscreen');
  630. if ( self::$has_medialib ) {
  631. add_thickbox();
  632. wp_enqueue_script('media-upload');
  633. }
  634. /**
  635. * Fires when scripts and styles are enqueued for the editor.
  636. *
  637. * @since 3.9.0
  638. *
  639. * @param array $to_load An array containing boolean values whether TinyMCE
  640. * and Quicktags are being loaded.
  641. */
  642. do_action( 'wp_enqueue_editor', array(
  643. 'tinymce' => self::$has_tinymce,
  644. 'quicktags' => self::$has_quicktags,
  645. ) );
  646. }
  647. /**
  648. * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n().
  649. * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object.
  650. *
  651. * @param string $mce_locale The locale used for the editor.
  652. * @param bool $json_only optional Whether to include the JavaScript calls to tinymce.addI18n() and tinymce.ScriptLoader.markDone().
  653. * @return string Translation object, JSON encoded.
  654. */
  655. public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
  656. $mce_translation = array(
  657. // Default TinyMCE strings
  658. 'New document' => __( 'New document' ),
  659. 'Formats' => _x( 'Formats', 'TinyMCE' ),
  660. 'Headings' => _x( 'Headings', 'TinyMCE' ),
  661. 'Heading 1' => __( 'Heading 1' ),
  662. 'Heading 2' => __( 'Heading 2' ),
  663. 'Heading 3' => __( 'Heading 3' ),
  664. 'Heading 4' => __( 'Heading 4' ),
  665. 'Heading 5' => __( 'Heading 5' ),
  666. 'Heading 6' => __( 'Heading 6' ),
  667. /* translators: block tags */
  668. 'Blocks' => _x( 'Blocks', 'TinyMCE' ),
  669. 'Paragraph' => __( 'Paragraph' ),
  670. 'Blockquote' => __( 'Blockquote' ),
  671. 'Div' => _x( 'Div', 'HTML tag' ),
  672. 'Pre' => _x( 'Pre', 'HTML tag' ),
  673. 'Address' => _x( 'Address', 'HTML tag' ),
  674. 'Inline' => _x( 'Inline', 'HTML elements' ),
  675. 'Underline' => __( 'Underline' ),
  676. 'Strikethrough' => __( 'Strikethrough' ),
  677. 'Subscript' => __( 'Subscript' ),
  678. 'Superscript' => __( 'Superscript' ),
  679. 'Clear formatting' => __( 'Clear formatting' ),
  680. 'Bold' => __( 'Bold' ),
  681. 'Italic' => __( 'Italic' ),
  682. 'Code' => _x( 'Code', 'editor button' ),
  683. 'Source code' => __( 'Source code' ),
  684. 'Font Family' => __( 'Font Family' ),
  685. 'Font Sizes' => __( 'Font Sizes' ),
  686. 'Align center' => __( 'Align center' ),
  687. 'Align right' => __( 'Align right' ),
  688. 'Align left' => __( 'Align left' ),
  689. 'Justify' => __( 'Justify' ),
  690. 'Increase indent' => __( 'Increase indent' ),
  691. 'Decrease indent' => __( 'Decrease indent' ),
  692. 'Cut' => __( 'Cut' ),
  693. 'Copy' => __( 'Copy' ),
  694. 'Paste' => __( 'Paste' ),
  695. 'Select all' => __( 'Select all' ),
  696. 'Undo' => __( 'Undo' ),
  697. 'Redo' => __( 'Redo' ),
  698. 'Ok' => __( 'OK' ),
  699. 'Cancel' => __( 'Cancel' ),
  700. 'Close' => __( 'Close' ),
  701. 'Visual aids' => __( 'Visual aids' ),
  702. 'Bullet list' => __( 'Bulleted list' ),
  703. 'Numbered list' => __( 'Numbered list' ),
  704. 'Square' => _x( 'Square', 'list style' ),
  705. 'Default' => _x( 'Default', 'list style' ),
  706. 'Circle' => _x( 'Circle', 'list style' ),
  707. 'Disc' => _x('Disc', 'list style' ),
  708. 'Lower Greek' => _x( 'Lower Greek', 'list style' ),
  709. 'Lower Alpha' => _x( 'Lower Alpha', 'list style' ),
  710. 'Upper Alpha' => _x( 'Upper Alpha', 'list style' ),
  711. 'Upper Roman' => _x( 'Upper Roman', 'list style' ),
  712. 'Lower Roman' => _x( 'Lower Roman', 'list style' ),
  713. // Anchor plugin
  714. 'Name' => _x( 'Name', 'Name of link anchor (TinyMCE)' ),
  715. 'Anchor' => _x( 'Anchor', 'Link anchor (TinyMCE)' ),
  716. 'Anchors' => _x( 'Anchors', 'Link anchors (TinyMCE)' ),
  717. // Fullpage plugin
  718. 'Document properties' => __( 'Document properties' ),
  719. 'Robots' => __( 'Robots' ),
  720. 'Title' => __( 'Title' ),
  721. 'Keywords' => __( 'Keywords' ),
  722. 'Encoding' => __( 'Encoding' ),
  723. 'Description' => __( 'Description' ),
  724. 'Author' => __( 'Author' ),
  725. // Media, image plugins
  726. 'Insert/edit image' => __( 'Insert/edit image' ),
  727. 'General' => __( 'General' ),
  728. 'Advanced' => __( 'Advanced' ),
  729. 'Source' => __( 'Source' ),
  730. 'Border' => __( 'Border' ),
  731. 'Constrain proportions' => __( 'Constrain proportions' ),
  732. 'Vertical space' => __( 'Vertical space' ),
  733. 'Image description' => __( 'Image description' ),
  734. 'Style' => __( 'Style' ),
  735. 'Dimensions' => __( 'Dimensions' ),
  736. 'Insert image' => __( 'Insert image' ),
  737. 'Insert date/time' => __( 'Insert date/time' ),
  738. 'Insert/edit video' => __( 'Insert/edit video' ),
  739. 'Poster' => __( 'Poster' ),
  740. 'Alternative source' => __( 'Alternative source' ),
  741. 'Paste your embed code below:' => __( 'Paste your embed code below:' ),
  742. 'Insert video' => __( 'Insert video' ),
  743. 'Embed' => __( 'Embed' ),
  744. // Each of these have a corresponding plugin
  745. 'Special character' => __( 'Special character' ),
  746. 'Right to left' => _x( 'Right to left', 'editor button' ),
  747. 'Left to right' => _x( 'Left to right', 'editor button' ),
  748. 'Emoticons' => __( 'Emoticons' ),
  749. 'Nonbreaking space' => __( 'Nonbreaking space' ),
  750. 'Page break' => __( 'Page break' ),
  751. 'Paste as text' => __( 'Paste as text' ),
  752. 'Preview' => __( 'Preview' ),
  753. 'Print' => __( 'Print' ),
  754. 'Save' => __( 'Save' ),
  755. 'Fullscreen' => __( 'Fullscreen' ),
  756. 'Horizontal line' => __( 'Horizontal line' ),
  757. 'Horizontal space' => __( 'Horizontal space' ),
  758. 'Restore last draft' => __( 'Restore last draft' ),
  759. 'Insert/edit link' => __( 'Insert/edit link' ),
  760. 'Remove link' => __( 'Remove link' ),
  761. 'Color' => __( 'Color' ),
  762. 'Custom color' => __( 'Custom color' ),
  763. 'Custom...' => _x( 'Custom...', 'label for custom color' ),
  764. 'No color' => __( 'No color' ),
  765. // Spelling, search/replace plugins
  766. 'Could not find the specified string.' => __( 'Could not find the specified string.' ),
  767. 'Replace' => _x( 'Replace', 'find/replace' ),
  768. 'Next' => _x( 'Next', 'find/replace' ),
  769. /* translators: previous */
  770. 'Prev' => _x( 'Prev', 'find/replace' ),
  771. 'Whole words' => _x( 'Whole words', 'find/replace' ),
  772. 'Find and replace' => __( 'Find and replace' ),
  773. 'Replace with' => _x('Replace with', 'find/replace' ),
  774. 'Find' => _x( 'Find', 'find/replace' ),
  775. 'Replace all' => _x( 'Replace all', 'find/replace' ),
  776. 'Match case' => __( 'Match case' ),
  777. 'Spellcheck' => __( 'Check Spelling' ),
  778. 'Finish' => _x( 'Finish', 'spellcheck' ),
  779. 'Ignore all' => _x( 'Ignore all', 'spellcheck' ),
  780. 'Ignore' => _x( 'Ignore', 'spellcheck' ),
  781. 'Add to Dictionary' => __( 'Add to Dictionary' ),
  782. // TinyMCE tables
  783. 'Insert table' => __( 'Insert table' ),
  784. 'Delete table' => __( 'Delete table' ),
  785. 'Table properties' => __( 'Table properties' ),
  786. 'Row properties' => __( 'Table row properties' ),
  787. 'Cell properties' => __( 'Table cell properties' ),
  788. 'Border color' => __( 'Border color' ),
  789. 'Row' => __( 'Row' ),
  790. 'Rows' => __( 'Rows' ),
  791. 'Column' => _x( 'Column', 'table column' ),
  792. 'Cols' => _x( 'Cols', 'table columns' ),
  793. 'Cell' => _x( 'Cell', 'table cell' ),
  794. 'Header cell' => __( 'Header cell' ),
  795. 'Header' => _x( 'Header', 'table header' ),
  796. 'Body' => _x( 'Body', 'table body' ),
  797. 'Footer' => _x( 'Footer', 'table footer' ),
  798. 'Insert row before' => __( 'Insert row before' ),
  799. 'Insert row after' => __( 'Insert row after' ),
  800. 'Insert column before' => __( 'Insert column before' ),
  801. 'Insert column after' => __( 'Insert column after' ),
  802. 'Paste row before' => __( 'Paste table row before' ),
  803. 'Paste row after' => __( 'Paste table row after' ),
  804. 'Delete row' => __( 'Delete row' ),
  805. 'Delete column' => __( 'Delete column' ),
  806. 'Cut row' => __( 'Cut table row' ),
  807. 'Copy row' => __( 'Copy table row' ),
  808. 'Merge cells' => __( 'Merge table cells' ),
  809. 'Split cell' => __( 'Split table cell' ),
  810. 'Height' => __( 'Height' ),
  811. 'Width' => __( 'Width' ),
  812. 'Caption' => __( 'Caption' ),
  813. 'Alignment' => __( 'Alignment' ),
  814. 'H Align' => _x( 'H Align', 'horizontal table cell alignment' ),
  815. 'Left' => __( 'Left' ),
  816. 'Center' => __( 'Center' ),
  817. 'Right' => __( 'Right' ),
  818. 'None' => _x( 'None', 'table cell alignment attribute' ),
  819. 'V Align' => _x( 'V Align', 'vertical table cell alignment' ),
  820. 'Top' => __( 'Top' ),
  821. 'Middle' => __( 'Middle' ),
  822. 'Bottom' => __( 'Bottom' ),
  823. 'Row group' => __( 'Row group' ),
  824. 'Column group' => __( 'Column group' ),
  825. 'Row type' => __( 'Row type' ),
  826. 'Cell type' => __( 'Cell type' ),
  827. 'Cell padding' => __( 'Cell padding' ),
  828. 'Cell spacing' => __( 'Cell spacing' ),
  829. 'Scope' => _x( 'Scope', 'table cell scope attribute' ),
  830. 'Insert template' => _x( 'Insert template', 'TinyMCE' ),
  831. 'Templates' => _x( 'Templates', 'TinyMCE' ),
  832. 'Background color' => __( 'Background color' ),
  833. 'Text color' => __( 'Text color' ),
  834. 'Show blocks' => _x( 'Show blocks', 'editor button' ),
  835. 'Show invisible characters' => __( 'Show invisible characters' ),
  836. /* translators: word count */
  837. 'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
  838. 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" . __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
  839. 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __( 'Rich Text Area. Press Alt-Shift-H for help' ),
  840. 'You have unsaved changes are you sure you want to navigate away?' => __( 'The changes you made will be lost if you navigate away from this page.' ),
  841. 'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
  842. // TinyMCE menus
  843. 'Insert' => _x( 'Insert', 'TinyMCE menu' ),
  844. 'File' => _x( 'File', 'TinyMCE menu' ),
  845. 'Edit' => _x( 'Edit', 'TinyMCE menu' ),
  846. 'Tools' => _x( 'Tools', 'TinyMCE menu' ),
  847. 'View' => _x( 'View', 'TinyMCE menu' ),
  848. 'Table' => _x( 'Table', 'TinyMCE menu' ),
  849. 'Format' => _x( 'Format', 'TinyMCE menu' ),
  850. // WordPress strings
  851. 'Keyboard Shortcuts' => __( 'Keyboard Shortcuts' ),
  852. 'Toolbar Toggle' => __( 'Toolbar Toggle' ),
  853. 'Insert Read More tag' => __( 'Insert Read More tag' ),
  854. 'Read more...' => __( 'Read more...' ), // Title on the placeholder inside the editor
  855. 'Distraction-free writing mode' => __( 'Distraction-free writing mode' ),
  856. 'No alignment' => __( 'No alignment' ), // Tooltip for the 'alignnone' button in the image toolbar
  857. 'Remove' => __( 'Remove' ), // Tooltip for the 'remove' button in the image toolbar
  858. 'Edit ' => __( 'Edit' ), // Tooltip for the 'edit' button in the image toolbar
  859. );
  860. /**
  861. * Link plugin (not included):
  862. * Insert link
  863. * Target
  864. * New window
  865. * Text to display
  866. * The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?
  867. * The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?
  868. * Url
  869. */
  870. if ( ! $mce_locale ) {
  871. $mce_locale = self::$mce_locale;
  872. }
  873. /**
  874. * Filter translated strings prepared for TinyMCE.
  875. *
  876. * @since 3.9.0
  877. *
  878. * @param array $mce_translation Key/value pairs of strings.
  879. * @param string $mce_locale Locale.
  880. */
  881. $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale );
  882. foreach ( $mce_translation as $key => $value ) {
  883. // Remove strings that are not translated.
  884. if ( $key === $value ) {
  885. unset( $mce_translation[$key] );
  886. continue;
  887. }
  888. if ( false !== strpos( $value, '&' ) ) {
  889. $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
  890. }
  891. }
  892. // Set direction
  893. if ( is_rtl() ) {
  894. $mce_translation['_dir'] = 'rtl';
  895. }
  896. if ( $json_only ) {
  897. return wp_json_encode( $mce_translation );
  898. }
  899. $baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' );
  900. return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
  901. "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
  902. }
  903. public static function editor_js() {
  904. global $tinymce_version, $concatenate_scripts, $compress_scripts;
  905. /**
  906. * Filter "tiny_mce_version" is deprecated
  907. *
  908. * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
  909. * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
  910. * If the plugin has a popup dialog, a query string can be added to the button action that opens it (in the plugin's code).
  911. */
  912. $version = 'ver=' . $tinymce_version;
  913. $tmce_on = !empty(self::$mce_settings);
  914. if ( ! isset($concatenate_scripts) )
  915. script_concat_settings();
  916. $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
  917. && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
  918. $mceInit = $qtInit = '';
  919. if ( $tmce_on ) {
  920. foreach ( self::$mce_settings as $editor_id => $init ) {
  921. $options = self::_parse_init( $init );
  922. $mceInit .= "'$editor_id':{$options},";
  923. }
  924. $mceInit = '{' . trim($mceInit, ',') . '}';
  925. } else {
  926. $mceInit = '{}';
  927. }
  928. if ( !empty(self::$qt_settings) ) {
  929. foreach ( self::$qt_settings as $editor_id => $init ) {
  930. $options = self::_parse_init( $init );
  931. $qtInit .= "'$editor_id':{$options},";
  932. }
  933. $qtInit = '{' . trim($qtInit, ',') . '}';
  934. } else {
  935. $qtInit = '{}';
  936. }
  937. $ref = array(
  938. 'plugins' => implode( ',', self::$plugins ),
  939. 'theme' => 'modern',
  940. 'language' => self::$mce_locale
  941. );
  942. $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '' : '.min';
  943. /**
  944. * Fires immediately before the TinyMCE settings are printed.
  945. *
  946. * @since 3.2.0
  947. *
  948. * @param array $mce_settings TinyMCE settings array.
  949. */
  950. do_action( 'before_wp_tiny_mce', self::$mce_settings );
  951. ?>
  952. <script type="text/javascript">
  953. tinyMCEPreInit = {
  954. baseURL: "<?php echo self::$baseurl; ?>",
  955. suffix: "<?php echo $suffix; ?>",
  956. <?php
  957. if ( self::$drag_drop_upload ) {
  958. echo 'dragDropUpload: true,';
  959. }
  960. ?>
  961. mceInit: <?php echo $mceInit; ?>,
  962. qtInit: <?php echo $qtInit; ?>,
  963. ref: <?php echo self::_parse_init( $ref ); ?>,
  964. load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
  965. };
  966. </script>
  967. <?php
  968. $baseurl = self::$baseurl;
  969. // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
  970. $mce_suffix = false !== strpos( $GLOBALS['wp_version'], '-src' ) ? '' : '.min';
  971. if ( $tmce_on ) {
  972. if ( $compressed ) {
  973. echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
  974. } else {
  975. echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
  976. echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
  977. }
  978. echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
  979. if ( self::$ext_plugins ) {
  980. // Load the old-format English strings to prevent unsightly labels in old style popups
  981. echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
  982. }
  983. }
  984. /**
  985. * Fires after tinymce.js is loaded, but before any TinyMCE editor
  986. * instances are created.
  987. *
  988. * @since 3.9.0
  989. *
  990. * @param array $mce_settings TinyMCE settings array.
  991. */
  992. do_action( 'wp_tiny_mce_init', self::$mce_settings );
  993. ?>
  994. <script type="text/javascript">
  995. <?php
  996. if ( self::$ext_plugins )
  997. echo self::$ext_plugins . "\n";
  998. if ( ! is_admin() )
  999. echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
  1000. ?>
  1001. ( function() {
  1002. var init, edId, qtId, firstInit, wrapper;
  1003. if ( typeof tinymce !== 'undefined' ) {
  1004. for ( edId in tinyMCEPreInit.mceInit ) {
  1005. if ( firstInit ) {
  1006. init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] );
  1007. } else {
  1008. init = firstInit = tinyMCEPreInit.mceInit[edId];
  1009. }
  1010. wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0];
  1011. if ( ( tinymce.DOM.hasClass( wrapper, 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) &&
  1012. ! init.wp_skip_init ) {
  1013. try {
  1014. tinymce.init( init );
  1015. if ( ! window.wpActiveEditor ) {
  1016. window.wpActiveEditor = edId;
  1017. }
  1018. } catch(e){}
  1019. }
  1020. }
  1021. }
  1022. if ( typeof quicktags !== 'undefined' ) {
  1023. for ( qtId in tinyMCEPreInit.qtInit ) {
  1024. try {
  1025. quicktags( tinyMCEPreInit.qtInit[qtId] );
  1026. if ( ! window.wpActiveEditor ) {
  1027. window.wpActiveEditor = qtId;
  1028. }
  1029. } catch(e){};
  1030. }
  1031. }
  1032. if ( typeof jQuery !== 'undefined' ) {
  1033. jQuery('.wp-editor-wrap').on( 'click.wp-editor', function() {
  1034. if ( this.id ) {
  1035. window.wpActiveEditor = this.id.slice( 3, -5 );
  1036. }
  1037. });
  1038. } else {
  1039. for ( qtId in tinyMCEPreInit.qtInit ) {
  1040. document.getElementById( 'wp-' + qtId + '-wrap' ).onclick = function() {
  1041. window.wpActiveEditor = this.id.slice( 3, -5 );
  1042. }
  1043. }
  1044. }
  1045. }());
  1046. </script>
  1047. <?php
  1048. if ( in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) )
  1049. self::wp_link_dialog();
  1050. if ( in_array( 'wpfullscreen', self::$plugins, true ) || in_array( 'fullscreen', self::$qt_buttons, true ) )
  1051. self::wp_fullscreen_html();
  1052. /**
  1053. * Fires after any core TinyMCE editor instances are created.
  1054. *
  1055. * @since 3.2.0
  1056. *
  1057. * @param array $mce_settings TinyMCE settings array.
  1058. */
  1059. do_action( 'after_wp_tiny_mce', self::$mce_settings );
  1060. }
  1061. public static function wp_fullscreen_html() {
  1062. global $content_width;
  1063. $post = get_post();
  1064. $width = isset( $content_width ) && 800 > $content_width ? $content_width : 800;
  1065. $width = $width + 22; // compensate for the padding and border
  1066. $dfw_width = get_user_setting( 'dfw_width', $width );
  1067. $save = isset( $post->post_status ) && $post->post_status == 'publish' ? __('Update') : __('Save');
  1068. ?>
  1069. <div id="wp-fullscreen-body" class="wp-core-ui<?php if ( is_rtl() ) echo ' rtl'; ?>" data-theme-width="<?php echo (int) $width; ?>" data-dfw-width="<?php echo (int) $dfw_width; ?>">
  1070. <div id="fullscreen-topbar">
  1071. <div id="wp-fullscreen-toolbar">
  1072. <div id="wp-fullscreen-close"><a href="#" onclick="wp.editor.fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
  1073. <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
  1074. <div id="wp-fullscreen-mode-bar">
  1075. <div id="wp-fullscreen-modes" class="button-group">
  1076. <a class="button wp-fullscreen-mode-tinymce" href="#" onclick="wp.editor.fullscreen.switchmode( 'tinymce' ); return false;"><?php _e( 'Visual' ); ?></a>
  1077. <a class="button wp-fullscreen-mode-html" href="#" onclick="wp.editor.fullscreen.switchmode( 'html' ); return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a>
  1078. </div>
  1079. </div>
  1080. <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="mce-toolbar">
  1081. <?php
  1082. $buttons = array(
  1083. // format: title, onclick, show in both editors
  1084. 'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'both' => false ),
  1085. 'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'both' => false ),
  1086. 'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'both' => false ),
  1087. 'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'both' => false ),
  1088. 'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'both' => false ),
  1089. 'wp-media-library' => array( 'title' => __('Media library (Alt + Shift + M)'), 'both' => true ),
  1090. 'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'both' => true ),
  1091. 'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'both' => false ),
  1092. 'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'both' => false ),
  1093. );
  1094. /**
  1095. * Filter the list of TinyMCE buttons for the fullscreen
  1096. * 'Distraction-Free Writing' editor.
  1097. *
  1098. * @since 3.2.0
  1099. *
  1100. * @param array $buttons An array of TinyMCE buttons for the DFW editor.
  1101. */
  1102. $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons );
  1103. foreach ( $buttons as $button => $args ) {
  1104. if ( 'separator' == $args ) {
  1105. continue;
  1106. }
  1107. $onclick = ! empty( $args['onclick'] ) ? ' onclick="' . $args['onclick'] . '"' : '';
  1108. $title = esc_attr( $args['title'] );
  1109. ?>
  1110. <div class="mce-widget mce-btn<?php if ( $args['both'] ) { ?> wp-fullscreen-both<?php } ?>">
  1111. <button type="button" aria-label="<?php echo $title; ?>" title="<?php echo $title; ?>"<?php echo $onclick; ?> id="wp_fs_<?php echo $button; ?>">
  1112. <i class="mce-ico mce-i-<?php echo $button; ?>"></i>
  1113. </button>
  1114. </div>
  1115. <?php
  1116. }
  1117. ?>
  1118. </div></div>
  1119. <div id="wp-fullscreen-save">
  1120. <input type="button" class="button button-primary right" value="<?php echo $save; ?>" onclick="wp.editor.fullscreen.save();" />
  1121. <span class="wp-fullscreen-saved-message"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
  1122. <span class="wp-fullscreen-error-message"><?php _e('Save failed.'); ?></span>
  1123. <span class="spinner"></span>
  1124. </div>
  1125. </div>
  1126. </div>
  1127. </div>
  1128. <div id="wp-fullscreen-statusbar">
  1129. <div id="wp-fullscreen-status">
  1130. <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
  1131. <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
  1132. </div>
  1133. </div>
  1134. </div>
  1135. <div class="fullscreen-overlay" id="fullscreen-overlay"></div>
  1136. <div class="fullscreen-overlay fullscreen-fader fade-300" id="fullscreen-fader"></div>
  1137. <?php
  1138. }
  1139. /**
  1140. * Performs post queries for internal linking.
  1141. *
  1142. * @since 3.1.0
  1143. *
  1144. * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
  1145. * @return false|array Results.
  1146. */
  1147. public static function wp_link_query( $args = array() ) {
  1148. $pts = get_post_types( array( 'public' => true ), 'objects' );
  1149. $pt_names = array_keys( $pts );
  1150. $query = array(
  1151. 'post_type' => $pt_names,
  1152. 'suppress_filters' => true,
  1153. 'update_post_term_cache' => false,
  1154. 'update_post_meta_cache' => false,
  1155. 'post_status' => 'publish',
  1156. 'posts_per_page' => 20,
  1157. );
  1158. $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
  1159. if ( isset( $args['s'] ) )
  1160. $query['s'] = $args['s'];
  1161. $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
  1162. /**
  1163. * Filter the link query arguments.
  1164. *
  1165. * Allows modification of the link query arguments before querying.
  1166. *
  1167. * @see WP_Query for a full list of arguments
  1168. *
  1169. * @since 3.7.0
  1170. *
  1171. * @param array $query An array of WP_Query arguments.
  1172. */
  1173. $query = apply_filters( 'wp_link_query_args', $query );
  1174. // Do main query.
  1175. $get_posts = new WP_Query;
  1176. $posts = $get_posts->query( $query );
  1177. // Check if any posts were found.
  1178. if ( ! $get_posts->post_count )
  1179. return false;
  1180. // Build results.
  1181. $results = array();
  1182. foreach ( $posts as $post ) {
  1183. if ( 'post' == $post->post_type )
  1184. $info = mysql2date( __( 'Y/m/d' ), $post->post_date );
  1185. else
  1186. $info = $pts[ $post->post_type ]->labels->singular_name;
  1187. $results[] = array(
  1188. 'ID' => $post->ID,
  1189. 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ),
  1190. 'permalink' => get_permalink( $post->ID ),
  1191. 'info' => $info,
  1192. );
  1193. }
  1194. /**
  1195. * Filter the link query results.
  1196. *
  1197. * Allows modification of the returned link query results.
  1198. *
  1199. * @since 3.7.0
  1200. *
  1201. * @see 'wp_link_query_args' filter
  1202. *
  1203. * @param array $results {
  1204. * An associative array of query results.
  1205. *
  1206. * @type array {
  1207. * @type int $ID Post ID.
  1208. * @type string $title The trimmed, escaped post title.
  1209. * @type string $permalink Post permalink.
  1210. * @type string $info A 'Y/m/d'-formatted date for 'post' post type,
  1211. * the 'singular_name' post type label otherwise.
  1212. * }
  1213. * }
  1214. * @param array $query An array of WP_Query arguments.
  1215. */
  1216. return apply_filters( 'wp_link_query', $results, $query );
  1217. }
  1218. /**
  1219. * Dialog for internal linking.
  1220. *
  1221. * @since 3.1.0
  1222. */
  1223. public static function wp_link_dialog() {
  1224. $search_panel_visible = '1' == get_user_setting( 'wplink', '0' ) ? ' search-panel-visible' : '';
  1225. // display: none is required here, see #WP27605
  1226. ?>
  1227. <div id="wp-link-backdrop" style="display: none"></div>
  1228. <div id="wp-link-wrap" class="wp-core-ui<?php echo $search_panel_visible; ?>" style="display: none">
  1229. <form id="wp-link" tabindex="-1">
  1230. <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?>
  1231. <div id="link-modal-title">
  1232. <?php _e( 'Insert/edit link' ) ?>
  1233. <button type="button" id="wp-link-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
  1234. </div>
  1235. <div id="link-selector">
  1236. <div id="link-options">
  1237. <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
  1238. <div>
  1239. <label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" name="href" /></label>
  1240. </div>
  1241. <div>
  1242. <label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" name="linktitle" /></label>
  1243. </div>
  1244. <div class="link-target">
  1245. <label><span>&nbsp;</span><input type="checkbox" id="link-target-checkbox" /> <?php _e( 'Open link in a new window/tab' ); ?></label>
  1246. </div>
  1247. </div>
  1248. <p class="howto"><a href="#" id="wp-link-search-toggle"><?php _e( 'Or link to existing content' ); ?></a></p>
  1249. <div id="search-panel">
  1250. <div class="link-search-wrapper">
  1251. <label>
  1252. <span class="search-label"><?php _e( 'Search' ); ?></span>
  1253. <input type="search" id="search-field" class="link-search-field" autocomplete="off" />
  1254. <span class="spinner"></span>
  1255. </label>
  1256. </div>
  1257. <div id="search-results" class="query-results" tabindex="0">
  1258. <ul></ul>
  1259. <div class="river-waiting">
  1260. <span class="spinner"></span>
  1261. </div>
  1262. </div>
  1263. <div id="most-recent-results" class="query-results" tabindex="0">
  1264. <div class="query-notice" id="query-notice-message">
  1265. <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em>
  1266. <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
  1267. </div>
  1268. <ul></ul>
  1269. <div class="river-waiting">
  1270. <span class="spinner"></span>
  1271. </div>
  1272. </div>
  1273. </div>
  1274. </div>
  1275. <div class="submitbox">
  1276. <div id="wp-link-cancel">
  1277. <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a>
  1278. </div>
  1279. <div id="wp-link-update">
  1280. <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button button-primary" id="wp-link-submit" name="wp-link-submit">
  1281. </div>
  1282. </div>
  1283. </form>
  1284. </div>
  1285. <?php
  1286. }
  1287. }