| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: ckeditor.admin.inc,v 1.2.2.19 2010/09/15 15:23:36 wwalc Exp $ 3 /** 4 * CKEditor - The text editor for Internet - http://ckeditor.com 5 * Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 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 * CKEditor Module for Drupal 6.x 25 * 26 * This module allows Drupal to replace textarea fields with CKEditor. 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 * Main administrative page 35 */ 36 function ckeditor_admin_main() { 37 module_load_include('inc', 'ckeditor', 'includes/ckeditor.lib'); 38 $editor_path = ckeditor_path(TRUE); 39 $ckconfig_file = $editor_path .'/config.js'; 40 if (!_ckeditor_requirements_isinstalled()) { 41 drupal_set_message(t('Checking for %filename or %file.', array('%filename' => $ckconfig_file, '%file' => 'sites/all/libraries/ckeditor/ckeditor.js'))); 42 drupal_set_message(t('The CKEditor component is not installed correctly. Please go to the <a href="!ckeditorlink">CKEditor homepage</a> to download the latest version. After that you must extract the files to %ckeditorpath or %librarypath and make sure that the file %ckeditorfile or %ckeditorlibrary exist. Refer to the <a href="!readme">readme.txt</a> for more information.', array('!ckeditorlink' => url('http://ckeditor.com/download'), '!readme' => url('admin/help/ckeditor'), '%ckeditorpath' => $editor_path, '%ckeditorsubdir' => $editor_path .'/editor', '%ckeditorfile' => $editor_path .'/ckeditor.js', '%ckeditorlibrary' => 'sites/all/libraries/ckeditor/ckeditor.js', '%librarypath' => 'sites/all/libraries/ckeditor')), 'error'); 43 drupal_set_message(t('If you have CKEditor already installed please edit <a href="!editg">CKEditor Global Profile</a> and update CKEditor path.', array('!editg' => url('admin/settings/ckeditor/editg'))), 'warning'); 44 return FALSE; 45 } 46 47 $access_ckeditor_roles = user_roles(FALSE, 'access ckeditor'); 48 if (!$access_ckeditor_roles) { 49 drupal_set_message(t('There is currently no role with the "access ckeditor" permission. Visit <a href="!acl">permissions</a> administration section.', array('!acl' => url('admin/user/permissions'))), 'warning'); 50 } 51 else { 52 $result = db_query_range("SELECT name FROM {ckeditor_settings} WHERE name<>'CKEditor Global Profile'", 0, 1); 53 $has_profiles = FALSE; 54 //find profile other than Global 55 if (($obj = db_fetch_object($result))) { 56 $has_profiles = TRUE; 57 } 58 59 //find roles with profiles 60 $result = db_query("SELECT rid FROM {ckeditor_role}"); 61 $rids = array(); 62 while (($obj = db_fetch_object($result))) { 63 $rids[] = $obj->rid; 64 } 65 $rids = array_unique($rids); 66 if (!$has_profiles) { 67 drupal_set_message(t('No CKEditor profiles found. At this moment, nobody is able to use CKEditor. Create new profile below.'), 'error'); 68 } 69 else { 70 //not all roles with access ckeditor has their CKEditor profile assigned 71 $diff = array_diff(array_keys($access_ckeditor_roles), $rids); 72 if ($diff) { 73 $list = "<ul>"; 74 foreach ($diff as $rid) { 75 $list .= "<li>". $access_ckeditor_roles[$rid] ."</li>"; 76 } 77 $list .= "</ul>"; 78 drupal_set_message(t('Not all roles with <a href="!access">access ckeditor</a> permission are associated with CKEditor profiles. As a result, users having the following roles may be unable to use CKEditor: !list Create new or edit CKEditor profiles below and in the <em>Basic setup</em> section, check "Roles allowed to use this profile".', array('!access' => url('admin/user/permissions'), '!list' => $list)), 'warning'); 79 } 80 } 81 } 82 83 return ckeditor_profile_overview(); 84 } 85 86 /** 87 * Controller for ckeditor profiles. 88 */ 89 function ckeditor_profile_overview() { 90 $output = ''; 91 92 $profiles = ckeditor_profile_load(); 93 if ($profiles) { 94 $access_ckeditor_roles = user_roles(FALSE, 'access ckeditor'); 95 $header = array(t('Profile'), t('Roles'), t('Operations')); 96 foreach ($profiles as $p) { 97 $rids = $p->rids; 98 if ($p->name !== "CKEditor Global Profile") { 99 foreach (array_keys($p->rids) as $rid) { 100 if (!isset($access_ckeditor_roles[$rid])) { 101 unset($rids[$rid]); 102 } 103 } 104 $rows[] = array( 105 array('data' => $p->name, 'valign' => 'top'), 106 array('data' => implode("<br />\n", $rids)), 107 array('data' => 108 l(t('edit'), 'admin/settings/ckeditor/edit/'. urlencode($p->name)) .' '. 109 l(t('clone'), 'admin/settings/ckeditor/clone/'. urlencode($p->name)) .' '. 110 l(t('delete'), 'admin/settings/ckeditor/delete/'. urlencode($p->name)), 'valign' => 'top' 111 ) 112 ); 113 } 114 } 115 $output .= '<h3>'. t('Profiles') .'</h3>'; 116 $output .= theme('table', $header, $rows); 117 $output .= '<p>'. l(t('Create new profile'), 'admin/settings/ckeditor/add') .'</p>'; 118 } 119 else { 120 drupal_set_message(t('No profiles found. Click here to <a href="!create">create a new profile</a>.', array('!create' => url('admin/settings/ckeditor/add')))); 121 } 122 123 $rows = array(); 124 if (!isset($profiles['CKEditor Global Profile'])) { 125 drupal_set_message(t('Global profile not found. Click here to <a href="!create">create the global profile</a>.', array('!create' => url('admin/settings/ckeditor/addg')))); 126 } 127 else { 128 $output .= "<h3>". t("Global settings") ."</h3>"; 129 $rows[] = array( 130 array('data' => t('CKEditor Global Profile'), 'valign' => 'top'), 131 array('data' => l(t('edit'), 'admin/settings/ckeditor/editg') ." ". l(t('delete'), 'admin/settings/ckeditor/delete/CKEditor Global Profile'), 'valign' => 'top') 132 ); 133 $output .= theme('table', array(t('Profile'), t('Operations')), $rows); 134 } 135 return $output; 136 } 137 138 /** 139 * Clone profile 140 */ 141 function ckeditor_admin_profile_clone_form($form_state, $oldprofile) { 142 return ckeditor_admin_profile_form($form_state, $oldprofile); 143 } 144 145 function ckeditor_admin_profile_clone_form_validate($form_state, $oldprofile) { 146 ckeditor_admin_profile_form_validate($form_state, $oldprofile); 147 } 148 149 function ckeditor_admin_profile_clone_form_submit($form, &$form_state) { 150 $edit =& $form_state['values']; 151 drupal_set_message(t('Your CKEditor profile has been created.')); 152 $settings = ckeditor_admin_values_to_settings($edit); 153 db_query("INSERT INTO {ckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings); 154 ckeditor_rebuild_selectors($edit['name']); 155 if (!empty($edit['rids'])) { 156 foreach (array_keys($edit['rids']) as $rid) { 157 if ($edit['rids'][$rid]!=0) { 158 db_query("INSERT INTO {ckeditor_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid); 159 } 160 } 161 } 162 $form_state['redirect'] = 'admin/settings/ckeditor'; 163 } 164 165 /** 166 * Form builder for a normal profile 167 */ 168 function ckeditor_admin_profile_form($form_state, $profile = NULL) { 169 global $theme; 170 if ($profile != NULL) { 171 $form['_profile'] = array( 172 '#type' => 'value', 173 '#value' => $profile, 174 ); 175 } 176 else { 177 $profile = new stdClass(); 178 } 179 180 module_load_include('inc', 'ckeditor', 'includes/ckeditor.lib'); 181 182 $toolbar_options = ckeditor_load_toolbar_options(); 183 $skin_options = ckeditor_load_skin_options(); 184 $lang_options = ckeditor_load_lang_options(); 185 186 // Only display the roles that currently don't have a ckeditor profile. One 187 // profile per role. 188 $orig_roles = user_roles(FALSE, 'access ckeditor'); 189 $roles = $orig_roles; 190 191 if (!empty($profile->rids) && !user_roles(FALSE, 'access ckeditor')) { 192 drupal_set_message(t('You haven\'t assigned "access ckeditor" <a href="!permissions">permissions</a> yet. It is recommended to assign the "access ckeditor" <a href="!permissions">permissions</a> before updating CKEditor profiles.', array('!permissions' => url('admin/user/permissions'))), 'warning'); 193 } 194 195 if (empty($profile->name)) { 196 $result = db_query("SELECT DISTINCT(rid) FROM {ckeditor_role}"); 197 while (($data = db_fetch_object($result))) { 198 if ((empty($profile->rids) || !in_array($data->rid, array_keys((array) $profile->rids))) && !form_get_errors()) { 199 unset($roles[$data->rid]); 200 } 201 } 202 if (count($orig_roles) != count($roles)) { 203 drupal_set_message(t('Not all user roles are shown since they already have ckeditor profiles. You must first unassign profiles in order to add them to a new one.')); 204 } 205 } 206 207 $form['basic'] = array( 208 '#type' => 'fieldset', 209 '#title' => t('Basic setup'), 210 '#collapsible' => TRUE, 211 '#collapsed' => TRUE 212 ); 213 214 215 switch (arg(3)) { 216 case 'clone': 217 //load all profiles to check their names 218 $profiles = ckeditor_profile_load(); 219 $oldname = $profile->name; 220 $maxsize=128; //default max name length 221 222 $res=array(); 223 $pat = "/^(.*?)_([0-9]+)$/"; 224 if (preg_match($pat, $oldname, $res)) { // oldname like 'name_nr' 225 $name=$res[1]; 226 $num=$res[2]+1; 227 } 228 else{ 229 $name=$oldname; 230 $num=2; 231 } 232 233 $newname=substr($name, 0, $maxsize-3) .'_'. $num; // +limit 234 while (isset($profiles[$newname])) { //find next free number 235 $num++; 236 $newname=substr($name, 0, $maxsize-3) .'_'. $num; 237 } 238 //dont clone rids 239 $profile->settings['rids']=array(); 240 $profile->rids=array(); 241 break; 242 case 'edit': 243 $newname = $profile->name; 244 break; 245 } 246 247 $form['basic']['name'] = array( 248 '#type' => 'textfield', 249 '#title' => t('Profile name'), 250 '#default_value' => !empty($profile->name) ? $newname : '', 251 '#size' => 40, 252 '#maxlength' => 128, 253 '#description' => t('Enter a name for this profile. This name is only visible within the ckeditor administration page.'), 254 '#required' => TRUE 255 ); 256 257 $form['basic']['rids'] = array( 258 '#type' => 'checkboxes', 259 '#title' => t('Roles allowed to use this profile'), 260 '#default_value' => !empty($profile->rids) ? array_keys((array) $profile->rids) : array(), 261 '#options' => $roles, 262 '#description' => t('Only roles with "access ckeditor" permission will be shown here. If no role is available, make sure that you have assigned the "access ckeditor" <a href="!permission">permission</a>.', array('!permission' => url('admin/user/permissions'))), 263 '#required' => TRUE 264 ); 265 266 $form['basic']['allow_user_conf'] = array( 267 '#type' => 'radios', 268 '#title' => t('Allow users to customize CKEditor appearance'), 269 '#default_value' => !empty($profile->settings['allow_user_conf']) ? $profile->settings['allow_user_conf'] : 'f', 270 '#options' => array( 271 'f' => t('No'), 272 't' => t('Yes') 273 ), 274 '#description' => t('If allowed, users will be able to override the "Editor appearance" by visiting their profile page.'), 275 ); 276 277 $form['security'] = array( 278 '#type' => 'fieldset', 279 '#title' => t('Security'), 280 '#description' => '<p>'. t('The CKEditor security system protects you from executing malicious code that is already in your database. In plain text areas database content is harmless because it is not executed, but the CKEditor WYSIWYG editor evaluates HTML like a web browser and content needs to be filtered before it is loaded.') .'</p>', 281 '#collapsible' => TRUE, 282 '#collapsed' => TRUE 283 ); 284 285 $all = filter_list_all(); 286 287 $form['security']['filters'] = array( 288 '#type' => 'fieldset', 289 '#title' => t('Security filters'), 290 '#description' => t('Please choose carefully all filters that protect your content (probably not all filters listed below are security filters).'), 291 '#tree' => TRUE, 292 ); 293 294 //don't bother administrator with filters that definitely are not security filters 295 $modules_with_filters_to_skip = array('amazon_filter', 'asy', 'bbcode', 'biblio', 'blockquote', 'bookpost', 'chessboard', 'citation_filter', 'codefilter', 'collapse_text', 'contextlinks', 'coolfilter', 'dialectic', 'dript', 'dme', 'drutex', 'embedfilter', 'ext_link_page', 'extlink', 'elf', 'flickr', 'flickrstickr', 'footnotes', 'formdefaults', 'freelinking', 'gallery', 'geogebra', 'geshifilter', 'gotwo', 'googtube', 'gotcha', 'gtspam', 'hidden_content', 'img_assist', 'image_filter', 'imagebrowser', 'inlinetags', 'insert_view', 'insertframe', 'insertnode', 'interwiki', 'jlightbox', 'jsmath', 'language_sections', 'link_node', 'lootz', 'markdown', 'marksmarty', 'mobile_codes', 'mykml', 'nofollowlist', 'oagwt', 'paging', 'pathfilter', 'pearwiki_filter', 'php', 'pirate', 'reptag', 'scrippet', 'scripturefilter', 'signwriter', 'slideshowpro', 'smartlinebreakconverter', 'smartypants', 'smileys', 'spamspan', 'spam_tokens', 'spoiler', 'table_altrow', 'tablemanager', 'tableofcontents', 'textile', 'tooltips', 'twikifilter', 'typogrify', 'unwrap', 'urlclass', 'urlicon', 'url_replace_filter', 'username_highlighter', 'video_filter', 'quote'); 296 297 if (!isset($profile->settings['ss'])) { 298 $profile->settings['filters']['filter/0'] = 1; 299 } 300 301 foreach ($all as $id => $filter) { 302 if (in_array(strtolower($filter->module), $modules_with_filters_to_skip)) { 303 continue; 304 } 305 //skip line break converter and email -> link 306 if ($filter->module == 'filter' && in_array($filter->delta, array(1, 2))) { 307 continue; 308 } 309 $form['security']['filters'][$id] = array( 310 '#type' => 'checkbox', 311 '#title' => $filter->name, 312 '#default_value' => !empty($profile->settings['filters'][$id]), 313 '#description' => module_invoke($filter->module, 'filter', 'description', $filter->delta), 314 ); 315 } 316 317 $form['security']['ss'] = array( 318 '#type' => 'radios', 319 '#title' => t('Security settings'), 320 '#default_value' => isset($profile->settings['ss']) ? $profile->settings['ss'] : '2', 321 '#options' => array( 322 '2' => t('Always run security filters for CKEditor.'), 323 '1' => t('Run security filters only when CKEditor is set to start automatically.'), 324 ), 325 '#description' => t('There are two ways of starting CKEditor: automatically and manually (via toggle or in a popup). If you decide to apply security filters only when CKEditor starts automatically, you will not be protected when toggling manually from plain text area to CKEditor or when using CKEditor in a popup mode. So choose this option only, if you can detect various attacks (mainly XSS) by yourself just by looking at the HTML code.'), 326 ); 327 328 $form['ckeditor_exclude_settings'] = array( 329 '#type' => 'fieldset', 330 '#title' => t('Visibility settings'), 331 '#collapsible' => TRUE, 332 '#collapsed' => TRUE, 333 '#description' => t('The following settings are combined with the visibility settings of the global profile.'), 334 ); 335 336 $form['ckeditor_exclude_settings']['min_rows'] = array( 337 '#type' => 'textfield', 338 '#title' => t('Minimum rows'), 339 '#default_value' => !empty($profile->settings['min_rows']) ? $profile->settings['min_rows'] : '1', 340 '#description' => t("CKEditor will be triggered if the textarea has more rows than entered here. Enter '1' if you do not want to use this feature."), 341 ); 342 343 $form['ckeditor_exclude_settings']['excl_mode'] = array( 344 '#type' => 'radios', 345 '#title' => t('Use inclusion or exclusion mode'), 346 '#default_value' => (empty($profile->settings['excl_mode']) || in_array($profile->settings['excl_mode'], array(0, 2))) ? 0 : 1, 347 '#options' => array( 348 '0' => t('Exclude'), 349 '1' => t('Include') 350 ), 351 '#description' => t('Choose the way of disabling/enabling CKEditor on selected fields/paths (see below). Use exclude to disable CKEditor on selected fields/paths. Use include if you want to load CKEditor only on selected paths/fields.'), 352 ); 353 354 /** 355 * get excluded fields - so we can have normal textareas too 356 * split the phrase by any number of commas or space characters, 357 * which include " ", \r, \t, \n and \f 358 */ 359 $form['ckeditor_exclude_settings']['excl'] = array( 360 '#type' => 'textarea', 361 '#title' => t('Fields to exclude/include'), 362 '#cols' => 60, 363 '#rows' => 5, 364 '#prefix' => '<div style="margin-left:20px">', 365 '#suffix' => '</div>', 366 '#default_value' => !empty($profile->settings['excl']) ? $profile->settings['excl'] : '', 367 '#description' => t('Enter the paths to the textarea fields on which you want to enable or disable CKEditor.') .' '. t('Please see the <a href="!helppagelink">help page</a> for more information about defining field names. Short instruction is available below.', array('!helppagelink' => url('admin/help/ckeditor', array('fragment' => 'fieldinclexcl')))) .' <ul><li>'. t('Path structure: <strong>content_type@path.element_id</strong>') .'</li><li>'. t('The following wildcards are available: "*", "?".') .'</li><li>'. t('Content type is optional. You may even specify only path or field id.') .'</li><li>'. t('Examples:') .'<ul><li><em>blog@*.edit-body</em> - '. t('matches all fields of type "blog" called edit-body, on any page.') .'<li><em>node/add/*.edit-user-*</em> - '. t('matches fields starting with "edit-user-" on pages starting with "node/add/') .'</li></ul></li></ul>', 368 '#wysiwyg' => FALSE, 369 ); 370 371 $form['ckeditor_exclude_settings']['simple_incl'] = array( 372 '#type' => 'textarea', 373 '#title' => t('Force simplified toolbar on the following fields'), 374 '#cols' => 60, 375 '#rows' => 5, 376 '#default_value' => !empty($profile->settings['simple_incl']) ? $profile->settings['simple_incl'] : '', 377 '#description' => t('Enter the paths to the textarea fields on which you want to force the simplified toolbar (!name).', array('!name' => CKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME)) .' '. t('Please see the <a href="!helppagelink">help page</a> for more information about defining field names. Take a look at the exclusion settings (above) for short instruction.', array('!helppagelink' => url('admin/help/ckeditor', array('fragment' => 'fieldinclexcl')))), 378 '#wysiwyg' => FALSE, 379 ); 380 381 $form['appearance'] = array( 382 '#type' => 'fieldset', 383 '#title' => t('Editor appearance'), 384 '#collapsible' => TRUE, 385 '#collapsed' => TRUE, 386 ); 387 388 $form['appearance']['default'] = array( 389 '#type' => 'radios', 390 '#title' => t('Default state'), 391 '#default_value' => !empty($profile->settings['default']) ? $profile->settings['default'] : 't', 392 '#options' => array( 393 't' => t('Enabled'), 394 'f' => t('Disabled') 395 ), 396 '#description' => t('Default editor state. If disabled, rich text editor may still be enabled using toggle or popup window.'), 397 ); 398 399 $form['appearance']['show_toggle'] = array( 400 '#type' => 'radios', 401 '#title' => t('Show disable/enable rich text editor toggle'), 402 '#default_value' => !empty($profile->settings['show_toggle']) ? $profile->settings['show_toggle'] : 't', 403 '#options' => array( 404 't' => t('Show'), 405 'f' => t('Hide') 406 ), 407 '#description' => t('Whether or not to show the disable/enable rich text editor toggle below the textarea. Works only if CKEditor is not running in a popup window (see below).'), 408 ); 409 410 $form['appearance']['popup'] = array( 411 '#type' => 'radios', 412 '#title' => t('Use CKEditor in a popup window'), 413 '#default_value' => !empty($profile->settings['popup']) ? $profile->settings['popup'] : 'f', 414 '#options' => array( 415 'f' => t('No'), 416 't' => t('Yes') 417 ), 418 '#description' => t('If this option is enabled a link to a popup window will be used instead of a textarea replace.'), 419 ); 420 421 $form['appearance']['skin'] = array( 422 '#type' => 'select', 423 '#title' => t('Skin'), 424 '#default_value' => !empty($profile->settings['skin']) ? $profile->settings['skin'] : 'default', 425 '#options' => $skin_options, 426 '#description' => t('Choose a default skin.'), 427 ); 428 429 $ui_colors = array( 430 "default" => t('CKEditor default'), 431 "custom" => t('Select manually') 432 ); 433 if (function_exists('color_get_palette')) { 434 // apparently $theme is not initialized (?) 435 if (empty($theme)) { 436 init_theme(); 437 } 438 $palette = @color_get_palette($theme, FALSE); //[#652274] 439 $color_palette['default'] = '#D3D3D3'; 440 if (!empty($palette)) { 441 if (!empty($palette['base'])) { 442 $color_palette['color_base'] = $palette['base']; 443 $ui_colors["color_base"] = t('Color module: base'); 444 } 445 if (!empty($palette['top'])) { 446 $color_palette['color_top'] = $palette['top']; 447 $ui_colors["color_top"] = t('Color module: top'); 448 } 449 if (!empty($palette['bottom'])) { 450 $color_palette['color_bottom'] = $palette['bottom']; 451 $ui_colors["color_bottom"] = t('Color module: bottom'); 452 } 453 } 454 drupal_add_js(array('ckeditor_uicolor' => $color_palette), 'setting'); 455 } 456 457 $editor_path = ckeditor_path(FALSE); 458 $module_drupal_path = drupal_get_path('module', 'ckeditor'); 459 drupal_add_js($module_drupal_path .'/includes/ckeditor.admin.js'); 460 drupal_set_html_head('<script type="text/javascript" src="'. $editor_path .'/ckeditor.js?I"></script>'); 461 462 $form['appearance']['uicolor'] = array( 463 '#type' => 'select', 464 '#title' => t('User Interface color'), 465 '#default_value' => !empty($profile->settings['uicolor']) ? $profile->settings['uicolor'] : 'default', 466 '#options' => $ui_colors, 467 '#description' => t('Works only with "!skin" skin.', 468 array( 469 '!skin' => 'Kama' 470 )), 471 ); 472 $form['appearance']['uicolor_textarea'] = array( 473 '#type' => 'textarea', 474 '#title' => '', 475 '#default_value' => 'Click on the <strong>UI Color Picker</strong> button to set your color preferences.', 476 '#attributes' => array('style' => 'display:none', 'class' => 'ckeditor_ui_demo'), 477 '#resizable' => FALSE, 478 '#wysiwyg' => FALSE 479 ); 480 $form['appearance']['uicolor_user'] = array( 481 '#type' => 'hidden', 482 '#default_value' => !empty($profile->settings['uicolor_user']) ? $profile->settings['uicolor_user'] : 'default', 483 ); 484 485 $form['appearance']['toolbar'] = array( 486 '#type' => 'select', 487 '#title' => t('Toolbar'), 488 '#default_value' => !empty($profile->settings['toolbar']) ? $profile->settings['toolbar'] : 'default', 489 '#options' => $toolbar_options, 490 '#description' => t('Choose a default toolbar set. To define new toolbar, edit "ckeditor.config.js" located in "!module_path".', array('!module_path' => drupal_get_path('module', 'ckeditor'))), 491 ); 492 493 $form['appearance']['expand'] = array( 494 '#type' => 'radios', 495 '#title' => t('Start the toolbar expanded'), 496 '#default_value' => !empty($profile->settings['expand']) ? $profile->settings['expand'] : 't', 497 '#options' => array( 498 't' => t('Expanded'), 499 'f' => t('Collapsed') 500 ), 501 '#description' => t('The toolbar start expanded or collapsed.'), 502 ); 503 504 $form['appearance']['width'] = array( 505 '#type' => 'textfield', 506 '#title' => t('Width'), 507 '#default_value' => !empty($profile->settings['width']) ? $profile->settings['width'] : '100%', 508 '#description' => t("Width in pixels or percent. Example: 400 or 100%."), 509 '#size' => 40, 510 '#maxlength' => 128, 511 ); 512 513 $form['appearance']['lang'] = array( 514 '#type' => 'select', 515 '#title' => t('Language'), 516 '#default_value' => !empty($profile->settings['lang']) ? $profile->settings['lang'] : 'en', 517 '#options' => $lang_options, 518 '#description' => t('The language for the CKEditor interface.') 519 ); 520 521 $form['appearance']['auto_lang'] = array( 522 '#type' => 'radios', 523 '#title' => t('Auto-detect language'), 524 '#default_value' => !empty($profile->settings['auto_lang']) ? $profile->settings['auto_lang'] : 't', 525 '#options' => array( 526 't' => t('Enabled'), 527 'f' => t('Disabled') 528 ), 529 '#description' => t('Use auto detect user language feature.') 530 ); 531 532 $form['appearance']['language_direction'] = array( 533 '#type' => 'select', 534 '#title' => t('Language Direction'), 535 '#default_value' => !empty($profile->settings['language_direction']) ? $profile->settings['language_direction'] : 'default', 536 '#options' => array( 537 'default' => t('Get from current locale (default)'), 538 'ltr' => t('Left-To-Right'),// language (like English) 539 'rtl' => t('Right-To-Left') // languages (like Arabic) 540 ), 541 '#description' => t('Choose language direction used in the editing area. Even when CKEditor auto detects user language and adjusts it\'s user interface, the editing area is not automatically changed into LTR or RTL mode. To be able to type LTR (like English) and RTL (like Arabic, Hebrew, Persian) content at the same time, please take a look at the <b>Styles</b> combo and <b>Language: RTL</b>, <b>Language: LTR</b> styles available there. Note: this combo box is not available in the default toolbar, the <b>Styles</b> button needs to be added to the toolbar in ckeditor.config.js.') 542 ); 543 544 $form['output'] = array( 545 '#type' => 'fieldset', 546 '#title' => t('Cleanup and output'), 547 '#collapsible' => TRUE, 548 '#collapsed' => TRUE, 549 ); 550 551 $form['output']['enter_mode'] = array( 552 '#type' => 'select', 553 '#title' => t('Enter mode'), 554 '#default_value' => !empty($profile->settings['enter_mode']) ? $profile->settings['enter_mode'] : 'p', 555 '#options' => array( 556 'p' => '<p>', 557 'br' => '<br>', 558 'div' => '<div>' 559 ), 560 '#description' => t('Set which tag CKEditor should use when [Enter] key is pressed.') 561 ); 562 563 $form['output']['shift_enter_mode'] = array( 564 '#type' => 'select', 565 '#title' => t('Shift + Enter mode'), 566 '#default_value' => !empty($profile->settings['shift_enter_mode']) ? $profile->settings['shift_enter_mode'] : 'br', 567 '#options' => array( 568 'p' => '<p>', 569 'br' => '<br>', 570 'div' => '<div>' 571 ), 572 '#description' => t('Set which tag CKEditor should use when [Shift] + [Enter] is pressed.') 573 ); 574 575 $form['output']['font_format'] = array( 576 '#type' => 'textfield', 577 '#title' => t('Font formats'), 578 '#default_value' => !empty($profile->settings['font_format']) ? $profile->settings['font_format'] : 'p;div;pre;address;h1;h2;h3;h4;h5;h6', 579 '#size' => 40, 580 '#maxlength' => 250, 581 '#description' => t('Semicolon separated list of HTML font formats. Allowed values are: p;div;pre;address;h1;h2;h3;h4;h5;h6'), 582 ); 583 584 if (!empty($profile->settings['formatting']['custom_formatting_options'])) 585 foreach( $profile->settings['formatting']['custom_formatting_options'] as $k => $v ) { 586 if ($v === 0) { 587 unset($profile->settings['formatting']['custom_formatting_options'][$k]); 588 } 589 } 590 591 $form['output']['custom_formatting'] = array( 592 '#type' => 'radios', 593 '#title' => t('Use custom formatting options'), 594 '#default_value' => !empty($profile->settings['custom_formatting']) ? $profile->settings['custom_formatting'] : 'f', 595 '#options' => array( 596 't' => t('Yes'), 597 'f' => t('No'), 598 ), 599 ); 600 601 $form['output']['formatting'] = array( 602 '#type' => 'fieldset', 603 '#title' => t('Custom formatting options'), 604 '#tree' => TRUE, 605 ); 606 607 $form['output']['formatting']['custom_formatting_options'] = array( 608 '#type' => 'checkboxes', 609 '#default_value' => isset($profile->settings['formatting']['custom_formatting_options']) ? array_keys((array) $profile->settings['formatting']['custom_formatting_options']) : array('indent' => 'indent', 'breakBeforeOpen' => 'breakBeforeOpen', 'breakAfterOpen' => 'breakAfterOpen', 'breakAfterClose' => 'breakAfterClose'), 610 '#options' => array( 611 'indent' => t('indent the element contents'), 612 'breakBeforeOpen' => t('break line before the opener tag'), 613 'breakAfterOpen' => t('break line after the opener tag'), 614 'breakBeforeClose' => t('break line before the closer tag'), 615 'breakAfterClose' => t('break line after the closer tag'), 616 'pre_indent' => t('indent the <pre> element contents'), 617 ), 618 ); 619 620 $form['css'] = array( 621 '#type' => 'fieldset', 622 '#title' => t('CSS'), 623 '#collapsible' => TRUE, 624 '#collapsed' => TRUE 625 ); 626 627 $form['css']['css_mode'] = array( 628 '#type' => 'select', 629 '#title' => t('Editor CSS'), 630 '#default_value' => !empty($profile->settings['css_mode']) ? $profile->settings['css_mode'] : 'theme', 631 '#options' => array( 632 'theme' => t('Use theme css'), 633 'self' => t('Define css'), 634 'none' => t('CKEditor default') 635 ), 636 '#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 />CKEditor default - uses default CSS from editor.') 637 ); 638 639 $form['css']['css_path'] = array( 640 '#type' => 'textfield', 641 '#title' => t('CSS path'), 642 '#default_value' => !empty($profile->settings['css_path']) ? $profile->settings['css_path'] : "", 643 '#size' => 40, 644 '#maxlength' => 255, 645 '#description' => t('Enter path to CSS file (Example: !example1) or a list of css files separated by a comma (Example: !example2). Make sure to select "Define css" above.', 646 array( 647 '!example1' => '"css/editor.css"', 648 '!example2' => '"/themes/garland/style.css,http://example.com/style.css"', 649 )) . 650 '<br />'. 651 t('Available placeholders:!h - host name (!host).!t - path to theme (!theme).', 652 array( 653 '!h' => '<br /><strong>%h</strong>', 654 '!t' => '<br /><strong>%t</strong>', 655 '!host' => base_path(), 656 '!theme' => base_path() . path_to_theme() .'/' 657 )) 658 ); 659 660 $form['css']['css_style'] = array( 661 '#type' => 'select', 662 '#title' => t('Predefined styles'), 663 '#default_value' => !empty($profile->settings['css_style']) ? $profile->settings['css_style'] : 'theme', 664 '#options' => array( 665 'theme' => t('Use theme ckeditor.styles.js'), 666 'self' => t('Define path to ckeditor.styles.js'), 667 'default' => t('CKEditor default') 668 ), 669 '#description' => t('Define the location of "ckeditor.styles.js" file. It is used by the "Style" dropdown list available in the default toolbar. Copy "!ckeditor.styles.js" inside your theme directory ("!theme") and adjust it to your needs.', array('!ckeditor.styles.js' => ckeditor_path(TRUE) .'/ckeditor.styles.js', '!theme' => path_to_theme() .'/ckeditor.styles.js')) 670 ); 671 672 $form['css']['styles_path'] = array( 673 '#type' => 'textfield', 674 '#title' => t('Predefined styles path'), 675 '#default_value' => !empty($profile->settings['styles_path']) ? $profile->settings['styles_path'] : "", 676 '#size' => 40, 677 '#maxlength' => 255, 678 '#description' => t('Enter path to a file with predefined styles (Example: "!example1"). Be sure to select "define path to ckeditor.styles.js" above.', 679 array( 680 '!example1' => '/ckeditor.styles.js' 681 )) . 682 '<br />'. 683 t('Available placeholders:!h - host name (!host).!t - path to theme (!theme).!m - path to CKEditor module (!module).', 684 array( 685 '!h' => '<br /><strong>%h</strong>', 686 '!t' => '<br /><strong>%t</strong>', 687 '!m' => '<br /><strong>%m</strong>', 688 '!host' => base_path(), 689 '!theme' => base_path() . path_to_theme() .'/', 690 '!module' => drupal_get_path('module', 'ckeditor')) 691 ) 692 ); 693 694 $form['ckeditor_upload_settings'] = array( 695 '#type' => 'fieldset', 696 '#title' => t('File browser settings'), 697 '#collapsible' => TRUE, 698 '#collapsed' => TRUE, 699 '#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. CKEditor is compatible with contributed modules like <a href="!imce">IMCE</a>, <a href="!ib">Image Browser</a> or <a href="!webfm">Web File Manager</a>. CKEditor can be also integrated with <a href="!ckfinder">CKFinder</a>.', array('!imce' => url('http://drupal.org/project/imce'), '!ib' => url('http://drupal.org/project/imagebrowser'), '!webfm' => url('http://drupal.org/project/webfm'), '!readme' => url('admin/help/ckeditor'), '!ckfinder' => url('http://ckfinder.com'))) 700 ); 701 702 $filebrowsers = array( 703 'none' => t('None'), 704 'ckfinder' => t('CKFinder'), 705 ); 706 707 $filebrowsers_dialogs = array( 708 '' => t('Same as in Link dialog'), 709 'ckfinder' => t('CKFinder'), 710 ); 711 712 if (module_exists('imce')) { 713 $filebrowsers['imce'] = t('IMCE'); 714 $filebrowsers_dialogs['imce'] = t('IMCE'); 715 } 716 717 if (module_exists('tinybrowser')) { 718 $filebrowsers['tinybrowser'] = t('TinyBrowser'); 719 $filebrowsers_dialogs['tinybrowser'] = t('TinyBrowser'); 720 } 721 722 if (module_exists('imagebrowser')) { 723 $filebrowsers['ib'] = t('Image Browser'); 724 $filebrowsers_dialogs['ib'] = t('Image Browser'); 725 } 726 727 if (module_exists('webfm_popup')) { 728 $filebrowsers['webfm'] = t('Web File Manager'); 729 $filebrowsers_dialogs['webfm'] = t('Web File Manager'); 730 } 731 732 $form['ckeditor_upload_settings']['filebrowser'] = array( 733 '#type' => 'select', 734 '#title' => t('File browser type (Link dialog)'), 735 '#default_value' => !empty($profile->settings['filebrowser']) ? $profile->settings['filebrowser'] : 'none', 736 '#options' => $filebrowsers, 737 '#description' => t('Select the file browser that you would like to use to upload files.'), 738 ); 739 740 $form['ckeditor_upload_settings']['filebrowser_image'] = array( 741 '#type' => 'select', 742 '#title' => t('File browser type (Image dialog)'), 743 '#default_value' => !empty($profile->settings['filebrowser_image']) ? $profile->settings['filebrowser_image'] : 'none', 744 '#options' => $filebrowsers_dialogs, 745 '#description' => t('Select the file browser that you would like to use to upload images.'), 746 ); 747 748 $form['ckeditor_upload_settings']['filebrowser_flash'] = array( 749 '#type' => 'select', 750 '#title' => t('File browser type (Flash dialog)'), 751 '#default_value' => !empty($profile->settings['filebrowser_flash']) ? $profile->settings['filebrowser_flash'] : 'none', 752 '#options' => $filebrowsers_dialogs, 753 '#description' => t('Select the file browser that you would like to use to upload flash movies.'), 754 ); 755 756 $current_user_files_path = empty($profile->settings['UserFilesPath']) ? "" : strtr($profile->settings['UserFilesPath'], array("%f" => file_directory_path(), "%u" => "UID", "%b" => base_path(), "%n" => "UNAME")); 757 $current_user_files_absolute_path = empty($profile->settings['UserFilesAbsolutePath']) ? "" : strtr($profile->settings['UserFilesAbsolutePath'], array("%f" => file_directory_path(), "%u" => "UID", "%b" => base_path(), "%d" => $_SERVER['DOCUMENT_ROOT'], "%n" => "UNAME")); 758 759 $form['ckeditor_upload_settings']['UserFilesPath'] = array( 760 '#type' => 'textfield', 761 '#prefix' => '<fieldset><legend>' . t('CKFinder settings') .'</legend>', 762 '#title' => t('Path to uploaded files'), 763 '#default_value' => !empty($profile->settings['UserFilesPath']) ? $profile->settings['UserFilesPath'] : "%b%f/", 764 '#size' => 40, 765 '#maxlength' => 255, 766 '#description' => t('Path to uploaded files relative to the document root.') . 767 '<br />'. 768 t('Available placeholders:!b - base URL path of the Drupal installation (!base).!f - Drupal file system path where the files are stored (!files).!u - User ID.!n - Username.<br />Current path: !path', 769 array( 770 '!n' => '<br /><strong>%n</strong>', 771 '!u' => '<br /><strong>%u</strong>', 772 '!f' => '<br/><strong>%f</strong>', 773 '!b' => '<br/><strong>%b</strong>', 774 '!path' => $current_user_files_path, 775 '!files' => file_directory_path(), 776 '!base' => base_path() 777 )) 778 ); 779 780 $form['ckeditor_upload_settings']['UserFilesAbsolutePath'] = array( 781 '#type' => 'textfield', 782 '#title' => t('Absolute path to uploaded files'), 783 '#default_value' => !empty($profile->settings['UserFilesAbsolutePath']) ? $profile->settings['UserFilesAbsolutePath'] : "%d%b%f/", 784 '#size' => 40, 785 '#maxlength' => 255, 786 '#suffix' => '</fieldset>', 787 '#description' => t('The path to the local directory (in the server) which points to the path defined above. If empty, CKEditor will try to discover the right path.') . 788 '<br />'. 789 t('Available placeholders:!d - server path to document root (!root).!b - base URL path of the Drupal installation (!base).!f - Drupal file system path where the files are stored (!files).!u - User ID.!n - Username.<br />Current path: !path', 790 array( 791 '!u' => '<br /><strong>%u</strong>', 792 '!n' => '<br /><strong>%n</strong>', 793 '!d' => '<br/><strong>%d</strong>', 794 '!b' => '<br /><strong>%b</strong>', 795 '!f' => '<br/><strong>%f</strong>', 796 '!path' => $current_user_files_absolute_path, 797 '!files' => file_directory_path(), 798 '!base' => base_path(), 799 '!root' => $_SERVER['DOCUMENT_ROOT'] 800 )) 801 ); 802 803 if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) { 804 $form['ckeditor_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. To change the location of uploaded files in the private file system, edit the <a href="!url">CKEditor Global Profile</a>.', array('!url' => url('admin/settings/ckeditor/editg'))); 805 $form['ckeditor_upload_settings']['UserFilesPath']['#disabled'] = TRUE; 806 $form['ckeditor_upload_settings']['UserFilesAbsolutePath']['#description'] = t('Setting path to uploaded files has been disabled because private downloads are enabled and this path is calculated automatically.To change the location of uploaded files in the private file system, edit the <a href="!url">CKEditor Global Profile</a>.', array('!global' => url('admin/settings/ckeditor/editg'))); 807 $form['ckeditor_upload_settings']['UserFilesAbsolutePath']['#disabled'] = TRUE; 808 } 809 810 $form['advanced'] = array( 811 '#type' => 'fieldset', 812 '#title' => t('Advanced options'), 813 '#collapsible' => TRUE, 814 '#collapsed' => TRUE, 815 ); 816 $form['advanced']['ckeditor_load_method'] = array( 817 '#type' => 'select', 818 '#title' => t('Load method'), 819 '#default_value' => !empty($profile->settings['ckeditor_load_method']) ? $profile->settings['ckeditor_load_method'] : 'ckeditor.js', 820 '#options' => _ckeditor_load_methods(), 821 '#description' => t('Select the load method of CKEditor. If ckeditor_basic.js is used, only a small file is initially loaded and the rest part of the editor is loaded later (see "Load timeout"). This might be handy if CKEditor is disabled by default.'), 822 ); 823 $form['advanced']['ckeditor_load_time_out'] = array( 824 '#type' => 'textfield', 825 '#title' => t('Load timeout'), 826 '#default_value' => !empty($profile->settings['ckeditor_load_time_out']) ? $profile->settings['ckeditor_load_time_out'] : "0", 827 '#size' => 40, 828 '#maxlength' => 255, 829 '#description' => t('The time to wait (in seconds) to load the full editor code after the page load, if the "ckeditor_basic.js" file is used. If set to zero, the editor is loaded on demand.'), 830 ); 831 $form['advanced']['forcePasteAsPlainText'] = array( 832 '#type' => 'select', 833 '#title' => t('Force pasting as plain text'), 834 '#default_value' => !empty($profile->settings['forcePasteAsPlainText']) ? $profile->settings['forcePasteAsPlainText'] : "f", 835 '#options' => array( 836 't' => t('Enabled'), 837 'f' => t('Disabled') 838 ), 839 '#description' => t('If enabled, HTML content will be automatically changed to plain text when pasting.'), 840 ); 841 $form['advanced']['scayt_autoStartup'] = array( 842 '#type' => 'radios', 843 '#title' => t('Spell checker'), 844 '#default_value' => !empty($profile->settings['scayt_autoStartup']) ? $profile->settings['scayt_autoStartup'] : 'f', 845 '#description' => t('If enabled, turns on SCAYT (Spell Check As You Type) automatically after loading the editor.'), 846 '#options' => array( 847 'f' => t('No'), 848 't' => t('Yes') 849 ), 850 ); 851 $form['advanced']['theme_config_js'] = array( 852 '#type' => 'radios', 853 '#title' => t('Load ckeditor.config.js from theme path'), 854 '#default_value' => !empty($profile->settings['theme_config_js']) ? $profile->settings['theme_config_js'] : 'f', 855 '#options' => array( 856 't' => t('Yes'), 857 'f' => t('No') 858 ), 859 '#description' => t('When set to "true" the editor will try to load the ckeditor.config.js file from theme directory.'), 860 ); 861 $form['advanced']['js_conf'] = array( 862 '#type' => 'textarea', 863 '#title' => t('Custom javascript configuration'), 864 '#default_value' => !empty($profile->settings['js_conf']) ? $profile->settings['js_conf'] : "", 865 '#cols' => 60, 866 '#rows' => 5, 867 '#description' => t('To change CKEditor configuration globally, you should modify the config file: "!ckeditor_config". Sometimes it is required to change the CKEditor configuration for a single profile only. Use this box to define settings that are unique for this profile. Available options are listed in the <a href="!docs">CKEditor documentation</a>. Add the following code snippet to change available fonts in combo boxes in CKEditor: <pre>@code</pre><strong>Warning</strong>: If you make something wrong here, CKEditor may fail to load.', array('!ckeditor_config' => drupal_get_path('module', 'ckeditor') ."/ckeditor.config.js", '!docs' => 'http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html', '@code' => "fontSize_sizes = '16/16px;24/24px;48/48px;'\nfont_names = 'Arial;Times New Roman;Verdana'")), 868 '#wysiwyg' => FALSE, 869 ); 870 871 $form['submit'] = array( 872 '#type' => 'submit', 873 '#value' => t('Save') 874 ); 875 876 return $form; 877 } 878 879 /** 880 * Profile validation. 881 */ 882 function ckeditor_admin_profile_form_validate($form, &$form_state) { 883 $edit =& $form_state['values']; 884 885 //include mode and all other fields are empty, invalid 886 if ($edit['excl_mode'] == 1 && empty($edit['excl'])) { 887 form_set_error('excl_mode', t('Include mode selected, but no paths given. Enter at least one path where CKEditor should appear.')); 888 } 889 else { 890 ckeditor_admin_profile_validate_fieldpaths('excl', $edit['excl']); 891 } 892 893 ckeditor_admin_profile_validate_fieldpaths('simple_incl', $edit['simple_incl']); 894 895 if (!preg_match('/^\d+$/', trim($edit['min_rows']))) { 896 form_set_error('min_rows', t('Minimum rows must be a valid number.')); 897 } 898 899 if ($edit['default'] == 't' && $edit['popup'] == 't') { 900 form_set_error('popup', t('If CKEditor is enabled by default, popup window must be disabled.')); 901 } 902 903 if ($edit['show_toggle'] == 't' && $edit['popup'] == 't') { 904 form_set_error('popup', t('If toggle is enabled, popup window must be disabled.')); 905 } 906 907 if (!$edit['name']) { 908 form_set_error('name', t('You must give a profile name.')); 909 } 910 elseif (!preg_match('/^[A-Za-z0-9_]+$/', $edit['name'])) { 911 form_set_error('name', t('Enter valid profile name. Only alphanumeric and underscore characters are allowed.')); 912 } 913 elseif ($edit['name'] == 'CKEditor Global Profile') { 914 form_set_error('name', t('This profile name is reserved. Please choose a different name.')); 915 } 916 elseif (!isset($edit['_profile']) || ($edit['_profile']->name != $edit['name'])) { 917 $result = ckeditor_profile_load($edit['name']); 918 if (!empty($result)) { 919 form_set_error('name', t('The profile name must be unique. A profile with this name already exists.')); 920 } 921 } 922 923 if (!preg_match('/^\d+%?$/', $edit['width'])) { 924 form_set_error('width', t('Enter valid width. Example: 400 or 100%.')); 925 } 926 927 if (!empty($edit['css_path'])) { 928 if ($edit['css_mode'] != 'self') { 929 form_set_error('css_path', t('CSS path is not empty. Please set the "Editor CSS" option to "define css" mode.')); 930 } 931 elseif (FALSE !== strpos($edit['css_path'], '"')) { 932 form_set_error('css_path', t('Double quotes are not allowed in CSS path.')); 933 } 934 elseif (substr($edit['css_path'], 0, 1) == "'" && substr($edit['css_path'], -1) == "'") { 935 form_set_error('css_path', t('Enter valid path, do not surround it with quotes.')); 936 } 937 } 938 939 if (!empty($edit['styles_path'])) { 940 if ($edit['css_style'] != 'self') { 941 form_set_error('styles_path', t('Path to predefined styles is not empty. Please set the "Predefined styles" option to "define path to ckeditor.styles.js" mode.')); 942 } 943 elseif (FALSE !== strpos($edit['styles_path'], '"')) { 944 form_set_error('styles_path', t('Double quotes are not allowed in path.')); 945 } 946 elseif (substr($edit['styles_path'], 0, 1) == "'" && substr($edit['styles_path'], -1) == "'") { 947 form_set_error('styles_path', t('Enter valid path, do not surround it with quotes.')); 948 } 949 } 950 951 if (!empty($edit['font_format'])) { 952 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'])) { 953 form_set_error('font_format', t('Enter valid, semicolon separated, list of HTML font formats (no semicolon at the end of list expected).')); 954 } 955 } 956 957 if (variable_get('file_downloads', '') !== FILE_DOWNLOADS_PRIVATE) { 958 if (!empty($edit['UserFilesAbsolutePath']) && empty($edit['UserFilesPath'])) { 959 form_set_error('UserFilesPath', t('Path to uploaded files is required.')); 960 } 961 if (!empty($edit['UserFilesPath']) && empty($edit['UserFilesAbsolutePath'])) { 962 form_set_error('UserFilesPath', t('Absolute path to uploaded files is required.')); 963 } 964 } 965 $load_methods=_ckeditor_load_methods(); 966 if (!isset($load_methods[$edit['ckeditor_load_method']])) { 967 form_set_error('ckeditor_load_method', t('Set valid load method.')); 968 } 969 if (!preg_match('#\d+#', $edit['ckeditor_load_time_out'])) { 970 form_set_error('ckeditor_load_time_out', t('Enter valid load timeout in seconds.')); 971 } 972 } 973 974 function ckeditor_admin_profile_form_submit($form, &$form_state) { 975 $edit =& $form_state['values']; 976 977 if (isset($edit['_profile'])) { 978 db_query("DELETE FROM {ckeditor_settings} WHERE name = '%s'", $edit['_profile']->name); 979 db_query("DELETE FROM {ckeditor_role} WHERE name = '%s'", $edit['_profile']->name); 980 drupal_set_message(t('Your CKEditor profile has been updated.')); 981 } 982 else { 983 drupal_set_message(t('Your CKEditor profile has been created.')); 984 } 985 986 $settings = ckeditor_admin_values_to_settings($edit); 987 db_query("INSERT INTO {ckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings); 988 ckeditor_rebuild_selectors($edit['name']); 989 if (!empty($edit['rids'])) { 990 foreach (array_keys($edit['rids']) as $rid) { 991 if ($edit['rids'][$rid]!=0) { 992 db_query("INSERT INTO {ckeditor_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid); 993 } 994 } 995 } 996 997 $form_state['redirect'] = 'admin/settings/ckeditor'; 998 } 999 1000 function ckeditor_admin_global_profile_form($form_state, $mode = 'add') { 1001 module_load_include('inc', 'ckeditor', 'includes/ckeditor.lib'); 1002 if ($mode == 'edit') { 1003 $profile = ckeditor_profile_load('CKEditor Global Profile'); 1004 1005 $form['_profile'] = array( 1006 '#type' => 'value', 1007 '#value' => $profile, 1008 ); 1009 } 1010 else { 1011 $profile = new stdClass(); 1012 } 1013 1014 if ($mode == 'add') { 1015 $data = ckeditor_profile_load('CKEditor Global Profile'); 1016 if (!empty($data)) { 1017 drupal_set_message(t('Global profile already exist. Only one global profile is allowed.'), 'error'); 1018 drupal_not_found(); 1019 } 1020 1021 $btn = t('Create global profile'); 1022 } 1023 else { 1024 $btn = t('Update global profile'); 1025 } 1026 1027 $form['common'] = array( 1028 '#type' => 'fieldset', 1029 '#title' => t('Main setup'), 1030 '#collapsible' => TRUE, 1031 '#collapsed' => TRUE 1032 ); 1033 1034 $roles = ckeditor_sorted_roles(); 1035 $rids = $rtext = array(); 1036 foreach ($roles as $rid => $name) { 1037 $rids[] = $rid; 1038 $rtext[] = '<strong>'. $rid .' - </strong>'. $name; 1039 } 1040 $form['common']['rank'] = array( 1041 '#type' => 'textfield', 1042 '#title' => t('Role precedence'), 1043 '#default_value' => implode('>', $rids), 1044 '#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.'), 1045 ); 1046 if ($rids) { 1047 $form['common']['rank']['#description'] .= '<br />'. t('Here is the id-name pairs of roles having access to CKEditor:') .'<div>'. implode('<br />', $rtext) .'</div>'; 1048 } 1049 else { 1050 $form['common']['rank']['#description'] .= '<br />'. t('You haven\'t assigned the "access ckeditor" <a href="!permissions">permissions</a> yet.', array('!permissions' => url('admin/user/permissions'))); 1051 } 1052 1053 $form['ckeditor_exclude_settings'] = array( 1054 '#type' => 'fieldset', 1055 '#title' => t('Visibility settings'), 1056 '#collapsible' => TRUE, 1057 '#collapsed' => TRUE, 1058 '#description' => t('The following settings are combined with the visibility settings of the specific profile.'), 1059 ); 1060 1061 $form['ckeditor_exclude_settings']['excl_mode'] = array( 1062 '#type' => 'radios', 1063 '#title' => t('Use inclusion or exclusion mode'), 1064 '#default_value' => (empty($profile->settings['excl_mode']) || in_array($profile->settings['excl_mode'], array(0, 2))) ? 0 : 1, 1065 '#options' => array( 1066 '0' => t('Exclude'), 1067 '1' => t('Include') 1068 ), 1069 '#description' => t('Choose the way of disabling/enabling CKEditor on selected fields/paths (see below). Use exclude to disable CKEditor on selected fields/paths. Use include if you want to load CKEditor only on selected paths/fields.'), 1070 ); 1071 /** 1072 * get excluded fields - so we can have normal textareas too 1073 * split the phrase by any number of commas or space characters, 1074 * which include " ", \r, \t, \n and \f 1075 */ 1076 $form['ckeditor_exclude_settings']['excl'] = array( 1077 '#type' => 'textarea', 1078 '#title' => t('Fields to exclude/include'), 1079 '#cols' => 60, 1080 '#rows' => 5, 1081 '#prefix' => '<div style="margin-left:20px">', 1082 '#suffix' => '</div>', 1083 '#default_value' => !empty($profile->settings['excl']) ? $profile->settings['excl'] : '', 1084 '#description' => t('Enter the paths to the textarea fields on which you want to enable or disable CKEditor.') .' '. t('Please see the <a href="!helppagelink">help page</a> for more information about defining field names. Short instruction is available below.', array('!helppagelink' => url('admin/help/ckeditor', array('fragment' => 'fieldinclexcl')))) .' <ul><li>'. t('Path structure: <strong>content_type@path.element_id</strong>') .'</li><li>'. t('The following wildcards are available: "*", "?".') .'</li><li>'. t('Content type is optional. You may even specify only path or field id.') .'</li><li>'. t('Examples:') .'<ul><li><em>blog@*.edit-body</em> - '. t('matches all fields of type "blog" called edit-body, on any page.') .'<li><em>node/add/*.edit-user-*</em> - '. t('matches fields starting with "edit-user-" on pages starting with "node/add/') .'</li></ul></li></ul>', 1085 '#wysiwyg' => FALSE, 1086 ); 1087 1088 $form['ckeditor_exclude_settings']['simple_incl'] = array( 1089 '#type' => 'textarea', 1090 '#title' => t('Force simplified toolbar on the following fields'), 1091 '#cols' => 60, 1092 '#rows' => 5, 1093 '#default_value' => !empty($profile->settings['simple_incl']) ? $profile->settings['simple_incl'] : '', 1094 '#description' => t('Enter the paths to the textarea fields on which you want to force the simplified toolbar (!name).', array('!name' => CKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME)) .' '. t('Please see the <a href="!helppagelink">help page</a> for more information about defining field names. Take a look at the exclusion settings (above) for short instruction.', array('!helppagelink' => url('admin/help/ckeditor', array('fragment' => 'fieldinclexcl')))), 1095 '#wysiwyg' => FALSE, 1096 ); 1097 1098 $form['ckeditor_advanced_settings'] = array( 1099 '#type' => 'fieldset', 1100 '#title' => t('Advanced settings'), 1101 '#collapsible' => TRUE, 1102 '#collapsed' => TRUE, 1103 ); 1104 1105 $module_drupal_path = drupal_get_path('module', 'ckeditor'); 1106 1107 $form['ckeditor_advanced_settings']['ckeditor_path'] = array( 1108 '#type' => 'textfield', 1109 '#title' => t('Path to CKEditor'), 1110 '#default_value' => !empty($profile->settings['ckeditor_path']) ? $profile->settings['ckeditor_path'] : '%m/ckeditor', 1111 '#size' => 40, 1112 '#maxlength' => 128, 1113 '#description' => t('Path to CKEditor (the HTML editor, downloaded from <a href="!ckeditorcom">ckeditor.com</a>) relative to the document root.') . 1114 '<br />'. 1115 t('Available placeholders:!b - base URL path of the Drupal installation (!base).!m - base URL path where CKEditor module is stored (!files).<br />Current path: !path', 1116 array( 1117 '!b' => '<br /><strong>%b</strong>', 1118 '!m' => '<br /><strong>%m</strong>', 1119 '!path' => ckeditor_path(FALSE), 1120 '!base' => base_path(), 1121 '!files' => base_path() . $module_drupal_path , 1122 '!ckeditorcom' => 'http://ckeditor.com/download' 1123 )), 1124 '#required' => TRUE 1125 ); 1126 1127 $form['ckeditor_advanced_settings']['ckeditor_local_path'] = array( 1128 '#type' => 'textfield', 1129 '#title' => t('Local path to CKEditor'), 1130 '#default_value' => isset($profile->settings['ckeditor_local_path'])?$profile->settings['ckeditor_local_path']:'', 1131 '#size' => 40, 1132 '#maxlength' => 128, 1133 '#description' => t('The path to the local directory (on the server) which points to the path defined above. Enter either an absolute server path or path relative to "index.php". If empty, CKEditor module will try to find the right path.<br />Current path: !path', array('!path' => ckeditor_path(TRUE))), 1134 ); 1135 1136 $form['ckeditor_advanced_settings']['show_fieldnamehint'] = array( 1137 '#type' => 'radios', 1138 '#title' => t('Show field name hint below each rich text editor'), 1139 '#default_value' => !empty($profile->settings['show_fieldnamehint']) ? $profile->settings['show_fieldnamehint'] : 't', 1140 '#options' => array( 1141 't' => t('Yes'), 1142 'f' => t('No') 1143 ), 1144 '#description' => t('This only applies for users with "administer ckeditor" permissions.'), 1145 ); 1146 1147 if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) { 1148 $current_private_dir = !empty($profile->settings['private_dir']) ? $profile->settings['private_dir'] : ''; 1149 $form['ckeditor_advanced_settings']['private_dir'] = array( 1150 '#type' => 'textfield', 1151 '#title' => t('Location of files uploaded with CKEditor in the private folder'), 1152 '#default_value' => !empty($profile->settings['private_dir']) ? $profile->settings['private_dir'] : '', 1153 '#size' => 40, 1154 '#maxlength' => 255, 1155 '#description' => t('The path relative to the location of the private directory where CKEditor should store uploaded files.') . '<br />' . t('<strong>Warning:</strong> CKEditor does not implement any kind of access protection on files available in this location. All files stored in the directory defined above might be accessible by unauthenticated users if there is no information about the file in the Drupal\'s database.') . '<br />' . t('System path to the private folder is: !system_path.', array('!system_path' => realpath(file_directory_path()) . DIRECTORY_SEPARATOR)) . '<br />' . t('Available wildcard characters:') . '<br/><strong>%u</strong> - ' . t('User ID') . '<br /><strong>%n</strong> - ' . t('Username') . '<br />' . t('Current path: !path', array('!path' => $current_private_dir . ' (' . file_create_path($current_private_dir) . ')')), 1156 ); 1157 } 1158 if (function_exists('linktocontent_node_menu') && function_exists('pathfilter_filter')) { 1159 $form['ckeditor_advanced_settings']['linktoc'] = array( 1160 '#type' => 'select', 1161 '#options' => array('p' => t('Link to paths only'), 'n' => t('Link using internal: links'), 'pn' => t('Allow user to select between paths and internal links')), 1162 '#title' => t('Path Filter & Link To Content integration'), 1163 '#default_value' => empty($profile->settings['linktoc']) ? 'p' : $profile->settings['linktoc'], 1164 '#description' => t('With !plink extension it is possible to use internal: links. By default !link extension is linking to nodes using paths.', array('!plink' => l(t('Path Filter'), 'http://drupal.org/project/pathfilter'), '!link' => l(t('Link To Content'), 'http://drupal.org/project/linktocontent'))), 1165 ); 1166 } 1167 1168 $form['submit'] = array( 1169 '#type' => 'submit', 1170 '#value' => $btn 1171 ); 1172 1173 return $form; 1174 } 1175 1176 function ckeditor_admin_global_profile_form_validate($form, &$form_state) { 1177 $edit =& $form_state['values']; 1178 1179 //include mode and all other fields are empty, invalid 1180 if ($edit['excl_mode'] == 1 && empty($edit['excl'])) { 1181 form_set_error('excl_mode', t('Include mode selected, but no paths given. Enter at least one path where CKEditor should appear.')); 1182 } 1183 else { 1184 ckeditor_admin_profile_validate_fieldpaths('excl', $edit['excl']); 1185 } 1186 1187 ckeditor_admin_profile_validate_fieldpaths('simple_incl', $edit['simple_incl']); 1188 } 1189 1190 function ckeditor_admin_global_profile_form_submit($form, &$form_state) { 1191 module_load_include('inc', 'ckeditor', 'includes/ckeditor.lib'); 1192 $edit =& $form_state['values']; 1193 $edit['name'] = 'CKEditor Global Profile'; 1194 1195 if (isset($edit['rank'])) { 1196 $edit['rank'] = explode('>', str_replace(' ', '', $edit['rank'])); 1197 } 1198 1199 if (isset($edit['_profile'])) { 1200 db_query("DELETE FROM {ckeditor_settings} WHERE name = '%s'", $edit['_profile']->name); 1201 db_query("DELETE FROM {ckeditor_role} WHERE name = '%s'", $edit['_profile']->name); 1202 } 1203 1204 //strip whitespaces 1205 if (empty($edit['ckeditor_local_path'])) { 1206 $edit['ckeditor_local_path'] = ''; 1207 } 1208 else { 1209 $edit['ckeditor_local_path'] = trim($edit['ckeditor_local_path']); 1210 } 1211 1212 //strip slash from the end 1213 if (empty($edit['ckeditor_path'])) { 1214 $edit['ckeditor_path'] = ''; 1215 } 1216 $edit['ckeditor_path'] = trim(rtrim($edit['ckeditor_path'], "/")); 1217 if ($edit['ckeditor_path'] && 0 !== strpos($edit['ckeditor_path'], "/") && 0 !== strpos($edit['ckeditor_path'], "%")) { 1218 //ensure that slash is at the beginning 1219 $edit['ckeditor_path'] = "/". $edit['ckeditor_path']; 1220 } 1221 //no slash at the end 1222 $edit['ckeditor_local_path'] = trim(rtrim($edit['ckeditor_local_path'], "/")); 1223 1224 $settings = ckeditor_admin_values_to_settings($edit); 1225 db_query("INSERT INTO {ckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings); 1226 ckeditor_rebuild_selectors($edit['name']); 1227 1228 drupal_set_message(t('CKEditor global profile has been saved.')); 1229 $form_state['redirect'] = 'admin/settings/ckeditor'; 1230 } 1231 1232 /** 1233 * Converts an array of form values to a serialized array that does not 1234 * contain Drupal Form API values 1235 */ 1236 function ckeditor_admin_values_to_settings($values) { 1237 unset($values['name'], $values['rids'], $values['_profile'], $values['op'], $values['submit'], $values['form_build_id'], $values['form_token'], $values['form_id']); 1238 return serialize($values); 1239 } 1240 1241 function ckeditor_admin_profile_validate_fieldpaths($fieldname, $paths) { 1242 $myerrors = array(); 1243 1244 $rules = preg_split('/[\s,]+/', $paths); 1245 1246 foreach ($rules as $rule) { 1247 $rule = trim($rule); 1248 if (!empty($rule) && strpos($rule, '.') === FALSE && strpos($rule, '/') === FALSE && strpos($rule, '-') === FALSE) { 1249 $myerrors[] = t('Rule %rule is ambiguous: please append .* if %rule is a path or prepend *. if %rule is a field', array('%rule' => $rule)); 1250 } 1251 } 1252 1253 if (!empty($myerrors)) { 1254 form_set_error($fieldname, implode('<br/>', $myerrors)); 1255 } 1256 } 1257 1258 function ckeditor_admin_profile_delete_form($form_state, $profile) { 1259 $form = array(); 1260 1261 $form['_profile'] = array( 1262 '#type' => 'value', 1263 '#value' => $profile, 1264 ); 1265 1266 $form['question'] = array( 1267 '#type' => 'item', 1268 '#value' => t('Are you sure that you want to delete the CKEditor profile %profile?', array('%profile' => $profile->name)), 1269 ); 1270 1271 $form['delete'] = array( 1272 '#type' => 'submit', 1273 '#id' => 'delete', 1274 '#value' => t('Delete'), 1275 ); 1276 1277 $form['back'] = array( 1278 '#type' => 'submit', 1279 '#id' => 'back', 1280 '#value' => t('Cancel'), 1281 ); 1282 1283 return $form; 1284 } 1285 1286 function ckeditor_admin_profile_delete_form_submit($form, &$form_state) { 1287 $v =& $form_state['values']; 1288 1289 if ($form_state['clicked_button']['#id'] == 'delete') { 1290 ckeditor_profile_delete($v['_profile']->name); 1291 drupal_set_message(t('Deleted CKEditor profile.')); 1292 } 1293 1294 $form_state['redirect'] = 'admin/settings/ckeditor'; 1295 } 1296 1297 /** 1298 * Rebuilds the regular expression that is used to match the inclusion/exclusion rules 1299 * and the simplified toolbar rules 1300 * 1301 * @param string $name Name of the profile to process. If omitted, all profiles are rebuilt 1302 */ 1303 function ckeditor_rebuild_selectors($name = NULL) { 1304 if ($name == NULL) { 1305 $result = db_query("SELECT * FROM {ckeditor_settings}"); 1306 } 1307 else { 1308 $result = db_query("SELECT * FROM {ckeditor_settings} WHERE name = '%s'", $name); 1309 } 1310 1311 while (($data = db_fetch_object($result))) { 1312 if ($data->settings) { 1313 $settings = unserialize($data->settings); 1314 // [#654626] 1315 if (!isset($settings['excl'])) { 1316 $settings['excl'] = ''; 1317 } 1318 if (!isset($settings['simple_incl'])) { 1319 $settings['simple_incl'] = ''; 1320 } 1321 1322 foreach (array('excl', 'simple_incl') as $var) { 1323 $settings[$var .'_regex'] = ''; 1324 $rules = preg_split('/[\s,]+/', $settings[$var]); 1325 $regex = array(); 1326 1327 if (!empty($rules)) { 1328 foreach ($rules as $rule) { 1329 if (!empty($rule)) { 1330 $rule = ckeditor_parse_rule($rule); 1331 $regex[] = '(?:'. ckeditor_rule_to_regex($rule) .')'; 1332 } 1333 } 1334 1335 if (!empty($regex)) { 1336 $settings[$var .'_regex'] = '#'. implode('|', $regex) .'#'; 1337 } 1338 } 1339 } 1340 1341 db_query("UPDATE {ckeditor_settings} SET settings='%s' WHERE name='%s'", serialize($settings), $data->name); 1342 } 1343 } 1344 } 1345 1346 function ckeditor_rule_create($nodetype = '*', $path = '*', $fieldname = '*') { 1347 $rule = new stdClass(); 1348 $rule->nodetype = $nodetype; 1349 $rule->path = $path; 1350 $rule->field = $fieldname; 1351 1352 return $rule; 1353 } 1354 1355 function ckeditor_parse_rule($rule) { 1356 $ruleobj = new stdClass(); 1357 1358 $atpos = strpos($rule, '@'); 1359 if ($atpos !== FALSE) { 1360 $ruleobj->nodetype = substr($rule, 0, $atpos); 1361 $rule = substr($rule, $atpos + 1); 1362 } 1363 else { 1364 $ruleobj->nodetype = '*'; 1365 } 1366 1367 $dotpos = strpos($rule, '.'); 1368 if ($dotpos === FALSE) { 1369 if (strpos($rule, '/') === FALSE && strpos($rule, '-') !== FALSE) { 1370 // assume it's a field 1371 $ruleobj->path = '*'; 1372 $ruleobj->field = $rule; 1373 } 1374 elseif (strpos($rule, '/') !== FALSE) { 1375 // assume it's a path 1376 $ruleobj->path = $rule; 1377 $ruleobj->field = '*'; 1378 } 1379 else { 1380 return NULL; 1381 } 1382 } 1383 else { 1384 $ruleobj->path = substr($rule, 0, $dotpos); 1385 $ruleobj->field = str_replace('\.', '.', substr($rule, $dotpos + 1)); 1386 } 1387 1388 return $ruleobj; 1389 } 1390 1391 function ckeditor_rule_to_regex($rule) { 1392 static $replace = array('\*' => '.*', '\?' => '.'); 1393 1394 $field = str_replace('.', '\.', $rule->field); 1395 $regex = '^'. preg_quote($rule->nodetype, '#') .'@'. preg_quote($rule->path, '#') .'\.'. preg_quote($field, '#') .'$'; 1396 $regex = strtr($regex, $replace); 1397 1398 return $regex; 1399 } 1400 1401 function ckeditor_rule_to_string($rule) { 1402 $field = str_replace('.', '\.', $rule->field); 1403 $rulestr = ''; 1404 if ($rule->nodetype != '*') { 1405 $rulestr .= $rule->nodetype .'@'; 1406 } 1407 return $rulestr . $rule->path .'.'. $field; 1408 } 1409 1410 /** 1411 * Remove a profile from the database. 1412 */ 1413 function ckeditor_profile_delete($name) { 1414 db_query("DELETE FROM {ckeditor_settings} WHERE name = '%s'", $name); 1415 db_query("DELETE FROM {ckeditor_role} WHERE name = '%s'", $name); 1416 } 1417 1418 function _ckeditor_load_methods() { 1419 module_load_include('module', 'ckeditor'); 1420 $result = array('ckeditor.js' => 'ckeditor.js'); 1421 if (file_exists(ckeditor_path(TRUE) . '/ckeditor_basic.js')) { 1422 $result['ckeditor_basic.js'] = 'ckeditor_basic.js'; 1423 } 1424 if (file_exists(ckeditor_path(TRUE) . '/ckeditor_source.js')) { 1425 $result['ckeditor_source.js'] = 'ckeditor_source.js (' . t('for developers only') . ')'; 1426 } 1427 return $result; 1428 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |