#304243 by sun: Code clean-up for wysiwyg_editor_profile_overview().
[project/wysiwyg.git] / wysiwyg_editor.admin.inc
1 <?php
2 // $Id$
3
4
5 /**
6 * @file
7 * Integrate Wysiwyg editors into Drupal.
8 */
9
10 /**
11 * Callback handler for admin pages; menu callback.
12 *
13 * @todo Move into hook_menu(), resp. FAPI functions.
14 */
15 function wysiwyg_editor_admin($arg = '') {
16 $edit = $_POST;
17 $op = isset($_POST['op']) ? $_POST['op'] : '';
18 $op = ($arg && !$op ? $arg : $op);
19 switch ($op) {
20 case 'add':
21 $breadcrumb[] = l(t('Home'), NULL);
22 $breadcrumb[] = l(t('Administer'), 'admin');
23 $breadcrumb[] = l(t('Site configuration'), 'admin/settings');
24 $breadcrumb[] = l(t('Wysiwyg'), 'admin/settings/wysiwyg');
25 $breadcrumb[] = l(t('Wysiwyg Profiles'), 'admin/settings/wysiwyg/profile');
26 drupal_set_breadcrumb($breadcrumb);
27 $output = drupal_get_form('wysiwyg_editor_profile_form', $edit);
28 break;
29
30 case 'edit':
31 drupal_set_title(t('Edit Wysiwyg Editor profile'));
32 $output = drupal_get_form('wysiwyg_editor_profile_form', wysiwyg_editor_profile_load(urldecode(arg(5))));
33 break;
34
35 case 'delete':
36 wysiwyg_editor_profile_delete(urldecode(arg(5)));
37 drupal_set_message(t('Deleted profile'));
38 drupal_goto('admin/settings/wysiwyg/profile');
39 break;
40
41 case t('Create profile'):
42 case t('Update profile'):
43 $output = drupal_get_form('wysiwyg_editor_profile_form', $edit);
44 break;
45
46 default:
47 $output = wysiwyg_editor_profile_overview();
48 }
49
50 return $output;
51 }
52
53 /**
54 * Return an HTML form for profile configuration.
55 */
56 function wysiwyg_editor_profile_form($form_state, $edit) {
57 // Merge in defaults.
58 settype($edit, 'array');
59 $edit += array(
60 'name' => NULL,
61 'rids' => NULL,
62 'settings' => array(
63 'default' => TRUE,
64 'user_choose' => FALSE,
65 'show_toggle' => TRUE,
66 'theme' => 'advanced',
67 'language' => 'en',
68 'safari_message' => FALSE,
69 'access' => 1,
70 'access_pages' => "node/*\nuser/*\ncomment/*",
71 'buttons' => array(),
72 'toolbar_loc' => 'top',
73 'toolbar_align' => 'left',
74 'path_loc' => 'bottom',
75 'resizing' => TRUE,
76 // Also available, but buggy in TinyMCE 2.x: blockquote,code,dt,dd,samp.
77 'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
78 'verify_html' => TRUE,
79 'preformatted' => FALSE,
80 'convert_fonts_to_spans' => TRUE,
81 'remove_linebreaks' => TRUE,
82 'apply_source_formatting' => FALSE,
83 'paste_auto_cleanup_on_paste' => FALSE,
84 'css_setting' => 'theme',
85 'css_path' => NULL,
86 'css_classes' => NULL,
87 ),
88 );
89 $edit = (object)$edit;
90
91 if (arg(4) == 'add') {
92 $btn = t('Create profile');
93 }
94 else {
95 $form['old_name'] = array('#type' => 'hidden', '#value' => $edit->name);
96 $btn = t('Update profile');
97 }
98
99 $form['basic'] = array(
100 '#type' => 'fieldset',
101 '#title' => t('Basic setup'),
102 '#collapsible' => TRUE,
103 '#collapsed' => TRUE,
104 );
105
106 $form['basic']['name'] = array(
107 '#type' => 'textfield',
108 '#title' => t('Profile name'),
109 '#default_value' => $edit->name,
110 '#size' => 40,
111 '#maxlength' => 128,
112 '#description' => t('Enter a name for this profile. This name is only visible within the Wysiwyg Editor administration page.'),
113 '#required' => TRUE,
114 );
115
116 $form['basic']['rids'] = array(
117 '#type' => 'checkboxes',
118 '#title' => t('Roles allowed to use this profile'),
119 '#default_value' => array_keys((array)$edit->rids),
120 '#options' => user_roles(FALSE, 'access wysiwyg editor'),
121 '#description' => t('Check at least one role. Only roles with \'access wysiwyg editor\' permission will be shown here.'),
122 '#required' => TRUE,
123 );
124
125 $form['basic']['default'] = array(
126 '#type' => 'select',
127 '#title' => t('Default state'),
128 '#default_value' => $edit->settings['default'],
129 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
130 '#description' => t('Default editor state for users in this profile. Users will be able to override this state if the next option is enabled.'),
131 );
132
133 $form['basic']['user_choose'] = array(
134 '#type' => 'select',
135 '#title' => t('Allow users to choose default'),
136 '#default_value' => $edit->settings['user_choose'],
137 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
138 '#description' => t('If allowed, users will be able to choose their own Wysiwyg Editor default state by visiting their profile page.'),
139 );
140
141 $form['basic']['show_toggle'] = array(
142 '#type' => 'select',
143 '#title' => t('Show disable/enable rich text editor toggle'),
144 '#default_value' => $edit->settings['show_toggle'],
145 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
146 '#description' => t('Whether or not to show the disable/enable rich text editor toggle below the textarea. If false, editor defaults to the global default or user default (see above).'),
147 );
148
149 $form['basic']['theme'] = array(
150 '#type' => 'hidden',
151 '#value' => $edit->settings['theme'],
152 );
153
154 $form['basic']['language'] = array(
155 '#type' => 'select',
156 '#title' => t('Language'),
157 '#default_value' => $edit->settings['language'],
158 '#options' => drupal_map_assoc(array('ar', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fr', 'fr_ca', 'he', 'hu', 'is', 'it', 'ja', 'ko', 'nb', 'nl', 'nn', 'pl', 'pt', 'pt_br', 'ru', 'ru_KOI8-R', 'ru_UTF-8', 'si', 'sk', 'sv', 'th', 'zh_cn', 'zh_tw', 'zh_tw_utf8')),
159 '#description' => t('The language for the Wysiwyg Editor interface. Language codes based on the <a href="http://www.loc.gov/standards/iso639-2/englangn.html">ISO-639-2</a> format.'),
160 );
161
162 $form['basic']['safari_message'] = array(
163 '#type' => 'select',
164 '#title' => t('Safari browser warning'),
165 '#default_value' => $edit->settings['safari_message'],
166 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
167 '#description' => t('TinyMCE support for the Safari web browser is experimental and a warning message is displayed when that browser is detected. You can disable this message here.'),
168 );
169
170 $form['visibility'] = array(
171 '#type' => 'fieldset',
172 '#title' => t('Visibility'),
173 '#collapsible' => TRUE,
174 '#collapsed' => TRUE,
175 );
176
177 // If the visibility is set to PHP mode but the user doesn't have this block
178 // permission, don't allow them to edit nor see this PHP code
179 $access = user_access('use PHP for block visibility');
180 if ($edit->settings['access'] == 2 && !$access) {
181 $form['visibility'] = array();
182 $form['visibility']['access'] = array(
183 '#type' => 'value',
184 '#value' => 2,
185 );
186 $form['visibility']['access_pages'] = array(
187 '#type' => 'value',
188 '#value' => $edit->settings['access_pages'],
189 );
190 }
191 else {
192 $options = array(t('Show on every page except the listed pages.'), t('Show on only the listed pages.'));
193 $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '%blog' for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
194 if ($access) {
195 $options[] = t('Show if the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
196 $description .= ' '. t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
197 }
198 $form['visibility']['access'] = array(
199 '#type' => 'radios',
200 '#title' => t('Show Wysiwyg Editor on specific pages'),
201 '#default_value' => $edit->settings['access'],
202 '#options' => $options,
203 );
204 $form['visibility']['access_pages'] = array(
205 '#type' => 'textarea',
206 '#title' => t('Pages'),
207 '#default_value' => $edit->settings['access_pages'],
208 '#description' => $description,
209 '#wysiwyg' => FALSE,
210 );
211 }
212
213 $form['buttons'] = array(
214 '#type' => 'fieldset',
215 '#title' => t('Buttons and plugins'),
216 '#collapsible' => TRUE,
217 '#collapsed' => TRUE,
218 '#tree' => TRUE,
219 '#theme' => 'wysiwyg_editor_admin_button_table',
220 );
221
222 $plugins = _wysiwyg_editor_get_buttons(TRUE);
223 // Generate the button list.
224 foreach ($plugins as $name => $meta) {
225 if (isset($meta['buttons']) && is_array($meta['buttons'])) {
226 foreach ($meta['buttons'] as $button => $title) {
227 $img_src = $meta['path'] ."/images/$name.gif";
228 // Handle plugins that have more than one button.
229 if (!file_exists($img_src)) {
230 $img_src = $meta['path'] ."/images/$button.gif";
231 }
232 $b = file_exists($img_src) ? '<img src="'. base_path() . $img_src .'" title="'. $button .'" style="border: 1px solid grey; vertical-align: middle;" />' : '';
233 if (isset($meta['url'])) {
234 $title = $b .' '. l($title, $meta['url'], array('target' => '_blank'));
235 }
236 else {
237 $title = $b .' '. $title;
238 }
239 $form['buttons'][$name][$button] = array(
240 '#type' => 'checkbox',
241 '#title' => $title,
242 '#default_value' => isset($edit->settings['buttons'][$name][$button]) ? $edit->settings['buttons'][$name][$button] : NULL,
243 );
244 }
245 }
246 else if (isset($meta['extensions']) && is_array($meta['extensions'])) {
247 foreach ($meta['extensions'] as $extension => $title) {
248 if (isset($meta['url'])) {
249 $title = l($title, $meta['url'], array('target' => '_blank'));
250 }
251 else {
252 $title = $title;
253 }
254 $form['buttons'][$name][$extension] = array(
255 '#type' => 'checkbox',
256 '#title' => $title,
257 '#default_value' => isset($edit->settings['buttons'][$name][$extension]) ? $edit->settings['buttons'][$name][$extension] : NULL,
258 );
259 }
260 }
261 }
262
263 $form['appearance'] = array(
264 '#type' => 'fieldset',
265 '#title' => t('Editor appearance'),
266 '#collapsible' => TRUE,
267 '#collapsed' => TRUE,
268 );
269
270 $form['appearance']['toolbar_loc'] = array(
271 '#type' => 'select',
272 '#title' => t('Toolbar location'),
273 '#default_value' => $edit->settings['toolbar_loc'],
274 '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
275 '#description' => t('Show toolbar at the top or bottom of the editor area?'),
276 );
277
278 $form['appearance']['toolbar_align'] = array(
279 '#type' => 'select',
280 '#title' => t('Toolbar alignment'),
281 '#default_value' => $edit->settings['toolbar_align'],
282 '#options' => array('center' => t('Center'), 'left' => t('Left'), 'right' => t('Right')),
283 '#description' => t('Align tool icons left, center, or right within the toolbar.'),
284 );
285
286 $form['appearance']['path_loc'] = array(
287 '#type' => 'select',
288 '#title' => t('Path location'),
289 '#default_value' => $edit->settings['path_loc'],
290 '#options' => array('none' => t('None'), 'top' => t('Top'), 'bottom' => t('Bottom')),
291 '#description' => t('Path to html elements (i.e. "body>table>tr>td"). Show at top, bottom, or not at all.'),
292 );
293
294 $form['appearance']['resizing'] = array(
295 '#type' => 'select',
296 '#title' => t('Enable resizing button'),
297 '#default_value' => $edit->settings['resizing'],
298 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
299 '#description' => t(' This option gives you the ability to enable/disable the resizing button. If enabled the <strong>Path location toolbar</strong> must be set to "top" or "bottom" in order to display the resize icon.'),
300 );
301
302 $form['output'] = array(
303 '#type' => 'fieldset',
304 '#title' => t('Cleanup and output'),
305 '#collapsible' => TRUE,
306 '#collapsed' => TRUE,
307 );
308
309 $form['output']['verify_html'] = array(
310 '#type' => 'select',
311 '#title' => t('Verify HTML'),
312 '#default_value' => $edit->settings['verify_html'],
313 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
314 '#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.'),
315 );
316
317 $form['output']['preformatted'] = array(
318 '#type' => 'select',
319 '#title' => t('Preformatted'),
320 '#default_value' => $edit->settings['preformatted'],
321 '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
322 '#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.'),
323 );
324
325 $form['output']['convert_fonts_to_spans'] = array(
326 '#type' => 'select',
327 '#title' => t('Convert &lt;font&gt; tags to styles'),
328 '#default_value' => $edit->settings['convert_fonts_to_spans'],
329 '#options' => array(TRUE => t('Enabled'), FALSE => t('Disabled')),
330 '#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.'),
331 );
332
333 $form['output']['remove_linebreaks'] = array(
334 '#type' => 'select',
335 '#title' => t('Remove linebreaks'),
336 '#default_value' => $edit->settings['remove_linebreaks'],
337 '#options' => array(TRUE => t('Enabled'), FALSE => t('Disabled')),
338 '#description' => t('Set this option to false to prevent TinyMCE from removing linebreaks from existing nodes. True avoids conflicts with some filters.'),
339 );
340
341 $form['output']['apply_source_formatting'] = array(
342 '#type' => 'select',
343 '#title' => t('Apply source formatting'),
344 '#default_value' => $edit->settings['apply_source_formatting'],
345 '#options' => array(TRUE => t('Enabled'), FALSE => t('Disabled')),
346 '#description' => t('This option makes TinyMCE apply source formatting. Set this to true for a cleaner HTML source. Choose false to avoid conflicts with some filters.'),
347 );
348
349 $form['output']['paste_auto_cleanup_on_paste'] = array(
350 '#type' => 'select',
351 '#title' => t('Force Word cleanup on standard paste'),
352 '#default_value' => $edit->settings['paste_auto_cleanup_on_paste'],
353 '#options' => array(TRUE => t('Enabled'), FALSE => t('Disabled')),
354 '#description' => t('Enable this option to have the default paste function (CTRL-V or SHIFT-INS) to behave like the "paste from word" plugin function.'),
355 );
356
357 $form['css'] = array(
358 '#type' => 'fieldset',
359 '#title' => t('CSS'),
360 '#collapsible' => TRUE,
361 '#collapsed' => TRUE,
362 );
363
364 $form['css']['block_formats'] = array(
365 '#type' => 'textfield',
366 '#title' => t('Block formats'),
367 '#default_value' => $edit->settings['block_formats'],
368 '#size' => 40,
369 '#maxlength' => 250,
370 '#description' => t('Comma separated list of HTML block formats. You can only remove elements, not add.'),
371 );
372
373 $form['css']['css_setting'] = array(
374 '#type' => 'select',
375 '#title' => t('Editor CSS'),
376 '#default_value' => $edit->settings['css_setting'],
377 '#options' => array('theme' => t('Use theme CSS'), 'self' => t('Define CSS'), 'none' => t('TinyMCE default CSS')),
378 '#description' => t('Defines the CSS to be used in the editor area.<br />use theme css - load style.css from current site theme.<br/>define css - enter path for css file below.<br />TinyMCE default - uses default CSS from editor.'),
379 );
380
381 $form['css']['css_path'] = array(
382 '#type' => 'textfield',
383 '#title' => t('CSS path'),
384 '#default_value' => $edit->settings['css_path'],
385 '#size' => 40,
386 '#maxlength' => 255,
387 '#description' => t('If "Define CSS" has been selected above, enter path to a CSS file or a list of CSS files seperated by a comma.') .'<br />'. t('Available tokens: %b (base path, f.e.: /), %t (path to theme, f.e.: themes/garland)') .'<br />'. t('Examples:') .' css/editor.css,/themes/garland/style.css,%b%t/style.css,http://example.com/external.css',
388 );
389
390 $form['css']['css_classes'] = array(
391 '#type' => 'textarea',
392 '#title' => t('CSS classes'),
393 '#default_value' => $edit->settings['css_classes'],
394 '#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).', array('!format' => '<code>[title]=[class]</code>', '!example' => 'My heading=header1')),
395 );
396
397 $form['submit'] = array(
398 '#type' => 'submit',
399 '#value' => $btn,
400 );
401
402 return $form;
403 }
404
405 /**
406 * Form submit callback for wysiwyg_editor_profile_form_build().
407 *
408 * @see wysiwyg_editor_profile_form_build()
409 */
410 function wysiwyg_editor_profile_form_submit($form, &$form_state) {
411 // Count enabled plugins for this profile.
412 $plugin_count = 0;
413 foreach ($form_state['values']['buttons'] as $plugin => $buttons) {
414 $form_state['values']['buttons'][$plugin] = array_filter($form_state['values']['buttons'][$plugin]);
415 $plugin_count += count($form_state['values']['buttons'][$plugin]);
416 }
417 // Store only enabled buttons.
418 $form_state['values']['buttons'] = array_filter($form_state['values']['buttons']);
419
420 // Filter enabled roles for this profile.
421 $form_state['values']['rids'] = array_filter($form_state['values']['rids']);
422
423 // Delete existing profile(s) with the current profile name.
424 if (!empty($form_state['values']['old_name'])) {
425 db_query("DELETE FROM {wysiwyg_editor_profile} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
426 db_query("DELETE FROM {wysiwyg_editor_role} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
427 }
428
429 // Remove FAPI values.
430 // @see system_settings_form_submit()
431 unset($form_state['values']['submit'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token']);
432
433 // Insert new profile data.
434 db_query("INSERT INTO {wysiwyg_editor_profile} (name, settings, plugin_count) VALUES ('%s', '%s', %d)", $form_state['values']['name'], serialize($form_state['values']), $plugin_count);
435 foreach ($form_state['values']['rids'] as $rid => $value) {
436 db_query("INSERT INTO {wysiwyg_editor_role} (name, rid) VALUES ('%s', %d)", $form_state['values']['name'], $rid);
437 }
438
439 if (isset($form_state['values']['old_name'])) {
440 drupal_set_message(t('Wysiwyg Editor profile %name has been updated.', array('%name' => $form_state['values']['name'])));
441 }
442 else {
443 drupal_set_message(t('Wysiwyg Editor profile %name has been created.', array('%name' => $form_state['values']['name'])));
444 }
445 drupal_goto('admin/settings/wysiwyg/profile');
446 }
447
448 /**
449 * Layout for the buttons in the Wysiwyg Editor profile form.
450 */
451 function theme_wysiwyg_editor_admin_button_table($form) {
452 $buttons = array();
453
454 // Flatten forms array.
455 foreach (element_children($form) as $name) {
456 foreach (element_children($form[$name]) as $button) {
457 $buttons[] = drupal_render($form[$name][$button]);
458 }
459 }
460
461 // Split checkboxes into rows with 3 columns.
462 $total = count($buttons);
463 $rows = array();
464 for ($i = 0; $i < $total; $i++) {
465 $row = array();
466 $row[] = array('data' => $buttons[$i]);
467 $row[] = array('data' => $buttons[++$i]);
468 $row[] = array('data' => $buttons[++$i]);
469 $rows[] = $row;
470 }
471
472 $output = theme('table', array(), $rows, array('width' => '100%'));
473
474 return $output;
475 }
476
477 /**
478 * Controller for Wysiwyg Editor profiles.
479 */
480 function wysiwyg_editor_profile_overview() {
481 $output = '';
482 $usable = TRUE;
483 // Check if a Wysiwyg Editor is installed.
484 // Hard-coded for TinyMCE for now.
485 $path_wysiwyg = drupal_get_path('module', 'wysiwyg_editor');
486 $tinymce_loc = $path_wysiwyg .'/tinymce/';
487 if (!is_dir($tinymce_loc)) {
488 drupal_set_message(t('Could not find the TinyMCE engine installed at <strong>!tinymce-directory</strong>. Please <a href="!tinymce-url">download TinyMCE</a>, uncompress it and copy the folder into !tinymce-path.', array('!tinymce-directory' => $tinymce_loc, '!tinymce-url' => 'http://tinymce.moxiecode.com/download.php', '!tinymce-path' => $path_wysiwyg)), 'error');
489 $usable = FALSE;
490 }
491 // Check if at least one role is granted access to Wysiwyg Editor.
492 $access_check = user_roles(FALSE, 'access wysiwyg editor');
493 if (!$access_check) {
494 drupal_set_message(t('You must <a href="!access-control-url">assign</a> at least one role with the \'access wysiwyg editor\' permission before creating a profile.', array('!access-control-url' => url('admin/user/permissions'))), 'error');
495 $usable = FALSE;
496 }
497 if (!$usable) {
498 return $output;
499 }
500
501 $profiles = wysiwyg_editor_profile_load();
502 if ($profiles) {
503 $roles = user_roles(FALSE, 'access wysiwyg editor');
504 $header = array(t('Profile'), t('Roles'), t('Operations'));
505 foreach ($profiles as $p) {
506 $rows[] = array(array('data' => $p->name, 'valign' => 'top'), array('data' => implode("<br />\n", $p->rids)), array('data' => l(t('Edit'), 'admin/settings/wysiwyg/profile/edit/'. urlencode($p->name)) .' '. l(t('Delete'), 'admin/settings/wysiwyg/profile/delete/'. urlencode($p->name)), 'valign' => 'top'));
507 }
508 $output .= theme('table', $header, $rows);
509 }
510 $output .= '<p>'. t('<a href="!create-profile-url">Create new profile</a>', array('!create-profile-url' => url('admin/settings/wysiwyg/profile/add'))) .'</p>';
511
512 return $output;
513 }
514
515 /**
516 * Remove a profile from the database.
517 */
518 function wysiwyg_editor_profile_delete($name) {
519 db_query("DELETE FROM {wysiwyg_editor_profile} WHERE name = '%s'", $name);
520 db_query("DELETE FROM {wysiwyg_editor_role} WHERE name = '%s'", $name);
521 }
522