tinymce.inc 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543
  1. <?php
  2. /**
  3. * @file
  4. * Editor integration functions for TinyMCE.
  5. */
  6. /**
  7. * Plugin implementation of hook_editor().
  8. *
  9. * @todo wysiwyg_<editor>_alter() to add/inject optional libraries like gzip.
  10. */
  11. function wysiwyg_tinymce_editor() {
  12. $editor['tinymce'] = array(
  13. 'title' => 'TinyMCE',
  14. 'vendor url' => 'https://www.tinymce.com',
  15. 'download url' => 'https://www.tinymce.com/download',
  16. 'library path' => wysiwyg_get_path('tinymce'),
  17. 'libraries' => array(
  18. '' => array(
  19. 'title' => 'Minified',
  20. 'files' => array('tiny_mce.js'),
  21. ),
  22. 'src' => array(
  23. 'title' => 'Source',
  24. 'files' => array('tiny_mce_src.js'),
  25. ),
  26. ),
  27. 'version callback' => 'wysiwyg_tinymce_version',
  28. 'verified version range' => array('3.3.9.2', '4.5.1'),
  29. 'themes callback' => 'wysiwyg_tinymce_themes',
  30. 'settings form callback' => 'wysiwyg_tinymce_settings_form',
  31. 'init callback' => 'wysiwyg_tinymce_init',
  32. 'settings callback' => 'wysiwyg_tinymce_settings',
  33. 'plugin callback' => '_wysiwyg_tinymce_plugins',
  34. 'plugin meta callback' => '_wysiwyg_tinymce_plugin_meta',
  35. 'migrate settings callback' => '_wysiwyg_tinymce_migrate_settings',
  36. 'proxy plugin' => array(
  37. 'drupal' => array(
  38. 'load' => TRUE,
  39. 'proxy' => TRUE,
  40. ),
  41. ),
  42. 'proxy plugin settings callback' => '_wysiwyg_tinymce_proxy_plugin_settings',
  43. 'versions' => array(
  44. '3.3' => array(
  45. 'js files' => array('tinymce-3.js'),
  46. 'library path' => wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce',
  47. 'css files' => array('tinymce-3.css'),
  48. 'libraries' => array(
  49. '' => array(
  50. 'title' => 'Minified',
  51. 'files' => array(
  52. // Some versions can not be aggregated.
  53. 'tiny_mce.js' => array('preprocess' => FALSE),
  54. ),
  55. ),
  56. 'jquery' => array(
  57. 'title' => 'jQuery',
  58. 'files' => array('tiny_mce_jquery.js'),
  59. ),
  60. 'src' => array(
  61. 'title' => 'Source',
  62. 'files' => array('tiny_mce_src.js'),
  63. ),
  64. ),
  65. ),
  66. '4' => array(
  67. 'js files' => array('tinymce-4.js'),
  68. 'css files' => array('tinymce-4.css'),
  69. 'library path' => wysiwyg_get_path('tinymce') . '/js/tinymce',
  70. 'libraries' => array(
  71. 'min' => array(
  72. 'title' => 'Minified',
  73. 'files' => array('tinymce.min.js'),
  74. ),
  75. ),
  76. ),
  77. ),
  78. );
  79. return $editor;
  80. }
  81. /**
  82. * Detect editor version.
  83. *
  84. * @param $editor
  85. * An array containing editor properties as returned from hook_editor().
  86. *
  87. * @return
  88. * The installed editor version.
  89. */
  90. function wysiwyg_tinymce_version($editor) {
  91. $script = $editor['library path'] . '/jscripts/tiny_mce/tiny_mce.js';
  92. if (!file_exists($script)) {
  93. $script = $editor['library path'] . '/js/tinymce/tinymce.min.js';
  94. if (!file_exists($script)) {
  95. return;
  96. }
  97. }
  98. $script = fopen($script, 'r');
  99. // Version is contained in the first 200 chars.
  100. $line = fgets($script, 200);
  101. fclose($script);
  102. // 2.x: this.majorVersion="2";this.minorVersion="1.3"
  103. // 3.x: majorVersion:'3',minorVersion:'2.0.1'
  104. // 4.x: 4.0b2 (2013-04-24)
  105. if (preg_match('@majorVersion[=:]["\'](\d).+?minorVersion[=:]["\']([\d\.]+)@', $line, $version) || preg_match('@(\d)\.([\d\.\w]+) @', $line, $version)) {
  106. return $version[1] . '.' . $version[2];
  107. }
  108. }
  109. /**
  110. * Determine available editor themes or check/reset a given one.
  111. *
  112. * @param $editor
  113. * A processed hook_editor() array of editor properties.
  114. * @param $profile
  115. * A wysiwyg editor profile.
  116. *
  117. * @return
  118. * An array of theme names. The first returned name should be the default
  119. * theme name.
  120. */
  121. function wysiwyg_tinymce_themes($editor, $profile) {
  122. /*
  123. $themes = array();
  124. $dir = $editor['library path'] . '/themes/';
  125. if (is_dir($dir) && $dh = opendir($dir)) {
  126. while (($file = readdir($dh)) !== FALSE) {
  127. if (!in_array($file, array('.', '..', 'CVS', '.svn')) && is_dir($dir . $file)) {
  128. $themes[$file] = $file;
  129. }
  130. }
  131. closedir($dh);
  132. asort($themes);
  133. }
  134. return $themes;
  135. */
  136. if (version_compare($editor['installed version'], '4', '>=')) {
  137. return array('modern');
  138. }
  139. else {
  140. return array('advanced', 'simple');
  141. }
  142. }
  143. /**
  144. * Enhances the editor profile settings form for TinyMCE.
  145. *
  146. * @see http://www.tinymce.com/wiki.php/Configuration
  147. */
  148. function wysiwyg_tinymce_settings_form(&$form, &$form_state) {
  149. $version = $form_state['wysiwyg']['editor']['installed version'];
  150. $profile = $form_state['wysiwyg_profile'];
  151. $settings = $profile->settings;
  152. $default_settings = array(
  153. 'convert_fonts_to_spans' => TRUE,
  154. 'paste_convert_middot_lists' => TRUE,
  155. 'paste_max_consecutive_linebreaks' => 2,
  156. 'paste_retain_style_properties' => 'none',
  157. 'paste_remove_styles' => TRUE,
  158. 'paste_remove_styles_if_webkit' => TRUE,
  159. 'paste_strip_class_attributes' => 'mso',
  160. 'paste_text_linebreaktype' => 'combined',
  161. 'verify_html' => TRUE,
  162. );
  163. if (version_compare($version, '3.2.5', '>=')) {
  164. $default_settings['autoresize_min_height'] = NULL;
  165. $default_settings['autoresize_max_height'] = NULL;
  166. }
  167. if (version_compare($version, '3.3', '>=')) {
  168. $default_settings['style_formats'] = '';
  169. $default_settings['formats'] = '';
  170. };
  171. if (version_compare($version, '3.4b1', '>=')) {
  172. $default_settings['indent'] = FALSE;
  173. }
  174. else {
  175. $default_settings['apply_source_formatting'] = FALSE;
  176. }
  177. if (version_compare($version, '4', '>=')) {
  178. $default_settings += array(
  179. 'block_formats' => '',
  180. 'image_advtab' => FALSE,
  181. 'paste_as_text' => FALSE,
  182. 'paste_data_images' => FALSE,
  183. 'resize' => TRUE,
  184. 'menu' => NULL,
  185. );
  186. }
  187. else {
  188. $default_settings += array(
  189. 'paste_auto_cleanup_on_paste' => TRUE,
  190. 'preformatted' => FALSE,
  191. 'remove_linebreaks' => TRUE,
  192. // Also available, but buggy in TinyMCE 2.x: blockquote,code,dt,dd,samp.
  193. 'theme_advanced_blockformats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
  194. 'theme_advanced_path' => TRUE,
  195. 'theme_advanced_statusbar_location' => 'bottom',
  196. 'theme_advanced_styles' => '',
  197. 'theme_advanced_resize_horizontal' => FALSE,
  198. 'theme_advanced_resizing' => TRUE,
  199. 'theme_advanced_toolbar_align' => 'left',
  200. 'theme_advanced_toolbar_location' => 'top',
  201. );
  202. }
  203. $settings += $default_settings;
  204. if (version_compare($version, '3.4b1', '<')) {
  205. $form['output']['apply_source_formatting'] = array(
  206. '#type' => 'checkbox',
  207. '#title' => t('Apply source formatting'),
  208. '#default_value' => $settings['apply_source_formatting'],
  209. '#return_value' => 1,
  210. '#description' => t('If enabled, the editor will re-format the HTML source code. Disabling this option could avoid conflicts with other input filters.') . ' ' . _wysiwyg_tinymce_get_setting_description('apply_source_formatting'),
  211. );
  212. }
  213. else {
  214. $form['output']['indent'] = array(
  215. '#type' => 'checkbox',
  216. '#title' => t('Indent source'),
  217. '#default_value' => $settings['indent'],
  218. '#return_value' => 1,
  219. '#description' => t('If enabled, the editor will re-format the HTML source code. Disabling this option could avoid conflicts with other input filters.'),
  220. );
  221. }
  222. $form['output']['verify_html'] = array(
  223. '#type' => 'checkbox',
  224. '#title' => t('Verify HTML'),
  225. '#default_value' => $settings['verify_html'],
  226. '#return_value' => 1,
  227. '#description' => t('If enabled, potentially malicious code like <code>&lt;HEAD&gt;</code> tags will be removed from HTML contents.') . (_wysiwyg_tinymce_is_v3() ? ' ' . _wysiwyg_tinymce_get_setting_description('verify_html') : ''),
  228. );
  229. $form['output']['convert_fonts_to_spans'] = array(
  230. '#type' => 'checkbox',
  231. '#title' => t('Convert &lt;font&gt; tags to styles'),
  232. '#default_value' => $settings['convert_fonts_to_spans'],
  233. '#return_value' => 1,
  234. '#description' => t('If enabled, HTML tags declaring the font size, font family, font color and font background color will be replaced by inline CSS styles.') . ' ' . _wysiwyg_tinymce_get_setting_description('convert_fonts_to_spans', 'content-filtering'),
  235. );
  236. if (version_compare($version, '4', '<')) {
  237. $form['appearance']['theme_advanced_toolbar_location'] = array(
  238. '#type' => 'select',
  239. '#title' => t('Toolbar location'),
  240. '#default_value' => $settings['theme_advanced_toolbar_location'],
  241. '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
  242. '#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_toolbar_location'),
  243. );
  244. $form['appearance']['theme_advanced_toolbar_align'] = array(
  245. '#type' => 'select',
  246. '#title' => t('Button alignment'),
  247. '#default_value' => $settings['theme_advanced_toolbar_align'],
  248. '#options' => array('center' => t('Center'), 'left' => t('Left'), 'right' => t('Right')),
  249. '#description' => t('This option controls the alignment of icons in the editor toolbar.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_toolbar_align'),
  250. );
  251. $form['appearance']['theme_advanced_statusbar_location'] = array(
  252. '#type' => 'select',
  253. '#title' => t('Status bar location'),
  254. '#default_value' => $settings['theme_advanced_statusbar_location'],
  255. '#options' => array('none' => t('Hide'), 'top' => t('Top'), 'bottom' => t('Bottom')),
  256. '#description' => t('This option enables you to specify where the element statusbar with the path and resize tool should be located.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_statusbar_location'),
  257. );
  258. $form['appearance']['theme_advanced_path'] = array(
  259. '#type' => 'checkbox',
  260. '#title' => t('Enable the element path'),
  261. '#default_value' => $settings['theme_advanced_path'],
  262. '#return_value' => 1,
  263. '#description' => t('This option gives you the ability to enable/disable the element path.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_path'),
  264. '#states' => array(
  265. 'invisible' => array(
  266. ':input[name="theme_advanced_statusbar_location"]' => array('value' => 'none'),
  267. ),
  268. ),
  269. );
  270. $form['appearance']['theme_advanced_resizing'] = array(
  271. '#type' => 'checkbox',
  272. '#title' => t('Enable resizing button'),
  273. '#default_value' => $settings['theme_advanced_resizing'],
  274. '#return_value' => 1,
  275. '#description' => t('This option gives you the ability to enable/disable the resizing button.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_resizing'),
  276. '#states' => array(
  277. 'invisible' => array(
  278. ':input[name="theme_advanced_statusbar_location"]' => array('value' => 'none'),
  279. ),
  280. ),
  281. );
  282. $form['appearance']['theme_advanced_resize_horizontal'] = array(
  283. '#type' => 'checkbox',
  284. '#title' => t('Enable horizontal resizing'),
  285. '#default_value' => $settings['theme_advanced_resize_horizontal'],
  286. '#return_value' => 1,
  287. '#description' => t('This option gives you the ability to enable/disable the horizontal resizing. If enabled, the Path location toolbar must be set to "Top" or "Bottom" in order to display the resize icon.') . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_resize_horizontal'),
  288. );
  289. $form['output']['remove_linebreaks'] = array(
  290. '#type' => 'checkbox',
  291. '#title' => t('Remove linebreaks'),
  292. '#default_value' => $settings['remove_linebreaks'],
  293. '#return_value' => 1,
  294. '#description' => t('If enabled, the editor will remove most linebreaks from contents. Disabling this option could avoid conflicts with other input filters.') . ' ' . _wysiwyg_tinymce_get_setting_description('remove_linebreaks'),
  295. );
  296. $form['output']['preformatted'] = array(
  297. '#type' => 'checkbox',
  298. '#title' => t('Preformatted'),
  299. '#default_value' => $settings['preformatted'],
  300. '#return_value' => 1,
  301. '#description' => t('If enabled, the editor will insert TAB characters on tab and preserve other whitespace characters just like a PRE element in HTML does.') . ' ' . _wysiwyg_tinymce_get_setting_description('preformatted'),
  302. );
  303. $form['css']['theme_advanced_blockformats'] = array(
  304. '#type' => 'textfield',
  305. '#title' => t('Block formats'),
  306. '#default_value' => $settings['theme_advanced_blockformats'],
  307. '#size' => 40,
  308. '#maxlength' => 250,
  309. '#description' => t('Comma separated list of HTML block formats. Possible values: <code>@format-list</code>.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,div,blockquote,address,pre,code,dt,dd')) . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_blockformats'),
  310. '#element_validate' => array('wysiwyg_tinymce_settings_form_validate_blockformats'),
  311. '#weight' => 30,
  312. );
  313. $form['css']['theme_advanced_styles'] = array(
  314. '#type' => 'textarea',
  315. '#title' => t('CSS classes'),
  316. '#default_value' => $settings['theme_advanced_styles'],
  317. '#description' => t('Optionally define CSS classes for the "Font style" dropdown list.<br />Enter one class on each line in the format: !format. Example: !example<br />If left blank, CSS classes are automatically imported from all loaded stylesheet(s).',
  318. array(
  319. '!format' => '<code>[title]=[class]</code>',
  320. '!example' => 'My heading=header1',
  321. )
  322. ) . ' ' . _wysiwyg_tinymce_get_setting_description('theme_advanced_styles'),
  323. '#weight' => 40,
  324. );
  325. }
  326. if (version_compare($version, '3.3', '>=')) {
  327. if (version_compare($version, '4', '>=')) {
  328. $form['css']['theme_advanced_styles']['#access'] = FALSE;
  329. }
  330. else {
  331. $form['css']['theme_advanced_styles']['#description'] .= '<br />' . t('This setting is only used if <em>Style formats</em> is empty.');
  332. }
  333. $form['css']['style_formats'] = array(
  334. '#type' => 'textarea',
  335. '#title' => t('Style formats'),
  336. '#default_value' => $settings['style_formats'],
  337. '#description' => t('A JSON object containing advanced style formats for text and other elements to add to the editor. The value will be rendered as styles in the Formats dropdown.') . ' ' . _wysiwyg_tinymce_get_setting_description('style_formats', 'content-formatting'),
  338. '#element_validate' => array('_wysiwyg_tinymce_settings_form_validate_style_formats'),
  339. '#weight' => 40,
  340. );
  341. $form['css']['formats'] = array(
  342. '#type' => 'textarea',
  343. '#title' => t('Formats'),
  344. '#default_value' => $settings['formats'],
  345. '#description' => t('This option enables you to override and add custom formats. A format is for example the style that get applied when you press the bold button inside the editor. TinyMCE has some built in formats that you can override, see the editor documentation for the complete list. The value to put in should be a JSON object containing format definitions.') . ' ' . _wysiwyg_tinymce_get_setting_description('formats', 'content-formatting'),
  346. '#element_validate' => array('_wysiwyg_tinymce_settings_form_validate_formats'),
  347. '#weight' => 50,
  348. );
  349. }
  350. if (version_compare($version, '4', '>=')) {
  351. $form['image'] = array(
  352. '#type' => 'fieldset',
  353. '#title' => t('Image plugin'),
  354. '#description' => t('Settings for the <a href="@url">@plugin</a> plugin.', array('@plugin' => 'image', '@url' => _wysiwyg_tinymce_get_plugin_url('image'))),
  355. '#collapsible' => TRUE,
  356. '#collapsed' => TRUE,
  357. '#group' => 'advanced',
  358. );
  359. $form['image']['image_advtab'] = array(
  360. '#type' => 'checkbox',
  361. '#title' => t('Advanced tab'),
  362. '#default_value' => $settings['image_advtab'],
  363. '#return_value' => 1,
  364. '#description' => t('Enable the advanced tab in the image dialog.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('image', 'image_advtab'),
  365. );
  366. if (version_compare($version, '4.0.13', '>=')) {
  367. $form['css']['style_formats_merge'] = array(
  368. '#type' => 'checkbox',
  369. '#title' => t('Merge style formats'),
  370. '#default_value' => !empty($settings['style_formats_merge']),
  371. '#return_value' => 1,
  372. '#description' => t('Append the styles formats to the default set instead of replacing it completely.') . ' ' . _wysiwyg_tinymce_get_setting_description('style_formats_merge', 'content-formatting'),
  373. '#weight' => 41,
  374. );
  375. }
  376. $form['css']['block_formats'] = array(
  377. '#type' => 'textfield',
  378. '#title' => t('Block formats'),
  379. '#default_value' => $settings['block_formats'],
  380. '#size' => 40,
  381. '#maxlength' => 250,
  382. '#description' => t('Semi-colon separated list of block formats for the block listbox. The format is: <code>@format-list</code>.', array('@format-list' => 'Paragraph=p;Header 1=h1;Header 2=h2;Header 3=h3')) . ' ' . _wysiwyg_tinymce_get_setting_description('block_formats', 'content-formatting'),
  383. '#weight' => 20,
  384. );
  385. }
  386. $form['paste'] = array(
  387. '#type' => 'fieldset',
  388. '#title' => t('Paste plugin'),
  389. '#description' => t('Settings for the <a href="@url">@plugin</a> plugin.', array('@plugin' => 'paste', '@url' => _wysiwyg_tinymce_get_plugin_url('paste'))),
  390. '#collapsible' => TRUE,
  391. '#collapsed' => TRUE,
  392. '#group' => 'advanced',
  393. );
  394. if (version_compare($version, '4', '<')) {
  395. $form['paste']['paste_auto_cleanup_on_paste'] = array(
  396. '#type' => 'checkbox',
  397. '#title' => t('Process contents on paste'),
  398. '#default_value' => !empty($settings['paste_auto_cleanup_on_paste']),
  399. '#return_value' => 1,
  400. '#description' => t('If enabled, contents will be automatically processed when you paste using Ctrl+V or similar methods. Cleaning up contents from MS Word or pasting as plain text will <strong>not</strong> work without this.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_auto_cleanup_on_paste'),
  401. '#element_validate' => array('wysiwyg_tinymce_settings_form_validate_blockformats'),
  402. );
  403. }
  404. $form['paste']['paste_auto_cleanup_on_paste'] = array(
  405. '#type' => 'checkbox',
  406. '#title' => t('Process contents on paste'),
  407. '#default_value' => !empty($settings['paste_auto_cleanup_on_paste']),
  408. '#return_value' => 1,
  409. '#description' => t('If enabled, contents will be automatically processed when you paste using Ctrl+V or similar methods. Cleaning up contents from MS Word or pasting as plain text will <strong>not</strong> work without this.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_auto_cleanup_on_paste'),
  410. );
  411. $form['paste']['paste_block_drop'] = array(
  412. '#type' => 'checkbox',
  413. '#title' => t('Block drag/drop'),
  414. '#default_value' => !empty($settings['paste_block_drop']),
  415. '#return_value' => 1,
  416. '#description' => t('If enabled, blocks drag/drop from/to the editor and inside it.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_block_drop'),
  417. );
  418. $form['paste']['paste_retain_style_properties'] = array(
  419. '#type' => 'textfield',
  420. '#title' => t('Retain style properties'),
  421. '#default_value' => $settings['paste_retain_style_properties'],
  422. '#description' => t('Comma separated list of style properties to retain during the paste operation from Word. For example: "font-size,color". If you want to remove all style properties use an empty string "" or "none". If you want to keep all style properties, use "all" or "*".') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_retain_style_properties'),
  423. );
  424. $form['paste']['paste_strip_class_attributes'] = array(
  425. '#type' => 'select',
  426. '#title' => t('Strip class attributes'),
  427. '#options' => array(
  428. 'none' => t('None'),
  429. 'all' => t('All'),
  430. 'mso' => t('MS Office'),
  431. ),
  432. '#default_value' => $settings['paste_strip_class_attributes'],
  433. '#description' => t('Enables you to strip the class attributes when pasted.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_strip_class*attributes'),
  434. );
  435. $form['paste']['paste_remove_spans'] = array(
  436. '#type' => 'checkbox',
  437. '#title' => t('Remove spans'),
  438. '#default_value' => !empty($settings['paste_remove_spans']),
  439. '#return_value' => 1,
  440. '#description' => t('If enabled, removes all span elements when pasting.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_remove_spans'),
  441. );
  442. $form['paste']['paste_remove_styles'] = array(
  443. '#type' => 'checkbox',
  444. '#title' => t('Remove styles'),
  445. '#default_value' => !empty($settings['paste_remove_styles']),
  446. '#return_value' => 1,
  447. '#description' => t('If enabled, removes all style information when pasting, regardless of browser type. Pasting from Word 2000 will cause TinyMCE to error.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_remove_styles'),
  448. );
  449. $form['paste']['paste_remove_styles_if_webkit'] = array(
  450. '#type' => 'checkbox',
  451. '#title' => t('Remove styles in WebKit'),
  452. '#default_value' => !empty($settings['paste_remove_styles_if_webkit']),
  453. '#return_value' => 1,
  454. '#description' => t('If enabled, removes all style information when pasting in WebKit since it has a serious paste bug.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_remove_styles_if_webkit'),
  455. );
  456. $form['paste']['paste_convert_middot_lists'] = array(
  457. '#type' => 'checkbox',
  458. '#title' => t('Convert Word lists'),
  459. '#default_value' => !empty($settings['paste_convert_middot_lists']),
  460. '#return_value' => 1,
  461. '#description' => t('If enabled, the paste plugin tries to convert Word lists into semantic XHTML list elements.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_convert_middot_lists'),
  462. );
  463. $form['paste']['paste_convert_headers_to_strong'] = array(
  464. '#type' => 'checkbox',
  465. '#title' => t('Convert headers to strong'),
  466. '#default_value' => !empty($settings['paste_convert_headers_to_srong']),
  467. '#return_value' => 1,
  468. '#description' => t('If enabled, the paste plugin tries to convert Word headers to strong tags.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_convert_headers_to_strong'),
  469. );
  470. $form['paste']['paste_max_consecutive_linebreaks'] = array(
  471. '#type' => 'textfield',
  472. '#title' => t('Max consecutive linebreaks'),
  473. '#size' => 5,
  474. '#maxlength' => '10',
  475. '#default_value' => !empty($settings['paste_max_consecutive_linebreaks']),
  476. '#description' => t('The maximum number of consecutive linebreaks to use.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_max_consecutive_linebreaks'),
  477. );
  478. $form['paste']['paste_text_use_dialog'] = array(
  479. '#type' => 'checkbox',
  480. '#title' => t('Use dialog'),
  481. '#default_value' => !empty($settings['paste_text_use_dialog']),
  482. '#return_value' => 1,
  483. '#description' => t('If enabled, uses legacy mode for the Paste as Text button. Will use a dialog instead of treating the button as a toggle.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_text_use_dialog'),
  484. );
  485. $form['paste']['paste_text_sticky'] = array(
  486. '#type' => 'checkbox',
  487. '#title' => t('Sticky "Paste text" button'),
  488. '#default_value' => !empty($settings['paste_text_sticky']),
  489. '#return_value' => 1,
  490. '#description' => t('If enabled, keeps the "Paste text" button selected after pasting. Requires the "Paste text" button and "Process contents on paste" to be enabled.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_text_sticky'),
  491. );
  492. $form['paste']['paste_text_sticky_default'] = array(
  493. '#type' => 'checkbox',
  494. '#title' => t('Paste as plain text by default'),
  495. '#default_value' => !empty($settings['paste_text_sticky_default']),
  496. '#return_value' => 1,
  497. '#description' => t('If enabled, the "Paste text" button is selected when the editor starts. Requires the "Paste text" button and "Process contents on paste" to be enabled.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_text_sticky_default'),
  498. );
  499. $form['paste']['paste_text_notifyalways'] = array(
  500. '#type' => 'checkbox',
  501. '#title' => t('Always notify when pasting plain text'),
  502. '#default_value' => !empty($settings['paste_text_notifyalways']),
  503. '#return_value' => 1,
  504. '#description' => t('If enabled, users will be notified each time plain text pasting mode is enabled.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_text_notifyalways'),
  505. );
  506. $form['paste']['paste_text_linebreaktype'] = array(
  507. '#type' => 'select',
  508. '#title' => t('Linebreak type'),
  509. '#options' => array(
  510. 'combined' => t('Combined'),
  511. 'p' => t('<p>'),
  512. 'br' => t('<br>'),
  513. ),
  514. '#default_value' => $settings['paste_text_linebreaktype'],
  515. '#description' => t('Converts plain text linebreaks to br or p elements.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_text_linebreaktype'),
  516. );
  517. if (version_compare($version, '4', '>=')) {
  518. $form['appearance']['menu'] = array(
  519. '#title' => t('Menu'),
  520. '#type' => 'textarea',
  521. '#default_value' => $settings['menu'],
  522. '#description' => t('The options to use in the menu, as a JSON&star; structure. The <a href="@controls">available controls</a> and format for this setting can be found in the official settings description. Note: You must make sure the plugin providing a control is also enabled.', array('@controls' => url('https://www.tinymce.com/docs/advanced/editor-control-identifiers/#menucontrols'))) . ' ' . _wysiwyg_tinymce_get_setting_description('menu', 'editor-appearance'),
  523. '#element_validate' => array('wysiwyg_tinymce_settings_form_validate_menu'),
  524. );
  525. $form['appearance']['resize'] = array(
  526. '#type' => 'select',
  527. '#title' => t('Resizing'),
  528. '#description' => t('This option gives you the ability to disable the resize handle or set it to resize both horizontal and vertically.') . ' ' . _wysiwyg_tinymce_get_setting_description('resize', 'editor-appearance'),
  529. '#options' => array(
  530. FALSE => t('Disabled'),
  531. TRUE => t('Vertical'),
  532. 'both' => t('Horizontal and vertical'),
  533. ),
  534. '#default_value' => $settings['resize'],
  535. );
  536. $form['paste']['paste_as_text'] = array(
  537. '#type' => 'checkbox',
  538. '#title' => t('Paste as text'),
  539. '#default_value' => !empty($settings['paste_as_text']),
  540. '#return_value' => 1,
  541. '#description' => t('If enabled, the default state of the "Paste as text" edit menu option is enabled.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('paste', 'paste_as_text'),
  542. );
  543. $form['paste']['paste_data_images'] = array(
  544. '#type' => 'checkbox',
  545. '#title' => t('Paste inline images'),
  546. '#default_value' => !empty($settings['paste_data_images']),
  547. '#return_value' => 1,
  548. '#description' => t('If enabled, users will be allowed to paste data:url (inline) images, embedding the actual images data with the text contents. This is normally not something people want, since say embedding a 600kb image will take a long time to upload, store the image in the database, block page loads, and prevent caching the image across multiple pages. Firefox is known to allow embedding images through pasting or drag and drop.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('plugin', 'paste_data_images'),
  549. );
  550. }
  551. if (version_compare($version, '3.2.5', '>=')) {
  552. $form['autoresize'] = array(
  553. '#type' => 'fieldset',
  554. '#title' => t('Autoresize plugin'),
  555. '#description' => t('Settings for the <a href="@url">@plugin</a> plugin.', array('@plugin' => 'autoresize', '@url' => _wysiwyg_tinymce_get_plugin_url('autoresize'))),
  556. '#collapsible' => TRUE,
  557. '#collapsed' => TRUE,
  558. '#group' => 'advanced',
  559. );
  560. $form['autoresize']['autoresize_min_height'] = array(
  561. '#title' => t('Min height'),
  562. '#type' => 'textfield',
  563. '#default_value' => $settings['autoresize_min_height'],
  564. '#size' => 10,
  565. '#description' => t('Minimum height value of the editor when it auto resizes.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('autoresize', 'autoresize_min_height'),
  566. );
  567. $form['autoresize']['autoresize_max_height'] = array(
  568. '#title' => t('Max height'),
  569. '#type' => 'textfield',
  570. '#default_value' => $settings['autoresize_max_height'],
  571. '#size' => 10,
  572. '#description' => t('Maximum height value of the editor when it auto resizes.') . ' ' . _wysiwyg_tinymce_get_plugin_setting_description('autoresize', 'autoresize_max_height'),
  573. );
  574. }
  575. }
  576. /**
  577. * #element_validate handler for theme_advanced_blockformats element added by wysiwyg_tinymce_settings_form().
  578. */
  579. function wysiwyg_tinymce_settings_form_validate_blockformats($element, &$form_state) {
  580. // Remove any white-space from 'theme_advanced_blockformats' setting, since
  581. // the editor relies on a comma-separated list to explode().
  582. form_set_value($element, preg_replace('@\s+@', '', $element['#value']), $form_state);
  583. }
  584. /**
  585. * #element_validate handler for formats element added by wysiwyg_tinymce_settings_form().
  586. */
  587. function _wysiwyg_tinymce_settings_form_validate_formats($element, &$form_state) {
  588. $helper = _wysiwyg_settings_form_validate_json_object($element, $form_state);
  589. if (!$helper['valid']) {
  590. form_error($element, t('The specified formats must be a valid JSON object.') . '&nbsp;&star;');
  591. }
  592. }
  593. /**
  594. * #element_validate handler for style_formats element added by wysiwyg_tinymce_settings_form().
  595. */
  596. function _wysiwyg_tinymce_settings_form_validate_style_formats($element, &$form_state) {
  597. $helper = _wysiwyg_settings_form_validate_json_object($element, $form_state);
  598. if (!$helper['valid']) {
  599. form_error($element, t('The specified style formats must be a valid JSON object.') . '&nbsp;&star;');
  600. }
  601. }
  602. /**
  603. * #element_validate handler for menu element added by wysiwyg_tinymce_settings_form().
  604. */
  605. function wysiwyg_tinymce_settings_form_validate_menu($element, &$form_state) {
  606. $helper = _wysiwyg_settings_form_validate_json_object($element, $form_state);
  607. if (!$helper['valid']) {
  608. form_error($element, t('The specified menu structure must be a valid JSON object.') . '&nbsp;&star;');
  609. }
  610. }
  611. /**
  612. * Returns an initialization JavaScript for this editor library.
  613. *
  614. * @param array $editor
  615. * The editor library definition.
  616. * @param string $library
  617. * The library variant key from $editor['libraries'].
  618. * @param object $profile
  619. * The (first) wysiwyg editor profile.
  620. *
  621. * @return string
  622. * A string containing inline JavaScript to execute before the editor library
  623. * script is loaded.
  624. */
  625. function wysiwyg_tinymce_init($editor, $library) {
  626. // TinyMCE unconditionally searches for its library filename in SCRIPT tags on
  627. // on the page upon loading the library in order to determine the base path to
  628. // itself. When JavaScript aggregation is enabled, this search fails and all
  629. // relative constructed paths within TinyMCE are broken. The library has a
  630. // tinyMCE.baseURL property, but it is not publicly documented and thus not
  631. // reliable. The official support forum suggests to solve the issue through
  632. // the global window.tinyMCEPreInit variable also used by various serverside
  633. // compressor scrips available from the official website.
  634. // @see http://www.tinymce.com/forum/viewtopic.php?id=23286
  635. $settings = drupal_json_encode(array(
  636. 'base' => base_path() . $editor['library path'],
  637. 'suffix' => (strpos($library, 'src') !== FALSE || strpos($library, 'dev') !== FALSE ? '_src' : (strpos($library, 'min') !== FALSE ? '.min' : '')),
  638. 'query' => '',
  639. ));
  640. return <<<EOL
  641. window.tinyMCEPreInit = $settings;
  642. EOL;
  643. }
  644. /**
  645. * Return runtime editor settings for a given wysiwyg profile.
  646. *
  647. * @param $editor
  648. * A processed hook_editor() array of editor properties.
  649. * @param $config
  650. * An array containing wysiwyg editor profile settings.
  651. * @param $theme
  652. * The name of a theme/GUI/skin to use.
  653. *
  654. * @return
  655. * A settings array to be populated in
  656. * Drupal.settings.wysiwyg.configs.{editor}
  657. */
  658. function wysiwyg_tinymce_settings($editor, $config, $theme) {
  659. $version = $editor['installed version'];
  660. $settings = array(
  661. // @todo Add a setting for this.
  662. 'button_tile_map' => TRUE,
  663. 'document_base_url' => base_path(),
  664. 'mode' => 'none',
  665. 'plugins' => array(),
  666. 'theme' => $theme,
  667. 'width' => '100%',
  668. // Strict loading mode must be enabled; otherwise TinyMCE would use
  669. // document.write() in IE and Chrome.
  670. 'strict_loading_mode' => TRUE,
  671. // TinyMCE's URL conversion magic breaks Drupal modules that use a special
  672. // syntax for paths. This makes 'relative_urls' obsolete.
  673. 'convert_urls' => FALSE,
  674. // The default entity_encoding ('named') converts too many characters in
  675. // languages (like Greek). Since Drupal supports Unicode, we only convert
  676. // HTML control characters and invisible characters. TinyMCE always converts
  677. // XML default characters '&', '<', '>'.
  678. 'entities' => '160,nbsp,173,shy,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm',
  679. );
  680. if (version_compare($version, '4', '<')) {
  681. $settings += array(
  682. 'theme_advanced_toolbar_location' => 'top',
  683. );
  684. }
  685. $check_if_set = array(
  686. 'apply_source_formatting',
  687. 'autoresize_min_height',
  688. 'autoresize_max_height',
  689. 'convert_fonts_to_spans',
  690. 'font_styles_merge',
  691. 'image_advtab',
  692. 'indent',
  693. 'language',
  694. 'paste_as_text',
  695. 'paste_auto_cleanup_on_paste',
  696. 'paste_block_drop',
  697. 'paste_convert_middot_lists',
  698. 'paste_convert_headers_to_strong',
  699. 'paste_data_images',
  700. 'paste_max_consecutive_linebreaks',
  701. 'paste_remove_spans',
  702. 'paste_remove_styles',
  703. 'paste_remove_styles_if_webkit',
  704. 'paste_retain_style_properties',
  705. 'paste_strip_class_attributes',
  706. 'paste_text_linebreaktype',
  707. 'paste_text_notifyalways',
  708. 'paste_text_use_dialog',
  709. 'paste_text_sticky',
  710. 'paste_text_sticky_default',
  711. 'preformatted',
  712. 'remove_linebreaks',
  713. 'resize',
  714. 'style_formats_merge',
  715. 'theme_advanced_blockformats',
  716. 'theme_advanced_path',
  717. 'theme_advanced_resize_horizontal',
  718. 'theme_advanced_resizing',
  719. 'theme_advanced_statusbar_location',
  720. 'theme_advanced_toolbar_location',
  721. 'theme_advanced_toolbar_align',
  722. );
  723. foreach ($check_if_set as $setting_name) {
  724. if (isset($config[$setting_name])) {
  725. $settings[$setting_name] = $config[$setting_name];
  726. }
  727. }
  728. if (!empty($settings['language']) && $settings['language'] == 'en') {
  729. unset($settings['language']);
  730. }
  731. if (isset($config['verify_html'])) {
  732. // TinyMCE performs a type-agnostic comparison on this particular setting.
  733. $settings['verify_html'] = (bool) $config['verify_html'];
  734. }
  735. if (!empty($config['style_formats'])) {
  736. $settings['style_formats'] = json_decode($config['style_formats']);
  737. }
  738. if (!empty($config['theme_advanced_styles'])) {
  739. $settings['theme_advanced_styles'] = implode(';', array_filter(explode("\n", str_replace("\r", '', $config['theme_advanced_styles']))));
  740. }
  741. if (!empty($config['menu'])) {
  742. $settings['menu'] = json_decode($config['menu']);
  743. }
  744. if (isset($config['css_setting'])) {
  745. if ($config['css_setting'] == 'theme') {
  746. $settings['content_css'] = implode(',', wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : ''));
  747. }
  748. elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
  749. $settings['content_css'] = strtr($config['css_path'], array(
  750. '%b' => base_path(),
  751. '%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
  752. '%q' => variable_get('css_js_query_string', ''),
  753. ));
  754. }
  755. }
  756. // Find the enabled buttons and the button row they belong on.
  757. // Also map the plugin metadata for each button.
  758. // @todo What follows is a pain; needs a rewrite.
  759. // $settings['buttons'] are stacked into $settings['theme_advanced_buttons1']
  760. // later.
  761. $settings['buttons'] = array();
  762. if (!empty($config['buttons']) && is_array($config['buttons'])) {
  763. // Only array keys in $settings['extensions'] matter; added to
  764. // $settings['plugins'] later.
  765. $settings['extensions'] = array();
  766. // $settings['extended_valid_elements'] are just stacked, unique'd later,
  767. // and transformed into a comma-separated string in
  768. // wysiwyg_add_editor_settings().
  769. // @todo Needs a complete plugin API redesign using arrays for
  770. // tag => attributes definitions and array_merge_recursive().
  771. $settings['extended_valid_elements'] = array();
  772. $plugins = wysiwyg_get_plugins($editor['name']);
  773. foreach ($config['buttons'] as $plugin => $buttons) {
  774. foreach ($buttons as $button => $enabled) {
  775. // Iterate separately over buttons and extensions properties.
  776. foreach (array('buttons', 'extensions') as $type) {
  777. // Skip unavailable plugins.
  778. if (!isset($plugins[$plugin][$type][$button])) {
  779. continue;
  780. }
  781. // Add buttons.
  782. if ($type == 'buttons') {
  783. if (!empty($plugins[$plugin]['proxy'])) {
  784. $settings['buttons'][] = 'drupal_' . $button;
  785. }
  786. else {
  787. $settings['buttons'][] = $button;
  788. }
  789. }
  790. // Add external Drupal plugins to the list of extensions.
  791. if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) {
  792. $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', 'drupal_' . $button)] = 1;
  793. }
  794. // Add external plugins to the list of extensions.
  795. elseif ($type == 'buttons' && empty($plugins[$plugin]['internal'])) {
  796. $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1;
  797. }
  798. // Add internal buttons that also need to be loaded as extension.
  799. elseif ($type == 'buttons' && !empty($plugins[$plugin]['load'])) {
  800. $settings['extensions'][$plugin] = 1;
  801. }
  802. // Add plain extensions.
  803. elseif ($type == 'extensions' && !empty($plugins[$plugin]['load'])) {
  804. $settings['extensions'][$plugin] = 1;
  805. }
  806. // Allow plugins to add valid HTML elements.
  807. if (!empty($plugins[$plugin]['extended_valid_elements'])) {
  808. $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
  809. }
  810. // Allow plugins to add or override global configuration settings.
  811. if (!empty($plugins[$plugin]['options'])) {
  812. $settings = array_merge($settings, $plugins[$plugin]['options']);
  813. }
  814. }
  815. }
  816. }
  817. // Clean-up.
  818. $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']);
  819. if ($settings['extensions']) {
  820. $settings['plugins'] = array_keys($settings['extensions']);
  821. }
  822. unset($settings['extensions']);
  823. }
  824. if (version_compare($version, '4', '>=')) {
  825. if (isset($settings['buttons'])) {
  826. // @todo Allow to sort/arrange editor buttons.
  827. for ($i = 0; $i < count($settings['buttons']); $i++) {
  828. $settings['toolbar'][] = $settings['buttons'][$i];
  829. }
  830. }
  831. // TinyMCE 3 allowed the callback to be the name of a funciton. Convert it
  832. // to a reference to keep compatibility with IMCE Wysiwyg bridge module.
  833. // Both isset() and is_string() needed, generates a notice if undefined.
  834. if (isset($settings['file_browser_callback']) && is_string($settings['file_browser_callback'])) {
  835. $settings['file_browser_callback'] = wysiwyg_wrap_js_callback($settings['file_browser_callback']);
  836. }
  837. }
  838. // Add theme-specific settings.
  839. switch ($theme) {
  840. case 'advanced':
  841. $settings += array(
  842. 'theme_advanced_resizing_use_cookie' => FALSE,
  843. );
  844. if (isset($settings['buttons'])) {
  845. // These rows explicitly need to be set to be empty, otherwise TinyMCE
  846. // loads its default buttons of the advanced theme for each row.
  847. $settings += array(
  848. 'theme_advanced_buttons1' => array(),
  849. 'theme_advanced_buttons2' => array(),
  850. 'theme_advanced_buttons3' => array(),
  851. );
  852. // @todo Allow to sort/arrange editor buttons.
  853. for ($i = 0; $i < count($settings['buttons']); $i++) {
  854. $settings['theme_advanced_buttons1'][] = $settings['buttons'][$i];
  855. }
  856. }
  857. break;
  858. }
  859. unset($settings['buttons']);
  860. // Convert the config values into the form expected by TinyMCE.
  861. $csv_settings = array('toolbar', 'plugins', 'extended_valid_elements', 'theme_advanced_buttons1', 'theme_advanced_buttons2', 'theme_advanced_buttons3');
  862. foreach ($csv_settings as $key) {
  863. if (isset($settings[$key]) && is_array($settings[$key])) {
  864. $settings[$key] = implode(',', $settings[$key]);
  865. }
  866. }
  867. return $settings;
  868. }
  869. /**
  870. * Callback to migrate settings between known TinyMCE versions.
  871. */
  872. function _wysiwyg_tinymce_migrate_settings(&$settings, $editor, $profile_version, $installed_version) {
  873. $version_diff = version_compare($installed_version, $profile_version);
  874. // Default to no changes needed.
  875. $migrated_version = TRUE;
  876. if ($version_diff === 1) {
  877. // Upgrading, starting at the profile version going up.
  878. if (version_compare($profile_version, '3.4b1', '<') && version_compare($installed_version, '3.4b1', '>=')) {
  879. if (isset($settings['apply_source_formatting'])) {
  880. $settings['indent'] = $settings['apply_source_formatting'];
  881. unset($settings['apply_source_formatting']);
  882. }
  883. $migrated_version = '3.4b1';
  884. }
  885. if (version_compare($profile_version, '4', '<') && version_compare($installed_version, '4', '>=')) {
  886. if (isset($settings['buttons']['advimage']['advimage']) && $settings['buttons']['advimage']['advimage']) {
  887. $settings['image_advtab'] = TRUE;
  888. }
  889. if (isset($settings['theme']) && ($settings['theme'] === 'advanced' || $settings['theme'] === 'simple')) {
  890. $settings['theme'] = 'modern';
  891. }
  892. if (isset($settings['theme_advanced_blockformats'])) {
  893. $formats = array();
  894. foreach (explode(',', $settings['theme_advanced_blockformats']) as $block) {
  895. switch ($block[0]) {
  896. case 'p':
  897. $formats[] = 'Paragraph=p';
  898. break;
  899. case 'h':
  900. $formats[] = 'Heading ' . $block[1] . '=' . $block;
  901. break;
  902. default:
  903. $formats[] = strtoupper($block[0]) . substr($block, 1) . '=' . $block;
  904. }
  905. }
  906. $settings['block_formats'] = implode(';', $formats);
  907. unset($settings['theme_advanced_blockformats']);
  908. }
  909. $settings['resize'] = FALSE;
  910. if (!empty($settings['theme_advanced_resizing'])) {
  911. if (!empty($settings['theme_advanced_resize_horizontal'])) {
  912. $settings['resize'] = 'both';
  913. }
  914. else {
  915. $settings['resize'] = TRUE;
  916. }
  917. }
  918. unset($settings['theme_advanced_resizing'], $settings['theme_advanced_resize_horizontal'], $settings['theme_advanced_toolbar_location'], $settings['theme_advanced_toolbar_align'], $settings['theme_advanced_statusbar_location'], $settings['theme_advanced_styles']);
  919. $fixed_buttons = array();
  920. foreach ($settings['buttons'] as $plugin => $buttons) {
  921. foreach ($buttons as $button => $enabled) {
  922. $converted = _wysiwyg_tinymce_3_to_4_plugin_remap($plugin, $button);
  923. if ($converted) {
  924. $fixed_buttons[$converted[0]][$converted[1]] = $enabled;
  925. }
  926. }
  927. }
  928. $settings['buttons'] = $fixed_buttons;
  929. $migrated_version = '4.0.0';
  930. }
  931. }
  932. else {
  933. // Downgrading, starting at the profile version going down.
  934. if (version_compare($profile_version, '4.5.0', '>=') && version_compare($installed_version, '4.5.0', '<')) {
  935. unset($settings['buttons']['toc']);
  936. $migrated_version = '4.5.0';
  937. }
  938. if (version_compare($profile_version, '4.3.0', '>=') && version_compare($installed_version, '4.3.0', '<')) {
  939. unset($settings['buttons']['codesample']);
  940. $migrated_version = '4.2.7';
  941. }
  942. if (version_compare($profile_version, '4', '>=') && version_compare($installed_version, '4', '<')) {
  943. if (isset($settings['theme']) && $settings['theme'] === 'modern') {
  944. $settings['theme'] = 'advanced';
  945. }
  946. if (!isset($settings['block_formats'])) {
  947. $formats = array();
  948. foreach (explode(';', $settings['block_formats']) as $block) {
  949. $parts = explode('=', $block, 2);
  950. $formats[] = $parts[1];
  951. }
  952. $settings['theme_advanced_blockformats'] = implode(',', $formats);
  953. unset($settings['block_formats']);
  954. }
  955. $settings['theme_advanced_resizing'] = $settings['theme_advanced_resize_horizontal'] = FALSE;
  956. if ($settings['resize'] == 'both' || $settings['resize'] == TRUE) {
  957. $settings['theme_advanced_resizing'] = TRUE;
  958. if ($settings['resize'] == 'both') {
  959. $settings['theme_advanced_resize_horizontal'] = TRUE;
  960. }
  961. }
  962. unset($settings['resize']);
  963. $fixed_buttons = array();
  964. foreach ($settings['buttons'] as $plugin => $buttons) {
  965. foreach ($buttons as $button => $enabled) {
  966. $converted = _wysiwyg_tinymce_4_to_3_plugin_remap($plugin, $button);
  967. if (is_array($converted[0])) {
  968. foreach ($converted as $sub_converted) {
  969. $fixed_buttons[$sub_converted[0]][$sub_converted[1]] = $enabled;
  970. }
  971. }
  972. else {
  973. if ($converted) {
  974. $fixed_buttons[$converted[0]][$converted[1]] = $enabled;
  975. }
  976. }
  977. }
  978. }
  979. $settings['buttons'] = $fixed_buttons;
  980. if (!empty($settings['image_advtab'])) {
  981. $settings['buttons']['advimage']['advimage'] = 1;
  982. unset($settings['image_advtab']);
  983. }
  984. $migrated_version = '4.0';
  985. }
  986. if (version_compare($profile_version, '3.4b1', '>=') && version_compare($installed_version, '3.4b1', '<')) {
  987. if (isset($settings['indent'])) {
  988. $settings['apply_source_formatting'] = $settings['indent'];
  989. unset($settings['indent']);
  990. }
  991. $migrated_version = '3.4b1';
  992. }
  993. }
  994. return $migrated_version;
  995. }
  996. /**
  997. * Build a JS settings array with global metadata for native external plugins.
  998. */
  999. function _wysiwyg_tinymce_plugin_meta($editor, $plugin) {
  1000. $meta = NULL;
  1001. if (!empty($plugin['load'])) {
  1002. // Add path for native external plugins; internal ones are loaded
  1003. // automatically.
  1004. if (empty($plugin['internal']) && isset($plugin['filename'])) {
  1005. $meta = base_path() . $plugin['path'] . '/' . $plugin['filename'];
  1006. }
  1007. }
  1008. return $meta;
  1009. }
  1010. /**
  1011. * Add or remove leading hiven to/of external plugin names.
  1012. *
  1013. * TinyMCE requires that external plugins, which should not be loaded from
  1014. * its own plugin repository are prefixed with a hiven in the name.
  1015. *
  1016. * @param string $op
  1017. * Operation to perform, 'add' or 'remove' (hiven).
  1018. * @param string $name
  1019. * A plugin name.
  1020. */
  1021. function _wysiwyg_tinymce_plugin_name($op, $name) {
  1022. if ($op == 'add') {
  1023. if (strpos($name, '-') !== 0) {
  1024. return '-' . $name;
  1025. }
  1026. return $name;
  1027. }
  1028. elseif ($op == 'remove') {
  1029. if (strpos($name, '-') === 0) {
  1030. return substr($name, 1);
  1031. }
  1032. return $name;
  1033. }
  1034. }
  1035. /**
  1036. * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
  1037. */
  1038. function _wysiwyg_tinymce_proxy_plugin_settings($editor, $profile, $plugins) {
  1039. $settings = array();
  1040. foreach ($plugins as $name => $plugin) {
  1041. // Just need a list of all enabled plugins for each instance.
  1042. $settings[$name] = TRUE;
  1043. }
  1044. return $settings;
  1045. }
  1046. /**
  1047. * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
  1048. */
  1049. function _wysiwyg_tinymce_plugins($editor) {
  1050. // Buttons which have no icons and only work as menu items. (TinyMCE 4)
  1051. // visualaid - Visual aid
  1052. // selectall - Select All https://github.com/tinymce/tinymce/issues/634
  1053. // Start with a list matching 3.x and modify to match the installed version.
  1054. $plugins = array(
  1055. 'default' => array(
  1056. 'path' => $editor['library path'] . '/themes/advanced',
  1057. 'buttons' => array(
  1058. 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
  1059. 'strikethrough' => t('Strikethrough'),
  1060. 'justifyleft' => t('Align left'), 'justifycenter' => t('Align center'), 'justifyright' => t('Align right'), 'justifyfull' => t('Align full'),
  1061. 'bullist' => t('Unordered list'), 'numlist' => t('Ordered list'),
  1062. 'outdent' => t('Outdent'), 'indent' => t('Indent'),
  1063. 'undo' => t('Undo'), 'redo' => t('Redo'),
  1064. 'link' => t('Link'), 'unlink' => t('Unlink'), 'anchor' => t('Anchor'),
  1065. 'image' => t('Image'),
  1066. 'cleanup' => t('Cleanup messy code'),
  1067. 'forecolor' => t('Select text color'), 'backcolor' => t('Select background color'),
  1068. 'formatselect' => t('Block format'), 'styleselect' => t('Styles'),
  1069. 'fontselect' => t('Font'), 'fontsizeselect' => t('Font size'),
  1070. 'sup' => t('Superscript'), 'sub' => t('Subscript'),
  1071. 'blockquote' => t('Blockquote'), 'code' => t('Edit HTML Source'),
  1072. 'hr' => t('Insert horizontal ruler'),
  1073. 'cut' => t('Cut'), 'copy' => t('Copy'), 'paste' => t('Paste'),
  1074. 'visualaid' => t('Toggle guidelines/invisible elements'),
  1075. 'removeformat' => t('Remove formatting'),
  1076. 'charmap' => t('Insert custom character'),
  1077. 'help' => t('Help'),
  1078. ),
  1079. ),
  1080. 'advhr' => array(
  1081. 'buttons' => array('advhr' => t('Advanced horizontal rule')),
  1082. 'extended_valid_elements' => array('hr[class|width|size|noshade]'),
  1083. 'url' => _wysiwyg_tinymce_get_plugin_url('advhr'),
  1084. ),
  1085. 'advimage' => array(
  1086. 'extensions' => array('advimage' => t('Advanced image')),
  1087. 'extended_valid_elements' => array('img[src|alt|title|align|width|height|usemap|hspace|vspace|border|style|class|onmouseover|onmouseout|id|name|longdesc]'),
  1088. 'url' => _wysiwyg_tinymce_get_plugin_url('advimage'),
  1089. ),
  1090. 'advlink' => array(
  1091. 'extensions' => array('advlink' => t('Advanced link')),
  1092. 'extended_valid_elements' => array('a[name|href|target|title|class|onfocus|onblur|onclick|ondlbclick|onmousedown|onmouseup|onmouseover|onmouseout|onkeypress|onkeydown|onkeyup|id|style|rel]'),
  1093. 'url' => _wysiwyg_tinymce_get_plugin_url('advlink'),
  1094. ),
  1095. 'autosave' => array(
  1096. 'extensions' => array('autosave' => t('Auto save')),
  1097. 'url' => _wysiwyg_tinymce_get_plugin_url('autosave'),
  1098. ),
  1099. 'bbcode' => array(
  1100. 'extensions' => array('bbcode' => t('BBCode')),
  1101. 'url' => _wysiwyg_tinymce_get_plugin_url('bbcode'),
  1102. ),
  1103. 'contextmenu' => array(
  1104. 'extensions' => array('contextmenu' => t('Context menu')),
  1105. 'url' => _wysiwyg_tinymce_get_plugin_url('contextmenu'),
  1106. ),
  1107. 'directionality' => array(
  1108. 'buttons' => array('ltr' => t('Left-to-right'), 'rtl' => t('Right-to-left')),
  1109. 'url' => _wysiwyg_tinymce_get_plugin_url('directionality'),
  1110. ),
  1111. 'emotions' => array(
  1112. 'buttons' => array('emotions' => t('Emotions')),
  1113. 'url' => _wysiwyg_tinymce_get_plugin_url('emotions'),
  1114. ),
  1115. 'fullscreen' => array(
  1116. 'buttons' => array('fullscreen' => t('Fullscreen')),
  1117. 'url' => _wysiwyg_tinymce_get_plugin_url('fullscreen'),
  1118. ),
  1119. 'inlinepopups' => array(
  1120. 'extensions' => array('inlinepopups' => t('Inline popups')),
  1121. 'options' => array(
  1122. 'dialog_type' => array('modal'),
  1123. ),
  1124. 'url' => _wysiwyg_tinymce_get_plugin_url('inlinepopups'),
  1125. ),
  1126. 'insertdatetime' => array(
  1127. 'buttons' => array('insertdate' => t('Insert date'), 'inserttime' => t('Insert time')),
  1128. 'options' => array(
  1129. 'plugin_insertdate_dateFormat' => '%Y-%m-%d',
  1130. 'plugin_insertdate_timeFormat' => '%H:%M:%S',
  1131. ),
  1132. 'url' => _wysiwyg_tinymce_get_plugin_url('insertdatetime'),
  1133. ),
  1134. 'media' => array(
  1135. 'buttons' => array('media' => t('Media')),
  1136. 'url' => _wysiwyg_tinymce_get_plugin_url('media'),
  1137. ),
  1138. 'nonbreaking' => array(
  1139. 'buttons' => array('nonbreaking' => t('Insert nonbreaking space')),
  1140. 'url' => _wysiwyg_tinymce_get_plugin_url('nonbreaking'),
  1141. ),
  1142. 'paste' => array(
  1143. 'buttons' => array('pastetext' => t('Paste text'), 'pasteword' => t('Paste from Word'), 'selectall' => t('Select all')),
  1144. 'url' => _wysiwyg_tinymce_get_plugin_url('paste'),
  1145. ),
  1146. 'preview' => array(
  1147. 'buttons' => array('preview' => t('Preview')),
  1148. 'url' => _wysiwyg_tinymce_get_plugin_url('preview'),
  1149. ),
  1150. 'print' => array(
  1151. 'buttons' => array('print' => t('Print')),
  1152. 'url' => _wysiwyg_tinymce_get_plugin_url('print'),
  1153. ),
  1154. 'safari' => array(
  1155. 'path' => $editor['library path'] . '/plugins/safari',
  1156. 'extensions' => array('safari' => t('Safari compatibility')),
  1157. ),
  1158. 'searchreplace' => array(
  1159. 'buttons' => array('search' => t('Search'), 'replace' => t('Replace')),
  1160. 'url' => _wysiwyg_tinymce_get_plugin_url('searchreplace'),
  1161. ),
  1162. 'style' => array(
  1163. 'buttons' => array('styleprops' => t('Advanced CSS styles')),
  1164. 'url' => _wysiwyg_tinymce_get_plugin_url('style'),
  1165. ),
  1166. 'table' => array(
  1167. 'buttons' => array('tablecontrols' => t('Table')),
  1168. 'url' => _wysiwyg_tinymce_get_plugin_url('table'),
  1169. ),
  1170. 'visualchars' => array(
  1171. 'buttons' => array('visualchars' => t('Show invisible characters')),
  1172. 'url' => _wysiwyg_tinymce_get_plugin_url('visualchars'),
  1173. ),
  1174. );
  1175. if (version_compare($editor['installed version'], '2.0.6', '>')) {
  1176. $plugins['media'] = array(
  1177. 'path' => $editor['library path'] . '/plugins/media',
  1178. 'buttons' => array('media' => t('Media')),
  1179. 'url' => 'http://www.tinymce.com/wiki.php/Plugin:media',
  1180. 'internal' => TRUE,
  1181. 'load' => TRUE,
  1182. );
  1183. $plugins['xhtmlxtras'] = array(
  1184. 'path' => $editor['library path'] . '/plugins/xhtmlxtras',
  1185. 'buttons' => array('cite' => t('Citation'), 'del' => t('Deleted'), 'abbr' => t('Abbreviation'), 'acronym' => t('Acronym'), 'ins' => t('Inserted'), 'attribs' => t('HTML attributes')),
  1186. 'url' => _wysiwyg_tinymce_get_plugin_url('xhtmlxtras'),
  1187. );
  1188. }
  1189. if (version_compare($editor['installed version'], '3.2.5', '>=')) {
  1190. $plugins['autoresize'] = array(
  1191. 'extensions' => array('autoresize' => t('Auto resize')),
  1192. 'url' => _wysiwyg_tinymce_get_plugin_url('autoresize'),
  1193. );
  1194. if (version_compare($editor['installed version'], '3.2.6', '>=')) {
  1195. $plugins['wordcount'] = array(
  1196. 'extensions' => array('wordcount' => t('Word count')),
  1197. 'url' => _wysiwyg_tinymce_get_plugin_url('wordcount'),
  1198. );
  1199. if (version_compare($editor['installed version'], '3.3', '>=')) {
  1200. unset($plugins['safari']);
  1201. $plugins['advlist'] = array(
  1202. 'extensions' => array('advlist' => t('Advanced list')),
  1203. 'url' => _wysiwyg_tinymce_get_plugin_url('advlist'),
  1204. );
  1205. if (version_compare($editor['installed version'], '3.4.1', '>=')) {
  1206. $plugins['lists'] = array(
  1207. 'extensions' => array('lists' => t('List normalizer')),
  1208. 'url' => 'http://www.tinymce.com/wiki.php/Plugin:lists',
  1209. 'extended_valid_elements' => array(
  1210. 'li[class|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title|type|value]',
  1211. 'ol[class|compact|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|start|style|title|type]',
  1212. 'ul[class|compact|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title|type]',
  1213. ),
  1214. );
  1215. if (version_compare($editor['installed version'], '3.5.b1', '>=')) {
  1216. $plugins['visualblocks'] = array(
  1217. 'buttons' => array('visualblocks' => t('Show blocks')),
  1218. 'url' => (version_compare($editor['installed version'], '4.0', '>') ? _wysiwyg_tinymce_get_plugin_url('visualblocks') : NULL),
  1219. );
  1220. }
  1221. }
  1222. }
  1223. }
  1224. }
  1225. if (version_compare($editor['installed version'], '4', '>=')) {
  1226. $removed_plugins = array('advhr', 'advimage', 'advlink', 'inlinepopups', 'style', 'emotions', 'xhtmlxtras');
  1227. foreach ($removed_plugins as $plugin) {
  1228. unset($plugins[$plugin]);
  1229. }
  1230. $removed_default_buttons = array('link', 'unlink', 'anchor', 'image', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', 'forecolor', 'backcolor', 'sup', 'sub', 'code', 'charmap', 'cleanup', 'hr');
  1231. foreach ($removed_default_buttons as $button) {
  1232. unset($plugins['default']['buttons'][$button]);
  1233. }
  1234. unset($plugins['insertdatetime']['buttons']['insertdate']);
  1235. $plugins['paste']['buttons'] = array(
  1236. 'pastetext' => t('Paste text'),
  1237. );
  1238. unset($plugins['paste']['buttons']['pasteword']);
  1239. $plugins['searchreplace']['buttons'] = array(
  1240. 'searchreplace' => t('Search & Replace'),
  1241. );
  1242. $plugins['table']['buttons'] = array(
  1243. 'table' => t('table'),
  1244. );
  1245. $plugins['hr'] = array(
  1246. 'buttons' => array('hr' => t('Insert horizontal rule')),
  1247. 'extended_valid_elements' => array('hr[class|width|size|noshade]'),
  1248. 'url' => _wysiwyg_tinymce_get_plugin_url('hr'),
  1249. );
  1250. $plugins['charmap'] = array(
  1251. 'buttons' => array('charmap' => t('Insert special character')),
  1252. 'url' => _wysiwyg_tinymce_get_plugin_url('charmap'),
  1253. );
  1254. $plugins['anchor'] = array(
  1255. 'buttons' => array('anchor' => t('Insert anchor/bookmark')),
  1256. 'url' => _wysiwyg_tinymce_get_plugin_url('anchor'),
  1257. );
  1258. $plugins['image'] = array(
  1259. 'buttons' => array('image' => t('Insert image')),
  1260. 'url' => _wysiwyg_tinymce_get_plugin_url('image'),
  1261. );
  1262. $plugins['link'] = array(
  1263. 'buttons' => array('link' => t('Insert link'), 'unlink' => t('Remove link')),
  1264. 'url' => _wysiwyg_tinymce_get_plugin_url('link'),
  1265. );
  1266. $plugins['emoticons'] = array(
  1267. 'buttons' => array('emoticons' => t('Insert emoticons')),
  1268. 'url' => _wysiwyg_tinymce_get_plugin_url('emoticons'),
  1269. );
  1270. $plugins['code'] = array(
  1271. 'buttons' => array('code' => t('Source code')),
  1272. 'url' => _wysiwyg_tinymce_get_plugin_url('code'),
  1273. );
  1274. $plugins['textcolor'] = array(
  1275. 'buttons' => array('forecolor' => t('Text color'), 'backcolor' => t('Background color')),
  1276. 'url' => _wysiwyg_tinymce_get_plugin_url('textcolor'),
  1277. );
  1278. $plugins['default']['buttons'] += array(
  1279. 'subscript' => t('Subscript'), 'superscript' => t('Superscript'),
  1280. 'alignleft' => t('Align left'), 'aligncenter' => t('Align center'),
  1281. 'alignright' => t('Align right'), 'alignjustify' => t('Justify'),
  1282. 'selectall' => t('Select all')
  1283. );
  1284. if (version_compare($editor['installed version'], '4.3.0', '>=')) {
  1285. $plugins['codesample'] = array(
  1286. 'buttons' => array('codesample' => t('Insert/edit code sample')),
  1287. 'url' => _wysiwyg_tinymce_get_plugin_url('codesample'),
  1288. );
  1289. }
  1290. if (version_compare($editor['installed version'], '4.5.0', '>=')) {
  1291. $plugins['toc'] = array(
  1292. 'buttons' => array('toc' => t('Table of Contents')),
  1293. 'url' => _wysiwyg_tinymce_get_plugin_url('toc'),
  1294. );
  1295. }
  1296. }
  1297. foreach ($plugins as &$plugin) {
  1298. // All the plugins are internal and need explicit loading.
  1299. $plugin['internal'] = TRUE;
  1300. $plugin['load'] = TRUE;
  1301. }
  1302. $plugins['default']['load'] = FALSE;
  1303. return $plugins;
  1304. }
  1305. /**
  1306. * Helper to get the URL for a plugin.
  1307. *
  1308. * @param $plugin_name
  1309. * Internal name of the plugin.
  1310. */
  1311. function _wysiwyg_tinymce_get_plugin_url($plugin_name) {
  1312. static $is_v3 = NULL;
  1313. if (!isset($is_v3)) {
  1314. $is_v3 = _wysiwyg_tinymce_is_v3();
  1315. }
  1316. return ($is_v3 ? "https://www.tinymce.com/docs-3x/reference/plugins/Plugin3x@$plugin_name/" : "https://www.tinymce.com/docs/plugins/$plugin_name/");
  1317. }
  1318. /**
  1319. * Helper to get the URL for a plugin settings.
  1320. *
  1321. * @param $plugin_name
  1322. * Internal name of the plugin.
  1323. * @param $setting_name
  1324. * Internal name of the setting.
  1325. */
  1326. function _wysiwyg_tinymce_get_plugin_setting_description($plugin_name, $setting_name) {
  1327. static $is_v3 = NULL;
  1328. if (!isset($is_v3)) {
  1329. $is_v3 = _wysiwyg_tinymce_is_v3();
  1330. }
  1331. return t('Uses the <a href="@url">@setting</a> setting internally.', array(
  1332. '@setting' => $setting_name,
  1333. '@url' => _wysiwyg_tinymce_get_plugin_url($plugin_name) . ($is_v3 ? '' : "#$setting_name"),
  1334. ));
  1335. }
  1336. /**
  1337. * Helper to get the URL for a general setting.
  1338. *
  1339. * @param $setting_name
  1340. * Internal name of the setting.
  1341. */
  1342. function _wysiwyg_tinymce_get_setting_description($setting_name, $section_name = NULL) {
  1343. static $is_v3 = NULL;
  1344. if (!isset($is_v3)) {
  1345. $is_v3 = _wysiwyg_tinymce_is_v3();
  1346. }
  1347. return t('Uses the <a href="@url">@setting</a> setting internally.', array(
  1348. '@setting' => $setting_name,
  1349. '@url' => ($is_v3 ? url("https://www.tinymce.com/docs-3x/reference/configuration/Configuration3x@$setting_name/")
  1350. : url("https://www.tinymce.com/docs/configure/$section_name/#$setting_name")),
  1351. ));
  1352. }
  1353. /**
  1354. * Check if the installed major version is 3.
  1355. *
  1356. * @return bool
  1357. * TRUE if the installed major version is 3.
  1358. */
  1359. function _wysiwyg_tinymce_is_v3() {
  1360. static $is_v3 = NULL;
  1361. if (!isset($is_v3)) {
  1362. $editor = wysiwyg_get_editor('tinymce');
  1363. if (!empty($editor['installed version'])) {
  1364. $is_v3 = version_compare($editor['installed version'], '4.0.0', '<');
  1365. }
  1366. else {
  1367. $is_v_3 = FALSE;
  1368. }
  1369. }
  1370. return $is_v3;
  1371. }
  1372. /**
  1373. * Helper for converting plugins and buttons between TinyMCE 3 and 4.
  1374. */
  1375. function _wysiwyg_tinymce_3_to_4_plugin_remap($plugin, $button) {
  1376. switch ($button) {
  1377. case 'link':
  1378. case 'unlink':
  1379. return array('link', $button);
  1380. case 'justifyleft':
  1381. return array('default', 'alignleft');
  1382. case 'justifyright':
  1383. return array('default', 'alignright');
  1384. case 'justifycenter':
  1385. return array('default', 'aligncenter');
  1386. case 'justifyfull':
  1387. return array('default', 'alignjustify');
  1388. case 'anchor':
  1389. case 'image':
  1390. case 'code':
  1391. case 'charmap':
  1392. case 'cleanup':
  1393. case 'hr':
  1394. return array($button, $button);
  1395. case 'forecolor':
  1396. case 'backcolor':
  1397. return array('textcolor', $button);
  1398. case 'search':
  1399. case 'replace':
  1400. return array('searchreplace', 'searchreplace');
  1401. case 'emotions':
  1402. return array('emoticons', 'emoticons');
  1403. case 'sup':
  1404. return array('default', 'superscript');
  1405. case 'sub':
  1406. return array('default', 'subscript');
  1407. case 'selectall':
  1408. return array('default', 'selectall');
  1409. case 'tablecontrols':
  1410. return array('table', 'table');
  1411. case 'pasteword':
  1412. return array('paste', 'paste');
  1413. default:
  1414. return array($plugin, $button);
  1415. }
  1416. }
  1417. /**
  1418. * Helper for converting plugins and buttons between TinyMCE 4 and 3.
  1419. */
  1420. function _wysiwyg_tinymce_4_to_3_plugin_remap($plugin, $button) {
  1421. switch ($button) {
  1422. case 'link':
  1423. case 'unlink':
  1424. case 'anchor':
  1425. case 'image':
  1426. case 'code':
  1427. case 'charmap':
  1428. case 'cleanup':
  1429. case 'hr':
  1430. return array('default', $button);
  1431. case 'alignleft':
  1432. return array('default', 'justifyleft');
  1433. case 'alignright':
  1434. return array('default', 'justifyright');
  1435. case 'aligncenter':
  1436. return array('default', 'justifycenter');
  1437. case 'alignjustify':
  1438. return array('default', 'justifyfull');
  1439. case 'forecolor':
  1440. case 'backcolor':
  1441. return array('default', $button);
  1442. case 'searchreplace':
  1443. return array(array('searchreplace', 'search'), array('searchreplace', 'replace'));
  1444. case 'emoticons':
  1445. return array('emotions', 'emotions');
  1446. case 'superscript':
  1447. return array('default', 'sup');
  1448. case 'subscript':
  1449. return array('default', 'sub');
  1450. case 'selectall':
  1451. return array('paste', 'selectall');
  1452. case 'table':
  1453. return array(array('table', 'table'), array('table', 'tablecontrols'));
  1454. default:
  1455. return array($plugin, $button);
  1456. }
  1457. }