[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/fckeditor/ -> fckeditor.admin.inc (source)

   1  <?php
   2  // $Id: fckeditor.admin.inc,v 1.1.2.53 2010/03/08 11:06:44 jorrit Exp $
   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   * Main administrative page
  35   */
  36  function fckeditor_admin_main() {
  37    $editor_path = fckeditor_path(TRUE);
  38    $fckconfig_file = $editor_path .'/fckconfig.js';
  39    if (!file_exists($fckconfig_file)) {
  40      drupal_set_message(
  41        t('The FCKeditor component appears to be not installed correctly, because %checkfile could not be found.',
  42          array(
  43            '%checkfile' => $fckconfig_file,
  44          )) .'<br/>'.
  45        t('Please go to the !fckeditorlink to download the latest version. After that you must extract the files to %fckeditorpath and make sure that the directory %fckeditorsubdir and the file %fckeditorfile exist. Refer to the !readme for more information.',
  46          array(
  47            '!fckeditorlink' => l(t('FCKeditor homepage'), 'http://ckeditor.com/download'),
  48            '!readme' => l(t('readme.txt'), 'admin/help/fckeditor'),
  49            '%fckeditorpath' => $editor_path,
  50            '%fckeditorsubdir' => $editor_path .'/editor',
  51            '%fckeditorfile' => $editor_path .'/fckeditor.js',
  52          )), 'error');
  53      drupal_set_message(t('If you have FCKeditor already installed please edit !editgloballink and update FCKeditor path.',
  54        array(
  55          '!editgloballink' => l(t('FCKeditor Global Profile'), 'admin/settings/fckeditor/editg'),
  56        )), 'warning');
  57      return FALSE;
  58    }
  59  
  60    $access_fckeditor_roles = user_roles(FALSE, 'access fckeditor');
  61    if (!$access_fckeditor_roles) {
  62      drupal_set_message(t('There is currently no role with the %accesspermission permission. Visit !permissionslink administration section.',
  63        array(
  64          '%accesspermission' => t('access fckeditor'),
  65          '!permissionslink' => l(t('permissions'), 'admin/user/permissions')
  66        )), 'warning');
  67    }
  68    else {
  69      $result = db_query_range("SELECT name FROM {fckeditor_settings} WHERE name <> 'FCKeditor Global Profile'", 0, 1);
  70      $has_profiles = FALSE;
  71      //find profile other than Global
  72      if (($obj = db_fetch_object($result))) {
  73        $has_profiles = TRUE;
  74      }
  75  
  76      //find roles with profiles
  77      $result = db_query("SELECT rid FROM {fckeditor_role}");
  78      $rids = array();
  79      while (($obj = db_fetch_object($result))) {
  80          $rids[] = $obj->rid;
  81      }
  82      $rids = array_unique($rids);
  83      if (!$has_profiles) {
  84        drupal_set_message(t('No FCKeditor profiles found. At this moment, nobody is able to use FCKeditor. Create new profile below.'), 'error');
  85      }
  86      else {
  87        //not all roles with access fckeditor has their FCKeditor profile assigned
  88        $diff = array_diff(array_keys($access_fckeditor_roles), $rids);
  89        if ($diff) {
  90          $items = array();
  91          foreach ($diff as $rid) {
  92            $items[] = $access_fckeditor_roles[$rid];
  93          }
  94          drupal_set_message(t('Not all roles with %accesspermission 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 %basicsetupsection section, check %rolesetting.',
  95            array(
  96              '%accesspermission' => t('access fckeditor'),
  97              '%basicsetupsection' => t('Basic setup'),
  98              '%rolesetting' => t('Roles allowed to use this profile'),
  99              '!list' => theme('item_list', $items),
 100            )), 'warning');
 101        }
 102      }
 103    }
 104  
 105    return fckeditor_profile_overview();
 106  }
 107  
 108  /**
 109   * Controller for fckeditor profiles.
 110   */
 111  function fckeditor_profile_overview() {
 112    $output = '';
 113  
 114    $profiles = fckeditor_profile_load();
 115    if ($profiles) {
 116      $access_fckeditor_roles = user_roles(FALSE, 'access fckeditor');
 117      $header = array(t('Profile'), t('Roles'), t('Operations'));
 118      foreach ($profiles as $p) {
 119        $rids = $p->rids;
 120        if ($p->name !== 'FCKeditor Global Profile') {
 121          foreach (array_keys($p->rids) as $rid) {
 122            if (!isset($access_fckeditor_roles[$rid])) {
 123              unset($rids[$rid]);
 124            }
 125          }
 126          $rows[] = array(
 127            array('data' => $p->name, 'valign' => 'top'),
 128            array('data' => implode("<br />\n", $rids)),
 129            array('data' =>
 130              l(t('edit'), 'admin/settings/fckeditor/edit/'. rawurlencode($p->name)) .' '.
 131              l(t('clone'), 'admin/settings/fckeditor/clone/'. rawurlencode($p->name)) .' '.
 132              l(t('delete'), 'admin/settings/fckeditor/delete/'. rawurlencode($p->name)), 'valign' => 'top'
 133            )
 134          );
 135        }
 136      }
 137      $output .= '<h3>'. t('Profiles') .'</h3>';
 138      $output .= theme('table', $header, $rows);
 139      $output .= '<p>'. l(t('Create new profile'), 'admin/settings/fckeditor/add') .'</p>';
 140    }
 141    else {
 142      drupal_set_message(t('No profiles found. Click here to !createlink.',
 143        array(
 144          '!createlink' => l(t('create a new profile'), 'admin/settings/fckeditor/add'),
 145        )));
 146    }
 147  
 148    $rows = array();
 149    if (!isset($profiles['FCKeditor Global Profile'])) {
 150      drupal_set_message(t('Global profile not found. Click here to !createlink.',
 151        array(
 152          '!createlink' => l(t('create the global profile'), 'admin/settings/fckeditor/addg'),
 153        )));
 154    }
 155    else {
 156      $output .= '<h3>'. t('Global settings') .'</h3>';
 157      $rows[] = array(
 158        array('data' => t('FCKeditor Global Profile'), 'valign' => 'top'),
 159        array('data' => l(t('edit'), 'admin/settings/fckeditor/editg') .' '. l(t('delete'), 'admin/settings/fckeditor/delete/FCKeditor Global Profile'), 'valign' => 'top')
 160      );
 161      $output .= theme('table', array(t('Profile'), t('Operations')), $rows);
 162    }
 163    return $output;
 164  }
 165  
 166  /**
 167   * Clone profile
 168   */
 169  function fckeditor_admin_profile_clone_form($form_state, $oldprofile) {
 170    return fckeditor_admin_profile_form($form_state, $oldprofile);
 171  }
 172  
 173  function fckeditor_admin_profile_clone_form_validate($form_state, $oldprofile) {
 174    fckeditor_admin_profile_form_validate($form_state, $oldprofile);
 175  }
 176  
 177  function fckeditor_admin_profile_clone_form_submit($form, &$form_state) {
 178    $edit =& $form_state['values'];
 179    drupal_set_message(t('Your FCKeditor profile has been created.'));
 180    $settings = fckeditor_admin_values_to_settings($edit);
 181    db_query("INSERT INTO {fckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings);
 182    fckeditor_rebuild_selectors($edit['name']);
 183    if (!empty($edit['rids'])) {
 184      foreach (array_keys($edit['rids']) as $rid) {
 185        if ($edit['rids'][$rid]!=0) {
 186          db_query("INSERT INTO {fckeditor_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid);
 187        }
 188      }
 189    }
 190    $form_state['redirect'] = 'admin/settings/fckeditor';
 191  }
 192  
 193  /**
 194   * Form builder for a normal profile
 195   */
 196  function fckeditor_admin_profile_form($form_state, $profile = NULL) {
 197    if ($profile != NULL) {
 198      $form['_profile'] = array(
 199        '#type' => 'value',
 200        '#value' => $profile,
 201      );
 202    }
 203    else {
 204      $profile = new stdClass();
 205    }
 206  
 207    module_load_include('lib.inc', 'fckeditor');
 208  
 209    $toolbar_options = fckeditor_load_toolbar_options();
 210    $skin_options = fckeditor_load_skin_options();
 211    $lang_options = fckeditor_load_lang_options();
 212  
 213    // Only display the roles that currently don't have a fckeditor profile. One
 214    // profile per role.
 215    $orig_roles = user_roles(FALSE, 'access fckeditor');
 216    $roles = $orig_roles;
 217  
 218    if (!empty($profile->rids) && !user_roles(FALSE, 'access fckeditor')) {
 219      drupal_set_message(t('You haven\'t assigned %accesspermission !permissionslink yet. It is recommended to assign the %accesspermission !permissionslink before updating FCKeditor profiles.',
 220        array(
 221          '%accesspermission' => t('access fckeditor'),
 222          '!permissionslink' => l(t('permission'), 'admin/user/permissions'),
 223        )), 'warning');
 224    }
 225  
 226    if (empty($profile->name)) {
 227      $result = db_query("SELECT DISTINCT(rid) FROM {fckeditor_role}");
 228      while (($data = db_fetch_object($result))) {
 229        if ((empty($profile->rids) || !in_array($data->rid, array_keys((array) $profile->rids))) && !form_get_errors()) {
 230          unset($roles[$data->rid]);
 231        }
 232      }
 233      if (count($orig_roles) != count($roles)) {
 234        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.'));
 235      }
 236    }
 237  
 238    $form['basic'] = array(
 239      '#type' => 'fieldset',
 240      '#title' => t('Basic setup'),
 241      '#collapsible' => TRUE,
 242      '#collapsed' => TRUE
 243    );
 244  
 245    if (arg(3) == 'clone') {
 246      //load all profiles to check their names
 247      $profiles = fckeditor_profile_load();
 248      $oldname = $profile->name;
 249      $maxsize=128;   //default max name length
 250  
 251      $res=array();
 252      $pat = "/^(?P<name>.*?)_(?P<n>[0-9]*)$/";
 253      if (preg_match_all($pat, $oldname, $res)) {     // oldname like 'name_nr'
 254        $name=$res['name'][0];
 255        $num=$res['n'][0]+1;
 256      }
 257      else{
 258        $name=$oldname;
 259        $num=2;
 260      }
 261  
 262      $newname=substr($name, 0, $maxsize-3) .'_'. $num;   // +limit
 263      while (isset($profiles[$newname])) {            //find next free number
 264        $num++;
 265        $newname=substr($name, 0, $maxsize-3) .'_'. $num;
 266  
 267      }
 268      //dont clone rids
 269      $profile->settings['rids']=array();
 270      $profile->rids=array();
 271    }
 272    else {
 273      $newname = $profile->name;
 274    }
 275  
 276    $form['basic']['name'] = array(
 277      '#type' => 'textfield',
 278      '#title' => t('Profile name'),
 279      '#default_value' => !empty($profile->name) ? $newname : '',
 280      '#size' => 40,
 281      '#maxlength' => 128,
 282      '#description' => t('Enter a name for this profile. This name is only visible within the fckeditor administration page.'),
 283      '#required' => TRUE
 284    );
 285  
 286    $form['basic']['rids'] = array(
 287      '#type' => 'checkboxes',
 288      '#title' => t('Roles allowed to use this profile'),
 289      '#default_value' => !empty($profile->rids) ? array_keys((array) $profile->rids) : array(),
 290      '#options' => $roles,
 291      '#description' =>  t('Only roles with %accesspermission permission will be shown here. If no role is available, make sure that you have assigned the %accesspermission !permissionslink.',
 292        array(
 293          '%accesspermission' => t('access fckeditor'),
 294          '!permissionslink' => l(t('permission'), 'admin/user/permissions'),
 295        )),
 296      '#required' => TRUE
 297    );
 298  
 299    $form['basic']['allow_user_conf'] = array(
 300      '#type' => 'radios',
 301      '#title' => t('Allow users to customize FCKeditor appearance'),
 302      '#default_value' => !empty($profile->settings['allow_user_conf']) ? $profile->settings['allow_user_conf'] : 'f',
 303      '#options' => array(
 304        'f' => t('No'),
 305        't' => t('Yes')
 306      ),
 307      '#description' => t('If allowed, users will be able to override the %appearancesection by visiting their profile page.', array('%appearancesection' => t('Editor appearance'))),
 308    );
 309  
 310    $form['security'] = array(
 311      '#type' => 'fieldset',
 312      '#title' => t('Security'),
 313      '#description' => '<p>'. t('The FCKeditor 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 FCKeditor WYSIWYG editor evaluates HTML like a web browser and content needs to be filtered before it is loaded.') .'</p>',
 314      '#collapsible' => TRUE,
 315      '#collapsed' => TRUE
 316    );
 317  
 318    $all = filter_list_all();
 319  
 320    $form['security']['filters'] = array(
 321      '#type' => 'fieldset',
 322      '#title' => t('Security filters'),
 323      '#description' => t('Please choose carefully all filters that protect your content (probably not all filters listed below are security filters).'),
 324      '#tree' => TRUE,
 325    );
 326  
 327    //don't bother administrator with filters that definitely are not security filters
 328    $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', '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');
 329  
 330    if (!isset($profile->settings['ss'])) {
 331      $profile->settings['filters']['filter/0'] = 1;
 332    }
 333  
 334    foreach ($all as $id => $filter) {
 335      if (in_array(strtolower($filter->module), $modules_with_filters_to_skip)) {
 336        continue;
 337      }
 338      //skip line break converter and email -> link
 339      if ($filter->module == 'filter' && in_array($filter->delta, array(1, 2))) {
 340        continue;
 341      }
 342      $form['security']['filters'][$id] = array(
 343        '#type' => 'checkbox',
 344        '#title' => $filter->name,
 345        '#default_value' => !empty($profile->settings['filters'][$id]),
 346        '#description' => module_invoke($filter->module, 'filter', 'description', $filter->delta),
 347      );
 348    }
 349  
 350    $form['security']['ss'] = array(
 351      '#type' => 'radios',
 352      '#title' => t('Security settings'),
 353      '#default_value' => isset($profile->settings['ss']) ? $profile->settings['ss'] : '2',
 354      '#options' => array(
 355        '2' => t('Always run security filters for FCKeditor.'),
 356        '1' => t('Run security filters only when FCKeditor is set to start automatically.'),
 357      ),
 358      '#description' => t('There are two ways of starting FCKeditor: automatically and manually (via toggle or in a popup). If you decide to apply security filters only when FCKeditor starts automatically, you will not be protected when toggling manually from plain text area to FCKeditor or when using FCKeditor 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.'),
 359    );
 360  
 361    $form['fckeditor_exclude_settings'] = array(
 362      '#type' => 'fieldset',
 363      '#title' => t('Visibility settings'),
 364      '#collapsible' => TRUE,
 365      '#collapsed' => TRUE,
 366      '#description' => t('The following settings are combined with the visibility settings of the global profile.'),
 367    );
 368  
 369    $form['fckeditor_exclude_settings']['min_rows'] = array(
 370      '#type' => 'textfield',
 371      '#title' => t('Minimum rows'),
 372      '#default_value' => !empty($profile->settings['min_rows']) ? $profile->settings['min_rows'] : '5',
 373      '#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.'),
 374    );
 375  
 376    $form['fckeditor_exclude_settings']['excl_mode'] = array(
 377      '#type' => 'radios',
 378      '#title' => t('Use inclusion or exclusion mode'),
 379      '#default_value' => (empty($profile->settings['excl_mode']) || in_array($profile->settings['excl_mode'], array(0, 2))) ? 0 : 1,
 380      '#options' => array(
 381        '0' => t('Exclude'),
 382        '1' => t('Include')
 383      ),
 384      '#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.'),
 385    );
 386  
 387    /**
 388     * get excluded fields - so we can have normal textareas too
 389     * split the phrase by any number of commas or space characters,
 390     * which include " ", \r, \t, \n and \f
 391     */
 392    $form['fckeditor_exclude_settings']['excl'] = array(
 393      '#type' => 'textarea',
 394      '#title' => t('Fields to exclude/include'),
 395      '#cols' => 60,
 396      '#rows' => 5,
 397      '#prefix' => '<div style="margin-left:20px">',
 398      '#suffix' => '</div>',
 399      '#default_value' => !empty($profile->settings['excl']) ? $profile->settings['excl'] : '',
 400      '#description' =>
 401        t('Enter the paths to the textarea fields on which you want to enable or disable FCKeditor.') .' '.
 402        t('Please see the !helppagelink for more information about defining field names. Short instruction is available below.',
 403          array(
 404            '!helppagelink' => l(t('help page'), 'admin/help/fckeditor', array('fragment' => 'fieldinclexcl')),
 405          )) .
 406        ' <ul>' .
 407        '<li>'. t('Path structure') .': <strong>content_type@path.element_id</strong></li>'.
 408        '<li>'. t('The following wildcards are available: *, ?') .'</li>'.
 409        '<li>'. t('Content type is optional. You may even specify only path or field id') .'</li>'.
 410        '<li>'. t('Examples') .':<ul>'.
 411        '<li><em>blog@*.edit-body</em> - '. t('matches all fields of type %type called %field, on any page', array('%type' => 'blog', '%field' => 'edit-body')) .
 412        '<li><em>node/add/*.edit-user-*</em> - '. t('matches fields starting with %field on pages starting with %path', array('%field' => 'edit-user-', '%path' => 'node/add/')) .'</li></ul>'.
 413        '</li></ul>',
 414      '#wysiwyg' => FALSE,
 415    );
 416  
 417    $form['fckeditor_exclude_settings']['simple_incl'] = array(
 418      '#type' => 'textarea',
 419      '#title' => t('Force simplified toolbar on the following fields'),
 420      '#cols' => 60,
 421      '#rows' => 5,
 422      '#default_value' => !empty($profile->settings['simple_incl']) ? $profile->settings['simple_incl'] : '',
 423      '#description' =>
 424        t('Enter the paths to the textarea fields on which you want to force the simplified toolbar (%toolbarname).',
 425          array(
 426            '%toolbarname' => FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME,
 427          )) .' '.
 428        t('Please see the !helppagelink for more information about defining field names. Take a look at the exclusion settings (above) for short instruction.',
 429          array(
 430            '!helppagelink' => l(t('help page'), 'admin/help/fckeditor', array('fragment' => 'fieldinclexcl')),
 431          )),
 432      '#wysiwyg' => FALSE,
 433    );
 434  
 435    $form['appearance'] = array(
 436      '#type' => 'fieldset',
 437      '#title' => t('Editor appearance'),
 438      '#collapsible' => TRUE,
 439      '#collapsed' => TRUE,
 440    );
 441  
 442    $form['appearance']['default'] = array(
 443      '#type' => 'radios',
 444      '#title' => t('Default state'),
 445      '#default_value' => !empty($profile->settings['default']) ? $profile->settings['default'] : 't',
 446      '#options' => array(
 447        't' => t('Enabled'),
 448        'f' => t('Disabled')
 449      ),
 450      '#description' => t('Default editor state. If disabled, rich text editor may still be enabled using toggle or popup window.'),
 451    );
 452  
 453    $form['appearance']['show_toggle'] = array(
 454      '#type' => 'radios',
 455      '#title' => t('Show disable/enable rich text editor toggle'),
 456      '#default_value' => !empty($profile->settings['show_toggle']) ? $profile->settings['show_toggle'] : 't',
 457      '#options' => array(
 458        't' => t('Show'),
 459        'f' => t('Hide')
 460      ),
 461      '#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).'),
 462    );
 463  
 464    $form['appearance']['popup'] = array(
 465      '#type' => 'radios',
 466      '#title' => t('Use FCKeditor in a popup window'),
 467      '#default_value' => !empty($profile->settings['popup']) ? $profile->settings['popup'] : 'f',
 468      '#options' => array(
 469        'f' => t('No'),
 470        't' => t('Yes')
 471      ),
 472      '#description' => t('If this option is enabled a link to a popup window will be used instead of a textarea replace.'),
 473    );
 474  
 475    $form['appearance']['skin'] = array(
 476      '#type' => 'select',
 477      '#title' => t('Skin'),
 478      '#default_value' => !empty($profile->settings['skin']) ? $profile->settings['skin'] : 'default',
 479      '#options' => $skin_options,
 480      '#description' => t('Choose a default skin.'),
 481    );
 482  
 483    $form['appearance']['toolbar'] = array(
 484      '#type' => 'select',
 485      '#title' => t('Toolbar'),
 486      '#default_value' => !empty($profile->settings['toolbar']) ? $profile->settings['toolbar'] : 'default',
 487      '#options' => $toolbar_options,
 488      '#description' => t('Choose a default toolbar set. To define new toolbar, edit %configfile located in %module_path.',
 489        array(
 490          '%configfile' => 'fckeditor.config.js',
 491          '%module_path' => drupal_get_path('module', 'fckeditor'),
 492        )),
 493    );
 494  
 495    $form['appearance']['expand'] = array(
 496      '#type' => 'radios',
 497      '#title' => t('Start the toolbar expanded'),
 498      '#default_value' => !empty($profile->settings['expand']) ? $profile->settings['expand'] : 't',
 499      '#options' => array(
 500        't' => t('Expanded'),
 501        'f' => t('Collapsed')
 502      ),
 503      '#description' => t('The toolbar start expanded or collapsed.'),
 504    );
 505  
 506    $form['appearance']['width'] = array(
 507      '#type' => 'textfield',
 508      '#title' => t('Width'),
 509      '#default_value' => !empty($profile->settings['width']) ? $profile->settings['width'] : '100%',
 510      '#description' => t('Width in pixels or percent.') .' '. t('Example') .': 400 '. t('or') .' 100%.',
 511      '#size' => 40,
 512      '#maxlength' => 128,
 513    );
 514  
 515    $form['appearance']['lang'] = array(
 516      '#type' => 'select',
 517      '#title' => t('Language'),
 518      '#default_value' => !empty($profile->settings['lang']) ? $profile->settings['lang'] : 'en',
 519      '#options' => $lang_options,
 520      '#description' => t('The language for the FCKeditor interface.')
 521    );
 522  
 523    $form['appearance']['auto_lang'] = array(
 524      '#type' => 'radios',
 525      '#title' => t('Auto-detect language'),
 526      '#default_value' => !empty($profile->settings['auto_lang']) ? $profile->settings['auto_lang'] : 't',
 527      '#options' => array(
 528        't' => t('Enabled'),
 529        'f' => t('Disabled')
 530      ),
 531      '#description' => t('Use auto detect user language feature.')
 532    );
 533  
 534    $form['output'] = array(
 535      '#type' => 'fieldset',
 536      '#title' => t('Cleanup and output'),
 537      '#collapsible' => TRUE,
 538      '#collapsed' => TRUE,
 539    );
 540  
 541    $form['output']['enter_mode'] = array(
 542      '#type' => 'select',
 543      '#title' => t('Enter mode'),
 544      '#default_value' => !empty($profile->settings['enter_mode']) ? $profile->settings['enter_mode'] : 'p',
 545      '#options' => array(
 546        'p' => '<p>',
 547        'br' => '<br />',
 548        'div' => '<div>'
 549      ),
 550      '#description' => t('Set which tag FCKeditor should use when [Enter] key is pressed.')
 551    );
 552  
 553    $form['output']['shift_enter_mode'] = array(
 554      '#type' => 'select',
 555      '#title' => t('Shift + Enter mode'),
 556      '#default_value' => !empty($profile->settings['shift_enter_mode']) ? $profile->settings['shift_enter_mode'] : 'br',
 557      '#options' => array(
 558        'p' => '<p>',
 559        'br' => '<br />',
 560        'div' => '<div>'
 561      ),
 562      '#description' => t('Set which tag FCKeditor should use when [Shift] + [Enter] is pressed.')
 563    );
 564  
 565    $form['output']['font_format'] = array(
 566      '#type' => 'textfield',
 567      '#title' => t('Font formats'),
 568      '#default_value' => !empty($profile->settings['font_format']) ? $profile->settings['font_format'] : 'p;div;pre;address;h1;h2;h3;h4;h5;h6',
 569      '#size' => 40,
 570      '#maxlength' => 250,
 571      '#description' => t('Semicolon separated list of HTML font formats.') .' '. t('Allowed values are') .': p;div;pre;address;h1;h2;h3;h4;h5;h6',
 572    );
 573  
 574    $form['output']['format_source'] = array(
 575      '#type' => 'radios',
 576      '#title' => t('Apply source formatting'),
 577      '#default_value' => !empty($profile->settings['format_source']) ? $profile->settings['format_source'] : 't',
 578      '#options' => array(
 579        't' => t('Yes'),
 580        'f' => t('No')
 581      ),
 582      '#description' =>
 583        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.',
 584        array(
 585          '%true' => t('Yes'),
 586        )),
 587    );
 588  
 589    $form['output']['format_output'] = array(
 590      '#type' => 'radios',
 591      '#title' => t('Format output'),
 592      '#default_value' => !empty($profile->settings['format_output']) ? $profile->settings['format_output'] : 't',
 593      '#options' => array(
 594        't' => t('Yes'),
 595        'f' => t('No')
 596      ),
 597      '#description' =>
 598        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.',
 599        array(
 600          '%true' => t('Yes'),
 601        )),
 602    );
 603  
 604    $form['output']['autofixplaintext'] = array(
 605      '#type' => 'radios',
 606      '#title' => t('Automatically detect and convert plain text'),
 607      '#default_value' => !empty($profile->settings['autofixplaintext']) ? $profile->settings['autofixplaintext'] : 'f',
 608      '#options' => array(
 609        't' => t('Yes'),
 610        'f' => t('No')
 611      ),
 612      '#description' =>
 613        t('When set to %true the editor will try to detect if a field was created using the plain text editor and convert line breaks to &lt;br /&gt; and &lt;p&gt; tags. This will alter the field contents and is not reversible.',
 614        array(
 615          '%true' => t('Yes'),
 616        )),
 617    );
 618  
 619    $form['css'] = array(
 620      '#type' => 'fieldset',
 621      '#title' => t('Style and template files'),
 622      '#collapsible' => TRUE,
 623      '#collapsed' => TRUE
 624    );
 625  
 626    $form['css']['css_mode'] = array(
 627      '#type' => 'select',
 628      '#title' => t('Editor CSS'),
 629      '#default_value' => !empty($profile->settings['css_mode']) ? $profile->settings['css_mode'] : 'theme',
 630      '#options' => array(
 631        'theme' => t('Use theme CSS'),
 632        'self' => t('Define CSS'),
 633        'none' => t('FCKeditor default')
 634      ),
 635      '#description' =>
 636        t('Defines the CSS to be used in the editor area.') .'<br />'.
 637        t('Use theme CSS') .': '. t('load style.css from current site theme.') .'<br/>'.
 638        t('Define CSS') .': '. t('enter path for CSS file below.') .'<br />'.
 639        t('FCKeditor default') .': '. t('uses default CSS from editor.')
 640    );
 641  
 642    $form['css']['css_path'] = array(
 643      '#type' => 'textfield',
 644      '#title' => t('CSS path'),
 645      '#default_value' => !empty($profile->settings['css_path']) ? $profile->settings['css_path'] : "",
 646      '#size' => 40,
 647      '#maxlength' => 255,
 648      '#description' =>
 649        t('Enter path to CSS file (Example: %cssexample1) or a list of CSS files separated by a comma (Example: %cssexample2). Make sure to select %defcss above.',
 650          array(
 651            '%cssexample1' => 'css/editor.css',
 652            '%cssexample2' => '/themes/garland/style.css,http://example.com/style.css',
 653            '%defcss' => 'Define css',
 654          )) .'<br />'.
 655        t('Available placeholders') .':<br />'.
 656        '<strong>%h</strong> - '. t('host name (%host)', array('%host' => base_path())) .'<br />'.
 657        '<strong>%t</strong> - '. t('path to theme (%theme)', array('%theme' => base_path() . fckeditor_path_to_theme() .'/'))
 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 fckstyles.xml'),
 666        'self' => t('Define path to fckstyles.xml'),
 667        'default' => t('FCKeditor default')
 668      ),
 669      '#description' =>
 670        t('Define the location of %file file. It is used by the %setting dropdown list available in the default toolbar. Copy %sourcefile inside your theme directory (%themefile) and adjust it to your needs.',
 671          array(
 672            '%file' => 'fckstyles.xml',
 673            '%setting' => t('Style'),
 674            '%sourcefile' => fckeditor_path(TRUE) .'/fckstyles.xml',
 675            '%themefile' => fckeditor_path_to_theme() .'/fckstyles.xml',
 676          ))
 677    );
 678  
 679    $form['css']['styles_path'] = array(
 680      '#type' => 'textfield',
 681      '#title' => t('Predefined styles path'),
 682      '#default_value' => !empty($profile->settings['styles_path']) ? $profile->settings['styles_path'] : "",
 683      '#size' => 40,
 684      '#maxlength' => 255,
 685      '#description' =>
 686        t('Enter the path to XML file with predefined @type (Example: %example). Be sure to select %setting above.',
 687          array(
 688            '@type' => t('styles'),
 689            '%example' => '/fckstyles.xml',
 690            '%setting' => t('Define path to fckstyles.xml'),
 691          )) .'<br />'.
 692        t('Available placeholders') .':<br />'.
 693        '<strong>%h</strong> - '. t('host name (%host)', array('%host' => base_path())) .'<br />'.
 694        '<strong>%t</strong> - '. t('path to theme (%theme)', array('%theme' => base_path() . fckeditor_path_to_theme() .'/')) .'<br />'.
 695        '<strong>%m</strong> - '. t('path to FCKeditor module (%module)', array('%module' => drupal_get_path('module', 'fckeditor'))),
 696    );
 697  
 698    $form['css']['templatefile_mode'] = array(
 699      '#type' => 'select',
 700      '#title' => t('Predefined templates'),
 701      '#default_value' => !empty($profile->settings['templatefile_mode']) ? $profile->settings['templatefile_mode'] : 'default',
 702      '#options' => array(
 703        'default' => t('FCKeditor default'),
 704        'theme' => t('Use theme fcktemplates.xml'),
 705        'self' => t('Define path to fcktemplates.xml'),
 706      ),
 707      '#description' =>
 708        t('Define the location of %file file. It is used by the %setting dropdown list available in the default toolbar. Copy %sourcefile inside your theme directory (%themefile) and adjust it to your needs.',
 709          array(
 710            '%file' => 'fcktemplates.xml',
 711            '%setting' => t('Templates'),
 712            '%sourcefile' => fckeditor_path(TRUE) .'/fcktemplates.xml',
 713            '%themefile' => fckeditor_path_to_theme() .'/fcktemplates.xml',
 714          ))
 715    );
 716  
 717    $form['css']['templatefile_path'] = array(
 718      '#type' => 'textfield',
 719      '#title' => t('Predefined templates path'),
 720      '#default_value' => !empty($profile->settings['templatefile_path']) ? $profile->settings['templatefile_path'] : '',
 721      '#size' => 40,
 722      '#maxlength' => 255,
 723      '#description' =>
 724        t('Enter the path to XML file with predefined @type (Example: %example). Be sure to select %setting above.',
 725          array(
 726            '@type' => t('templates'),
 727            '%example' => '/fckstyles.xml',
 728            '%setting' => t('Define path to fckstyles.xml'),
 729          )) .'<br />'.
 730        t('Available placeholders') .':<br />'.
 731        '<strong>%h</strong> - '. t('host name (%host)', array('%host' => base_path())) .'<br />'.
 732        '<strong>%t</strong> - '. t('path to theme (%theme)', array('%theme' => base_path() . fckeditor_path_to_theme() .'/')) .'<br />'.
 733        '<strong>%m</strong> - '. t('path to FCKeditor module (%module)', array('%module' => drupal_get_path('module', 'fckeditor'))),
 734    );
 735  
 736    $form['fckeditor_upload_settings'] = array(
 737      '#type' => 'fieldset',
 738      '#title' => t('File browser settings'),
 739      '#collapsible' => TRUE,
 740      '#collapsed' => TRUE,
 741      '#description' =>
 742        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 !imcelink, !iblink or !webfmlink. 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 !readmelink for more information.',
 743          array(
 744            '!imcelink' => l(t('IMCE'), 'http://drupal.org/project/imce'),
 745            '!iblink' => l(t('Image Browser'), 'http://drupal.org/project/imagebrowser'),
 746            '!webfmlink' => l(t('Web File Manager'), 'http://drupal.org/project/webfm'),
 747            '!readmelink' => l(t('readme.txt'), 'admin/help/fckeditor'),
 748          ))
 749    );
 750  
 751    $filebrowsers = array(
 752      'none' => t('None'),
 753      'builtin' => t('Built-in file browser'),
 754    );
 755  
 756    if (module_exists('imce')) {
 757      $filebrowsers['imce'] = t('IMCE');
 758    }
 759  
 760    if (module_exists('imagebrowser')) {
 761      $filebrowsers['ib'] = t('Image Browser');
 762    }
 763  
 764    if (module_exists('webfm_popup')) {
 765      $filebrowsers['webfm'] = t('Web File Manager');
 766    }
 767  
 768    $form['fckeditor_upload_settings']['filebrowser'] = array(
 769      '#type' => 'select',
 770      '#title' => t('File browser type'),
 771      '#default_value' => !empty($profile->settings['filebrowser']) ? $profile->settings['filebrowser'] : 'none',
 772      '#options' => $filebrowsers,
 773      '#description' => t('Select the file browser that you would like to use to upload files, images and flash movies.'),
 774    );
 775  
 776    $form['fckeditor_upload_settings']['quickupload'] = array(
 777      '#type' => 'radios',
 778      '#title' => t('Allow quick uploads'),
 779      '#default_value' => !empty($profile->settings['quickupload']) ? $profile->settings['quickupload'] : 'f',
 780      '#options' => array(
 781        'f' => t('No'),
 782        't' => t('Yes')
 783      ),
 784      '#description' => t('The quick upload functionality can be disabled and enabled independently of the file browser. It will always use the settings below. To enable quick uploads you must follow the same configuration procedure as when enabling the built-in file browser.'),
 785    );
 786  
 787    $current_user_files_path = empty($profile->settings['UserFilesPath']) ? "" : strtr($profile->settings['UserFilesPath'], array("%f" => file_directory_path(), "%u" => "UID", "%b" => base_path(), "%n" => "UNAME"));
 788    $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"));
 789  
 790    $form['fckeditor_upload_settings']['UserFilesPath'] = array(
 791      '#type' => 'textfield',
 792      '#title' => t('Path to uploaded files'),
 793      '#default_value' => !empty($profile->settings['UserFilesPath']) ? $profile->settings['UserFilesPath'] : "%b%f/",
 794      '#size' => 40,
 795      '#maxlength' => 255,
 796      '#description' =>
 797        t('Path to uploaded files relative to the document root.') .'<br />'.
 798        t('Available placeholders') .':<br/>'.
 799        '<strong>%b</strong> - '. t('base URL path of the Drupal installation (%base)', array('%base' => base_path())) .'<br/>'.
 800        '<strong>%f</strong> - '. t('Drupal file system path where the files are stored (%files)', array('%files' => file_directory_path())) .'<br />'.
 801        '<strong>%u</strong> - '. t('User ID') .'<br />'.
 802        '<strong>%n</strong> - '. t('Username') .'<br />'.
 803        t('Current path: %path', array('%path' => $current_user_files_path)),
 804    );
 805  
 806    $form['fckeditor_upload_settings']['UserFilesAbsolutePath'] = array(
 807      '#type' => 'textfield',
 808      '#title' => t('Absolute path to uploaded files'),
 809      '#default_value' => !empty($profile->settings['UserFilesAbsolutePath']) ? $profile->settings['UserFilesAbsolutePath'] : "%d%b%f/",
 810      '#size' => 40,
 811      '#maxlength' => 255,
 812      '#description' =>
 813        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 />'.
 814        t('Available placeholders') .':<br/>'.
 815        '<strong>%d</strong> - '. t('server path to document root (%root)', array('%root' => $_SERVER['DOCUMENT_ROOT'])) .'<br />'.
 816        '<strong>%b</strong> - '. t('base URL path of the Drupal installation (%base)', array('%base' => base_path())) .'<br/>'.
 817        '<strong>%f</strong> - '. t('Drupal file system path where the files are stored (%files)', array('%files' => file_directory_path())) .'<br />'.
 818        '<strong>%u</strong> - '. t('User ID') .'<br />'.
 819        '<strong>%n</strong> - '. t('Username') .'<br />'.
 820        t('Current path: %path', array('%path' => $current_user_files_absolute_path)),
 821    );
 822  
 823    if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) {
 824      $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. To change the location of uploaded files in the private file system, edit the !globallink.', array('!globallink' => l(t('FCKeditor Global Profile'), 'admin/settings/fckeditor/editg')));
 825      $form['fckeditor_upload_settings']['UserFilesPath']['#disabled'] = TRUE;
 826      $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. To change the location of uploaded files in the private file system, edit the !globallink.', array('!globallink' => l(t('FCKeditor Global Profile'), 'admin/settings/fckeditor/editg')));
 827      $form['fckeditor_upload_settings']['UserFilesAbsolutePath']['#disabled'] = TRUE;
 828    }
 829  
 830    $form['advanced'] = array(
 831      '#type' => 'fieldset',
 832      '#title' => t('Advanced options'),
 833      '#collapsible' => TRUE,
 834      '#collapsed' => TRUE,
 835    );
 836    $form['advanced']['theme_config_js'] = array(
 837      '#type' => 'radios',
 838      '#title' => t('Load fckeditor.config.js from theme path'),
 839      '#default_value' => !empty($profile->settings['theme_config_js']) ? $profile->settings['theme_config_js'] : 'f',
 840      '#options' => array(
 841        't' => t('Yes'),
 842        'f' => t('No')
 843      ),
 844      '#description' =>
 845        t('When set to %true the editor will try to load the %configfile file from theme directory.',
 846          array(
 847            '%true' => t('Yes'),
 848            '%configfile' => 'fckeditor.config.js',
 849          )),
 850    );
 851    $form['advanced']['js_conf'] = array(
 852      '#type' => 'textarea',
 853      '#title' => t('Custom javascript configuration'),
 854      '#default_value' => !empty($profile->settings['js_conf']) ? $profile->settings['js_conf'] : "",
 855      '#cols' => 60,
 856      '#rows' => 5,
 857      '#description' =>
 858        t('To change FCKeditor configuration globally, you should modify the config file: %fckeditor_config. Sometimes it is required to change the FCKeditor configuration for a single profile only. Use this box to define settings that are unique for this profile. Available options are listed in the !docslink. Add the following code snippet to disable some advanced tabs in dialog windows of FCKeditor.',
 859          array(
 860            '%fckeditor_config' => drupal_get_path('module', 'fckeditor') .'/fckeditor.config.js',
 861            '!docslink' => l(t('FCKeditor documentation'), 'http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options'),
 862          )) .
 863        "<pre>LinkDlgHideTarget = true;\nLinkDlgHideAdvanced = true;\nImageDlgHideLink = true;\nImageDlgHideAdvanced = true;\nFlashDlgHideAdvanced = true;</pre>".
 864        '<strong>'. t('Warning') .'</strong>: '.
 865        t('If you do something wrong here, FCKeditor may fail to load.'),
 866      '#wysiwyg' => FALSE,
 867    );
 868  
 869    $form['submit'] = array(
 870      '#type' => 'submit',
 871      '#value' => t('Save')
 872    );
 873  
 874    return $form;
 875  }
 876  
 877  /**
 878   * Profile validation.
 879   */
 880  function fckeditor_admin_profile_form_validate($form, &$form_state) {
 881    $edit =& $form_state['values'];
 882  
 883    //include mode and all other fields are empty, invalid
 884    if ($edit['excl_mode'] == 1 && empty($edit['excl'])) {
 885      form_set_error('excl_mode', t('Include mode selected, but no paths given. Enter at least one path where FCKeditor should appear.'));
 886    }
 887    else {
 888      fckeditor_admin_profile_validate_fieldpaths('excl', $edit['excl']);
 889    }
 890  
 891    fckeditor_admin_profile_validate_fieldpaths('simple_incl', $edit['simple_incl']);
 892  
 893    if (!preg_match('/^\d+$/', trim($edit['min_rows']))) {
 894      form_set_error('min_rows', t('Minimum rows must be a valid number.'));
 895    }
 896  
 897    if ($edit['default'] == 't' && $edit['popup'] == 't') {
 898      form_set_error('popup', t('If FCKeditor is enabled by default, popup window must be disabled.'));
 899    }
 900  
 901    if ($edit['show_toggle'] == 't' && $edit['popup'] == 't') {
 902      form_set_error('popup', t('If toggle is enabled, popup window must be disabled.'));
 903    }
 904  
 905    if (!$edit['name']) {
 906      form_set_error('name', t('You must give a profile name.'));
 907    }
 908    elseif ($edit['name'] == 'FCKeditor Global Profile') {
 909      form_set_error('name', t('This profile name is reserved. Please choose a different name.'));
 910    }
 911    elseif (!isset($edit['_profile']) || ($edit['_profile']->name != $edit['name'])) {
 912      $result = fckeditor_profile_load($edit['name']);
 913      if (!empty($result)) {
 914        form_set_error('name', t('The profile name must be unique. A profile with this name already exists.'));
 915      }
 916    }
 917  
 918    if (!preg_match('/^\d+%?$/', $edit['width'])) {
 919      form_set_error('width', t('Enter valid width.') .' '. t('Example') .': 400 '. t('or') .' 100%.');
 920    }
 921  
 922    if (!empty($edit['css_path'])) {
 923      if ($edit['css_mode'] != 'self') {
 924        form_set_error('css_path', t('CSS path is not empty.') .' '. t('Please set the %settingname option to %settingoption mode.',
 925          array('%settingname' => t('Editor CSS'), '%settingoption' => t('Define CSS'))));
 926      }
 927      elseif (FALSE !== strpos($edit['css_path'], '"')) {
 928        form_set_error('css_path', t('Double quotes are not allowed in CSS path.'));
 929      }
 930      elseif (substr($edit['css_path'], 0, 1) == "'" && substr($edit['css_path'], -1) == "'") {
 931        form_set_error('css_path', t('Enter valid path, do not surround it with quotes.'));
 932      }
 933    }
 934  
 935    if (!empty($edit['styles_path'])) {
 936      if ($edit['css_style'] != 'self') {
 937        form_set_error('styles_path', t('Path to predefined styles is not empty.') .' '. t('Please set the %settingname option to %settingoption mode.',
 938          array('%settingname' => t('Predefined styles'), '%settingoption' => t('Define path to fckstyles.xml'))));
 939      }
 940      elseif (FALSE !== strpos($edit['styles_path'], '"')) {
 941        form_set_error('styles_path', t('Double quotes are not allowed in path.'));
 942      }
 943      elseif (substr($edit['styles_path'], 0, 1) == "'" && substr($edit['styles_path'], -1) == "'") {
 944        form_set_error('styles_path', t('Enter valid path, do not surround it with quotes.'));
 945      }
 946    }
 947  
 948    if (!empty($edit['templatefile_path'])) {
 949      if ($edit['templatefile_mode'] != 'self') {
 950        form_set_error('templatefile_path', t('Path to predefined templates is not empty.') .' '. t('Please set the %settingname option to %settingoption mode.',
 951          array('%settingname' => t('Predefined templates'), '%settingoption' => t('Define path to fcktemplates.xml'))));
 952      }
 953      elseif (FALSE !== strpos($edit['templatefile_path'], '"')) {
 954        form_set_error('templatefile_path', t('Double quotes are not allowed in path.'));
 955      }
 956      elseif (substr($edit['templatefile_path'], 0, 1) == "'" && substr($edit['styles_path'], -1) == "'") {
 957        form_set_error('templatefile_path', t('Enter valid path, do not surround it with quotes.'));
 958      }
 959    }
 960  
 961    if (!empty($edit['font_format'])) {
 962      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'])) {
 963        form_set_error('font_format', t('Enter valid, semicolon separated, list of HTML font formats (no semicolon at the end of list expected).'));
 964      }
 965    }
 966  
 967    if (variable_get('file_downloads', '') !== FILE_DOWNLOADS_PRIVATE) {
 968      if (!empty($edit['UserFilesAbsolutePath']) && empty($edit['UserFilesPath'])) {
 969        form_set_error('UserFilesPath', t('Path to uploaded files is required.'));
 970      }
 971      if (!empty($edit['UserFilesPath']) && empty($edit['UserFilesAbsolutePath'])) {
 972        form_set_error('UserFilesPath', t('Absolute path to uploaded files is required.'));
 973      }
 974    }
 975  }
 976  
 977  function fckeditor_admin_profile_form_submit($form, &$form_state) {
 978    $edit =& $form_state['values'];
 979  
 980    if (isset($edit['_profile'])) {
 981      db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s'", $edit['_profile']->name);
 982      db_query("DELETE FROM {fckeditor_role} WHERE name = '%s'", $edit['_profile']->name);
 983      drupal_set_message(t('Your FCKeditor profile has been updated.'));
 984    }
 985    else {
 986      drupal_set_message(t('Your FCKeditor profile has been created.'));
 987    }
 988  
 989    $settings = fckeditor_admin_values_to_settings($edit);
 990    db_query("INSERT INTO {fckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings);
 991    fckeditor_rebuild_selectors($edit['name']);
 992    if (!empty($edit['rids'])) {
 993      foreach (array_keys($edit['rids']) as $rid) {
 994        if ($edit['rids'][$rid]!=0) {
 995          db_query("INSERT INTO {fckeditor_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid);
 996        }
 997      }
 998    }
 999  
1000    $form_state['redirect'] = 'admin/settings/fckeditor';
1001  }
1002  
1003  function fckeditor_admin_global_profile_form($form_state, $mode = 'add') {
1004    module_load_include('lib.inc', 'fckeditor');
1005    if ($mode == 'edit') {
1006      $profile = fckeditor_profile_load('FCKeditor Global Profile');
1007  
1008      $form['_profile'] = array(
1009        '#type' => 'value',
1010        '#value' => $profile,
1011      );
1012    }
1013    else {
1014      $profile = new stdClass();
1015    }
1016  
1017    if ($mode == 'add') {
1018      $data = fckeditor_profile_load('FCKeditor Global Profile');
1019      if (!empty($data)) {
1020        drupal_set_message(t('Global profile already exist. Only one global profile is allowed.'), 'error');
1021        drupal_not_found();
1022      }
1023  
1024      $btn = t('Create global profile');
1025    }
1026    else {
1027      $btn = t('Update global profile');
1028    }
1029  
1030    $form['common'] = array(
1031      '#type' => 'fieldset',
1032      '#title' => t('Main setup'),
1033      '#collapsible' => TRUE,
1034      '#collapsed' => TRUE
1035    );
1036  
1037    $roles = fckeditor_sorted_roles();
1038    $rids = $rtext = array();
1039    foreach ($roles as $rid => $name) {
1040      $rids[] = $rid;
1041      $rtext[] = '<strong>'. $rid .' - </strong>'. $name;
1042    }
1043    $form['common']['rank'] = array(
1044      '#type' => 'textfield',
1045      '#title' => t('Role precedence'),
1046      '#default_value' => implode('>', $rids),
1047      '#description' => t('A user having multiple roles gets the permissions of the highest one. Sort role IDs according to their precedence from higher to lower by putting &gt; in between.'),
1048    );
1049    if ($rids) {
1050      $form['common']['rank']['#description'] .= '<br />'. t('Here are the id-name pairs of roles having access to FCKeditor:') .'<div>'. implode('<br />', $rtext) .'</div>';
1051    }
1052    else {
1053      $form['common']['rank']['#description'] .= '<br />'. t('You haven\'t assigned the %accesspermission !permissionslink yet.', array('%accesspermission' => t('access fckeditor'), '!permissionslink' => l(t('permission'), 'admin/user/permissions')));
1054    }
1055  
1056    $form['fckeditor_exclude_settings'] = array(
1057      '#type' => 'fieldset',
1058      '#title' => t('Visibility settings'),
1059      '#collapsible' => TRUE,
1060      '#collapsed' => TRUE,
1061      '#description' => t('The following settings are combined with the visibility settings of the specific profile.'),
1062    );
1063  
1064    $form['fckeditor_exclude_settings']['excl_mode'] = array(
1065      '#type' => 'radios',
1066      '#title' => t('Use inclusion or exclusion mode'),
1067      '#default_value' => (empty($profile->settings['excl_mode']) || in_array($profile->settings['excl_mode'], array(0, 2))) ? 0 : 1,
1068      '#options' => array(
1069        '0' => t('Exclude'),
1070        '1' => t('Include')
1071      ),
1072      '#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.'),
1073    );
1074    /**
1075     * get excluded fields - so we can have normal textareas too
1076     * split the phrase by any number of commas or space characters,
1077     * which include " ", \r, \t, \n and \f
1078     */
1079    $form['fckeditor_exclude_settings']['excl'] = array(
1080      '#type' => 'textarea',
1081      '#title' => t('Fields to exclude/include'),
1082      '#cols' => 60,
1083      '#rows' => 5,
1084      '#prefix' => '<div style="margin-left:20px">',
1085      '#suffix' => '</div>',
1086      '#default_value' => !empty($profile->settings['excl']) ? $profile->settings['excl'] : '',
1087      '#description' =>
1088        t('Enter the paths to the textarea fields on which you want to enable or disable FCKeditor.') .' '.
1089        t('Please see the !helppagelink for more information about defining field names. Short instruction is available below.',
1090          array(
1091            '!helppagelink' => l(t('help page'), 'admin/help/fckeditor', array('fragment' => 'fieldinclexcl')),
1092          )) .
1093        ' <ul>' .
1094        '<li>'. t('Path structure') .': <strong>content_type@path.element_id</strong></li>'.
1095        '<li>'. t('The following wildcards are available: *, ?') .'</li>'.
1096        '<li>'. t('Content type is optional. You may even specify only path or field id') .'</li>'.
1097        '<li>'. t('Examples') .':<ul>'.
1098        '<li><em>blog@*.edit-body</em> - '. t('matches all fields of type %type called %field, on any page', array('%type' => 'blog', '%field' => 'edit-body')) .
1099        '<li><em>node/add/*.edit-user-*</em> - '. t('matches fields starting with %field on pages starting with %path', array('%field' => 'edit-user-', '%path' => 'node/add/')) .'</li></ul>'.
1100        '</li></ul>',
1101      '#wysiwyg' => FALSE,
1102    );
1103  
1104    $form['fckeditor_exclude_settings']['simple_incl'] = array(
1105      '#type' => 'textarea',
1106      '#title' => t('Force simplified toolbar on the following fields'),
1107      '#cols' => 60,
1108      '#rows' => 5,
1109      '#default_value' => !empty($profile->settings['simple_incl']) ? $profile->settings['simple_incl'] : '',
1110      '#description' =>
1111        t('Enter the paths to the textarea fields on which you want to force the simplified toolbar (%toolbarname).',
1112          array(
1113            '%toolbarname' => FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME,
1114          )) .' '.
1115        t('Please see the !helppagelink for more information about defining field names. Take a look at the exclusion settings (above) for short instruction.',
1116          array(
1117            '!helppagelink' => l(t('help page'), 'admin/help/fckeditor', array('fragment' => 'fieldinclexcl')),
1118          )),
1119      '#wysiwyg' => FALSE,
1120    );
1121  
1122    $form['fckeditor_advanced_settings'] = array(
1123      '#type' => 'fieldset',
1124      '#title' => t('Advanced settings'),
1125      '#collapsible' => TRUE,
1126      '#collapsed' => TRUE,
1127    );
1128  
1129    $module_drupal_path = drupal_get_path('module', 'fckeditor');
1130  
1131    $form['fckeditor_advanced_settings']['fckeditor_path'] = array(
1132      '#type' => 'textfield',
1133      '#title' => t('Path to FCKeditor'),
1134      '#default_value' =>  !empty($profile->settings['fckeditor_path']) ? $profile->settings['fckeditor_path'] : '%m/fckeditor',
1135      '#size' => 40,
1136      '#maxlength' => 128,
1137      '#description' =>
1138        t('Path to FCKeditor (the HTML editor, downloaded from !fckeditorlink) relative to the document root.',
1139          array(
1140            '!fckeditorlink' => l('http://ckeditor.com', 'http://ckeditor.com/download'),
1141          )) .'<br />'.
1142        t('Available placeholders') .':<br />'.
1143        '<strong>%b</strong> - '. t('base URL path of the Drupal installation (%base)', array('%base' => base_path())) .'<br />'.
1144        '<strong>%m</strong> - '. t('base URL path where FCKeditor module is stored (%files)', array('%files' => base_path() . $module_drupal_path)) .'<br />'.
1145        t('Current path: %path', array('%path' => fckeditor_path(FALSE))),
1146      '#required' => TRUE,
1147    );
1148  
1149    $form['fckeditor_advanced_settings']['fckeditor_local_path'] = array(
1150      '#type' => 'textfield',
1151      '#title' => t('Local path to FCKeditor'),
1152      '#default_value' =>  isset($profile->settings['fckeditor_local_path'])?$profile->settings['fckeditor_local_path']:'',
1153      '#size' => 40,
1154      '#maxlength' => 128,
1155      '#description' =>
1156        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. If empty, FCKeditor module will try to find the right path.', array('%index' => 'index.php')) .'<br />'.
1157        t('Current path: %path', array('%path' => fckeditor_path(TRUE))),
1158    );
1159  
1160    $form['fckeditor_advanced_settings']['show_fieldnamehint'] = array(
1161      '#type' => 'radios',
1162      '#title' => t('Show field name hint below each rich text editor'),
1163      '#default_value' => !empty($profile->settings['show_fieldnamehint']) ? $profile->settings['show_fieldnamehint'] : 't',
1164      '#options' => array(
1165        't' => t('Yes'),
1166        'f' => t('No')
1167      ),
1168      '#description' => t('This only applies for users with %adminpermission permissions.', array('%adminpermission' => t('administer fckeditor'))),
1169    );
1170  
1171    if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) {
1172      global $user;
1173      $current_private_dir = isset($profile->settings['private_dir']) ? trim($profile->settings['private_dir'], '\/') : '';
1174      $current_private_dir = strtr($current_private_dir, array('%u' => $user->uid, '%n' => $user->name));
1175  
1176      $form['fckeditor_advanced_settings']['private_dir'] = array(
1177        '#type' => 'textfield',
1178        '#title' => t('Location of files uploaded with FCKeditor in the private folder'),
1179        '#default_value' => !empty($profile->settings['private_dir']) ? $profile->settings['private_dir'] : '',
1180        '#size' => 40,
1181        '#maxlength' => 255,
1182        '#description' =>
1183          t('The path relative to the location of the private directory where FCKeditor should store uploaded files.') .'<br />'.
1184          '<strong>'. t('Warning') .':</strong> '.
1185          t('FCKeditor 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 />'.
1186          t('System path to the private folder is: @system_path.', array('@system_path' => realpath(file_directory_path()) . DIRECTORY_SEPARATOR)) .'<br />'.
1187          t('Available placeholders') .':<br/>'.
1188          '<strong>%u</strong> - '. t('User ID') .'<br />'.
1189          '<strong>%n</strong> - '. t('Username') .'<br />'.
1190          t('Current path: @path', array('@path' => $current_private_dir .' ('. file_create_path($current_private_dir) .')')),
1191      );
1192    }
1193  
1194    $form['submit'] = array(
1195      '#type' => 'submit',
1196      '#value' => $btn,
1197    );
1198  
1199    return $form;
1200  }
1201  
1202  function fckeditor_admin_global_profile_form_validate($form, &$form_state) {
1203    $edit =& $form_state['values'];
1204  
1205    //include mode and all other fields are empty, invalid
1206    if ($edit['excl_mode'] == 1 && empty($edit['excl'])) {
1207      form_set_error('excl_mode', t('Include mode selected, but no paths given. Enter at least one path where FCKeditor should appear.'));
1208    }
1209    else {
1210      fckeditor_admin_profile_validate_fieldpaths('excl', $edit['excl']);
1211    }
1212  
1213    fckeditor_admin_profile_validate_fieldpaths('simple_incl', $edit['simple_incl']);
1214  }
1215  
1216  function fckeditor_admin_global_profile_form_submit($form, &$form_state) {
1217    module_load_include('lib.inc', 'fckeditor');
1218    $edit =& $form_state['values'];
1219    $edit['name'] = 'FCKeditor Global Profile';
1220  
1221    if (isset($edit['rank'])) {
1222      $edit['rank'] = explode('>', str_replace(' ', '', $edit['rank']));
1223    }
1224  
1225    if (isset($edit['_profile'])) {
1226      db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s'", $edit['_profile']->name);
1227      db_query("DELETE FROM {fckeditor_role} WHERE name = '%s'", $edit['_profile']->name);
1228    }
1229  
1230    //strip whitespaces
1231    if (empty($edit['fckeditor_local_path'])) {
1232      $edit['fckeditor_local_path'] = '';
1233    }
1234    else {
1235      $edit['fckeditor_local_path'] = trim($edit['fckeditor_local_path']);
1236    }
1237  
1238    //strip slash from the end
1239    if (empty($edit['fckeditor_path'])) {
1240      $edit['fckeditor_path'] = '';
1241    }
1242    $edit['fckeditor_path'] = trim(rtrim($edit['fckeditor_path'], '/'));
1243    if ($edit['fckeditor_path'] && 0 !== strpos($edit['fckeditor_path'], '/') && 0 !== strpos($edit['fckeditor_path'], '%')) {
1244      //ensure that slash is at the beginning
1245      $edit['fckeditor_path'] = '/'. $edit['fckeditor_path'];
1246    }
1247    //no slash at the end
1248    $edit['fckeditor_local_path'] = trim(rtrim($edit['fckeditor_local_path'], '/'));
1249  
1250    $settings = fckeditor_admin_values_to_settings($edit);
1251    db_query("INSERT INTO {fckeditor_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], $settings);
1252    fckeditor_rebuild_selectors($edit['name']);
1253  
1254    drupal_set_message(t('FCKeditor global profile has been saved.'));
1255    $form_state['redirect'] = 'admin/settings/fckeditor';
1256  }
1257  
1258  /**
1259   * Converts an array of form values to a serialized array that does not
1260   * contain Drupal Form API values
1261   */
1262  function fckeditor_admin_values_to_settings($values) {
1263    unset($values['name'], $values['rids'], $values['_profile'], $values['op'], $values['submit'], $values['form_build_id'], $values['form_token'], $values['form_id']);
1264    return serialize($values);
1265  }
1266  
1267  function fckeditor_admin_profile_validate_fieldpaths($fieldname, $paths) {
1268    $myerrors = array();
1269  
1270    $rules = preg_split('/[\s,]+/', $paths);
1271  
1272    foreach ($rules as $rule) {
1273      $rule = trim($rule);
1274      if (!empty($rule) && strpos($rule, '.') === FALSE && strpos($rule, '/') === FALSE && strpos($rule, '-') === FALSE) {
1275        $myerrors[] = t('Rule %rule is ambiguous: please append .* if %rule is a path or prepend *. if %rule is a field', array('%rule' => $rule));
1276      }
1277    }
1278  
1279    if (!empty($myerrors)) {
1280      form_set_error($fieldname, implode('<br/>', $myerrors));
1281    }
1282  }
1283  
1284  function fckeditor_admin_profile_delete_form($form_state, $profile) {
1285    $form = array();
1286  
1287    $form['_profile'] = array(
1288      '#type' => 'value',
1289      '#value' => $profile,
1290    );
1291  
1292    $form['question'] = array(
1293      '#type' => 'item',
1294      '#value' => t('Are you sure that you want to delete the FCKeditor profile %profile?', array('%profile' => $profile->name)),
1295    );
1296  
1297    $form['delete'] = array(
1298      '#type' => 'submit',
1299      '#id' => 'delete',
1300      '#value' => t('Delete'),
1301    );
1302  
1303    $form['back'] = array(
1304      '#type' => 'submit',
1305      '#id' => 'back',
1306      '#value' => t('Cancel'),
1307    );
1308  
1309    return $form;
1310  }
1311  
1312  function fckeditor_admin_profile_delete_form_submit($form, &$form_state) {
1313    $v =& $form_state['values'];
1314  
1315    if ($form_state['clicked_button']['#id'] == 'delete') {
1316      fckeditor_profile_delete($v['_profile']->name);
1317      drupal_set_message(t('Deleted FCKeditor profile.'));
1318    }
1319  
1320    $form_state['redirect'] = 'admin/settings/fckeditor';
1321  }
1322  
1323  /**
1324   * Rebuilds the regular expression that is used to match the inclusion/exclusion rules
1325   * and the simplified toolbar rules
1326   *
1327   * @param string $name Name of the profile to process. If omitted, all profiles are rebuilt
1328   */
1329  function fckeditor_rebuild_selectors($name = NULL) {
1330    if ($name == NULL) {
1331      $result = db_query("SELECT * FROM {fckeditor_settings}");
1332    }
1333    else {
1334      $result = db_query("SELECT * FROM {fckeditor_settings} WHERE name = '%s'", $name);
1335    }
1336  
1337    while (($data = db_fetch_object($result))) {
1338      if ($data->settings) {
1339        $settings = unserialize($data->settings);
1340  
1341        foreach (array('excl', 'simple_incl') as $var) {
1342          $settings[$var .'_regex'] = '';
1343  
1344          if (!empty($settings[$var])) {
1345            $rules = preg_split('/[\s,]+/', $settings[$var]);
1346            $regex = array();
1347  
1348            if (!empty($rules)) {
1349              foreach ($rules as $rule) {
1350                if (!empty($rule)) {
1351                  $rule = fckeditor_parse_rule($rule);
1352                  $regex[] = '(?:'. fckeditor_rule_to_regex($rule) .')';
1353                }
1354              }
1355  
1356              if (!empty($regex)) {
1357                $settings[$var .'_regex'] = '#'. implode('|', $regex) .'#';
1358              }
1359            }
1360          }
1361        }
1362  
1363        db_query("UPDATE {fckeditor_settings} SET settings='%s' WHERE name='%s'", serialize($settings), $data->name);
1364      }
1365    }
1366  }
1367  
1368  function fckeditor_rule_create($nodetype = '*', $path = '*', $fieldname = '*') {
1369    $rule = new stdClass();
1370    $rule->nodetype = $nodetype;
1371    $rule->path = $path;
1372    $rule->field = $fieldname;
1373  
1374    return $rule;
1375  }
1376  
1377  function fckeditor_parse_rule($rule) {
1378    $ruleobj = new stdClass();
1379  
1380    $atpos = strpos($rule, '@');
1381    if ($atpos !== FALSE) {
1382      $ruleobj->nodetype = substr($rule, 0, $atpos);
1383      $rule = substr($rule, $atpos + 1);
1384    }
1385    else {
1386      $ruleobj->nodetype = '*';
1387    }
1388  
1389    $dotpos = strpos($rule, '.');
1390    if ($dotpos === FALSE) {
1391      if (strpos($rule, '/') === FALSE && strpos($rule, '-') !== FALSE) {
1392        // assume it's a field
1393        $ruleobj->path = '*';
1394        $ruleobj->field = $rule;
1395      }
1396      elseif (strpos($rule, '/') !== FALSE) {
1397        // assume it's a path
1398        $ruleobj->path = $rule;
1399        $ruleobj->field = '*';
1400      }
1401      else {
1402        return NULL;
1403      }
1404    }
1405    else {
1406      $ruleobj->path = substr($rule, 0, $dotpos);
1407      $ruleobj->field = str_replace('\.', '.', substr($rule, $dotpos + 1));
1408    }
1409  
1410    return $ruleobj;
1411  }
1412  
1413  function fckeditor_rule_to_regex($rule) {
1414    static $replace = array('\*' => '.*', '\?' => '.');
1415  
1416    $field = str_replace('.', '\.', $rule->field);
1417    $regex = '^'. preg_quote($rule->nodetype, '#') .'@'. preg_quote($rule->path, '#') .'\.'. preg_quote($field, '#') .'$';
1418    $regex = strtr($regex, $replace);
1419  
1420    return $regex;
1421  }
1422  
1423  function fckeditor_rule_to_string($rule) {
1424    $field = str_replace('.', '\.', $rule->field);
1425    $rulestr = '';
1426    if ($rule->nodetype != '*') {
1427      $rulestr .= $rule->nodetype .'@';
1428    }
1429    return $rulestr . $rule->path .'.'. $field;
1430  }
1431  
1432  /**
1433   * Remove a profile from the database.
1434   */
1435  function fckeditor_profile_delete($name) {
1436    db_query("DELETE FROM {fckeditor_settings} WHERE name = '%s'", $name);
1437    db_query("DELETE FROM {fckeditor_role} WHERE name = '%s'", $name);
1438  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7