cc9a71f22c6ed5e2c7eb1e9bf55ca6e82107e065
[project/fckeditor.git] / fckeditor.module
1 <?php
2 // $Id$
3 /**
4 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
5 * Copyright (C) 2003-2008 Frederico Caldeira Knabben
6 *
7 * == BEGIN LICENSE ==
8 *
9 * Licensed under the terms of any of the following licenses at your
10 * choice:
11 *
12 * - GNU General Public License Version 2 or later (the "GPL")
13 * http://www.gnu.org/licenses/gpl.html
14 *
15 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
16 * http://www.gnu.org/licenses/lgpl.html
17 *
18 * - Mozilla Public License Version 1.1 or later (the "MPL")
19 * http://www.mozilla.org/MPL/MPL-1.1.html
20 *
21 * == END LICENSE ==
22 *
23 * @file
24 * FCKeditor Module for Drupal 6.x
25 *
26 * This module allows Drupal to replace textarea fields with FCKeditor.
27 *
28 * This HTML text editor brings to the web many of the powerful functionalities
29 * of known desktop editors like Word. It's really lightweight and doesn't
30 * require any kind of installation on the client computer.
31 */
32
33 /**
34 * The name of simplified toolbar which should be forced
35 * Be sure that this toolbar is defined in fckeditor.config.js or fckconfig.js
36 */
37 define('FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME', 'DrupalBasic') ;
38
39 /**
40 * Implementation of hook_help
41 */
42 function fckeditor_help($path, $arg) {
43 switch ($path) {
44 case 'admin/settings/help#description':
45 $output = t("Enables the usage of FCKeditor (WYSIWYG editor) instead of plain text fields.");
46 break;
47 case 'admin/settings/fckeditor':
48 if (!empty($arg[3]) && in_array($arg[3], array("addg", "editg"))) {
49 $output = t("<p>The Global Profile allows you to define settings that are common for all profiles. Values defined in other profiles will be appended to the global configuration. This way you can avoid repeating some of the settings that are usually the same in each profile.</p>");
50 break;
51 }
52 else if (!empty($arg[3]) && in_array($arg[3], array("add", "edit"))) {
53 $output = t("<p>Note: FCKeditor is highly configurable. The most commonly used features are listed below. If you want to take a look at all available settings, open <code>!fckconfig</code> and then customize <code>!fckeditor_config</code> to your needs. This is also the only way to define new toolbar sets. It is advised to not edit <code>fckconfig.js</code> because you may overwrite it accidentally when you update the editor.</p>", array('!fckconfig' => drupal_get_path('module', 'fckeditor') ."/fckeditor/fckconfig.js", '!fckeditor_config' => drupal_get_path('module', 'fckeditor') ."/fckeditor.config.js"));
54 break;
55 }
56 else if (!empty($arg[3]) && in_array($arg[3], array("delete", "deleteg"))) {
57 break;
58 }
59 $output = t("<p>The FCKeditor module allows Drupal to replace textarea fields with a rich text or <acronym title=\"What You See Is What You Get\">WYSIWYG</acronym> editor. This editor brings many of the powerful functionalities of known desktop editors like Word to the web. It's relatively lightweight and doesn't require any kind of installation on the client computer.</p><p>More information about the editor is located at the !fckeditorlink. A small user guide is located at !userguidelink.</p>",
60 array(
61 '!fckeditorlink' => l(t('FCKeditor homepage'), 'http://www.fckeditor.net'),
62 '!userguidelink' => l(t('FCKeditor userguide'), 'http://docs.fckeditor.net/FCKeditor/Users_Guide'))
63 );
64 $output .= t('<p>Profiles can be defined based on user roles. A FCKeditor profile can define which pages receive this FCKeditor capability, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor functions. It is possible also to define the Global Profile that will hold values that will be appended to all other profiles.</p><p>Lastly, only users with the <code>!access1</code> !permission will be able to use FCKeditor. </p>', array('!permission' => l(t('permission'), 'admin/user/permissions'), '!access1' => t('access fckeditor')));
65 break;
66 case 'admin/help#fckeditor':
67 $output = t("<p>The FCKeditor module allows Drupal to replace textarea fields with a rich text or <acronym title=\"What You See Is What You Get\">WYSIWYG</acronym> editor. This editor brings many of the powerful functionalities of known desktop editors like Word to the web. It's relatively lightweight and doesn't require any kind of installation on the client computer.</p><p>More information is located at the !fckeditorlink. A small user guide is located at !userguidelink.</p>",
68 array(
69 '!fckeditorlink' => l(t('FCKeditor homepage'), 'http://www.fckeditor.net'),
70 '!userguidelink' => l(t('FCKeditor userguide'), 'http://docs.fckeditor.net/FCKeditor/Users_Guide'))
71 );
72 $output .= t('<h3>Configuration</h3><ol><li>Go to the !fckeditorlink and download the latest version of FCKeditor. Then uncompress the contents of the "fckeditor" directory of the downloaded file to %fckeditordir.</li><li>Enable the module as usual from Drupal\'s admin pages.</li><li>Grant permissions for use of FCKeditor in <code>!path2</code><br/>Note: to enable the file browser, read also the <i>How to enable the file browser</i> section.</li><li>Under <code>!path1</code>, adjust the fckeditor profiles. In each profile you can choose which textareas will be replaced by FCKeditor, select default toolbar and configure some more advanced settings.</li><li>For the Rich Text Editing to work you also need to configure your !filterlink for the users that may access Rich Text Editing. Either grant those users Full HTML access or use the following: <br/><code>!filter</code>. </li><li>To have a better control over line breaks, you may disable <code>Line break converter</code> in the chosen filter (recommended).</li><li>Modify the fckeditor.config.js file to custom your needs (optional).<br />You may copy the needed configuration lines from the default FCKeditor configuration settings (!fckconfig), the lines in fckeditor.config.js will override most settings.</li></ol>',
73 array(
74 '!fckeditorlink' => l(t('FCKeditor homepage'), 'http://www.fckeditor.net/download'),
75 '%fckeditordir' => base_path() . drupal_get_path('module', 'fckeditor') .'/fckeditor/',
76 '!path1' => l(t('Administer > Site configuration > FCKeditor'), 'admin/settings/fckeditor'),
77 '!path2' => l(t('Administer > User Management > Permissions'), 'admin/user/permissions'),
78 '!filter' => htmlentities('<a> <p> <span> <div> <h1> <h2> <h3> <h4> <h5> <h6> <img> <map> <area>
79 <hr> <br> <br /> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td> <em>
80 <b> <u> <i> <strong> <font> <del> <ins> <sub> <sup> <quote> <blockquote>
81 <pre> <address> <code> <cite> <embed> <object> <param> <strike> <caption>'),
82 '!fckconfig' => base_path() . drupal_get_path('module', 'fckeditor') .'/fckeditor/fckconfig.js',
83 '!moduledir' => base_path() . drupal_get_path('module', 'fckeditor') .'/fckeditor',
84 '!filterlink' => l(t('filters'), 'admin/settings/filters'))
85 );
86 $output .= t('<h3>Installation troubleshooting</h3><p>If your FCKeditor does not show you must check if all files are extracted correctly. The directory %fckeditordir should have the following files: <code>fckeditor.js, fckconfig.js, fckstyles.xml, fcktemplates.xml</code> and a directory named <code>editor</code>.</p>',
87 array(
88 '%fckeditordir' => base_path() . drupal_get_path('module', 'fckeditor') .'/fckeditor/')
89 );
90 $output .= t('The correct directory structure is as follows: <blockquote><pre>!structure</pre></blockquote>', array(
91 '!structure' => "modules\n fckeditor\n <em>fckeditor.module</em>\n fckeditor\n _samples\n editor\n <em>COPY_HERE.txt</em>\n <em>fckconfig.js</em>\n ..."
92 ));
93 $output .= t("<h3>Plugins: Teaser break and Pagebreak</h3><p>By default, FCKeditor module comes with two plugins that can handle teaser break (&lt;!--break--&gt;) and pagebreak (&lt;!--pagebreak--&gt;). You can enable any (or even both) of them.<ol><li>Open <code>!fckeditor.config.js</code> and uncomment these three lines: <pre>!code</pre></li><li>The second step is to add buttons to the toolbar (in the same file). The button names are: <code>DrupalBreak, DrupalPageBreak</code>. For example if you have a toolbar with an array of buttons defined as follows: <pre>!buttons1</pre> simply add those two buttons at the end of array: <pre>!buttons2</pre> (remember about single quotes).</li></ol></p>",
94 array(
95 '!fckeditor.config.js' => base_path() . drupal_get_path('module', 'fckeditor') .'/fckeditor.config.js',
96 '!code' => "
97 FCKConfig.PluginsPath = '../../plugins/' ;
98 FCKConfig.Plugins.Add( 'drupalbreak' ) ;
99 FCKConfig.Plugins.Add( 'drupalpagebreak' ) ;
100 ",
101 '!buttons1' => "['Image','Flash','Table','Rule','SpecialChar']",
102 '!buttons2' => "['Image','Flash','Table','Rule','SpecialChar', 'DrupalBreak', 'DrupalPageBreak']",
103 ));
104 $output .= t('<h3>Uploading images and files</h3><p>There are three ways of uploading files: By using the built-in file browser, by using a module like !imce or using the core upload module.</p>',
105 array(
106 '!imce' => l(t('IMCE'), 'http://drupal.org/project/imce')
107 )
108 );
109 // the rest is untranslated for the moment
110 $output .= t("<h3>How to enable the file browser</h3><p>The editor gives the end user the flexibility to create a custom file browser that can be integrated on it. The included file browser allows users to view the content of a specific directory on the server and add new content to that directory (create folders and upload files).</p><p><ol><li>To enable file browsing you need to edit the connector configuration file in your fckeditor module directory, the file should be in:<blockquote><code>!config3</code> <br/> (FCKeditor 2.5+)<br/><br/> or <br/><br/><code>!config1</code><br/> and <br/><code>!config2</code> <br/> (FCKeditor 2.3.x - 2.4.x)</blockquote></p><p>In this file(s) you will need to enable the file browser by adding one line that includes file with the special authentication function for Drupal (<code>filemanager.config.php</code>). Add this code: <blockquote><code>!code1</code><br/> (FCKeditor 2.5+)</blockquote> or <blockquote><code>!code2</code> <br/> (FCKeditor 2.3.x - 2.4.x)</blockquote> straight below this line: <blockquote><code>!code3</code></blockquote> The config.php file also holds some other important settings, please take a look at it and adjust it to your needs (optional).</p></li>",
111 array('!config1' => base_path() . drupal_get_path('module', 'fckeditor') ."/fckeditor/editor/filemanager/browser/default/connectors/php/config.php",
112 '!config2' => base_path() . drupal_get_path('module', 'fckeditor') ."/fckeditor/editor/filemanager/upload/php/config.php",
113 '!config3' => base_path() . drupal_get_path('module', 'fckeditor') ."/fckeditor/editor/filemanager/connectors/php/config.php",
114 '!filesdir' => file_directory_path(),
115 '!code1' => 'require_once "../../../../../filemanager.config.php";', //2.5
116 '!code2' => 'require_once "'. str_replace("\\", "\\\\", dirname(__FILE__) . DIRECTORY_SEPARATOR .'filemanager.config.php"'), //2.4
117 '!code3' => "\$Config['UserFilesAbsolutePath'] = '' ;",
118 )
119 );
120 $output .= t("<li>As of Drupal 5.2, additional step is required: locate file named <code>settings.php</code> inside your drupal directory (usually <code>sites/default/settings.php</code>) and set <strong><code>&#36;cookie_domain</code></strong> variable to the appropiate domain (remember to uncomment that line). If you not do this, FCKeditor will claim that file browser is disabled</li>");
121 $output .= t('<li>Enabling file uploads is <strong>a security risk</strong>. That\'s why you have to grant a !link to enable the file browser to certain groups (assign the &quot;!allowupload&quot; permissions).</li>', array('!link' => l(t('separate permission'), 'admin/user/permissions'), "!allowupload" => t("allow fckeditor file uploads")));
122 $output .= t('<li>Lastly, adjust the !fb for each !profile.</li></ol>', array('!fb' => t('File browser settings'), '!profile' => l(t('profile'), 'admin/settings/fckeditor')));
123 $output .= t("<h3>Modules: Image Assist</h3><p>Image Assist can be integrated with FCKeditor. To do this, simply copy the <code>!iaf1</code> file to <code>!iaf2</code>.</p>", array("!iaf1" => drupal_get_path('module', 'fckeditor') ."/img_assist_fckeditor.js", "!iaf2" => drupal_get_path('module', 'img_assist') ."/img_assist_fckeditor.js"));
124
125 break;
126 }
127 return !empty($output) ? $output : "";
128 }
129
130 /**
131 * Implementation of hook_perm
132 * Administer -> User management -> Permissions
133 */
134 function fckeditor_perm() {
135 return array('administer fckeditor', 'access fckeditor', 'allow fckeditor file uploads');
136 }
137
138
139 /**
140 * Implementation of textarea
141 * Replace textarea with FCKeditor using callback function (fckeditor_process_textarea)
142 */
143 function fckeditor_elements() {
144 $type = array();
145 $type['textfield'] = array(
146 '#process' => array(
147 'fckeditor_process_input'
148 ),
149 );
150 if (user_access('access fckeditor')) {
151 // only roles with permission get the fckeditor
152 if (fckeditor_is_compatible_client()) {
153 // it would be useless to dig deeper if we're not able or allowed to
154 $type['textarea'] = array(
155 '#process' => array(
156 'fckeditor_process_textarea'
157 ),
158 );
159 }
160 }
161 return $type;
162 }
163
164 /**
165 * Allow more than 255 chars in Allowed HTML tags textfield
166 *
167 */
168 function fckeditor_process_input($element) {
169 if ($element['#id']=='edit-allowed-html-1') {
170 $element['#maxlength'] = max($element['#maxlength'], 1024);
171 }
172 return $element;
173 }
174
175 /**
176 * Add link to FCKeditor configuration in "Administer -> Site configuration" section
177 *
178 */
179 function fckeditor_menu() {
180
181 $items = array();
182
183 $items['fckeditor/xss'] = array(
184 'title' => 'XSS Filter',
185 'description' => 'XSS Filter.',
186 'page callback' => 'fckeditor_filter_xss',
187 'access arguments' => array('access fckeditor'),
188 'type' => MENU_CALLBACK,
189 );
190
191 $items['admin/settings/fckeditor'] = array(
192 'title' => 'FCKeditor',
193 'description' => 'Configure the rich editor.',
194 'page callback' => 'fckeditor_admin',
195 'access arguments' => array('administer fckeditor'),
196 'type' => MENU_NORMAL_ITEM,
197 );
198
199 return $items;
200 }
201
202 /**
203 * AJAX callback - XSS filter
204 */
205 function fckeditor_filter_xss() {
206 if (!isset($_POST['text']) || !is_string($_POST['text'])) {
207 exit;
208 }
209
210 $text = strtr($_POST['text'], array('<!--' => '__COMMENT__START__', '-->' => '__COMMENT__END__'));
211 preg_match_all("|</?([a-z][a-z0-9]*)(?:\b[^>]*)>|i", $text, $matches);
212 $tags = array_unique($matches[1]);
213
214 $secure_text = filter_xss($text, $tags);
215 $secure_text = strtr($secure_text, array('__COMMENT__START__' => '<!--', '__COMMENT__END__' => '-->'));
216
217 echo $secure_text;
218 exit;
219 }
220
221 //Remove a profile from the database.
222 function fckeditor_profile_delete($name) {
223 db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s'", $name);
224 db_query("DELETE FROM {fckeditor_role} WHERE name = '%s'", $name);
225 }
226
227 /**
228 * Profile validation.
229 */
230 function fckeditor_profile_validate($edit) {
231 $errors = array();
232
233 //include mode and all other fields are empty, invalid
234 if ($edit['excl_mode'] == 1 && !$edit['excl_fields'] && !$edit['excl_paths']) {
235 $errors['excl_mode'] = t('Include mode selected, but no fields/paths given. Enter at least one path or field where FCKeditor should appear.');
236 }
237
238 if (!preg_match("/^\d+$/", trim($edit['min_rows']))) {
239 $errors['min_rows'] = t('Minimum rows must be a valid number');
240 }
241
242 if ($edit['default'] == 't' && $edit['popup'] == 't') {
243 $errors['popup'] = t('If FCKeditor is enabled by default, popup window must be disabled.');
244 }
245
246 if ($edit['show_toggle'] == 't' && $edit['popup'] == 't') {
247 $errors['popup'] = t('If toggle is enabled, popup window must be disabled.');
248 }
249
250 if (!$edit['name']) {
251 $errors['name'] = t('You must give a profile name.');
252 }
253
254 if (!preg_match("/^\d+%?$/", $edit['width'])) {
255 $errors['width'] = t('Enter valid width. Ex: 400 or 100%');
256 }
257
258 if (!empty($edit['css_path'])) {
259 if ($edit['css_mode'] != 'self') {
260 $errors['css_path'] = t('CSS path is not empty. Please set the "Editor CSS" option to "define css" mode.');
261 }
262 else if (false !== strpos($edit['css_path'], '"')) {
263 $errors['css_path'] = t('Double quotes are not allowed in CSS path.');
264 }
265 else if (substr($edit['css_path'], 0, 1) == "'" && substr($edit['css_path'], -1) == "'") {
266 $errors['css_path'] = t('Enter valid path, do not surround it with quotes.');
267 }
268 }
269
270 if (!empty($edit['styles_path'])) {
271 if ($edit['css_style'] != 'self') {
272 $errors['styles_path'] = t('Path to predefined styles is not empty. Please set the "Predefined styles" option to "define path to fckstyles.xml" mode.');
273 }
274 else if (false !== strpos($edit['styles_path'], '"')) {
275 $errors['styles_path'] = t('Double quotes are not allowed in path.');
276 }
277 else if (substr($edit['styles_path'], 0, 1) == "'" && substr($edit['styles_path'], -1) == "'") {
278 $errors['styles_path'] = t('Enter valid path, do not surround it with quotes.');
279 }
280 }
281
282 if (!empty($edit['font_format'])) {
283 if (!preg_match("/^((p|div|pre|address|h1|h2|h3|h4|h5|h6);)*(p|div|pre|address|h1|h2|h3|h4|h5|h6)$/", $edit['font_format'])) {
284 $errors['font_format'] = t('Enter valid, semicolon separated, list of HTML font formats (no semicolon at the end of list expected).');
285 }
286 }
287
288 //validate fields
289 $fields = preg_split("/[\s,]+/", strip_tags($edit['excl_fields']));
290 foreach ($fields as $field) {
291 if ($field && !preg_match("/^[a-z]+(\-[[:alnum:]]+)+$/i", $field)) {
292 $errors['excl_fields'] = t("Invalid field specified: %1", array("%1" => $field));
293 break;
294 }
295 }
296
297 $fields = preg_split("/[\s,]+/", strip_tags($edit['simple_incl_fields']));
298 foreach ($fields as $field) {
299 if ($field && !preg_match("/^[a-z]+(\-[[:alnum:]]+)+$/i", $field)) {
300 $errors['excl_fields'] = t("Invalid field specified: %1", array("%1" => $field));
301 break;
302 }
303 }
304
305 //validate paths
306 $paths = preg_split("/[\s,]+/", strip_tags($edit['excl_paths']));
307 foreach ($paths as $path) {
308 if ($path && !preg_match("|^[_a-z0-9-\*/]*$|i", $path)) {
309 $errors['excl_paths'] = t("Invalid path specified: %1", array("%1" => $path));
310 break;
311 }
312 }
313
314 $paths = preg_split("/[\s,]+/", strip_tags($edit['simple_incl_paths']));
315 foreach ($paths as $path) {
316 if ($path && !preg_match("|^[_a-z0-9-\*/]*$|i", $path)) {
317 $errors['excl_paths'] = t("Invalid path specified: %1", array("%1" => $path));
318 break;
319 }
320 }
321
322 if (variable_get('file_downloads', '') !== FILE_DOWNLOADS_PRIVATE) {
323 if ($edit['UserFilesAbsolutePath'] && !$edit['UserFilesPath']) {
324 $errors['UserFilesPath'] = t("Path to uploaded files is required.");
325 }
326 if ($edit['UserFilesPath'] && !$edit['UserFilesAbsolutePath']) {
327 $errors['UserFilesPath'] = t("Absolute path to uploaded files is required.");
328 }
329 }
330
331 foreach ($errors as $name => $message) {
332 form_set_error($name, $message);
333 }
334
335 return count($errors) == 0;
336 }
337
338 /**
339 * Global profile validation.
340 */
341 function fckeditor_global_profile_validate($edit) {
342 $errors = array();
343
344 //include mode and all other fields are empty, invalid
345 if ($edit['excl_mode'] == 1 && !$edit['excl_fields'] && !$edit['excl_paths']) {
346 $errors['excl_mode'] = t('Include mode selected, but no fields/paths given. Enter at least one path or field where FCKeditor should appear.');
347 }
348
349 //validate fields
350 $fields = preg_split("/[\s,]+/", strip_tags($edit['excl_fields']));
351 foreach ($fields as $field) {
352 if ($field && !preg_match("/^[a-z]+(\-[[:alnum:]]+)+$/i", $field)) {
353 $errors['excl_fields'] = t("Invalid field specified: %1", array("%1" => $field));
354 break;
355 }
356 }
357
358 $fields = preg_split("/[\s,]+/", strip_tags($edit['simple_incl_fields']));
359 foreach ($fields as $field) {
360 if ($field && !preg_match("/^[a-z]+(\-[[:alnum:]]+)+$/i", $field)) {
361 $errors['excl_fields'] = t("Invalid field specified: %1", array("%1" => $field));
362 break;
363 }
364 }
365
366 //validate paths
367 $paths = preg_split("/[\s,]+/", strip_tags($edit['excl_paths']));
368 foreach ($paths as $path) {
369 if ($path && !preg_match("|^[_a-z0-9-\*/]*$|i", $path)) {
370 $errors['excl_paths'] = t("Invalid path specified: %1", array("%1" => $path));
371 break;
372 }
373 }
374
375 $paths = preg_split("/[\s,]+/", strip_tags($edit['simple_incl_paths']));
376 foreach ($paths as $path) {
377 if ($path && !preg_match("|^[_a-z0-9-\*/]*$|i", $path)) {
378 $errors['excl_paths'] = t("Invalid path specified: %1", array("%1" => $path));
379 break;
380 }
381 }
382
383 foreach ($errors as $name => $message) {
384 form_set_error($name, $message);
385 }
386
387 return count($errors) == 0;
388 }
389
390 /**
391 * Controller for FCKeditor administrative settings.
392 */
393 function fckeditor_admin($arg = NULL) {
394
395 $module_drupal_path = drupal_get_path('module', 'fckeditor');
396 $fckconfig_file = $module_drupal_path .'/fckeditor/fckconfig.js';
397 if (!file_exists($fckconfig_file)) {
398 drupal_set_message(t('checking for %filename', array('%filename' => $fckconfig_file)));
399 drupal_set_message(
400 t('The FCKeditor component is not installed correctly. Please go to the !fckeditorlink to download the latest version. After that you must extract the files to %modulepath and make sure that the directory %modulesubdir and the file %modulefile exist. Refer to the !readme for more information.',
401 array(
402 '!fckeditorlink' => l(t('FCKeditor homepage'), 'http://www.fckeditor.net/download'),
403 '!readme' => l('readme.txt', 'admin/help/fckeditor'),
404 '%modulepath' => base_path() . $module_drupal_path .'/fckeditor/',
405 '%modulesubdir' => base_path() . $module_drupal_path .'/fckeditor/editor',
406 '%modulefile' => base_path() . $module_drupal_path .'/fckeditor/fckeditor.js')),
407 'error');
408 return FALSE;
409 }
410
411 $edit = $_POST;
412 $op = isset($_POST['op']) ? $_POST['op'] : "";
413
414 $op = $arg && !$op ? $arg : $op;
415
416 switch ($op) {
417 case 'add':
418 $output = fckeditor_profile_form($edit);
419 break;
420
421 case 'addg':
422 $output = fckeditor_global_profile_form($edit);
423 break;
424
425 case 'edit':
426 drupal_set_title(t('Edit FCKeditor profile'));
427 $output = fckeditor_profile_form(fckeditor_profile_load(urldecode(arg(4))));
428 break;
429
430 case 'editg':
431 drupal_set_title(t('Edit FCKeditor profile'));
432 $output = fckeditor_global_profile_form(fckeditor_profile_load("FCKeditor Global Profile"));
433 break;
434
435 case 'deleteg':
436 $output = fckeditor_ask_delete_confirmation(TRUE);
437 break;
438
439 case 'delete':
440 $output = fckeditor_ask_delete_confirmation(FALSE, urldecode(arg(4)));
441 break;
442
443 case 'deleteconfirmed':
444 fckeditor_profile_delete(urldecode(arg(4)));
445 drupal_set_message(t('Deleted profile'));
446 drupal_goto('admin/settings/fckeditor');
447 break;
448
449 case 'deletegconfirmed':
450 fckeditor_profile_delete("FCKeditor Global Profile");
451 drupal_set_message(t('Deleted Global profile'));
452 drupal_goto('admin/settings/fckeditor');
453 break;
454
455 case t('Create profile');
456 case t('Update profile');
457 if (fckeditor_profile_validate($edit)) {
458 fckeditor_profile_save($edit);
459 !empty($edit['old_name']) ? drupal_set_message(t('Your FCKeditor profile has been updated.')) : drupal_set_message(t('Your FCKeditor profile has been created.'));
460 drupal_goto('admin/settings/fckeditor');
461 }
462 else {
463 $output = fckeditor_profile_form($edit);
464 }
465 break;
466
467 case t('Create global profile');
468 case t('Update global profile');
469 if (fckeditor_global_profile_validate($edit)) {
470 $edit['name'] = 'FCKeditor Global Profile';
471 fckeditor_global_profile_save($edit);
472 drupal_set_message(t('FCKeditor global profile has been saved.'));
473 drupal_goto('admin/settings/fckeditor');
474 }
475 else {
476 $output = fckeditor_global_profile_form($edit);
477 }
478 break;
479
480 default:
481 drupal_set_title(t('FCKeditor settings'));
482 //Check if FCKeditor is installed.
483 $fckeditor_loc = drupal_get_path('module', 'fckeditor') .'/fckeditor/';
484 if (!is_dir($fckeditor_loc)) {
485 drupal_set_message(t('Could not find the FCKeditor engine installed at <strong>!fckeditor-directory</strong>. Please !download, uncompress it and copy the folder into !fckeditor-path.', array('!fckeditor-path' => drupal_get_path('module', 'fckeditor'), '!fckeditor-directory' => $fckeditor_loc, '!download' => l(t("download FCKeditor"), "http://www.fckeditor.net/download"))), 'error');
486 }
487
488 $access_fckeditor_roles = user_roles(FALSE, 'access fckeditor');
489 if (!$access_fckeditor_roles) {
490 drupal_set_message(t('There is currently no role with the <strong>!access</strong> permission. Visit !acl administration section.',
491 array("!access" => t("access fckeditor"), "!acl" => l(t("Permissions"), "admin/user/permissions"))), "warning");
492 }
493 else {
494 $result = db_query_range("SELECT name FROM {fckeditor_settings} WHERE name<>'FCKeditor Global Profile'", 0, 1);
495 $has_profiles = FALSE;
496 //find profile other than Global
497 if ($obj = db_fetch_object($result)) {
498 $has_profiles = TRUE;
499 }
500
501 //find roles with profiles
502 $result = db_query("SELECT rid FROM {fckeditor_role}");
503 $rids = array();
504 while ($obj = db_fetch_object($result)) {
505 $rids[] = $obj->rid;
506 }
507 $rids = array_unique($rids);
508 if (!$has_profiles) {
509 drupal_set_message(t("No FCKeditor profiles found. At this moment, nobody is able to use FCKeditor. Create new profile below."), "error");
510 }
511 else {
512 //not all roles with access fckeditor has their FCKeditor profile assigned
513 $diff = array_diff(array_keys($access_fckeditor_roles), $rids);
514 if ($diff) {
515 $list = "<ul>";
516 foreach ($diff as $rid) {
517 $list .= "<li>". $access_fckeditor_roles[$rid] ."</li>";
518 }
519 $list .= "</ul>";
520 drupal_set_message(t("Not all roles with <strong>!access</strong> permission are associated with FCKeditor profiles. As a result, users having the following roles may be unable to use FCKeditor: !list Create new or edit FCKeditor profiles below and in the <strong>Basic setup</strong> section, check &quot;Roles allowed to use this profile&quot;.", array("!access" => l(t("access fckeditor"), "admin/user/permissions"), "!list" => $list)), "warning");
521 }
522 }
523 }
524
525 $output = fckeditor_profile_overview();
526 }
527
528 return $output;
529 }
530
531 /**
532 * Save a profile to the database.
533 * @todo add more entries to array in the user_save line
534 */
535 function fckeditor_profile_save($edit) {
536 db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s' or name = '%s'", $edit['name'], empty($edit['old_name']) ? "" : $edit['old_name']);
537 db_query("DELETE FROM {fckeditor_role} WHERE name = '%s' or name = '%s'", $edit['name'], empty($edit['old_name']) ? "" : $edit['old_name']);
538 db_query("INSERT INTO {fckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], serialize($edit));
539 if (!empty($edit['rids']))
540 foreach ($edit['rids'] as $rid => $value) {
541 db_query("INSERT INTO {fckeditor_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid);
542 }
543
544 // if users can't set their own defaults, make sure to remove $user->fckeditor_status so their default doesn't override the main default
545 if (!empty($edit['user_choose']) && $edit['user_choose'] == 'false') {
546 global $user;
547 user_save($user, array('fckeditor_status' => NULL));
548 }
549 }
550
551 function fckeditor_global_profile_save($edit) {
552 if (isset($edit['rank'])) {
553 $edit['rank'] = explode('>', str_replace(' ', '', $edit['rank']));
554 }
555
556 db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s' or name = '%s'", $edit['name'], empty($edit['old_name']) ? "" : $edit['old_name']);
557 db_query("DELETE FROM {fckeditor_role} WHERE name = '%s' or name = '%s'", $edit['name'], empty($edit['old_name']) ? "" : $edit['old_name']);
558 db_query("INSERT INTO {fckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], serialize($edit));
559 }
560
561 /**
562 * Controller for fckeditor profiles.
563 */
564 function fckeditor_profile_overview() {
565 $output = '';
566
567 $profiles = fckeditor_profile_load();
568 if ($profiles) {
569 $roles = user_roles();
570 $access_fckeditor_roles = user_roles(FALSE, 'access fckeditor');
571 $header = array(t('Profile'), t('Roles'), t('Operations'));
572 foreach ($profiles as $p) {
573 $rids = $p->rids;
574 if ($p->name !== "FCKeditor Global Profile") {
575 foreach ($p->rids as $rid => $name) {
576 if (!isset($access_fckeditor_roles[$rid])) {
577 unset($rids[$rid]);
578 }
579 }
580 $rows[] = array(array('data' => $p->name, 'valign' => 'top'), array('data' => implode("<br />\n", $rids)), array('data' => l(t('edit'), 'admin/settings/fckeditor/edit/'. urlencode($p->name)) .' '. l(t('delete'), 'admin/settings/fckeditor/delete/'. urlencode($p->name)), 'valign' => 'top'));
581 }
582 }
583 $output .= "<h3>". t("Profiles") ."</h3>";
584 $output .= theme('table', $header, $rows);
585 $output .= '<p>'. l(t('Create new profile'), 'admin/settings/fckeditor/add') .'</p>';
586 }
587 else {
588 drupal_set_message(t('No profiles found. Click here to !create.', array('!create' => l(t("create a new profile"), 'admin/settings/fckeditor/add'))));
589 }
590
591 $rows = array();
592 if (!isset($profiles['FCKeditor Global Profile'])) {
593 drupal_set_message(t('Global Profile not found. Click here to !create.', array('!create' => l(t("create the global profile"), 'admin/settings/fckeditor/addg'))));
594 }
595 else {
596 $output .= "<h3>". t("Global Settings") ."</h3>";
597 $rows[] = array(array('data' => t('FCKeditor Global Profile'), 'valign' => 'top'), array('data' => l(t('edit'), 'admin/settings/fckeditor/editg') ." ". l(t('delete'), 'admin/settings/fckeditor/deleteg'), 'valign' => 'top'));
598 $output .= theme('table', array(t('Profile'), t('Operations')), $rows);
599 }
600
601 return $output;
602 }
603
604 /**
605 * Load all profiles. Just load one profile if $name is passed in.
606 */
607 function fckeditor_profile_load($name = '') {
608 static $profiles = array();
609
610 if (!$profiles) {
611 $roles = user_roles();
612 $result = db_query('SELECT * FROM {fckeditor_settings}');
613 while ($data = db_fetch_object($result)) {
614 $data->settings = unserialize($data->settings);
615 $result2 = db_query("SELECT rid FROM {fckeditor_role} WHERE name = '%s'", $data->name);
616 $role = array();
617 while ($r = db_fetch_object($result2)) {
618 $role[$r->rid] = $roles[$r->rid];
619 }
620 $data->rids = $role;
621
622 $profiles[$data->name] = $data;
623 }
624 }
625
626 return ($name ? $profiles[$name] : $profiles);
627 }
628
629 /**
630 * @param int $excl_mode 1/include, exclude otherwise
631 * @param string $excl_fields fields (HTML IDs)
632 * @param string $excl_paths paths (drupal paths)
633 * @param string $element_id current ID
634 * @param string $get_q current path
635 *
636 * @return boolean
637 * returns true if FCKeditor is enabled
638 */
639 function fckeditor_is_enabled($excl_mode, $excl_fields, $excl_paths, $element_id, $get_q) {
640 $arr_excl_fields = preg_split("/[\s,]+/", strip_tags($excl_fields));
641 $field_found = fckeditor_idsearch($element_id, $arr_excl_fields);
642
643 $path = drupal_get_path_alias($get_q);
644 $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($excl_paths, '/')) .')$/';
645 $path_found = preg_match($regexp, $path);
646
647 $found = $field_found || $path_found;
648
649 $result = ($excl_mode == 1) ? $found : !$found;
650 return $result;
651 }
652
653 /**
654 * This function create the HTML objects required for the FCKeditor
655 *
656 * @param $element
657 * A fully populated form elment to add the editor to
658 * @return
659 * The same $element with extra FCKeditor markup and initialization
660 */
661 function fckeditor_process_textarea($element) {
662 static $is_running = FALSE;
663 static $num = 1;
664 static $id2id = array();
665 static $processed_elements = array();
666 global $user, $fckeditor_simple_toolbar_ids;
667
668 $processed = in_array($element['#id'], $processed_elements);
669
670 //hack for module developers that want to disable FCKeditor on their textareas
671 if (key_exists('#wysiwyg', $element) && !$element['#wysiwyg']) {
672 return $element;
673 }
674
675 //skip this one, surely nobody wants WYSIWYG here
676 switch ($element['#id']) {
677 case 'edit-excl-list':
678 case 'edit-simple-incl-list':
679 case 'edit-simple-incl-paths':
680 case 'edit-simple-incl-fields':
681 case 'edit-log':
682 case 'edit-excl-fields':
683 case 'edit-excl-paths':
684 case 'edit-js-conf':
685 case 'edit-teaser-js':
686 return $element;
687 break;
688 }
689
690 if (isset($element['#attributes']['disabled']) && $element['#attributes']['disabled'] == 'disabled') {
691 return $element;
692 }
693
694 $profile = fckeditor_user_get_profile($user);
695 if (!$profile) {
696 return $element;
697 }
698
699 $conf = array();
700 $conf = $profile->settings;
701
702 if ($conf['allow_user_conf']=='t') {
703 foreach (array('default', 'show_toggle', 'popup', 'skin', 'toolbar', 'expand', 'width', 'lang', 'auto_lang') as $setting) {
704 $conf[$setting] = fckeditor_user_get_setting($user, $profile, $setting);
705 }
706 }
707 if ($conf["popup"]=="t" && $conf["show_toggle"]=="t") {
708 $conf["show_toggle"]="f";
709 }
710
711 if (!isset($conf['xss'])) {
712 $conf['xss'] = 1;
713 }
714 if (!isset($conf['xss_t'])) {
715 $conf['xss_t'] = 1;
716 }
717
718 $themepath = path_to_theme() .'/';
719 $host = base_path();
720
721 $enabled = fckeditor_is_enabled(empty($conf['excl_mode']) ? "" : $conf['excl_mode'], empty($conf['excl_fields']) ? "" : $conf['excl_fields'], empty($conf['excl_paths']) ? "" : $conf['excl_paths'], $element['#id'], $_GET['q']);
722 if ($enabled) {
723 $global_profile = fckeditor_profile_load("FCKeditor Global Profile");
724 $global_conf = $global_profile->settings;
725 if ($global_conf) {
726 $enabled = fckeditor_is_enabled(empty($global_conf['excl_mode']) ? "" : $global_conf['excl_mode'], empty($global_conf['excl_fields']) ? "" : $global_conf['excl_fields'], empty($global_conf['excl_paths']) ? "" : $global_conf['excl_paths'], $element['#id'], $_GET['q']);
727 }
728 }
729 if (!isset($element['#suffix'])) {
730 $element['#suffix'] = "";
731 }
732
733 if ((($element['#rows'] > $conf['min_rows']) || ($conf['min_rows'] <= 1 && empty($element['#rows']))) && $enabled) {
734 // only replace textarea when it has enough rows and it is enabled
735
736 // Set resizable to false to avoid drupal.js resizable function from taking control of the textarea
737 if ($conf["popup"]=="f") {
738 $element['#resizable'] = FALSE;
739 }
740
741 //it's not a problem when adding new content
742 if ($conf['xss'] && arg(1) != "add") {
743 //let FCKeditor know when perform XSS checks
744 if ($conf['xss_t'] == 1) {
745 $xss_class = 'filterxss1';
746 }
747 else {
748 $xss_class = 'filterxss2';
749 }
750
751 if (!isset($element['#attributes']['class'])) {
752 $element['#attributes']['class'] = '';
753 }
754
755 //always enabled
756 if ($conf['xss'] == 2) {
757 $element['#attributes']['class'] .= ' '. $xss_class;
758 }
759 else {
760 //check for HTML filter
761 $menuitem = menu_get_item();
762 if (!empty($menuitem['map'][1])) {
763 if (is_object($menuitem['map'][1]) && in_array($menuitem['map'][1]->format, fckeditor_html_filter_formats())) {
764 $element['#attributes']['class'] .= ' '. $xss_class;
765 }
766 }
767 else if (arg(0) == 'comment' && arg(1) == 'edit' && is_numeric(arg(2))) {
768 $comment = _comment_load(arg(2));
769
770 if (in_array($comment->format, fckeditor_html_filter_formats())) {
771 $element['#attributes']['class'] .= ' '. $xss_class;
772 }
773 }
774 //force XSS check if we don't have an information about input format
775 else {
776 $element['#attributes']['class'] .= ' '. $xss_class;
777 }
778 }
779 }
780 $xss_check = ((isset($xss_class) && $xss_class)) ? 1 : 0;
781
782 if (in_array($element['#id'], $processed_elements)) {
783 $js_id = $id2id[$element['#id']];
784 }
785 else {
786 $js_id = 'oFCK_'. $num++;
787 $id2id[$element['#id']] = $js_id;
788 }
789 $fckeditor_on = ($conf['default']=='t') ? 1 : 0 ;
790
791 $content = "";
792 if (isset($element['#post']['teaser_js'])) {
793 $content .= $element['#post']['teaser_js'] ."<!--break-->";
794 }
795 $content .= $element['#value'];
796 $wysiwyg_link = "<div id=\"fck_{$js_id}\"><textarea id=\"{$js_id}\">". htmlspecialchars($content) ."</textarea></div>\n";
797 $wysiwyg_link .= "<a href=\"javascript:Toggle('{$js_id}','{$element['#id']}','". str_replace("'", "\\'", t("Switch to plain text editor")) ."','". str_replace("'", "\\'", t("Switch to rich text editor")) ."',". $xss_check .");\" id=\"switch_{$js_id}\" ". ($fckeditor_on?"style=\"display:none\"":"") .">";
798 $wysiwyg_link .= $fckeditor_on ? t("Switch to plain text editor") : t("Switch to rich text editor");
799 $wysiwyg_link .= "</a>";
800 if ($conf['show_toggle'] == 't' && !$processed) {
801 drupal_add_js('if (Drupal.jsEnabled) {$(document).ready(function() {CreateToggle("'.$element['#id'].'","'.$js_id.'", '.$fckeditor_on.');});}', 'inline');
802 }
803 //settings are saved as strings, not booleans
804 if ($conf['show_toggle'] == 't') {
805 // Make sure to append to #suffix so it isn't completely overwritten
806 $element['#suffix'] .= $wysiwyg_link;
807 }
808 // setting some variables
809 $module_drupal_path = drupal_get_path('module', 'fckeditor');
810 $module_full_path = $host . $module_drupal_path;
811 // get the default drupal files path
812 $files_path = $host . file_directory_path();
813 // module_drupal_path:
814 // 'modules/fckeditor' (length=17)
815 // module_full_path:
816 // '/drupal5/modules/fckeditor' (length=26)
817 // files_path:
818 // '/drupal5/files' (length=14)
819 // configured in settings
820 $width = $conf['width'];
821
822 // sensible default for small toolbars
823 $height = $element['#rows'] * 14 + 140;
824
825 if (!$is_running) {
826 drupal_add_js($module_drupal_path .'/fckeditor/fckeditor.js');
827 drupal_add_js($module_drupal_path .'/fckeditor.utils.js?'.@filemtime($module_drupal_path."/fckeditor.utils.js"));
828 $is_running = TRUE;
829 }
830
831 $toolbar = $conf['toolbar'];
832 //$height += 100; // for larger toolbars
833
834 $force_simple_toolbar = fckeditor_is_enabled(1, empty($conf['simple_incl_fields']) ? "" : $conf['simple_incl_fields'], empty($conf['simple_incl_paths']) ? "" : $conf['simple_incl_paths'], $element['#id'], $_GET['q']);
835 if (!$force_simple_toolbar) {
836 $force_simple_toolbar = fckeditor_is_enabled(1, empty($global_conf['simple_incl_fields']) ? "" : $global_conf['simple_incl_fields'], empty($global_conf['simple_incl_paths']) ? "" : $global_conf['simple_incl_paths'], $element['#id'], $_GET['q']);
837 }
838 if ($force_simple_toolbar) {
839 $toolbar = FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME;
840 }
841 $textarea_id = $conf['show_toggle'] == 't' ? $js_id : $element['#id'];
842 $js = $js_id ." = new FCKeditor( '". $textarea_id ."' );
843 ". $js_id .".BasePath = '". $module_full_path ."/fckeditor/';
844 ". $js_id .".Config['CustomConfigurationsPath'] = \"". $module_full_path ."/fckeditor.config.js?".@filemtime($module_drupal_path."/fckeditor.config.js")."\";
845 ". $js_id .".Config['TextareaID'] = \"". $element['#id'] ."\";";
846
847 //if ($conf['appearance_conf'] == 'f') {
848 $js .= "\n". $js_id .".ToolbarSet = \"". $toolbar ."\";
849 ". $js_id .".Config['SkinPath'] = ". $js_id .".BasePath + \"editor/skins/". $conf['skin'] ."/\";
850 ". $js_id .".Config['DefaultLanguage'] = \"". $conf['lang'] ."\";
851 ". $js_id .".Config['AutoDetectLanguage'] = ". ($conf['auto_lang']=="t"?"true":"false") .";
852 ". $js_id .".Height = \"". $height ."\";
853 ". $js_id .".Config['ToolbarStartExpanded'] = ". ($conf['expand']=="t"?"true":"false") .";
854 ". $js_id .".Width = \"". $width ."\";\n";
855 //}
856 //if ($conf['output_conf'] == 'f') {
857 $js .= "\n". $js_id .".Config['EnterMode'] = '". $conf['enter_mode'] ."';
858 ". $js_id .".Config['ShiftEnterMode'] = \"". $conf['shift_enter_mode'] ."\";
859 ". $js_id .".Config['FontFormats'] = \"". str_replace(",", ";", $conf['font_format']) ."\";
860 ". $js_id .".Config['FormatSource'] = ". ($conf['format_source']=="t"?"true":"false") .";
861 ". $js_id .".Config['FormatOutput'] = ". ($conf['format_output']=="t"?"true":"false") .";\n";
862 //}
863
864 if (function_exists('img_assist_perm')) { //#275158
865 drupal_add_js("var fckImgAssistPath = '". base_path() . drupal_get_path('module', 'img_assist') ."';", 'inline');
866 }
867 // add code for filebrowser for users that have access
868 if (user_access('allow fckeditor file uploads')==1) {
869 $filebrowser = $conf['filebrowser'];
870 if ($filebrowser == 'imce' && !module_exists('imce')) {
871 $filebrowser = 'none';
872 }
873 $quickupload = $conf['quickupload'] == 't';
874
875 // load variables used by both quick upload and filebrowser
876 // and assure that the $_SESSION variables are loaded
877 if ($quickupload || $filebrowser == 'builtin') {
878
879 $connector_path = $module_drupal_path ."/fckeditor/editor/filemanager/connectors/php/connector.php";
880 if (file_exists($connector_path)) {
881 //FCKeditor 2.5 and above
882 $connector_path = $module_full_path ."/fckeditor/editor/filemanager/connectors/php/connector.php";
883 }
884 else {
885 //FCKeditor 2.4.3-
886 $connector_path = "connectors/php/connector.php";
887 }
888 $upload_path = $module_drupal_path ."/fckeditor/editor/filemanager/connectors/php/upload.php";
889 if (file_exists($upload_path)) {
890 //FCKeditor 2.5 and above
891 $upload_path = $module_full_path ."/fckeditor/editor/filemanager/connectors/php/upload.php";
892 }
893 else {
894 //FCKeditor 2.4.3-
895 $upload_path = "/fckeditor/editor/filemanager/upload/php/upload.php";
896 }
897
898 if (!empty($profile->settings['UserFilesPath'])) $_SESSION['FCKeditor']['UserFilesPath'] = strtr($profile->settings['UserFilesPath'], array("%f" => file_directory_path(), "%u" => $user->uid, "%b" => $host));
899 if (!empty($profile->settings['UserFilesAbsolutePath'])) $_SESSION['FCKeditor']['UserFilesAbsolutePath'] = strtr($profile->settings['UserFilesAbsolutePath'], array("%f" => file_directory_path(), "%u" => $user->uid, "%b" => base_path(), "%d" => $_SERVER['DOCUMENT_ROOT']));
900 if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) {
901 $_SESSION['FCKeditor']['UserFilesPath'] = url('system/files') .'/';
902 $_SESSION['FCKeditor']['UserFilesAbsolutePath'] = realpath(file_directory_path()) . DIRECTORY_SEPARATOR;
903 }
904 }
905
906 if ($quickupload) {
907 $js .= $js_id .".Config['LinkUpload'] = true;\n";
908 $js .= $js_id .".Config['ImageUpload'] = true;\n";
909 $js .= $js_id .".Config['FlashUpload'] = true;\n";
910 $js .= $js_id .".Config['LinkUploadURL'] = '". $upload_path ."';\n";
911 $js .= $js_id .".Config['ImageUploadURL'] = '". $upload_path ."?Type=Image';\n";
912 $js .= $js_id .".Config['FlashUploadURL'] = '". $upload_path ."?Type=Flash';\n";
913 }
914 else {
915 $js .= $js_id .".Config['LinkUpload'] = false;\n";
916 $js .= $js_id .".Config['ImageUpload'] = false;\n";
917 $js .= $js_id .".Config['FlashUpload'] = false;\n";
918 }
919
920 switch ($filebrowser) {
921 case 'imce':
922 $js .= $js_id .".Config['LinkBrowser']= true;\n";
923 $js .= $js_id .".Config['ImageBrowser']= true;\n";
924 $js .= $js_id .".Config['FlashBrowser']= true;\n";
925 $js .= $js_id .".Config['LinkBrowserURL']= '". $host ."?q=imce&app=FCKEditor|url@txtUrl';\n";
926 $js .= $js_id .".Config['ImageBrowserURL']= '". $host ."?q=imce&app=FCKEditor|url@txtUrl|width@txtWidth|height@txtHeight';\n";
927 $js .= $js_id .".Config['FlashBrowserURL']= '". $host ."?q=imce&app=FCKEditor|url@txtUrl';\n";
928 break;
929 case 'builtin':
930 $js .= $js_id .".Config['LinkBrowser'] = true;\n";
931 $js .= $js_id .".Config['ImageBrowser'] = true;\n";
932 $js .= $js_id .".Config['FlashBrowser'] = true;\n";
933 $js .= $js_id .".Config['LinkBrowserURL'] = '". $module_full_path ."/fckeditor/editor/filemanager/browser/default/browser.html?Connector=". $connector_path ."&ServerPath=". $files_path ."';\n";
934 $js .= $js_id .".Config['ImageBrowserURL'] = '". $module_full_path ."/fckeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=". $connector_path ."&ServerPath=". $files_path ."';\n";
935 $js .= $js_id .".Config['FlashBrowserURL'] = '". $module_full_path ."/fckeditor/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=". $connector_path ."&ServerPath=". $files_path ."';\n";
936 break;
937 default:
938 case 'none':
939 $js .= $js_id .".Config['LinkBrowser'] = false;\n";
940 $js .= $js_id .".Config['ImageBrowser'] = false;\n";
941 $js .= $js_id .".Config['FlashBrowser'] = false;\n";
942 break;
943 }
944 }
945 else {
946 $js .= $js_id .".Config['LinkBrowser'] = false;\n";
947 $js .= $js_id .".Config['ImageBrowser'] = false;\n";
948 $js .= $js_id .".Config['FlashBrowser'] = false;\n";
949 $js .= $js_id .".Config['LinkUpload'] = false;\n";
950 $js .= $js_id .".Config['ImageUpload'] = false;\n";
951 $js .= $js_id .".Config['FlashUpload'] = false;\n";
952 }
953
954 if (!empty($conf['js_conf'])) {
955 $lines = preg_split("/[\n\r]+/", $conf['js_conf']);
956 foreach ($lines as $l)
957 if ($l && strlen($l) > 5) {
958 $eqpos = strpos($l, "=");
959 if (false !== $eqpos) {
960 $option = str_replace("FCKConfig.", "", substr($l, 0, $eqpos));
961 $js .= "\n". $js_id .".Config['". trim($option) ."'] =". substr($l, $eqpos + 1);
962 }
963 }
964 }
965
966 // add custom xml stylesheet if it exists
967 if (!empty($conf['css_style']) && $conf['css_style'] == 'theme') {
968 if (file_exists($themepath .'/fckstyles.xml')) {
969 $styles_xml_path = $host . $themepath .'/fckstyles.xml';
970 $js .= $js_id .".Config['StylesXmlPath'] = \"". $styles_xml_path ."\";\n";
971 }
972 }
973 else if (!empty($conf['css_style']) && $conf['css_style'] == 'self') {
974 $conf['styles_path'] = str_replace("%h%t", "%t", $conf['styles_path']);
975 $js .= $js_id .".Config['StylesXmlPath'] = \"". str_replace(array('%h', '%t', '%m'), array($host, $host . $themepath, $module_drupal_path), $conf['styles_path']) ."\";\n";
976 }
977 // add custom stylesheet if configured
978 // lets hope it exists but we'll leave that to the site admin
979 if ($conf['css_mode'] == 'theme') {
980 $css = $themepath .'style.css';
981 if (file_exists($css)) {
982 $js .= $js_id .".Config['EditorAreaCSS'] = \"". $host . $css .",". $module_full_path ."/fckeditor.css\";";
983 }
984 }
985 else if ($conf['css_mode'] == 'self') {
986 $conf['css_path'] = str_replace("%h%t", "%t", $conf['css_path']);
987 $js .= $js_id .".Config['EditorAreaCSS'] = \"". str_replace(array('%h', '%t'), array($host, $host . $themepath), $conf['css_path']) .",". $module_full_path ."/fckeditor.css\";";
988 }
989
990 if (!$processed) {
991 drupal_add_js('var '. $js_id .';if (Drupal.jsEnabled) {$(document).ready(function() {'. $js .'});}', 'inline');
992 }
993
994 if ($conf['popup']=="t") {
995 // Add the script file with the popup open function.
996 if (!$processed) {
997 drupal_add_js($module_drupal_path .'/fckeditor.popup.js');
998 }
999 $element['#suffix'] .= " <span class=\"fckeditor_popuplink\">(<a href=\"#\" onclick=\"FCKeditor_OpenPopup('". $module_full_path ."/fckeditor.popup.html?var=". $js_id ."&el=". $element['#id'] ."');return false;\">". t('Open rich editor') ."</a>)</span>";
1000 }
1001 else {
1002 // if no popup mode, add the editor initialization to the footer
1003 // this obviously needs print($closure) in page.tpl.php
1004 if ($fckeditor_on && !$processed) {
1005 drupal_add_js('if (Drupal.jsEnabled) {$(document).ready(function() {if (typeof ('. $js_id .') != "undefined") {
1006 if ($("#edit-teaser-js").size() && $("#edit-teaser-js").val().length){
1007 $("#edit-body").val($("#edit-teaser-js").val() + "<!--break-->" + $("#edit-body").val());
1008 }
1009 window.setTimeout("FCKeditorReplaceTextarea(\''. $textarea_id .'\','. $js_id .','. $xss_check .');",100);}});}', 'inline', 'footer');
1010 }
1011 }
1012 }
1013
1014 // display the field id for administrators
1015 if (user_access('administer fckeditor')) {
1016 $element['#suffix'] .= '<div class="textarea-identifier description">'. t('The ID for !excluding this element is: !id - the path is: !path', array(
1017 '!excluding' => l(t("excluding or including"), 'admin/settings/fckeditor'),
1018 '!id' => $element['#id'],
1019 '!path' => $_GET['q'],
1020 )) .'</div>';
1021 }
1022
1023 $processed_elements[] = $element['#id'];
1024
1025 return $element;
1026 }
1027
1028 /**
1029 * Implementation of hook_user().
1030 */
1031 function fckeditor_user($type, &$edit, &$user, $category = NULL) {
1032 if ($type == 'form' && $category == 'account' && user_access('access fckeditor')) {
1033 $profile = fckeditor_user_get_profile($user);
1034 $toolbar_options = fckeditor_load_toolbar_options();
1035 $skin_options = fckeditor_load_skin_options();
1036 $lang_options = fckeditor_load_lang_options();
1037
1038 // because the settings are saved as strings we need to test for the string 'true'
1039 if ($profile->settings['allow_user_conf'] == 't') {
1040 $form['fckeditor'] = array(
1041 '#type' => 'fieldset',
1042 '#title' => t('Rich Text Editor settings'),
1043 '#weight' => 10,
1044 '#collapsible' => TRUE,
1045 '#collapsed' => TRUE
1046 );
1047
1048 $form['fckeditor']['fckeditor_default'] = array(
1049 '#type' => 'select',
1050 '#title' => t('Default state'),
1051 '#default_value' => isset($user->fckeditor_default) ? $user->fckeditor_default : (isset($profile->settings['default']) ? $profile->settings['default'] : 'f'),
1052 '#options' => array('t' => t('enabled'), 'f' => t('disabled')),
1053 '#description' => t('Should rich-text editing be enabled or disabled by default in textarea fields? If disabled, rich text editor may still be enabled using toggle or popup window.'),
1054 );
1055
1056 $form['fckeditor']['fckeditor_show_toggle'] = array(
1057 '#type' => 'select',
1058 '#title' => t('Show disable/enable rich text editor toggle'),
1059 '#default_value' => isset($user->fckeditor_show_toggle) ? $user->fckeditor_show_toggle : (isset($profile->settings['show_toggle']) ? $profile->settings['show_toggle'] : 't'),
1060 '#options' => array('t' => t('true'), 'f' => t('false')),
1061 '#description' => t('Whether or not to show the disable/enable rich text editor toggle below the textarea. Works only if FCKeditor is not running a popup window (see below).'),
1062 );
1063
1064 $form['fckeditor']['fckeditor_popup'] = array(
1065 '#type' => 'select',
1066 '#title' => t('Use FCKeditor in a popup window'),
1067 '#default_value' => isset($user->fckeditor_popup) ? $user->fckeditor_popup : (isset($profile->settings['popup']) ? $profile->settings['popup'] : 'f'),
1068 '#options' => array('f' => t('false'), 't' => t('true')),
1069 '#description' => t('If this option is enabled a link to a popup window will be used instead of a textarea replace.'),
1070 );
1071
1072 $form['fckeditor']['fckeditor_skin'] = array(
1073 '#type' => 'select',
1074 '#title' => t('Skin'),
1075 '#default_value' => isset($user->fckeditor_skin) ? $user->fckeditor_skin : (isset($profile->settings['skin']) ? $profile->settings['skin'] : 'default'),
1076 '#options' => $skin_options,
1077 '#description' => t('Choose a FCKeditor skin.'),
1078 );
1079
1080 $form['fckeditor']['fckeditor_toolbar'] = array(
1081 '#type' => 'select',
1082 '#title' => t('Toolbar'),
1083 '#default_value' => isset($user->fckeditor_toolbar) ? $user->fckeditor_toolbar : (isset($profile->settings['toolbar']) ? $profile->settings['toolbar'] : 'default'),
1084 '#options' => $toolbar_options,
1085 '#description' => t('Choose a FCKeditor toolbar set.'),
1086 );
1087
1088 $form['fckeditor']['fckeditor_expand'] = array(
1089 '#type' => 'select',
1090 '#title' => t('Start the toolbar expanded'),
1091 '#default_value' => isset($user->fckeditor_expand) ? $user->fckeditor_expand : (isset($profile->settings['expand']) ? $profile->settings['expand'] : 't'),
1092 '#options' => array('t' => t('enabled'), 'f' => t('disabled')),
1093 '#description' => t('The toolbar start expanded or collapsed.'),
1094 );
1095
1096 $form['fckeditor']['fckeditor_width'] = array(
1097 '#type' => 'textfield',
1098 '#title' => t('Width'),
1099 '#default_value' => isset($user->fckeditor_width) ? $user->fckeditor_width : (isset($profile->settings['width']) ? $profile->settings['width'] : '100%'),
1100 '#description' => t("Width in pixels or percent. Ex: 400 or 100%"),
1101 '#size' => 40,
1102 '#maxlength' => 128,
1103 );
1104
1105 $form['fckeditor']['fckeditor_lang'] = array(
1106 '#type' => 'select',
1107 '#title' => t('Language'),
1108 '#default_value' => isset($user->fckeditor_lang) ? $user->fckeditor_lang : (isset($profile->settings['lang']) ? $profile->settings['lang'] : 'en'),
1109 '#options' => $lang_options,
1110 '#description' => t('The language for the FCKeditor interface.')
1111 );
1112
1113 $form['fckeditor']['fckeditor_auto_lang'] = array(
1114 '#type' => 'select',
1115 '#title' => t('Auto-detect language'),
1116 '#default_value' => isset($user->fckeditor_auto_lang) ? $user->fckeditor_auto_lang : (isset($profile->settings['auto_lang']) ? $profile->settings['auto_lang'] : 't'),
1117 '#options' => array('t' => t('true'), 'f' => t('false')),
1118 '#description' => t('Use auto detect user language feature.')
1119 );
1120
1121 return array('fckeditor' => $form);
1122 }
1123 }
1124
1125 if ($type == 'validate') {
1126 if (isset($edit['fckeditor_default'], $edit['fckeditor_popup']) && $edit['fckeditor_default'] == 't' && $edit['fckeditor_popup'] == 't') {
1127 form_set_error('fckeditor_popup', t('If FCKeditor is enabled by default, popup window must be disabled.'));
1128 }
1129
1130 if (isset($edit['fckeditor_show_toggle'], $edit['fckeditor_popup']) && $edit['fckeditor_show_toggle'] == 't' && $edit['fckeditor_popup'] == 't') {
1131 form_set_error('fckeditor_popup', t('If toggle is enabled, popup window must be disabled.'));
1132 }
1133
1134 if (isset($edit['fckeditor_width']) && !preg_match('/^\d+%?$/', $edit['fckeditor_width'])) {
1135 form_set_error('fckeditor_width', t('Enter valid width. Example: 400 or 100%.'));
1136 }
1137 }
1138 }
1139
1140 /**
1141 * Return an HTML form for profile configuration.
1142 */
1143 function fckeditor_profile_form($edit) {
1144
1145 $output = drupal_get_form('fckeditor_profile_form_build', $edit);
1146
1147 return $output;
1148 }
1149
1150 /**
1151 * Return an HTML form for global profile configuration.
1152 */
1153 function fckeditor_global_profile_form($edit) {
1154
1155 $output = drupal_get_form('fckeditor_global_profile_form_build', $edit);
1156
1157 return $output;
1158 }
1159
1160 function fckeditor_load_toolbar_options() {
1161 $arr = array();
1162 $module_drupal_path = drupal_get_path('module', 'fckeditor');
1163 $fckconfig_js = $module_drupal_path .'/fckeditor/fckconfig.js';
1164 $fckeditor_config_js = $module_drupal_path .'/fckeditor.config.js';
1165 if (file_exists($fckconfig_js) && is_readable($fckconfig_js)) {
1166 $fp = @fopen($fckconfig_js, "r");
1167 if ($fp) {
1168 while (!feof($fp)) {
1169 $line = fgets($fp, 1024);
1170 if (preg_match("/FCKConfig\.ToolbarSets\[(\"|')(.*?)\\1\]/i", $line, $matches)) {
1171 $arr[$matches[2]] = ucfirst($matches[2]);
1172 }
1173 }
1174 fclose($fp);
1175 }
1176 }
1177 if (file_exists($fckeditor_config_js) && is_readable($fckeditor_config_js)) {
1178 $fp = @fopen($fckeditor_config_js, "r");
1179 if ($fp) {
1180 while (!feof($fp)) {
1181 $line = fgets($fp, 1024);
1182 if (preg_match("/FCKConfig\.ToolbarSets\[(\"|')(.*?)\\1\]/i", $line, $matches)) {
1183 $arr[$matches[2]] = ucfirst($matches[2]);
1184 }
1185 }
1186 fclose($fp);
1187 }
1188 }
1189
1190 //oops, we have no information about toolbars, let's use hardcoded array
1191 if (empty($arr)) {
1192 $arr = array(
1193 'Basic' => 'Basic',
1194 'Default' => 'Default',
1195 );
1196 }
1197 asort($arr);
1198
1199 return $arr;
1200 }
1201
1202 /**
1203 * Get an array of input formats that include HTML filter
1204 *
1205 * @return array
1206 */
1207 function fckeditor_html_filter_formats() {
1208 static $return;
1209
1210 if (isset($return)) {
1211 return $return;
1212 }
1213
1214 $return = array();
1215 $r = db_query("SELECT format FROM {filters} WHERE module = 'filter' AND delta = 0");
1216 while ($row = db_fetch_object($r)) {
1217 $return[] = $row->format;
1218 }
1219
1220 //Let other filter modules that call filter_xss() add more formats
1221 //that should be considered as implementing HTML filter by FCKeditor
1222 foreach (module_implements('implements_filter_xss') as $name) {
1223 $function = $name .'_implements_filter_xss';
1224 $result = $function();
1225 if (isset($result) && is_array($result)) {
1226 $return = array_merge($return, $result);
1227 }
1228 else if (isset($result)) {
1229 $return[] = $result;
1230 }
1231 }
1232
1233 return $return;
1234 }
1235
1236 function fckeditor_load_skin_options() {
1237 $arr = array();
1238 $module_drupal_path = drupal_get_path('module', 'fckeditor');
1239 $skin_dir = $module_drupal_path .'/fckeditor/editor/skins';
1240 if (is_dir($skin_dir)) {
1241 $dh = @opendir($skin_dir);
1242 if (FALSE !== $dh) {
1243 while (($file = readdir($dh)) !== FALSE ) {
1244 if (in_array($file, array(".", "..", "CVS", ".svn"))) {
1245 continue;
1246 }
1247 if (is_dir($skin_dir . DIRECTORY_SEPARATOR . $file)) {
1248 $arr[$file] = ucfirst($file);
1249 }
1250 }
1251 closedir( $dh );
1252 }
1253 }
1254
1255 //oops, we have no information about skins, let's use only default
1256 if (empty($arr)) {
1257 $arr = array(
1258 'default' => 'Default',
1259 );
1260 }
1261 asort($arr);
1262
1263 return $arr;
1264 }
1265
1266 function fckeditor_load_lang_options() {
1267 $arr = array();
1268 $module_drupal_path = drupal_get_path('module', 'fckeditor');
1269 $lang_dir = $module_drupal_path .'/fckeditor/editor/lang';
1270 if (is_dir($lang_dir)) {
1271 $dh = @opendir($lang_dir);
1272 if (false !== $dh ) {
1273 while (($file = readdir($dh)) !== FALSE) {
1274 if (in_array($file, array(".", "..", "CVS", ".svn"))) {
1275 continue;
1276 }
1277 if (is_file($lang_dir . DIRECTORY_SEPARATOR . $file) && preg_match("/^(.*?)\.js$/", $file, $matches)) {
1278 $lang = $matches[1];
1279 $arr[$lang] = strtoupper($lang);
1280 }
1281 }
1282 closedir( $dh );
1283 }
1284 }
1285
1286 //oops, we have no information about languages, let's use those available in FCKeditor 2.4.3
1287 if (empty($arr)) {
1288 $arr = array(
1289 'af' => 'Afrikaans',
1290 'ar' => 'Arabic',
1291 'bg' => 'Bulgarian',
1292 'bn' => 'Bengali/Bangla',
1293 'bs' => 'Bosnian',
1294 'ca' => 'Catalan',
1295 'cs' => 'Czech',
1296 'da' => 'Danish',
1297 'de' => 'German',
1298 'el' => 'Greek',
1299 'en' => 'English',
1300 'en-au' => 'English (Australia)',
1301 'en-ca' => 'English (Canadian)',
1302 'en-uk' => 'English (United Kingdom)',
1303 'eo' => 'Esperanto',
1304 'es' => 'Spanish',
1305 'et' => 'Estonian',
1306 'eu' => 'Basque',
1307 'fa' => 'Persian',
1308 'fi' => 'Finnish',
1309 'fo' => 'Faroese',
1310 'fr' => 'French',
1311 'gl' => 'Galician',
1312 'he' => 'Hebrew',
1313 'hi' => 'Hindi',
1314 'hr' => 'Croatian',
1315 'hu' => 'Hungarian',
1316 'it' => 'Italian',
1317 'ja' => 'Japanese',
1318 'km' => 'Khmer',
1319 'ko' => 'Korean',
1320 'lt' => 'Lithuanian',
1321 'lv' => 'Latvian',
1322 'mn' => 'Mongolian',
1323 'ms' => 'Malay',
1324 'nb' => 'Norwegian Bokmal',
1325 'nl' => 'Dutch',
1326 'no' => 'Norwegian',
1327 'pl' => 'Polish',
1328 'pt' => 'Portuguese (Portugal)',
1329 'pt-br' => 'Portuguese (Brazil)',
1330 'ro' => 'Romanian',
1331 'ru' => 'Russian',
1332 'sk' => 'Slovak',
1333 'sl' => 'Slovenian',
1334 'sr' => 'Serbian (Cyrillic)',
1335 'sr-latn' => 'Serbian (Latin)',
1336 'sv' => 'Swedish',
1337 'th' => 'Thai',
1338 'tr' => 'Turkish',
1339 'uk' => 'Ukrainian',
1340 'vi' => 'Vietnamese',
1341 'zh' => 'Chinese Traditional',
1342 'zh-cn' => 'Chinese Simplified',
1343 );
1344 }
1345
1346 asort($arr);
1347
1348 return $arr;
1349 }
1350
1351 /**
1352 * sort roles according to precedence settings. previously sorted roles are followed by latest added roles.
1353 */
1354 function fckeditor_sorted_roles() {
1355 static $order;
1356 if (isset($order)) {
1357 return $order;
1358 }
1359 $order = array();
1360 $roles = user_roles(0, 'access fckeditor');
1361
1362 $result = db_query("SELECT settings FROM {fckeditor_settings} WHERE name='FCKeditor Global Profile'");
1363 $data = db_fetch_object($result);
1364 if (!empty($data->settings)) {
1365 $settings = unserialize($data->settings);
1366 if (isset($settings['rank']) && !empty($settings['rank']))
1367 foreach ($settings['rank'] as $rid) {
1368 if (isset($roles[$rid])) {
1369 $order[$rid] = $roles[$rid];
1370 unset($roles[$rid]);
1371 }
1372 }
1373 }
1374 krsort($roles);//sort the remaining unsorted roles by id, descending.
1375 $order += $roles;
1376 return $order;
1377 }
1378
1379 function fckeditor_global_profile_form_build($sth, $edit) {
1380 $edit = (object) $edit;
1381
1382 if (arg(3) == 'addg') {
1383 drupal_set_breadcrumb(array(l(t('administer'), 'admin'), l(t('fckeditor'), 'admin/settings/fckeditor'), l(t('Add new FCKeditor Global Profile'), 'admin/settings/fckeditor/addg')));
1384
1385 $result = db_query("SELECT DISTINCT(rid) FROM {fckeditor_role} WHERE name='FCKeditor Global Profile'");
1386 $data = db_fetch_object($result);
1387
1388 if (!empty($data->rid)) {
1389 drupal_set_message(t("Global profile already exist. Only one global profile is allowed."), "error");
1390 return array();
1391 }
1392
1393 $btn = t('Create global profile');
1394 }
1395 else {
1396 $form['old_name'] = array('#type' => 'hidden', '#value' => $edit->name);
1397 $btn = t('Update global profile');
1398 }
1399
1400 $form['common'] = array(
1401 '#type' => 'fieldset',
1402 '#title' => t('Main setup'),
1403 '#collapsible' => TRUE,
1404 '#collapsed' => TRUE
1405 );
1406
1407 $roles = fckeditor_sorted_roles();
1408 $rids = $rtext = array();
1409 foreach ($roles as $rid => $name) {
1410 $rids[] = $rid;
1411 $rtext[] = '<strong>'. $rid .' - </strong>'. $name;
1412 }
1413 $form['common']['rank'] = array('#type' => 'textfield',
1414 '#title' => t('Role precedence'),
1415 '#default_value' => implode('>', $rids),
1416 '#description' => t('A user having <strong>multiple roles</strong> gets the permissions of the highest one. Sort role IDs according to their <strong>precedence from higher to lower</strong> by putting > in between.<br />'),
1417 );
1418 if ($rids) {
1419 $form['common']['rank']['#description'] .= t('Here is the id-name pairs of roles having access to FCKeditor:') .'<div>'. implode('<br />', $rtext) .'</div>';
1420 }
1421 else {
1422 $form['common']['rank']['#description'] .= t('You haven\'t assigned the <code>!access1</code> !permissions yet.', array('!access1' => t('access fckeditor'), '!permissions' => l(t('permissions'), 'admin/user/permissions')));
1423 }
1424
1425 $form['fckeditor_exclude_settings'] = array(
1426 '#type' => 'fieldset',
1427 '#title' => t('Visibility settings'),
1428 '#collapsible' => TRUE,
1429 '#collapsed' => TRUE,
1430 );
1431
1432 $form['fckeditor_exclude_settings']['excl_mode'] = array(
1433 '#type' => 'select',
1434 '#title' => t('Use inclusion or exclusion mode'),
1435 '#default_value' => (empty($edit->settings['excl_mode']) || in_array($edit->settings['excl_mode'], array(0, 2))) ? 0 : 1,
1436 '#options' => array('0' => t('exclude'), '1' => t('include')),
1437 '#description' => t('Choose the way of disabling/enabling FCKeditor on selected fields/paths (see below). Use exclude to disable FCKeditor on selected fields/paths. Use include if you want to load FCKeditor only on selected paths/fields.'),
1438 );
1439 /**
1440 * get excluded fields - so we can have normal textareas too
1441 * split the phrase by any number of commas or space characters,
1442 * which include " ", \r, \t, \n and \f
1443 */
1444 $form['fckeditor_exclude_settings']['excl_fields'] = array(
1445 '#type' => 'textarea',
1446 '#title' => t('Fields to exclude/include'),
1447 '#cols' => 60,
1448 '#rows' => 5,
1449 '#prefix' => '<div style="margin-left:20px">',
1450 '#suffix' => '</div>',
1451 '#default_value' => !empty($edit->settings['excl_fields']) ? $edit->settings['excl_fields'] : '',
1452 '#description' => t("Enter names (HTML ID's) of fields that may or may not have an FCKeditor, depending on the chosen option for the inclusion/exclusion mode.<br />You may separate the different entries by commas, spaces or newlines."),
1453 );
1454
1455 /**
1456 * get excluded paths - so we can have normal textareas too
1457 * split the phrase by any number of commas or space characters,
1458 * which include " ", \r, \t, \n and \f
1459 */
1460 $form['fckeditor_exclude_settings']['excl_paths'] = array(
1461 '#type' => 'textarea',
1462 '#title' => t('Paths to exclude/include'),
1463 '#prefix' => '<div style="margin-left:20px">',
1464 '#suffix' => '</div>',
1465 '#cols' => 60,
1466 '#rows' => 5,
1467 '#default_value' => !empty($edit->settings['excl_paths']) ? $edit->settings['excl_paths'] : '',
1468 '#description' => t("Enter drupal paths here, depending on the chosen option for the inclusion/exclusion mode.<br />Paths may be used the same way as in the drupal blocks configuration.<br />You may separate the different entries by commas, spaces or newlines. <br />You may also use * as a wildcard character (for example <code>comment/*</code>)."),
1469 );
1470
1471 $form['fckeditor_exclude_settings']['simple_incl_fields'] = array(
1472 '#type' => 'textarea',
1473 '#title' => t('Force simplified toolbar on the following fields'),
1474 '#cols' => 60,
1475 '#rows' => 5,
1476 '#default_value' => !empty($edit->settings['simple_incl_fields']) ? $edit->settings['simple_incl_fields'] : '',
1477 '#description' => t("Enter names (HTML ID's) of fields that should have the simplified toolbar.<br />If you don't want to use this feature, simply leave this field empty.<br />You may separate the different entries by commas, spaces or newlines."),
1478 );
1479
1480 $form['fckeditor_exclude_settings']['simple_incl_paths'] = array(
1481 '#type' => 'textarea',
1482 '#title' => t('Force simplified toolbar on the following paths'),
1483 '#cols' => 60,
1484 '#rows' => 5,
1485 '#default_value' => !empty($edit->settings['simple_incl_paths']) ? $edit->settings['simple_incl_paths'] : '',
1486 '#description' => t("Enter drupal paths that should have the simplified toolbar.<br />If you don't want to use this feature, simply leave this field empty.<br />Paths may be used the same way as in the drupal blocks configuration.<br />You may separate the different entries by commas, spaces or newlines.<br />You may also use * as a wildcard character (for example <code>comment/*</code>)."),
1487 );
1488
1489 $form['submit'] = array(
1490 '#type' => 'submit',
1491 '#value' => $btn
1492 );
1493
1494 return $form;
1495 }
1496
1497 /**
1498 * Return an HTML form for profile configuration.
1499 */
1500 function fckeditor_profile_form_build($sth, $edit) {
1501 $edit = (object) $edit;
1502
1503 $toolbar_options = fckeditor_load_toolbar_options();
1504 $skin_options = fckeditor_load_skin_options();
1505 $lang_options = fckeditor_load_lang_options();
1506
1507 // Only display the roles that currently don't have a fckeditor profile. One
1508 // profile per role.
1509 $orig_roles = user_roles(FALSE, 'access fckeditor');
1510 $roles = $orig_roles;
1511
1512 if (!empty($edit->rids) && !user_roles(false, 'access fckeditor')) {
1513 drupal_set_message(t('You haven\'t assigned <code>!access1</code> !permissions yet.<br/>It is recommended to assign the <code>!access1</code> !permissions before updating FCKeditor profiles.',
1514 array(
1515 '!access1' => t('access fckeditor'),
1516 '!permissions' => l(t('permissions'), 'admin/user/permissions'))), 'warning');
1517 }
1518
1519 if (arg(3) == 'add') {
1520 drupal_set_breadcrumb(array(l(t('administer'), 'admin'), l(t('fckeditor'), 'admin/settings/fckeditor'), l(t('Add new FCKeditor profile'), 'admin/settings/fckeditor/add')));
1521
1522 $result = db_query('SELECT DISTINCT(rid) FROM {fckeditor_role}');
1523 while ($data = db_fetch_object($result)) {
1524 if ((empty($edit->rids) || !in_array($data->rid, array_keys((array) $edit->rids))) && !form_get_errors()) {
1525 unset($roles[$data->rid]);
1526 }
1527 }
1528 if (count($orig_roles) != count($roles)) {
1529 drupal_set_message(t('Not all user roles are shown since they already have fckeditor profiles. You must first unassign profiles in order to add them to a new one.'));
1530 }
1531 $btn = t('Create profile');
1532 }
1533 else {
1534 $form['old_name'] = array('#type' => 'hidden', '#value' => $edit->name);
1535 $btn = t('Update profile');
1536 }
1537
1538 $form['basic'] = array(
1539 '#type' => 'fieldset',
1540 '#title' => t('Basic setup'),
1541 '#collapsible' => TRUE,
1542 '#collapsed' => TRUE
1543 );
1544
1545 $form['basic']['name'] = array(
1546 '#type' => 'textfield',
1547 '#title' => t('Profile name'),
1548 '#default_value' => !empty($edit->name) ? $edit->name : "",
1549 '#size' => 40,
1550 '#maxlength' => 128,
1551 '#description' => t('Enter a name for this profile. This name is only visible within the fckeditor administration page.'),
1552 '#required' => TRUE
1553 );
1554
1555 $form['basic']['rids'] = array(
1556 '#type' => 'checkboxes',
1557 '#title' => t('Roles allowed to use this profile'),
1558 '#default_value' => !empty($edit->rids) ? array_keys((array) $edit->rids) : array(),
1559 '#options' => $roles,
1560 '#description' => t('Only roles with \'!access1\' permission will be shown here. If no role is available, make sure that you have assigned the \'!access1\' !permission.', array('!access1' => t('access fckeditor'), '!permission' => l(t("permission"), "admin/user/permissions"))),
1561 '#required' => TRUE
1562 );
1563
1564 $form['basic']['allow_user_conf'] = array(
1565 '#type' => 'select',
1566 '#title' => t('Allow users to customize FCKeditor appearance'),
1567 '#default_value' => !empty($edit->settings['allow_user_conf']) ? $edit->settings['allow_user_conf'] : 'f',
1568 '#options' => array('f' => t('false'), 't' => t('true')),
1569 '#description' => t('If allowed, users will be able to override <code>Editor appearance</code> by visiting their profile page.'),
1570 );
1571
1572 $form['security'] = array(
1573 '#type' => 'fieldset',
1574 '#title' => t('Security'),
1575 '#description' => t('Because Drupal stores content in an unmodified form, it is necessary to perform additional security checks before passing any unsafe value to FCKeditor. Without XSS filters, when you decide to edit an article which contains malformed data, any malicious code will be executed by your browser. You don\'t need to turn those filters on for users that are editing only their own content. However, unless you trust all your users, it is strongly advised to enable XSS filters for all accounts that can edit other\'s nodes (e.g. administrators, moderators).'),
1576 '#collapsible' => TRUE,
1577 '#collapsed' => TRUE
1578 );
1579
1580 $form['security']['xss'] = array(
1581 '#type' => 'radios',
1582 '#title' => t('XSS filter'),
1583 '#default_value' => isset($edit->settings['xss']) ? $edit->settings['xss'] : '1',
1584 '#options' => array(
1585 '2' => t('Enable XSS filter on all textareas replaced by FCKeditor'),
1586 '1' => t('Enable XSS filter only on textareas associated with HTML filter'),
1587 '0' => t('Disable XSS filter'),
1588 ),
1589 '#description' => t('Choose on which textareas FCKeditor should call a XSS filter before displaying it. Note that original user\'s data will be altered to protect you against hacking attempts. If you decide to disable XSS filter completely, it is advised to disable FCKeditor by default and manually check if HTML code is secure before switching to FCKeditor.'),
1590 );
1591
1592 $form['security']['xss_t'] = array(
1593 '#type' => 'radios',
1594 '#title' => t('XSS filter settings'),
1595 '#default_value' => isset($edit->settings['xss_t']) ? $edit->settings['xss_t'] : '1',
1596 '#options' => array(
1597 '2' => t('Always run XSS filter when FCKeditor is loaded for the first time.'),
1598 '1' => t('Run XSS filter only when FCKeditor starts automatically.'),
1599 ),
1600 '#description' => t('Applies only if XSS filter is enabled above. If you decide to run XSS filter when FCKeditor starts automatically, you\'ll not be protected when toggling manually from plain textarea to FCKeditor or when using FCKeditor in a popup mode. You may choose this option if you can detect XSS by yourself just by looking at the HTML code.'),
1601 );
1602
1603 $form['fckeditor_exclude_settings'] = array(
1604 '#type' => 'fieldset',
1605 '#title' => t('Visibility settings'),
1606 '#collapsible' => TRUE,
1607 '#collapsed' => TRUE,
1608 );
1609
1610 $form['fckeditor_exclude_settings']['min_rows'] = array(
1611 '#type' => 'textfield',
1612 '#title' => t('Minimum rows'),
1613 '#default_value' => !empty($edit->settings['min_rows']) ? $edit->settings['min_rows'] : '5',
1614 '#description' => t("FCKeditor will be triggered if the textarea has more rows than entered here. Enter '1' if you do not want to use this feature."),
1615 );
1616
1617 $form['fckeditor_exclude_settings']['excl_mode'] = array(
1618 '#type' => 'select',
1619 '#title' => t('Use inclusion or exclusion mode'),
1620 '#default_value' => (empty($edit->settings['excl_mode']) || in_array($edit->settings['excl_mode'], array(0, 2))) ? 0 : 1,
1621 '#options' => array('0' => t('exclude'), '1' => t('include')),
1622 '#description' => t('Choose the way of disabling/enabling FCKeditor on selected fields/paths (see below). Use exclude to disable FCKeditor on selected fields/paths. Use include if you want to load FCKeditor only on selected paths/fields.'),
1623 );
1624
1625 /**
1626 * get excluded fields - so we can have normal textareas too
1627 * split the phrase by any number of commas or space characters,
1628 * which include " ", \r, \t, \n and \f
1629 */
1630 $form['fckeditor_exclude_settings']['excl_fields'] = array(
1631 '#type' => 'textarea',
1632 '#title' => t('Fields to exclude/include'),
1633 '#cols' => 60,
1634 '#rows' => 5,
1635 '#prefix' => '<div style="margin-left:20px">',
1636 '#suffix' => '</div>',
1637 '#default_value' => !empty($edit->settings['excl_fields']) ? $edit->settings['excl_fields'] : '',
1638 '#description' => t("Enter names (HTML ID's) of fields that may or may not have an FCKeditor, depending on the chosen option for the inclusion/exclusion mode.<br />You may separate the different entries by commas, spaces or newlines."),
1639 );
1640
1641 /**
1642 * get excluded paths - so we can have normal textareas too
1643 * split the phrase by any number of commas or space characters,
1644 * which include " ", \r, \t, \n and \f
1645 */
1646 $form['fckeditor_exclude_settings']['excl_paths'] = array(
1647 '#type' => 'textarea',
1648 '#title' => t('Paths to exclude/include'),
1649 '#prefix' => '<div style="margin-left:20px">',
1650 '#suffix' => '</div>',
1651 '#cols' => 60,
1652 '#rows' => 5,
1653 '#default_value' => !empty($edit->settings['excl_paths']) ? $edit->settings['excl_paths'] : '',
1654 '#description' => t("Enter drupal paths here, depending on the chosen option for the inclusion/exclusion mode.<br />Paths may be used the same way as in the drupal blocks configuration.<br />You may separate the different entries by commas, spaces or newlines. <br />You may also use * as a wildcard character (for example <code>comment/*</code>)."),
1655 );
1656
1657 $form['fckeditor_exclude_settings']['simple_incl_fields'] = array(
1658 '#type' => 'textarea',
1659 '#title' => t('Force simplified toolbar on the following fields'),
1660 '#cols' => 60,
1661 '#rows' => 5,
1662 //'#prefix' => t('Here you can define where FCKeditor should force the <code>!simple</code> toolbar.<br/>Useful for smaller textareas where we usually don\'t use very complicated HTML code, like in signatures.', array('!simple' => FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME)),
1663 '#default_value' => !empty($edit->settings['simple_incl_fields']) ? $edit->settings['simple_incl_fields'] : '',
1664 '#description' => t("Enter names (HTML ID's) of fields that should have the simplified toolbar.<br />If you don't want to use this feature, simply leave this field empty.<br />You may separate the different entries by commas, spaces or newlines."),
1665 );
1666
1667 $form['fckeditor_exclude_settings']['simple_incl_paths'] = array(
1668 '#type' => 'textarea',
1669 '#title' => t('Force simplified toolbar on the following paths'),
1670 '#cols' => 60,
1671 '#rows' => 5,
1672 //'#prefix' => t('Here you can define where FCKeditor should force the <code>!simple</code> toolbar.<br/>Useful for smaller textareas where we usually don\'t use very complicated HTML code, like in signatures.', array('!simple' => FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME)),
1673 '#default_value' => !empty($edit->settings['simple_incl_paths']) ? $edit->settings['simple_incl_paths'] : '',
1674 '#description' => t("Enter drupal paths that should have the simplified toolbar.<br />If you don't want to use this feature, simply leave this field empty.<br />Paths may be used the same way as in the drupal blocks configuration.<br />You may separate the different entries by commas, spaces or newlines.<br />You may also use * as a wildcard character (for example <code>comment/*</code>)."),
1675 );
1676
1677 $form['appearance'] = array(
1678 '#type' => 'fieldset',
1679 '#title' => t('Editor appearance'),
1680 '#collapsible' => TRUE,
1681 '#collapsed' => TRUE,
1682 );
1683
1684 $form['appearance']['default'] = array(
1685 '#type' => 'select',
1686 '#title' => t('Default state'),
1687 '#default_value' => !empty($edit->settings['default']) ? $edit->settings['default'] : 't',
1688 '#options' => array('t' => t('enabled'), 'f' => t('disabled')),
1689 '#description' => t('Default editor state. If disabled, rich text editor may still be enabled using toggle or popup window.'),
1690 );
1691
1692 $form['appearance']['show_toggle'] = array(
1693 '#type' => 'select',
1694 '#title' => t('Show disable/enable rich text editor toggle'),
1695 '#default_value' => !empty($edit->settings['show_toggle']) ? $edit->settings['show_toggle'] : 't',
1696 '#options' => array('t' => t('true'), 'f' => t('false')),
1697 '#description' => t('Whether or not to show the disable/enable rich text editor toggle below the textarea. Works only if FCKeditor is not running in a popup window (see below).'),
1698 );
1699
1700 $form['appearance']['popup'] = array(
1701 '#type' => 'select',
1702 '#title' => t('Use FCKeditor in a popup window'),
1703 '#default_value' => !empty($edit->settings['popup']) ? $edit->settings['popup'] : 'f',
1704 '#options' => array('f' => t('false'), 't' => t('true')),
1705 '#description' => t('If this option is enabled a link to a popup window will be used instead of a textarea replace.'),
1706 );
1707
1708 $form['appearance']['skin'] = array(
1709 '#type' => 'select',
1710 '#title' => t('Skin'),
1711 '#default_value' => !empty($edit->settings['skin']) ? $edit->settings['skin'] : 'default',
1712 '#options' => $skin_options,
1713 '#description' => t('Choose a default skin.'),
1714 );
1715
1716 $form['appearance']['toolbar'] = array(
1717 '#type' => 'select',
1718 '#title' => t('Toolbar'),
1719 '#default_value' => !empty($edit->settings['toolbar']) ? $edit->settings['toolbar'] : 'default',
1720 '#options' => $toolbar_options,
1721 '#description' => t('Choose a default toolbar set. To define new toolbar, edit <code>fckeditor.config.js</code> located in !module_path.', array('!module_path' => drupal_get_path('module', 'fckeditor'))),
1722 );
1723
1724 $form['appearance']['expand'] = array(
1725 '#type' => 'select',
1726 '#title' => t('Start the toolbar expanded'),
1727 '#default_value' => !empty($edit->settings['expand']) ? $edit->settings['expand'] : 't',
1728 '#options' => array('t' => t('enabled'), 'f' => t('disabled')),
1729 '#description' => t('The toolbar start expanded or collapsed.'),
1730 );
1731
1732 $form['appearance']['width'] = array(
1733 '#type' => 'textfield',
1734 '#title' => t('Width'),
1735 '#default_value' => !empty($edit->settings['width']) ? $edit->settings['width'] : '100%',
1736 '#description' => t("Width in pixels or percent. Ex: 400 or 100%"),
1737 '#size' => 40,
1738 '#maxlength' => 128,
1739 );
1740
1741 $form['appearance']['lang'] = array(
1742 '#type' => 'select',
1743 '#title' => t('Language'),
1744 '#default_value' => !empty($edit->settings['lang']) ? $edit->settings['lang'] : 'en',
1745 '#options' => $lang_options,
1746 '#description' => t('The language for the FCKeditor interface.')
1747 );
1748
1749 $form['appearance']['auto_lang'] = array(
1750 '#type' => 'select',
1751 '#title' => t('Auto-detect language'),
1752 '#default_value' => !empty($edit->settings['auto_lang']) ? $edit->settings['auto_lang'] : 't',
1753 '#options' => array('t' => t('true'), 'f' => t('false')),
1754 '#description' => t('Use auto detect user language feature.')
1755 );
1756
1757 /*
1758 $form['appearance']['appearance_conf'] = array(
1759 '#type' => 'select',
1760 '#title' => t('Ignore this section, use default settings defined in config files'),
1761 '#default_value' => $edit->settings['appearance_conf'] ? $edit->settings['appearance_conf'] : 'f',
1762 '#options' => array('f' => t('false'), 't' => t('true')),
1763 '#description' => t('Although it is less handy, defining settings only in config files (<code>fckconfig.js</code> and <code>fckeditor.config.js</code>) will slightly leverage your traffic and improve load time of your site. <br/>Warning: if set to true, all changes made in <code>Editor appearance</code> will have no affect on FCKeditor\'s behaviour.'),
1764 );
1765 */
1766
1767 $form['output'] = array(
1768 '#type' => 'fieldset',
1769 '#title' => t('Cleanup and output'),
1770 '#collapsible' => TRUE,
1771 '#collapsed' => TRUE,
1772 );
1773
1774 $form['output']['enter_mode'] = array(
1775 '#type' => 'select',
1776 '#title' => t('Enter mode'),
1777 '#default_value' => !empty($edit->settings['enter_mode']) ? $edit->settings['enter_mode'] : 'p',
1778 '#options' => array('p' => '<p>', 'br' => '<br>', 'div' => '<div>'),
1779 '#description' => t('Set which tag FCKeditor should use when [Enter] key is pressed.')
1780 );
1781
1782 $form['output']['shift_enter_mode'] = array(
1783 '#type' => 'select',
1784 '#title' => t('Shift + Enter mode'),
1785 '#default_value' => !empty($edit->settings['shift_enter_mode']) ? $edit->settings['shift_enter_mode'] : 'br',
1786 '#options' => array('p' => '<p>', 'br' => '<br>', 'div' => '<div>'),
1787 '#description' => t('Set which tag FCKeditor should use when [Shift] + [Enter] is pressed.')
1788 );
1789
1790 $form['output']['font_format'] = array(
1791 '#type' => 'textfield',
1792 '#title' => t('Font formats'),
1793 '#default_value' => !empty($edit->settings['font_format']) ? $edit->settings['font_format'] : 'p;div;pre;address;h1;h2;h3;h4;h5;h6',
1794 '#size' => 40,
1795 '#maxlength' => 250,
1796 '#description' => t('Semicolon separated list of HTML font formats. Allowed values are: p;div;pre;address;h1;h2;h3;h4;h5;h6'),
1797 );
1798
1799 $form['output']['format_source'] = array(
1800 '#type' => 'select',
1801 '#title' => t('Apply source formatting'),
1802 '#default_value' => !empty($edit->settings['format_source']) ? $edit->settings['format_source'] : 't',
1803 '#options' => array('t' => t('true'), 'f' => ('false')),
1804 '#description' => t('When set to "true" the editor will format the XHTML when switching from WYSIWYG view to Source view, by inserting line breaks on some tags endings and indenting paragraphs, tables and lists.'),
1805 );
1806
1807 $form['output']['format_output'] = array(
1808 '#type' => 'select',
1809 '#title' => t('Format output'),
1810 '#default_value' => !empty($edit->settings['format_output']) ? $edit->settings['format_output'] : 't',
1811 '#options' => array('t' => t('true'), 'f' => t('false')),
1812 '#description' => t('When set to "true" the editor will format the XHTML output by inserting line breaks on some tags endings and indenting paragraphs, tables and lists.'),
1813 );
1814
1815 /*
1816 $form['output']['output_conf'] = array(
1817 '#type' => 'select',
1818 '#title' => t('Ignore this section, use default settings defined in config files'),
1819 '#default_value' => $edit->settings['output_conf'] ? $edit->settings['output_conf'] : 'f',
1820 '#options' => array('f' => t('false'), 't' => t('true')),
1821 '#description' => t('Although it is less handy, defining settings only in config files (<code>fckconfig.js</code> and <code>fckeditor.config.js</code>) will slightly leverage your traffic and improve load time of your site. <br/>Warning: if set to true, all changes made in <code>Cleanup and output</code> will have no affect on FCKeditor\'s behaviour.'),
1822 );
1823 */
1824
1825 $form['css'] = array(
1826 '#type' => 'fieldset',
1827 '#title' => t('CSS'),
1828 '#collapsible' => TRUE,
1829 '#collapsed' => TRUE
1830 );
1831
1832 $form['css']['css_mode'] = array(
1833 '#type' => 'select',
1834 '#title' => t('Editor CSS'),
1835 '#default_value' => !empty($edit->settings['css_mode']) ? $edit->settings['css_mode'] : 'theme',
1836 '#options' => array('theme' => t('use theme css'), 'self' => t('define css'), 'none' => t('FCKeditor default')),
1837 '#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 />FCKeditor default - uses default CSS from editor.')
1838 );
1839
1840 $form['css']['css_path'] = array(
1841 '#type' => 'textfield',
1842 '#title' => t('CSS path'),
1843 '#default_value' => !empty($edit->settings['css_path']) ? $edit->settings['css_path'] : "",
1844 '#size' => 40,
1845 '#maxlength' => 255,
1846 '#description' => t('Enter path to CSS file (<em>example: css/editor.css</em>) or a list of css files seperated by a comma (<em>example: /themes/garland/style.css,http://example.com/style.css</em>).<br />Macros: %h (host name: !host), %t (path to theme: !theme)<br />Be sure to select "define css" above.', array('!host' => base_path(), '!theme' => base_path() . path_to_theme() .'/'))
1847 );
1848
1849 $form['css']['css_style'] = array(
1850 '#type' => 'select',
1851 '#title' => t('Predefined styles'),
1852 '#default_value' => !empty($edit->settings['css_style']) ? $edit->settings['css_style'] : 'theme',
1853 '#options' => array('theme' => t('use theme fckstyles.xml'), 'self' => t('define path to fckstyles.xml'), 'default' => t('FCKeditor default')),
1854 '#description' => t('Define the location of <code>fckstyles.xml</code> file. It is used by the &quot;Style&quot; dropdown list available in the Default toolbar.<br />Copy !fckstyles.xml inside your theme directory (<code>!theme</code>) and adjust it to your needs.', array('!fckstyles.xml' => drupal_get_path('module', 'fckeditor') .'/fckeditor/fckstyles.xml', '!theme' => path_to_theme() .'/fckstyles.xml'))
1855 );
1856
1857 $form['css']['styles_path'] = array(
1858 '#type' => 'textfield',
1859 '#title' => t('Predefined styles path'),
1860 '#default_value' => !empty($edit->settings['styles_path']) ? $edit->settings['styles_path'] : "",
1861 '#size' => 40,
1862 '#maxlength' => 255,
1863 '#description' => t('Enter path to XML file with predefined styles (<em>example: /fckstyles.xml</em>).<br />Macros: %h (host name: !host), %t (path to theme: !theme), %m (path to FCKeditor module: !module)<br />Be sure to select "define path to fckstyles.xml" above.', array('!host' => base_path(), '!theme' => base_path() . path_to_theme() .'/', '!module' => drupal_get_path('module', 'fckeditor')))
1864 );
1865
1866 $form['fckeditor_upload_settings'] = array(
1867 '#type' => 'fieldset',
1868 '#title' => t('File browser settings'),
1869 '#collapsible' => TRUE,
1870 '#collapsed' => TRUE,
1871 '#description' => t('Set file browser settings. A file browser will allow you to explore the files contained on the server and embed them as links, images or flash movies. Besides the built-in FCKeditor file browser, you can also use a contributed module like !imce. The quick upload setting controls whether images, flash movies and files can be uploaded using the Upload tab of the respective dialogs. Please note that these options require manual configuration, check !readme for more information.<br />',
1872 array(
1873 '!imce' => l(t('IMCE'), 'http://drupal.org/project/imce'),
1874 '!readme' => l('readme.txt', 'admin/help/fckeditor'),
1875 )
1876 )
1877 );
1878
1879 $filebrowsers = array(
1880 'none' => t('None'),
1881 'builtin' => t('Built-in filebrowser'),
1882 );
1883
1884 if (module_exists('imce')) {
1885 $filebrowsers['imce'] = t('IMCE');
1886 }
1887
1888 $form['fckeditor_upload_settings']['filebrowser'] = array(
1889 '#type' => 'select',
1890 '#options' => $filebrowsers,
1891 '#title' => t('File browser type'),
1892 '#default_value' => !empty($edit->settings['filebrowser']) ? $edit->settings['filebrowser'] : 'none',
1893 '#description' => t('Select the file browser that you would like to use to upload files, images and flash movies.'),
1894 );
1895
1896 $form['fckeditor_upload_settings']['quickupload'] = array(
1897 '#type' => 'select',
1898 '#options' => array('f' => t('false'), 't' => t('true')),
1899 '#title' => t('Allow quick uploads'),
1900 '#default_value' => !empty($edit->settings['quickupload']) ? $edit->settings['quickupload'] : 'f',
1901 '#description' => t('The quick upload functionality can be disabled and enabled independently of the file browser. It will always use the settings below.'),
1902 );
1903
1904 $current_user_files_path = empty($edit->settings['UserFilesPath']) ? "" : strtr($edit->settings['UserFilesPath'], array("%f" => file_directory_path(), "%u" => "UID", "%b" => base_path()));
1905 $current_user_files_absolute_path = empty($edit->settings['UserFilesAbsolutePath']) ? "" : strtr($edit->settings['UserFilesAbsolutePath'], array("%f" => file_directory_path(), "%u" => "UID", "%b" => base_path(), "%d" => $_SERVER['DOCUMENT_ROOT']));
1906
1907 $form['fckeditor_upload_settings']['UserFilesPath'] = array(
1908 '#type' => 'textfield',
1909 '#title' => t('Path to uploaded files'),
1910 '#default_value' => !empty($edit->settings['UserFilesPath']) ? $edit->settings['UserFilesPath'] : "%b%f/",
1911 '#size' => 40,
1912 '#maxlength' => 255,
1913 '#description' => t('Path to uploaded files relative to the document root.<br />Available wildcard characters:<br/><strong>%b</strong> - base URL path of the Drupal installation (!base).<br/><strong>%f</strong> - Drupal file system path where the files are stored (!files).<br /><strong>%u</strong> - User ID.<br />Current path: !path', array('!path' => $current_user_files_path, '!files' => file_directory_path(), '!base' => base_path())),
1914 );
1915
1916 $form['fckeditor_upload_settings']['UserFilesAbsolutePath'] = array(
1917 '#type' => 'textfield',
1918 '#title' => t('Absolute path to uploaded files'),
1919 '#default_value' => !empty($edit->settings['UserFilesAbsolutePath']) ? $edit->settings['UserFilesAbsolutePath'] : "%d%b%f/",
1920 '#size' => 40,
1921 '#maxlength' => 255,
1922 '#description' => t('The path to the local directory (in the server) which points to the path defined above. If empty, FCKeditor will try to discover the right path.<br />Available wildcard characters:<br/><strong>%d</strong> - server path to document root (!root).<br /><strong>%b</strong> - base URL path of the Drupal installation (!base).<br/><strong>%f</strong> - Drupal file system path where the files are stored (!files).<br /><strong>%u</strong> - User ID.<br />Current path: !path', array('!path' => $current_user_files_absolute_path, '!files' => file_directory_path(), '!base' => base_path(), '!root' => $_SERVER['DOCUMENT_ROOT'])),
1923 );
1924
1925 if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) {
1926 $form['fckeditor_upload_settings']['UserFilesPath']['#description'] = t('Setting relative path to uploaded files has been disabled because private downloads are enabled and this path is calculated automatically.');
1927 $form['fckeditor_upload_settings']['UserFilesPath']['#disabled'] = TRUE;
1928 $form['fckeditor_upload_settings']['UserFilesAbsolutePath']['#description'] = t('Setting path to uploaded files has been disabled because private downloads are enabled and this path is calculated automatically.');
1929 $form['fckeditor_upload_settings']['UserFilesAbsolutePath']['#disabled'] = TRUE;
1930 }
1931
1932 $form['advanced'] = array(
1933 '#type' => 'fieldset',
1934 '#title' => t('Advanced options'),
1935 '#collapsible' => TRUE,
1936 '#collapsed' => TRUE,
1937 );
1938 $form['advanced']['js_conf'] = array(
1939 '#type' => 'textarea',
1940 '#title' => t('Custom javascript configuration'),
1941 '#default_value' => !empty($edit->settings['js_conf']) ? $edit->settings['js_conf'] : "",
1942 '#cols' => 60,
1943 '#rows' => 5,
1944 '#description' => t('Warning: to change FCKeditor configuration globally, you should modify the config file: <code>!fckeditor_config</code>.<br/>Sometimes it is required to change the FCKeditor configuration for selected profile. Use this box to define settings that are uniqe for this profile.<br/>Available options are listed in the !docs.<br/>Warning: if you make something wrong here, FCKeditor may fail to load.<br/>For example to disable some advanced tabs in dialog windows in FCKeditor, add the following: !example',
1945 array(
1946 '!fckeditor_config' => drupal_get_path('module', 'fckeditor') ."/fckeditor.config.js",
1947 '!docs' => l(t("FCKeditor documentation"), "http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options"),
1948 "!example" => "<pre>LinkDlgHideTarget = true ;
1949 LinkDlgHideAdvanced = true ;
1950 ImageDlgHideLink = true ;
1951 ImageDlgHideAdvanced = true ;
1952 FlashDlgHideAdvanced = true ;</pre>")
1953 ));
1954
1955 $form['submit'] = array(
1956 '#type' => 'submit',
1957 '#value' => $btn
1958 );
1959
1960 return $form;
1961 }
1962
1963 /**
1964 * Search the field id for matches in array of matches
1965 *
1966 * @param $search
1967 * A string representing a form field id
1968 * @ param $array
1969 * An $array with strings to match the $search parameter against
1970 *
1971 * @return
1972 * TRUE on match, FALSE on no match
1973 */
1974 function fckeditor_idsearch($search, $array) {
1975 foreach ($array as $key => $value) {
1976 if (!empty($value) && preg_match('/^'. str_replace('*', '.*', addslashes($value)) .'$/i', $search)) {
1977 // on any first match we know we're done here so return positive
1978 return TRUE;
1979 }
1980 }
1981 return FALSE;
1982 }
1983
1984 /**
1985 * Test if client can render the FCKeditor
1986 * Use built-in test method in fckeditor.php
1987 * If fckeditor.php is not found, return false (probably in such case fckeditor is not installed correctly)
1988 *
1989 * @return
1990 * TRUE if the browser is reasonably capable
1991 */
1992 function fckeditor_is_compatible_client() {
1993
1994 $fckeditor_main_file = drupal_get_path('module', 'fckeditor') .'/fckeditor/fckeditor.php';
1995 if (file_exists($fckeditor_main_file)) {
1996 include_once $fckeditor_main_file;
1997 if (function_exists('FCKeditor_IsCompatibleBrowser')) {
1998 return FCKeditor_IsCompatibleBrowser();
1999 }
2000 else {
2001 $fck = new FCKeditor("fake");
2002 return $fck->IsCompatible();
2003 }
2004 }
2005
2006 return FALSE;
2007 }
2008
2009 function fckeditor_user_get_setting($user, $profile, $setting) {
2010 $default = array(
2011 'default' => 't',
2012 'show_toggle' => 't',
2013 'popup' => 'f',
2014 'skin' => 'default',
2015 'toolbar' => 'default',
2016 'expand' => 't',
2017 'width' => '100%',
2018 'lang' => 'en',
2019 'auto_lang' => 't',
2020 );
2021 $settings = $profile->settings;
2022
2023 if ($settings['allow_user_conf']) {
2024 $status = isset($user->{"fckeditor_". $setting}) ? $user->{"fckeditor_". $setting} : (isset($settings[$setting]) ? $settings[$setting] : $default[$setting]);
2025 }
2026 else {
2027 $status = isset($settings[$setting]) ? $settings[$setting] : $default[$setting];
2028 }
2029
2030 return $status;
2031 }
2032
2033 function fckeditor_user_get_profile($user) {
2034 static $profile_name;
2035
2036 // Since fckeditor_profile_load() makes a db hit, only call it when we're pretty sure
2037 // we're gonna render fckeditor.
2038 if (!isset($profile_name[$user->uid])) {
2039 $sorted_roles = fckeditor_sorted_roles();
2040 foreach ($sorted_roles as $rid => $name) {
2041 if (isset($user->roles[$rid])) {
2042 break;
2043 }
2044 }
2045
2046 if (isset($rid) && isset($user->roles[$rid])) {
2047 $profile_name[$user->uid] = db_result(db_query("SELECT s.name FROM {fckeditor_settings} s INNER JOIN {fckeditor_role} r ON r.name = s.name WHERE r.rid='%s'", $rid));
2048 }
2049 else if ($user->uid == "1") {
2050 $profile_name[$user->uid] = db_result(db_query_range("SELECT s.name FROM {fckeditor_settings} s INNER JOIN {fckeditor_role} r ON r.name = s.name ORDER BY r.rid DESC", 1));
2051 }
2052 }
2053
2054 if (isset($profile_name[$user->uid]) && $profile_name[$user->uid]) {
2055 $profile = fckeditor_profile_load($profile_name[$user->uid]);
2056 return $profile;
2057 }
2058
2059 return FALSE;
2060 }
2061
2062 function fckeditor_init() {
2063 drupal_add_css(drupal_get_path('module', 'fckeditor') .'/fckeditor.css');
2064 }
2065
2066 function fckeditor_ask_delete_confirmation($is_global, $profile = "") {
2067 if (!$is_global) {
2068 $delete_link = l(t('Yes, delete!'), 'admin/settings/fckeditor/deleteconfirmed/'. urlencode($profile));
2069 $profile_name = t('!profile profile', array('!profile' => $profile));
2070 }
2071 else {
2072 $delete_link = l(t('Yes, delete!'), 'admin/settings/fckeditor/deletegconfirmed');
2073 $profile_name = t('Global Profile');
2074 }
2075
2076 drupal_set_title(t('Confirm profile deletion'));
2077 drupal_set_message(t("You're about to delete the FCKeditor profile, read the question below carefully."), "warning");
2078
2079 return t("<p>Are you sure that you want to delete the !profile?</p><p>!yes !no</p>",
2080 array('!profile' => $profile_name,
2081 '!yes' => $delete_link,
2082 '!no' => l(t('Cancel'), 'admin/settings/fckeditor', array('attributes' => array('style' => 'margin-left:40px'))),
2083 ));
2084 }
2085
2086 /**
2087 * Implementation of hook_file_download().
2088 * Support for private downloads.
2089 */
2090 function fckeditor_file_download($file) {
2091 if (module_exists('imce') && variable_get('imce_settings_fck', 0)) {
2092 return NULL;
2093 }
2094 if (module_exists('upload')) {
2095 $result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $file);
2096 if (db_fetch_object($result)) {
2097 return NULL;
2098 }
2099 }
2100 //it is probably file uploaded with FCKeditor
2101 if ($path = file_create_path($file)) {
2102 $ctype = ($info = @getimagesize($path)) ? $info['mime'] : (function_exists('mime_content_type') ? mime_content_type($path) : 'application/x-download');
2103 return array('Content-type: '. $ctype);
2104 }
2105
2106 return -1;
2107 }