<?php /** * EGroupware - Class which generates JSON encoded configuration for the ckeditor * * @link http://www.egroupware.org * @author RalfBecker-AT-outdoor-training.de * @author Andreas Stoeckel <as-AT-stylite.de> * @package api * @subpackage tools * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ /** * CK-Editor configuration */ class egw_ckeditor_config { private static $lang = null; private static $country = null; private static $enterMode = null; private static $skin = null; // Defaults, defined in phpgwapi/js/ckeditor/plugins/font/plugin.js public static $font_options = array( 'arial, helvetica, sans-serif' => 'Arial', 'Comic Sans MS, cursive' => 'Comic Sans MS', 'Courier New, Courier, monospace' => 'Courier New', 'Georgia, serif' => 'Georgia', 'Lucida Sans Unicode, Lucida Grande, sans-serif' => 'Lucida Sans Unicode', 'Tahoma, Geneva, sans-serif' => 'Tahoma', 'times new roman, times, serif' => 'Times New Roman', 'Trebuchet MS, Helvetica, sans-serif' => 'Trebuchet MS', 'Verdana, Geneva, sans-serif' => 'Verdana' ); public static $font_size_options = array( 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 14 => '14', 16 => '16', 18 => '18', 20 => '20', 22 => '22', 24 => '24', 26 => '26', 28 => '28', 36 => '36', 48 => '48', 72 => '72', ); public static $font_unit_options = array( 'pt' => 'pt: points (1/72 inch)', 'px' => 'px: display pixels', ); /** * Get available CKEditor Skins * @return array */ public static function getAvailableCKEditorSkins() { /* drwxr-xr-x 3 kl kl 4096 Dez 9 14:26 icy_orange drwxr-xr-x 3 kl kl 4096 Dez 9 14:26 Moono_blue drwxr-xr-x 3 kl kl 4096 Dez 9 14:26 office2013 */ return array( 'kama' => lang('kama theme'), 'moono' => lang('moono theme (default)'), 'moonocolor' => lang('moono color theme'), 'moono-dark' => lang('dark moono theme'), 'Moono_blue' => lang('blue moono theme'), 'bootstrapck' => lang('bootstrap theme for ckeditor'), 'icy_orange' => lang('icy-orange theme for ckeditor'), 'office2013' => lang('office-2013 theme for ckeditor'), ); } /** * Get font size from preferences * * @param array $prefs=null default $GLOBALS['egw_info']['user']['preferences'] * @param string &$size=null on return just size, without unit * @param string &$unit=null on return just unit * @return string font-size including unit */ public static function font_size_from_prefs(array $prefs=null, &$size=null, &$unit=null) { if (is_null($prefs)) $prefs = $GLOBALS['egw_info']['user']['preferences']; $size = $prefs['common']['rte_font_size']; $unit = $prefs['common']['rte_font_unit']; if (substr($size, -2) == 'px') { $unit = 'px'; $size = (string)(int)$size; } return $size.($size?$unit:''); } /** * Read language and country settings for the ckeditor and store them in static * variables */ private static function read_lang_country() { //use the lang and country information to construct a possible lang info for CKEditor UI and scayt_slang self::$lang = ($GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang'] ? $GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang']: $GLOBALS['egw_info']['user']['preferences']['common']['lang']); self::$country = $GLOBALS['egw_info']['user']['preferences']['common']['country']; if (!(strpos(self::$lang, '-')===false)) list(self::$lang, self::$country) = explode('-', self::$lang); } /** * Returns the current user language */ private static function get_lang() { if (self::$lang == null || self::$country == null) self::read_lang_country(); return self::$lang; } /** * Returns the current user country */ private static function get_country() { if (self::$lang == null || self::$country == null) self::read_lang_country(); return strtoupper(self::$country); } /** * Returns the ckeditor basepath */ private static function get_base_path() { //Get the ckeditor base url return $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/js/ckeditor/'; } /** * Returns the ckeditor enter mode which defaults to "BR" */ private static function get_enter_mode() { if (self::$enterMode == null) { //Get the input name $enterMode = 2; if (isset($GLOBALS['egw_info']['user']['preferences']['common']['rte_enter_mode'])) { switch ($GLOBALS['egw_info']['user']['preferences']['common']['rte_enter_mode']) { case 'p': $enterMode = 1; break; case 'br': $enterMode = 2; break; case 'div': $enterMode = 3; break; } } self::$enterMode = $enterMode; } return self::$enterMode; } /** * Returns the skin the ckeditor should use */ private static function get_skin() { if (self::$skin == null) { //Get the skin name $skin = $GLOBALS['egw_info']['user']['preferences']['common']['rte_skin']; //error_log(__METHOD__.__LINE__.' UserAgent:'.html::$user_agent); //Convert old fckeditor skin names to new ones switch ($skin) { case 'kama': $skin = "kama"; //if (html::$user_agent=='firefox' || html::$user_agent=='msie') $skin='moonocolor'; break; case 'silver': case 'bootstrapck': $skin = "bootstrapck"; break; case 'moono-dark': $skin = "moono-dark"; break; case 'Moono_blue': $skin = "Moono_blue"; break; case 'icy_orange': $skin = "icy_orange"; break; case 'office2013': $skin = "office2013"; break; case 'office2003': case 'moonocolor': $skin = "moonocolor"; break; case 'moono': case 'default': default: $skin = "moono"; } //Check whether the skin actually exists, if not, switch to a default if (!(file_exists($basePath.'skins/'.$skin) || file_exists($skin) || !empty($skin))) $skin = "moono"; //this is the basic skin for ckeditor self::$skin = $skin; } return self::$skin; } /** * Returns the URL of the filebrowser * * @param string $start_path start path for file browser */ private static function get_filebrowserBrowseUrl($start_path = '') { return $GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=filemanager.filemanager_select.select&mode=open&method=ckeditor_return' .($start_path != '' ? '&path='.$start_path : ''); } /** * Adds all "easy to write" options to the configuration * * @param array& $config array were config get's added to * @param int|string $height integer height in pixel or string with css unit * @param boolean|string $expanded_toolbar show toolbar expanded, boolean value, string "false", or string casted to boolean * @param string $start_path start path for file browser */ private static function add_default_options(&$config, $height, $expanded_toolbar, $start_path) { //Convert the pixel height to an integer value $config['resize_enabled'] = false; $config['height'] = is_numeric($height) ? (int)$height : $height; //disable encoding as entities needs to set the config value to false, as the default is true with the current ckeditor version $config['entities'] = false; $config['entities_latin'] = false; $config['editingBlock'] = true; $config['disableNativeSpellChecker'] = true; // we set allowedContent to true as the 4.1 contentFiltering system allows only activated features as content $config['allowedContent'] = true; $config['removePlugins'] = 'elementspath'; $config['toolbarCanCollapse'] = true; $config['toolbarStartupExpanded'] = is_bool($expanded_toolbar) ? $expanded_toolbar : ($expanded_toolbar === 'false' ? false : (boolean)$expanded_toolbar); $config['filebrowserBrowseUrl'] = self::get_filebrowserBrowseUrl($start_path); $config['filebrowserWindowHeight'] = 640; $config['filebrowserWindowWidth'] = 580; $config['language'] = self::get_lang(); $config['enterMode'] = self::get_enter_mode(); $config['skin'] = self::get_skin(); $config['fontSize_sizes'] = ''; $unit = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font_unit']; if (empty($unit)) $unit = 'px'; foreach(self::$font_size_options as $k => $v) { $config['fontSize_sizes'] .= $v.$unit.'/'.$k.$unit.';'; } } /** * Adds the spellchecker configuration to the options and writes the name of * the spellchecker toolbar button to the "spellchecker_button" parameter */ private static function add_spellchecker_options(&$config, &$spellchecker_button, &$scayt_button) { //error_log(__METHOD__.__LINE__.' Spellcheck:'.$GLOBALS['egw_info']['server']['enabled_spellcheck']); if (isset($GLOBALS['egw_info']['server']['enabled_spellcheck']) && $GLOBALS['egw_info']['server']['enabled_spellcheck']) { // enable browsers native spellchecker as default, if e.g.: aspell fails // to use browsers native spellchecker, you have to hold CMD/CTRL button on rightclick to // access the browsers spell correction options if ($GLOBALS['egw_info']['server']['enabled_spellcheck']!='YesNoSCAYT') $config['disableNativeSpellChecker'] = false; $spellchecker_button = 'SpellChecker'; if (!empty($GLOBALS['egw_info']['server']['aspell_path']) && is_executable($GLOBALS['egw_info']['server']['aspell_path']) && ($GLOBALS['egw_info']['server']['enabled_spellcheck']!='YesUseWebSpellCheck' && $GLOBALS['egw_info']['server']['enabled_spellcheck']!='YesBrowserBased') ) { $spellchecker_button = 'SpellCheck'; $config['extraPlugins'] = "aspell"; } if ($GLOBALS['egw_info']['server']['enabled_spellcheck']!='YesNoSCAYT' && $GLOBALS['egw_info']['server']['enabled_spellcheck']!='YesBrowserBased' ) { $scayt_button='Scayt'; $config['scayt_autoStartup'] = true; $config['scayt_sLang'] = self::get_lang().'_'.self::get_country(); $config['disableNativeSpellChecker'] = true; // only one spell as you type } } else { $config['scayt_autoStartup'] = false; } } /** * Writes the toolbar configuration to the options which depends on the chosen * mode and the spellchecker_button written by the add_spellchecker_options button */ private static function add_toolbar_options(&$config, $mode, $spellchecker_button, $scayt_button=false) { $config['toolbar'] = array(); switch ($mode) { case 'advanced': $config['toolbar'][] = array('name' => 'document', 'items' => array('Source','DocProps','-','Preview','-','Templates')); $config['toolbar'][] = array('name' => 'clipboard', 'items' => array('Cut','Copy','Paste','PasteText','PasteFromWord','-','Print')); if ($spellchecker_button||$scayt_button) { $configArray = array(); if ($spellchecker_button) $configArray[] = $spellchecker_button; if ($scayt_button) $configArray[] = $scayt_button; $config['toolbar'][] = array('name' => 'tools', 'items' => $configArray); } $config['toolbar'][] = array('name' => 'edit', 'items' => array('Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat')); $config['toolbar'][] = '/'; $config['toolbar'][] = array('name' => 'basicstyles', 'items' => array('Bold','Italic','Underline','Strike','-','Subscript','Superscript')); $config['toolbar'][] = array('name' => 'justify', 'items' => array('JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock')); $config['toolbar'][] = array('name' => 'paragraph', 'items' => array('BulletedList','NumberedList','-','Outdent','Indent')); $config['toolbar'][] = array('name' => 'links', 'items' => array('Link','Unlink','Anchor')); $config['toolbar'][] = array('name' => 'insert', 'items' => array('Maximize','Image','Table','HorizontalRule','SpecialChar'/*,'Smiley'*/)); $config['toolbar'][] = '/'; $config['toolbar'][] = array('name' => 'styles', 'items' => array('Style','Format','Font','FontSize')); $config['toolbar'][] = array('name' => 'colors', 'items' => array('TextColor','BGColor')); $config['toolbar'][] = array('name' => 'tools', 'items' => array('ShowBlocks','-','About')); break; case 'extended': default: $config['toolbar'][] = array('name' => 'clipboard', 'items' => array('Bold','Italic','Underline')); $config['toolbar'][] = array('name' => 'justify', 'items' => array('JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock')); $config['toolbar'][] = array('name' => 'paragraph', 'items' => array('BulletedList','NumberedList'/*,'Smiley'*/,'Outdent','Indent','Undo','Redo')); $config['toolbar'][] = array('name' => 'clipboard', 'items' => array('Cut','Copy','Paste','PasteText','PasteFromWord','-','Print')); if ($mode == 'extended') { $config['toolbar'][] = array('name' => 'insert', 'items' => array('Image','Link','Unlink','Anchor')); if ($spellchecker_button||$scayt_button) { $configArray = array('Maximize'); if ($spellchecker_button) $configArray[] = $spellchecker_button; if ($scayt_button) $configArray[] = $scayt_button; $config['toolbar'][] = array('name' => 'tools', 'items' => $configArray); } else $config['toolbar'][] = array('name' => 'insert', 'items' => array('Maximize'));//, 'Image', 'Table'); $config['toolbar'][count($config['toolbar']) - 1][] = array('name' => 'insert', 'items' => array('Image', 'Table')); } else { if ($spellchecker_button||$scayt_button) { $configArray = array('Maximize'); if ($spellchecker_button) $configArray[] = $spellchecker_button; if ($scayt_button) $configArray[] = $scayt_button; $config['toolbar'][] = array('name' => 'tools', 'items' => $configArray); } else $config['toolbar'][] = array('name' => 'tools', 'items' => array('Maximize')); } $config['toolbar'][] = '/'; $config['toolbar'][] = array('name' => 'edit', 'items' => array('Find','Replace','-','SelectAll','RemoveFormat')); if ($mode == 'simple-withimage') $config['toolbar'][] = array('name' => 'links', 'items' => array('Image','Link','Unlink')); $config['toolbar'][] = array('name' => 'styles', 'items' => array('Format','Font','FontSize')); $config['toolbar'][] = array('name' => 'colors', 'items' => array('TextColor','BGColor')); $config['toolbar'][] = array('name' => 'tools', 'items' => array('ShowBlocks','-','About')); } } /** * @see get_ckeditor_config */ public static function get_ckeditor_config_array($mode = '', $height = 400, $expanded_toolbar = true, $start_path = '') { // set for CK-Editor necessary CSP script-src attributes self::set_csp_script_src_attrs(); // If not explicitly set, use preference for toolbar mode if(!$mode || trim($mode) == '') $mode = $GLOBALS['egw_info']['user']['preferences']['common']['rte_features']; $config = array(); $spellchecker_button = null; self::add_default_options($config, $height, $expanded_toolbar, $start_path); self::add_spellchecker_options($config, $spellchecker_button, $scayt_button); self::add_toolbar_options($config, $mode, $spellchecker_button, $scayt_button); //error_log(__METHOD__."('$mode', $height, ".array2string($expanded_toolbar).") returning ".array2string($config)); return $config; } /** * Returns a json encoded string containing the configuration for the ckeditor. * @param string $mode specifies the count of toolbar buttons available to the user. Possible * values are 'simple', 'extended' and 'advanced'. All other values will default to 'simple' * @param integer $height contains the height of the ckeditor in pixels * @param boolean $expanded_toolbar specifies whether the ckeditor should start with an expanded toolbar or not * @param string $start_path specifies */ public static function get_ckeditor_config($mode = '', $height = 400, $expanded_toolbar = true, $start_path = '') { return json_encode(self::get_ckeditor_config_array($mode, $height, $expanded_toolbar, $start_path)); } /** * URL webspellchecker uses for scripts and style-sheets */ const WEBSPELLCHECK_HOST = 'svc.webspellchecker.net'; /** * Set for CK-Editor necessary CSP script-src attributes * * Get's called automatic from get_ckeditor_config(_array) */ public static function set_csp_script_src_attrs() { $attrs = array('unsafe-eval', 'unsafe-inline'); $url = ($_SERVER['HTTPS'] ? 'https://' : 'http://').self::WEBSPELLCHECK_HOST; // if webspellchecker is enabled in EGroupware config, allow access to it's url if (in_array($GLOBALS['egw_info']['server']['enabled_spellcheck'], array('True', 'YesUseWebSpellCheck'))) { $attrs[] = $url; egw_framework::csp_style_src_attrs($url); } //error_log(__METHOD__."() egw_info[server][enabled_spellcheck]='{$GLOBALS['egw_info']['server']['enabled_spellcheck']}' --> attrs=".array2string($attrs)); // tell framework CK Editor needs eval and inline javascript :( egw_framework::csp_script_src_attrs($attrs); } }