don't show tinymce on taxonomy 'add term' pages
[project/tinymce.git] / tinymce.module
CommitLineData
6f760c55
MW
1<?php
2// $Id$
76a1692d
MW
3// A collaborative project by Matt Westgate <drupal at asitis dot org>,
4// Richard Bennett <richard.b@ at ritechnologies dot com> and Jeff Robbins <robbins at jjeff dot com>
6f760c55
MW
5
6/**
7 * @file
8 * Integrate the TinyMCE editor (http://tinymce.moxiecode.com/) into Drupal.
9 */
10
11/**
12 * Implementation of hook_menu().
6f760c55
MW
13 */
14function tinymce_menu($may_cache) {
01a85ffd 15 $items = array();
277d4328
MW
16 if ($may_cache) {
17 $items[] = array('path' => 'admin/settings/tinymce', 'title' => t('tinymce'),
18 'callback' => 'tinymce_admin',
19 'access' => user_access('administer tinymce'));
01a85ffd
MW
20 }
21 return $items;
6f760c55
MW
22}
23
24/**
25 * Implementation of hook_help().
26 */
27function tinymce_help($section) {
28 switch ($section) {
29 case 'admin/modules#description':
30 return t('The TinyMCE Javascript HTML WYSIWYG editor.');
31
32 case 'admin/settings/tinymce#pages':
33 return "node/*\nuser/*\ncomment/*";
2227c39d
MW
34
35 case 'admin/settings/tinymce':
c7dac433 36 return t('<p>TinyMCE adds what-you-see-is-what-you-get (WYSIWYG) html editing to textareas. Profiles can be defined based on user roles. TinyMCE profile can define which pages receive this TinyMCE capability, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor functions. The default profile setting uses the "simple" TinyMCE theme which just shows the most minimal buttons set (bold, italic, underline, etc), but many other settings are available by switching to "advanced". And by default, textareas on affected pages will be automatically swapped out with the rich text editor. Users may disable the editor for any textarea without reloading the page. This setting can be reversed so that pages load with conventional textarea form fields. A link below each textarea allows TinyMCE to be turned on or off "on the fly".</p><p>Be aware that things can get confusing if a user is a member of roles defined in multiple profiles. In this case, the user will receive the profile for role with lowest role id they belong to. Did I mention it was confusing?</p><p>lastly, only users with the <code>access tinymce</code> permission will be able to use TinyMCE.</p>');
6f760c55
MW
37 }
38}
39
40/**
41 * Implementation of hook_perm().
42 */
43function tinymce_perm() {
01a85ffd 44 return array('administer tinymce', 'access tinymce');
6f760c55
MW
45}
46
47/**
64d74024
MW
48 * Implementation of hook_img_assist_head().
49 */
50function tinymce_img_assist_head() {
01a85ffd 51 global $base_url;
619695bd 52 // The tinymce docs say to include tiny_mce_popup.js, but this was killing IE!
093240f8 53 $popup_path = $base_url .'/'. drupal_get_path('module', 'tinymce'). '/tinymce/jscripts/tiny_mce/tiny_mce_popup.js';
76a1692d
MW
54 $img_assist_prop = $img_template = '';
55 if (module_exist('img_assist')) {
56 $img_assist_prop = $base_url .'/'. drupal_get_path('module', 'img_assist'). '/properties.js';
57 $img_assist_prop = '<script language="javascript" src="'. $img_assist_prop .'"></script>';
58
59 $img_template = variable_get('img_assist_img_html', img_assist_help('img_assist/template'));
d286eb02
MW
60 $nl = "\n";
61 $img_template = preg_replace('/\n|\r|(\r\n)/m', '\\'. $nl, $img_template);
76a1692d 62 }
093240f8 63 $clean_url = variable_get('clean_url', 0);
64d74024
MW
64
65$output = <<<EOD
76a1692d 66$img_assist_prop
64d74024 67<script language="javascript">
093240f8 68 var clean_url = $clean_url;
d286eb02 69 var img_template = '$img_template';
093240f8 70
64d74024
MW
71 function insertImage(form) {
72 if (window.opener) {
093240f8
MW
73 form['edit[filepath]'].value = window.opener.tinyMCE.convertURL(form['edit[filepath]'].value);
74 form['edit[nodePath]'].value = window.opener.tinyMCE.convertURL(form['edit[nodePath]'].value);
75
76 var img = generate_image_tag(form, 'html');
76a1692d 77 //img = img.replace(/\\r|\\n|\\t/g, '');
93c073b0 78
65badaba 79 window.opener.tinyMCE.execInstanceCommand(myTextarea.name, 'mceInsertContent', false, img, true);
64d74024
MW
80 }
81 }
82</script>
83EOD;
84
85 return $output;
86}
87
88/**
89 * Implementation of hook_img_assist_on_submit().
90 */
91function tinymce_img_assist_on_submit() {
92 return 'parent.insertImage(this.form);';
93}
94
95/**
5120a0c6 96 * Implementation of hook_elements().
64d74024 97 */
5120a0c6
MW
98function tinymce_elements() {
99 $type['textarea'] = array('#process' => 'tinymce_process_textarea');
100 return $type;
101}
102
103/**
104 * Attach tinymce to a textarea
105 */
106function tinymce_process_textarea($element) {
dd7d7335
MW
107 static $is_running = FALSE;
108 if (!user_access('access tinymce')) return NULL;
6f760c55
MW
109
110 global $user;
277d4328 111 global $base_url;
6a17f63a 112 static $profile_name;
5120a0c6
MW
113
114 //$element is an array of attributes for the textarea but there is no just 'name' value, so we extract this from the #id field
115 $textarea_name = substr($element['#id'], strpos($element['#id'], '-') + 1);
6f760c55 116
01a85ffd
MW
117 // Since tinymce_config() makes a db hit, only call it when we're pretty sure
118 // we're gonna render tinymce.
5120a0c6
MW
119 if (!$profile_name) {
120 $profile_name = db_result(db_query('SELECT s.name FROM {tinymce_settings} s INNER JOIN {tinymce_role} r ON r.name = s.name WHERE r.rid IN (%s)', implode(',', array_keys($user->roles))));
01a85ffd 121 }
5120a0c6
MW
122 $profile = tinymce_profile_load($profile_name);
123 $init = tinymce_config($profile);
124 $init['elements'] = 'edit['. $textarea_name .']';
6739c75d 125
5120a0c6 126 if (_tinymce_page_match($profile)) {
2227c39d
MW
127 // Merge user-defined TinyMCE settings.
128 $init = (array) theme('tinymce_theme', $init, $textarea_name, $init['theme'], $is_running);
5120a0c6
MW
129
130 // If $init array is empty no need to execute rest of code since there are no textareas to theme with TinyMCE
131 if (count($init) < 1) {
132 return $element;
133 }
134
2227c39d 135 $settings = array();
6739c75d 136 foreach ($init as $k => $v) {
a59a8ef6 137 $v = is_array($v) ? implode(',', $v) : $v;
76a1692d
MW
138 // Don't wrap the JS init in quotes for boolean values or functions.
139 if (strtolower($v) != 'true' && strtolower($v) != 'false' && $v[0] != '{') {
01a85ffd
MW
140 $v = '"'. $v. '"';
141 }
142 $settings[] = $k. ' : '. $v;
6739c75d 143 }
2b031c84 144 $tinymce_settings = implode(",\n ", $settings);
6739c75d 145
76a1692d 146 if (module_exist('img_assist')) {
65badaba
TD
147 $img_assist_js_on = $base_url .'/'. url('img_assist/add&editor=tinymce') .'&textarea=';
148 $img_assist_js_off = $base_url .'/'. url('img_assist/add') .'&textarea=';
6a17f63a
MW
149 $img_assist_on = $base_url .'/'. url('img_assist/add&editor=tinymce') .'&textarea=edit['. $textarea_name .']';
150 $img_assist_off = $base_url .'/'. url('img_assist/add') .'&textarea=edit['. $textarea_name .']';
93c073b0
MW
151 }
152
277d4328
MW
153 $enable = t('enable rich-text');
154 $disable = t('disable rich-text');
155
01a85ffd 156$tinymce_invoke = <<<EOD
6f760c55
MW
157<script language="javascript" type="text/javascript">
158 tinyMCE.init({
6739c75d 159 $tinymce_settings
6f760c55 160 });
65badaba 161</script>
277d4328
MW
162EOD;
163
164$js_toggle = <<<EOD
65badaba 165<script language="javascript" type="text/javascript">
277d4328
MW
166 function mceToggle(id, linkid) {
167 element = document.getElementById(id);
168 link = document.getElementById(linkid);
169 img_assist = document.getElementById('img_assist-link-'+ id);
170
171 if (tinyMCE.getEditorId(element.name) == null) {
172 tinyMCE.addMCEControl(element, element.name);
173 element.togg = 'on';
174 link.innerHTML = '$disable';
175 link.href = "javascript:mceToggle('" +id+ "', '" +linkid+ "');";
176 if (img_assist)
65badaba 177 img_assist.href = "$img_assist_js_on"+ element.name;
277d4328
MW
178 link.blur();
179 }
180 else {
181 tinyMCE.removeMCEControl(tinyMCE.getEditorId(element.name));
182 element.togg = 'off';
183 link.innerHTML = '$enable';
184 link.href = "javascript:mceToggle('" +id+ "', '" +linkid+ "');";
185 if (img_assist)
65badaba 186 img_assist.href = "$img_assist_js_off"+ element.name;
277d4328
MW
187 link.blur();
188 }
189 }
6f760c55
MW
190</script>
191EOD;
192
75d3842c 193$status = isset($user->tinymce_status) ? $user->tinymce_status : variable_get('tinymce_default_state', 0);
277d4328
MW
194$link_text = $status == 1 ? $disable : $enable;
195$no_wysiwyg = t('Your current web browser does not support WYSIWYG editing.');
01a85ffd
MW
196$wysiwyg_link = <<<EOD
197<script language="javascript" type="text/javascript">
6a17f63a
MW
198 img_assist = document.getElementById('img_assist-link-edit-$textarea_name');
199 if (img_assist) {
200 img_assist.href = tinyMCE.getEditorId('edit-$textarea_name') == null ? "$img_assist_on" : "$img_assist_off";
201 }
01a85ffd 202 if (typeof(document.execCommand) == 'undefined') {
e473c3ca 203 img_assist.href = "$img_assist_off";
c7dac433 204 document.write('<div style="font-size:x-small">$no_wysiwyg<\\/div>');
01a85ffd
MW
205 }
206 else {
c7dac433 207 document.write("<div><a href=\"javascript:mceToggle('edit-$textarea_name', 'wysiwyg4$textarea_name');\" id=\"wysiwyg4$textarea_name\">$link_text<\\/div><\\/a>");
01a85ffd
MW
208 }
209</script>
210EOD;
211
65badaba 212 // We only load the TinyMCE js file once per request
76a1692d 213 if (!$is_running) {
dd7d7335 214 $is_running = TRUE;
1689159f
MW
215 $tinymce_mod_path = drupal_get_path('module', 'tinymce');
216 if (is_dir($tinymce_mod_path.'/imagemanager/')) {
9a6185a0 217 // if tinymce imagemanager is installed
a59a8ef6 218 drupal_add_js(drupal_get_path('module', 'tinymce') .'/imagemanager/jscripts/mcimagemanager.js');
9a6185a0 219 }
1689159f
MW
220 // TinyMCE Compressor
221 if (file_exists($tinymce_mod_path. 'tinymce/jscripts/tiny_mce/tiny_mce_gzip.php')) {
222 drupal_add_js($base_url. '/'. $tinymce_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce_gzip.php');
223 }
224 else {
225 // For some crazy reason IE will only load this JS file if the absolute reference is given to it.
226 drupal_add_js($base_url. '/'. $tinymce_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce.js');
227 }
277d4328
MW
228 drupal_set_html_head($js_toggle);
229 // We have to do this becuase of some unfocused CSS in certain themes. See http://drupal.org/node/18879 for details
230 drupal_set_html_head('<style type="text/css" media="all">.mceEditor img { display: inline; }</style>');
dd7d7335 231 }
65badaba 232 // Load a TinyMCE init for each textarea.
a59a8ef6 233 if ($init) drupal_set_html_head($tinymce_invoke);
5120a0c6
MW
234
235 // Make sure to append to #suffix so it isn't completely overwritten
236 $element['#suffix'] .= $wysiwyg_link;
6f760c55 237 }
5120a0c6
MW
238
239 return $element;
6f760c55
MW
240}
241
242/**
243 * Implementation of hook_user().
244 */
245function tinymce_user($type, &$edit, &$user, $category = NULL) {
dd7d7335 246 if ($type == 'form' && $category == 'account' && user_access('access tinymce')) {
9670911b 247 $user_status = $edit['tinymce_status'] != NULL ? $edit['tinymce_status'] : ($user->tinymce_status != NULL ? $user->tinymce_status : variable_get('tinymce_default_state', 0));
a48b5273
MW
248 $form['tinymce'] = array('#type' => 'fieldset', '#title' => t('TinyMCE settings'), '#weight' => 5, '#collapsible' => TRUE, '#collapsed' => TRUE);
249 $form['tinymce']['tinymce_status'] = array('#type' => 'radios', '#title' => t('Default status'), '#default_value' => $user_status, '#options' => array(t('Off'), t('On')), '#description' => t('Should rich-text editing be enabled or disabled by default in textarea fields?'));
250 return array('tinymce' => $form);
6f760c55
MW
251 }
252 if ($type == 'validate') {
01a85ffd 253 return array('tinymce_status' => $edit['tinymce_status']);
6f760c55
MW
254 }
255}
256
257/**
6739c75d
MW
258 * @addtogroup themeable
259 * @{
260 */
261
262/**
ae7b55ca 263 * Customize a TinyMCE theme.
6739c75d
MW
264 *
265 * @param init
ae7b55ca
MW
266 * An array of settings TinyMCE should invoke a theme. You may override any
267 * of the TinyMCE settings. Details here:
6739c75d
MW
268 *
269 * http://tinymce.moxiecode.com/wrapper.php?url=tinymce/docs/using.htm
ae7b55ca
MW
270 *
271 * @param textarea_name
272 * The name of the textarea TinyMCE wants to enable.
273 *
274 * @param theme_name
2227c39d
MW
275 * The default tinymce theme name to be enabled for this textarea. The
276 * sitewide default is 'simple', but the user may also override this.
dd7d7335
MW
277 *
278 * @param is_running
279 * A boolean flag that identifies id TinyMCE is currently running for this
2227c39d 280 * request life cycle. It can be ignored.
6739c75d 281 */
dd7d7335 282function theme_tinymce_theme($init, $textarea_name, $theme_name, $is_running) {
9670911b 283 switch ($textarea_name) {
2227c39d 284 // Disable tinymce for these textareas
4048ba98 285 case 'log': // Book log
f3d0d7eb 286 case 'message': // Page log
2227c39d 287 case 'img_assist_pages':
4048ba98 288 case 'caption': // signature
097b5629 289 case 'pages':
5120a0c6 290 case 'access_pages': //TinyMCE profile settings.
af1fa2b0
TS
291 case 'user_mail_welcome_body': // user config settings
292 case 'user_mail_approval_body': // user config settings
293 case 'user_mail_pass_body': // user config settings
294 case 'synonyms': // taxonomy terms
295 case 'description': // taxonomy terms
2227c39d
MW
296 unset($init);
297 break;
298
299 // Force the 'simple' theme for some of the smaller textareas.
9670911b
MW
300 case 'signature':
301 case 'site_mission':
302 case 'site_footer':
5120a0c6 303 case 'access_pages':
f3d0d7eb
TS
304 case 'site_offline_message':
305 case 'page_help':
306 case 'user_registration_help':
307 case 'user_picture_guidelines':
9670911b 308 $init['theme'] = 'simple';
a59a8ef6
MW
309 foreach ($init as $k => $v) {
310 if (strstr($k, 'theme_advanced_')) unset($init[$k]);
311 }
2227c39d 312 break;
9670911b
MW
313 }
314
2227c39d 315 // Add some extra features when using the advanced theme.
ae7b55ca
MW
316 switch ($theme_name) {
317 case 'advanced':
a59a8ef6 318 //$init['extended_valid_elements'] = array('a[href|target|name|title|onclick]');
2227c39d 319 break;
ae7b55ca 320 }
2227c39d
MW
321
322 // Always return $init; !!
323 return $init;
6739c75d
MW
324}
325
326/** @} End of addtogroup themeable */
327
328/**
6f760c55
MW
329 * Grab the themes available to TinyMCE.
330 *
331 * TinyMCE themes control the functionality and buttons that are available to a
332 * user. Themes are only looked for within the default TinyMCE theme directory.
333 *
a59a8ef6
MW
334 * NOTE: This function is not used in this release. We are only using advanced theme.
335 *
6f760c55
MW
336 * @return
337 * An array of theme names.
338 */
339function _tinymce_get_themes() {
340 static $themes = array();
341
342 if (!$themes) {
343 $theme_loc = drupal_get_path('module', 'tinymce') .'/tinymce/jscripts/tiny_mce/themes/';
344 if (is_dir($theme_loc) && $dh = opendir($theme_loc)) {
345 while (($file = readdir($dh)) !== false) {
ae24db96 346 if (!in_array($file, array('.', '..', 'CVS')) && is_dir($theme_loc . $file)) {
6f760c55
MW
347 $themes[$file] = $file;
348 }
349 }
350 closedir($dh);
351 asort($themes);
352 }
353 }
354
355 return $themes;
356}
357
a59a8ef6
MW
358/**
359 * Return plugin metadata from the plugin registry.
360 *
361 * We also scrape each plugin's *.js file for the human friendly name and help
362 * text URL of each plugin.
363 *
364 * @return
365 * An array for each plugin.
366 */
367function _tinymce_get_buttons($skip_metadata = TRUE) {
368 include_once(drupal_get_path('module', 'tinymce'). '/plugin_reg.php');
369 $plugins = _tinymce_plugins();
370 if ($skip_metadata == FALSE && is_array($plugins)) {
371 foreach ($plugins as $name => $plugin) {
372 $file = drupal_get_path('module', 'tinymce'). '/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
373 // Grab the plugin metadata by scanning the *.js file.
374 if (file_exists($file)) {
a48b5273 375 $lines = file($file);
a59a8ef6
MW
376 $has_longname = FALSE;
377 $has_infourl = FALSE;
378 foreach ($lines as $line) {
379 if ($has_longname && $has_infourl) break;
380 if (strstr($line, 'longname')) {
381 $start = strpos($line, "'") + 1;
382 $end = strrpos($line, "'") - $start;
383 $metadata[$name]['longname'] = substr($line, $start, $end);
384 $has_longname = TRUE;
385 }
386 elseif (strstr($line, 'infourl')) {
387 $start = strpos($line, "'") + 1;
388 $end = strrpos($line, "'") - $start;
389 $metadata[$name]['infourl'] = substr($line, $start, $end);
390 $has_infourl = TRUE;
391 }
392 }
393 }
394
395 // Find out the buttons a plugin has.
396 foreach ($plugin as $k => $v) {
397 if (strstr($k, 'theme_advanced_buttons')) {
398 $metadata[$name]['buttons'] = array_merge((array) $metadata[$name]['buttons'], $plugin[$k]);
399 }
400 }
401 }
402 return $metadata;
403 }
404
405 return $plugins;
406}
01a85ffd
MW
407/********************************************************************
408 * Module Functions :: Public
409 ********************************************************************/
410
411/**
412 * Controller for tinymce administrative settings.
413 */
414function tinymce_admin($arg = NULL) {
415 $edit = $_POST['edit'];
416 $op = $_POST['op'];
417
418 $op = $arg && !$op ? $arg : $op;
419
420 switch ($op) {
421 case 'add':
422 $breadcrumb[] = array('path' => 'admin', 'title' => t('administer'));
277d4328
MW
423 $breadcrumb[] = array('path' => 'admin/settings/tinymce', 'title' => t('tinymce'));
424 $breadcrumb[] = array('path' => 'admin/settings/tinymce/add', 'title' => t('Add new tinymce profile'));
01a85ffd
MW
425 menu_set_location($breadcrumb);
426 $output = tinymce_profile_form($edit);
427 break;
428
429 case 'edit':
430 drupal_set_title(t('Edit tinymce profile'));
277d4328 431 $output = tinymce_profile_form(tinymce_profile_load(urldecode(arg(4))));
01a85ffd
MW
432 break;
433
434 case 'delete':
277d4328 435 tinymce_profile_delete(urldecode(arg(4)));
01a85ffd 436 drupal_set_message(t('Deleted profile'));
277d4328 437 drupal_goto('admin/settings/tinymce');
01a85ffd
MW
438 break;
439
440 case t('Create profile');
441 case t('Update profile');
442 if (tinymce_profile_validate($edit)) {
443 tinymce_profile_save($edit);
444 $edit['old_name'] ? drupal_set_message(t('Your tinymce profile has been updated.')) : drupal_set_message(t('Your tinymce profile has been created.'));
277d4328 445 drupal_goto('admin/settings/tinymce');
01a85ffd
MW
446 }
447 else {
448 $output = tinymce_profile_form($edit);
449 }
450 break;
451
452 case t('Save settings'):
453 variable_set('tinymce_default_state', $edit['tinymce_default_state']);
454 drupal_set_message(t('Settings updated'));
277d4328 455 drupal_goto('admin/settings/tinymce');
01a85ffd
MW
456 break;
457
458 default:
459 drupal_set_title(t('TinyMCE settings (%revision)', array('%revision' => '$Revision$')));
460 //Check if TinyMCE is installed.
461 $tinymce_loc = drupal_get_path('module', 'tinymce') .'/tinymce/';
462 if (!is_dir($tinymce_loc)) {
463 drupal_set_message(t('Could not find the TinyMCE engine installed at <strong>%tinymce-directory</strong>. Please <a href="http://tinymce.moxiecode.com/">download TinyMCE</a>, uncompress it and copy the folder into %tinymce-path.', array('%tinymce-path' => drupal_get_path('module', 'tinymce'), '%tinymce-directory' => $tinymce_loc)), 'error');
464 }
465 $output = tinymce_profile_overview();
466 }
467
a808fa67 468 print theme('page', $output);
01a85ffd
MW
469}
470
471/**
472 * Return an array of initial tinymce config options from the current role.
473 */
474function tinymce_config($profile) {
475 global $base_url;
9670911b 476 global $user;
a59a8ef6 477 // Drupal theme path.
26444876
JR
478 $themepath = drupal_get_path('theme', init_theme()).'/';
479 $host = $base_url.'/';
01a85ffd 480
195b5d64 481 $settings = $profile->settings;
01a85ffd
MW
482
483 // Build a default list of TinyMCE settings.
9670911b
MW
484
485 // Is tinymce on by default?
75d3842c 486 $status = isset($user->tinymce_status) ? $user->tinymce_status : variable_get('tinymce_default_state', 0);
76a1692d
MW
487 $init['mode'] = $status == 1 ? 'exact' : 'none';
488 $init['theme'] = $settings['theme'] ? $settings['theme'] : 'simple';
489 $init['document_base_url'] = "$base_url/";
490
491 $init['verify_html'] = $settings['verify_html'] ? $settings['verify_html'] : 'false';
76a1692d 492 $init['preformatted'] = $settings['preformatted'] ? $settings['preformatted'] : 'false';
a59a8ef6 493 $init['convert_fonts_to_styles'] = $settings['convert_fonts_to_styles'] ? $settings['convert_fonts_to_styles'] : 'false';
93c073b0 494 if ($init['theme'] == 'advanced') {
a59a8ef6 495 $init['plugins'] = array();
76a1692d
MW
496 $init['theme_advanced_toolbar_location'] = $settings['toolbar_loc'] ? $settings['toolbar_loc'] : 'bottom';
497 $init['theme_advanced_toolbar_align'] = $settings['toolbar_align'] ? $settings['toolbar_align'] : 'left';
498 $init['theme_advanced_path_location'] = $settings['path_loc'] ? $settings['path_loc'] : 'none';
a59a8ef6 499 $init['theme_advanced_resizing'] = $settings['resizing'] ? $settings['resizing'] : 'false';
76a1692d 500 $init['theme_advanced_blockformats'] = $settings['block_formats'] ? $settings['block_formats'] : 'p,address,pre,h1,h2,h3,h4,h5,h6';
a59a8ef6 501
5120a0c6 502 if (is_array($settings['buttons'])) {
a59a8ef6
MW
503 // This gives us the $plugins variable.
504 $plugins = _tinymce_get_buttons();
505
506 // Find the enabled buttons and the mce row they belong on. Also map the
507 // plugin metadata for each button.
508 $plugin_tracker = array();
509 foreach ($plugins as $rname => $rplugin) { // Plugin name
510 foreach ($rplugin as $mce_key => $mce_value) { // TinyMCE key
511 foreach ($mce_value as $k => $v) { // Buttons
5120a0c6 512 if ($settings['buttons'][$rname . '-' . $v]) {
c6bfd92a
TS
513 // Font isn't a true plugin, rather it's buttons made available by the advanced theme
514 if (!in_array($rname, $plugin_tracker) && $rname != 'font') $plugin_tracker[] = $rname;
a59a8ef6
MW
515 $init[$mce_key][] = $v;
516 }
517 }
518 }
17e0f529
TS
519 // Some advanced plugins only have an $rname and no buttons
520 if ($settings['buttons'][$rname]) {
521 if (!in_array($rname, $plugin_tracker)) $plugin_tracker[] = $rname;
522 }
a59a8ef6
MW
523 }
524
525 // Add the rest of the TinyMCE config options to the $init array for each button.
526 if (is_array($plugin_tracker)) {
527 foreach ($plugin_tracker as $pname) {
528 if ($pname != 'default') $init['plugins'][] = $pname;
529 foreach ($plugins[$pname] as $mce_key => $mce_value) {
17e0f529
TS
530 // Don't overwrite buttons or extended_valid_elements
531 if ($mce_key == 'extended_valid_elements') {
532 // $mce_value is an array for extended_valid_elements so just grab the first element in the array (never more than one)
533 $init[$mce_key][] = $mce_value[0];
534 }
535 else if (!strstr($mce_key, 'theme_advanced_buttons')) {
a59a8ef6
MW
536 $init[$mce_key] = $mce_value;
537 }
538 }
539 }
540 }
541
542 // Cleanup
543 foreach ($init as $mce_key => $mce_value) {
544 if (is_array($mce_value)) $mce_value = array_unique($mce_value);
545 $init[$mce_key] = $mce_value;
546 }
547
548 // Shuffle buttons around so that row 1 always has the most buttons,
549 // followed by row 2, etc. Note: These rows need to be set to NULL otherwise
550 // TinyMCE loads it's own buttons inherited from the theme.
551 if (!$init['theme_advanced_buttons1']) $init['theme_advanced_buttons1'] = array();
552 if (!$init['theme_advanced_buttons2']) $init['theme_advanced_buttons2'] = array();
553 if (!$init['theme_advanced_buttons3']) $init['theme_advanced_buttons3'] = array();
554
555 $min_btns = 5; // Minimum number of buttons per row.
556 $num2 = count($init['theme_advanced_buttons2']);
557 $num3 = count($init['theme_advanced_buttons3']);
558
559 if ($num3 < $min_btns) {
560 $init['theme_advanced_buttons2'] = array_merge($init['theme_advanced_buttons2'], $init['theme_advanced_buttons3']);
561 $init['theme_advanced_buttons3'] = array();
562 }
563 if ($num2 < $min_btns) {
564 $init['theme_advanced_buttons1'] = array_merge($init['theme_advanced_buttons1'], $init['theme_advanced_buttons2']);
565 $init['theme_advanced_buttons2'] = array();
566 }
567 }
93c073b0 568 }
01a85ffd
MW
569
570 if ($edit['css_classes']) $init['theme_advanced_styles'] = $settings['css_classes'];
01a85ffd
MW
571
572 if ($settings['css_setting'] == 'theme') {
26444876 573 $css = $themepath . 'style.css';
01a85ffd 574 if (file_exists($css)) {
26444876 575 $init['content_css'] = $host . $css;
01a85ffd 576 }
9a6185a0 577 }
26444876
JR
578 else if ($settings['css_setting'] == 'self') {
579 $init['content_css'] = str_replace(array('%h', '%t'), array($host, $themepath), $settings['css_path']);
580 }
a59a8ef6 581
01a85ffd
MW
582 return $init;
583}
584
6f760c55 585/**
01a85ffd
MW
586 * Remove a profile from the database.
587 */
588function tinymce_profile_delete($name) {
195b5d64
MW
589 db_query("DELETE FROM {tinymce_settings} WHERE name = '%s'", $name);
590 db_query("DELETE FROM {tinymce_role} WHERE name = '%s'", $name);
01a85ffd
MW
591}
592
593/**
594 * Return an HTML form for profile configuration.
595 */
596function tinymce_profile_form($edit) {
195b5d64 597 $edit = array2object($edit);
01a85ffd
MW
598
599 // Only display the roles that currently don't have a tinymce profile. One
600 // profile per role.
4048ba98 601 $orig_roles = user_roles(FALSE, 'access tinymce');
01a85ffd 602 $roles = $orig_roles;
277d4328 603 if (arg(3) == 'add') {
195b5d64 604 $result = db_query('SELECT DISTINCT(rid) FROM {tinymce_role}');
01a85ffd 605 while ($data = db_fetch_object($result)) {
195b5d64 606 unset($roles[$data->rid]);
01a85ffd 607 }
5120a0c6
MW
608 if (!$orig_roles) {
609 drupal_set_message(t('You must assign at least one role with the \'access tinymce\' permission before creating a profile.'), 'error');
610 }
611 else if (!$roles) {
4048ba98 612 drupal_set_message(t('You will not be allowed to create a new profile since all user roles have already been assigned profiles. Either remove an existing tinymce profile from at least one role or assign another role the \'access tinymce\' permission.'), 'error');
01a85ffd
MW
613 }
614 else if (count($orig_roles) != count($roles)) {
615 drupal_set_message(t('Not all user roles are shown since they already have tinymce profiles. You must first unassign profiles in order to add them to a new one.'));
616 }
617 $btn = t('Create profile');
618 }
619 else {
a48b5273 620 $form['old_name'] = array('#type' => 'hidden', '#value' => $edit->name);
01a85ffd
MW
621 $btn = t('Update profile');
622 }
623
a48b5273
MW
624 $form['basic'] = array('#type' => 'fieldset', '#title' => t('Basic setup'), '#collapsible' => TRUE, '#collapsed' => TRUE);
625 $form['basic']['name'] = array('#type' => 'textfield', '#title' => t('Profile name'), '#default_value' => $edit->name, '#size' => 40, '#maxlength' => 128, '#description' => t('Enter a unique name for this profile. This name is only visible in the tinymce administration page.'), '#required' => TRUE);
626 $form['basic']['rids'] = array('#type' => 'checkboxes', '#title' => t('Roles allowed to use this profile'), '#default_value' => array_keys((array) $edit->rids), '#options' => $roles, '#description' => t('Select at least one role. Listed are the roles with \'access tinymce\' permission.'), '#required' => TRUE);
627 $form['basic']['theme'] = array('#type' => 'hidden', '#value' => $edit->settings['theme'] ? $edit->settings['theme'] : 'advanced');
628 $form['basic']['access'] = array('#type' => 'radios', '#title' => t('Make tinymce visible on'), '#default_value' => $edit->settings['access'], '#options' => array(t('all pages'), t('specific pages')));
629 $form['basic']['access_pages'] = array('#type' => 'textarea', '#title' => t('Specific pages'), '#default_value' => $edit->settings['access_pages'] ? $edit->settings['access_pages'] : tinymce_help('admin/settings/tinymce#pages'), '#cols' => 40, '#rows' => 5, '#description' => t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page."));
01a85ffd 630
17e0f529 631 $form['buttons'] = array('#type' => 'fieldset', '#title' => t('Buttons &amp; Plugins'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#tree' => TRUE, '#theme' => 'tinymce_profile_form_buttons');
a48b5273 632
a59a8ef6
MW
633 $metadata = _tinymce_get_buttons(FALSE);
634 // Generate the button list.
635 foreach($metadata as $name => $meta) {
636 if (is_array($meta['buttons'])) {
637 foreach ($meta['buttons'] as $button) {
638 if ($name != 'default') {
639 $img_src = drupal_get_path('module', 'tinymce'). "/tinymce/jscripts/tiny_mce/plugins/$name/images/$name.gif";
5120a0c6
MW
640
641 //correct for plugins that have more than one button
642 if (!file_exists($img_src)) {
643 $img_src = drupal_get_path('module', 'tinymce'). "/tinymce/jscripts/tiny_mce/plugins/$name/images/$button.gif";
644 }
a59a8ef6
MW
645 }
646 else {
647 $img_src = drupal_get_path('module', 'tinymce'). "/tinymce/jscripts/tiny_mce/themes/advanced/images/$button.gif";
648 }
5120a0c6 649 $b = file_exists($img_src) ? '<img src="'. $img_src .'" title="'. $button .'" style="border: 1px solid grey; vertical-align: middle;" />' : $button;
a59a8ef6
MW
650
651 if ($name == 'default') {
652 $title = $b;
653 }
654 else {
655 $title = $metadata[$name]['longname'] ? $metadata[$name]['longname'] : $name;
656 if ($metadata[$name]['infourl']) {
657 $title = '<a href="'. $metadata[$name]['infourl'] .'" target="_blank">'. $title .'</a>';
658 }
5120a0c6 659 $title = $b . ' &#8211; '. $title;
a59a8ef6 660 }
5120a0c6
MW
661 $form_value = $edit->settings['buttons'][$name . '-' . $button];
662 $form['buttons'][$name . '-' . $button] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => $form_value, '#description' => $description);
a59a8ef6
MW
663 }
664 }
665 else {
666 $title = $metadata[$name]['longname'] ? $metadata[$name]['longname'] : $name;
667 if ($metadata[$name]['infourl']) {
668 $title = '<a href="'. $metadata[$name]['infourl'] .'" target="_blank">'. $title .'</a>';
669 }
5120a0c6 670 $form_value = $edit->settings['buttons'][$name];
a48b5273 671 $form['buttons'][$name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => $form_value, '#description' => $description);
a59a8ef6
MW
672 }
673 }
5120a0c6 674
a48b5273
MW
675 $form['appearance'] = array('#type' => 'fieldset', '#title' => t('Editor appearance'), '#collapsible' => TRUE, '#collapsed' => TRUE);
676 $form['appearance']['toolbar_loc'] = array('#type' => 'select', '#title' => t('Toolbar location'), '#default_value' => $edit->settings['toolbar_loc'], '#options' => array('bottom' => 'bottom', 'top' => 'top'), '#description' => t('Show toolbar at the top or bottom of the editor area?'));
677 $form['appearance']['toolbar_align'] = array('#type' => 'select', '#title' => t('Toolbar alignment'), '#default_value' => $edit->settings['toolbar_align'], '#options' => array('center' => 'center', 'left' => 'left', 'right' => 'right'), '#description' => t('Align tool icons left, center, or right within the toolbar.'));
678 $form['appearance']['path_loc'] = array('#type' => 'select', '#title' => t('Path location'), '#default_value' => $edit->settings['path_loc'], '#options' => array('none' => 'none', 'top' => 'top', 'bottom' => 'bottom'), '#description' => t('Path to html elements (i.e. "body>table>tr>td"). Show at top, bottom, or not at all.'));
679 $form['appearance']['resizing'] = array('#type' => 'select', '#title' => t('Enable resizing button'), '#default_value' => $edit->settings['resizing'], '#options' => array('false' => 'false', 'true' => 'true'), '#description' => t(' This option gives you the ability to enable/disable the resizing button.'));
680 $form['appearance']['block_formats'] = array('#type' => 'textfield', '#title' => t('Block formats'), '#default_value' => $edit->settings['block_formats'] ? $edit->settings['block_formats'] : 'p,address,pre,h1,h2,h3,h4,h5,h6', '#size' => 40, '#maxlength' => 250, '#description' => t('Comma separated list of HTML block formats. You can only remove elements, not add.'));
681
682 $form['output'] = array('#type' => 'fieldset', '#title' => t('Cleanup/Output'), '#collapsible' => TRUE, '#collapsed' => TRUE);
683 $form['output']['verify_html'] = array('#type' => 'select', '#title' => t('Verify HTML'), '#default_value' => $edit->settings['verify_html'], '#options' => array('true' => 'true', 'false' => 'false'), '#description' => t('Should the HTML contents be verified or not? Verifying will strip &lt;head&gt tags, so choose false if you will be editing full page HTML.'));
684 $form['output']['preformatted'] = array('#type' => 'select', '#title' => t('Preformatted'), '#default_value' => $edit->settings['preformatted'], '#options' => array('false' => 'false', 'true' => 'true'), '#description' => t('If this option is set to true, the editor will insert TAB characters on tab and preserve other whitespace characters just like a PRE HTML element does.'));
685 $form['output']['convert_fonts_to_styles'] = array('#type' => 'select', '#title' => t('Convert &lt;font&gt; tags to styles'), '#default_value' => $edit->settings['convert_fonts_to_styles'], '#options' => array('true' => 'true', 'false' => 'false'), '#description' => t('If you set this option to true, font size, font family, font color and font background color will be replaced by inline styles.'));
686
687 $form['css'] = array('#type' => 'fieldset', '#title' => t('CSS'), '#collapsible' => TRUE, '#collapsed' => TRUE);
688 $form['css']['css_setting'] = array('#type' => 'select', '#title' => t('Editor CSS'), '#default_value' => $edit->settings['css_setting'] ? $edit->settings['css_setting'] : 'theme', '#options' => array('theme' => 'use theme css', 'self' => 'define css', 'none' => 'tinyMCE default'), '#description' => t('Defines the CSS to be used in the editor area.<br />use theme css - get css from current Drupal theme.<br/>define css - enter path for css file below.<br />tinyMCE default - uses default CSS from editor.'));
689 $form['css']['css_path'] = array('#type' => 'textfield', '#title' => t('CSS path'), '#default_value' => $edit->settings['css_path'], '#size' => 40, '#maxlength' => 255, '#description' => t('Enter path to CSS file (example: "css/editor.css").<br />Macros: %h (host name: http://www.example.com/), %t (path to theme: theme/yourtheme/)<br />Be sure to select "define css" above.'));
690 $form['css']['css_classes'] = array('#type' => 'textfield', '#title' => t('CSS classes'), '#default_value' => $edit->settings['css_classes'], '#size' => 40, '#maxlength' => 255, '#description' => t('Adds CSS classes to the "styles" droplist. Format is "&lt;title&gt;=&lt;class&gt;;"<br/> Example: "Header 1=header1;Header 2=header2;Header 3=header3;"<br />Leave blank to automatically import list of CSS classes from style sheet.'));
691
692 $form['submit'] = array('#type' => 'submit', '#value' => $btn);
693
694 $output .= drupal_get_form('tinymce_profile_form', $form);
695
696 return $output;
697}
698
699/**
700 * Layout for the buttons in the tinymce profile form
701 */
702function theme_tinymce_profile_form_buttons($form) {
703 $buttons = array();
704
705 // Flatten forms array
706 foreach (element_children($form) as $key) {
707 $buttons[] = form_render($form[$key]);
708 }
a59a8ef6 709
5120a0c6
MW
710 //split checkboxes into rows with 3 columns
711 $total = count($buttons);
712 $rows = array();
713 for ($i = 0; $i < $total; $i++) {
714 $row = array();
715 $row[] = array('data' => $buttons[$i]);
716 $row[] = array('data' => $buttons[++$i]);
717 $row[] = array('data' => $buttons[++$i]);
718 $rows[] = $row;
a59a8ef6 719 }
5120a0c6
MW
720
721 $output = theme('table', array(), $rows, array('width' => '100%'));
a59a8ef6 722
a48b5273 723 return $output;
01a85ffd
MW
724}
725
726/**
76a1692d 727 * Load all profiles. Just load one profile if $name is passed in.
01a85ffd
MW
728 */
729function tinymce_profile_load($name = '') {
730 static $profiles = array();
731
732 if (!$profiles) {
65badaba 733 $roles = user_roles();
01a85ffd
MW
734 $result = db_query('SELECT * FROM {tinymce_settings}');
735 while ($data = db_fetch_object($result)) {
736 $data->settings = unserialize($data->settings);
195b5d64 737 $result2 = db_query("SELECT rid FROM {tinymce_role} WHERE name = '%s'", $data->name);
01a85ffd 738 $role = array();
195b5d64
MW
739 while ($r = db_fetch_object($result2)) {
740 $role[$r->rid] = $roles[$r->rid];
01a85ffd
MW
741 }
742 $data->rids = $role;
93c073b0 743
01a85ffd
MW
744 $profiles[$data->name] = $data;
745 }
746 }
93c073b0
MW
747
748 return ($name ? $profiles[$name] : $profiles);
01a85ffd
MW
749}
750
751/**
752 * Controller for tinymce profiles.
753 */
754function tinymce_profile_overview() {
755 $output = '';
01a85ffd
MW
756
757 $profiles = tinymce_profile_load();
758 if ($profiles) {
3b4c16b2 759 $output .= t('<p><a href="%create-profile-url">Create new profile</a></p>', array('%create-profile-url' => url('admin/settings/tinymce/add')));
65badaba 760 $roles = user_roles();
01a85ffd
MW
761 $header = array(t('Profile'), t('Roles'), t('Operations'));
762 foreach ($profiles as $p) {
277d4328 763 $rows[] = array(array('data' => $p->name, 'valign' => 'top'), array('data' => implode("<br />\n", $p->rids)), array('data' => l(t('edit'), 'admin/settings/tinymce/edit/'. urlencode($p->name)) . ' '. l(t('delete'), 'admin/settings/tinymce/delete/'. urlencode($p->name)), 'valign' => 'top'));
01a85ffd
MW
764 }
765 $output .= theme('table', $header, $rows). '<p>&nbsp;</p>';
766 }
767 else {
3b4c16b2 768 drupal_set_message(t('No profiles found. Click here to <a href="%create-profile-url">create a new profile</a>.', array('%create-profile-url' => url('admin/settings/tinymce/add'))));
01a85ffd
MW
769 }
770
a48b5273
MW
771 $form['settings'] = array('#type' => 'fieldset', '#title' => t('Default settings'), '#collapsible' => TRUE);
772 $form['settings']['tinymce_default_state'] = array('#type' => 'radios', '#title' => t('Default tinymce state'), '#default_value' => variable_get('tinymce_default_state', 0), '#options' => array(t('Off'), t('On')), '#description' => t('Should tinymce be enabled or disabled by default when it\'s first loaded from a textarea? Note: The user may override this setting in their profile.'));
773 $form['settings']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
01a85ffd 774
a48b5273
MW
775 $output .= drupal_get_form('settings', $form);
776
777 return $output;
01a85ffd
MW
778}
779
780/**
781 * Save a profile to the database.
782 */
783function tinymce_profile_save($edit) {
784 db_query("DELETE FROM {tinymce_settings} WHERE name = '%s' or name = '%s'", $edit['name'], $edit['old_name']);
195b5d64 785 db_query("DELETE FROM {tinymce_role} WHERE name = '%s' or name = '%s'", $edit['name'], $edit['old_name']);
a48b5273
MW
786 db_query("INSERT INTO {tinymce_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], serialize($edit));
787 foreach ($edit['rids'] as $rid => $value) {
788 //if this rid has been checked, insert it
789 if ($value == 1) {
790 db_query("INSERT INTO {tinymce_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid);
791 }
195b5d64 792 }
01a85ffd
MW
793}
794
795/**
796 * Profile validation.
797 */
798function tinymce_profile_validate($edit) {
799 $errors = array();
800
801 if (!$edit['name']) {
195b5d64 802 $errors['name'] = t('You must give a profile name.');
01a85ffd
MW
803 }
804
805 if (!$edit['rids']) {
806 $errors['rids'] = t('You must select at least one role.');
807 }
808
809 foreach ($errors as $name => $message) {
810 form_set_error($name, $message);
811 }
812
813 return count($errors) == 0;
814}
815
816/********************************************************************
817 * Module Functions :: Private
818 ********************************************************************/
819
820/**
821 * Determine if TinyMCE has permission to be used on the current page.
6f760c55
MW
822 *
823 * @return
824 * TRUE if can render, FALSE if not allowed.
825 */
01a85ffd 826function _tinymce_page_match($edit) {
6f760c55 827 //Kill TinyMCE if we're editing a textarea with PHP in it!
9670911b
MW
828 if ($_POST['edit']['format'] == 2) {
829 return FALSE;
6f760c55
MW
830 }
831 else {
832 // PHP input formats are #2 in the filters table.
833 preg_match("|^node/(\d+)(/edit)$|", $_GET['q'], $match);
834 if (intval($match[1]) > 0) {
3b4c16b2 835 if (db_result(db_query('SELECT format FROM {node_revisions} WHERE nid = %d AND vid = %d AND format = 2', $match[1], $match[1]))) {
6f760c55
MW
836 return FALSE;
837 }
838 }
839 }
840
c22c8ac5 841 if ($edit->settings['access'] == 0) {
6f760c55
MW
842 return TRUE;
843 }
844 else {
845 $page_match = FALSE;
277d4328 846 $pages = $edit->settings['access_pages'] ? $edit->settings['access_pages'] : tinymce_help('admin/settings/tinymce#pages');
6f760c55
MW
847 if ($pages) {
848 $path = drupal_get_path_alias($_GET['q']);
849 $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. variable_get('site_frontpage', 'node') .'\2'), preg_quote($pages, '/')) .')$/';
850 $page_match = preg_match($regexp, $path);
851 }
852 return $page_match;
853 }
854}
ae24db96 855?>