mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
move egw_framework::csp_*_source_attrs($attrs) to Api\Header\ContentSecurityPolicy::add(*-src, $attrs) and egw_ckeditor_config to Api\Html\CkEditorConfig
This commit is contained in:
parent
69a070498b
commit
99714aa9e9
164
api/src/Header/ContentSecurityPolicy.php
Normal file
164
api/src/Header/ContentSecurityPolicy.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware API - Content Security Policy headers
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @package api
|
||||
* @subpackage header
|
||||
* @access public
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Header;
|
||||
|
||||
/**
|
||||
* Content Security Policy headers
|
||||
*/
|
||||
class ContentSecurityPolicy
|
||||
{
|
||||
/**
|
||||
* Additional attributes or urls for CSP beside always added "self"
|
||||
*
|
||||
* - "script-src 'self' 'unsafe-eval'" allows only self and eval (eg. ckeditor), but forbids inline scripts, onchange, etc
|
||||
* - "connect-src 'self'" allows ajax requests only to self
|
||||
* - "style-src 'self' 'unsafe-inline'" allows only self and inline style, which we need
|
||||
* - "frame-src 'self' manual.egroupware.org" allows frame and iframe content only for self or manual.egroupware.org
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $sources = array(
|
||||
'script-src' => array("'unsafe-eval'"),
|
||||
'style-src' => array("'unsafe-inline'"),
|
||||
'connect-src' => array(),
|
||||
'frame-src' => null, // NOT array(), to allow setting no default frame-src!
|
||||
);
|
||||
|
||||
/**
|
||||
* Add Content-Security-Policy sources
|
||||
*
|
||||
* Calling this method with an empty array for frame-src, sets no defaults but "'self'"!
|
||||
*
|
||||
* @param string|array $set =array() URL (incl. protocol!)
|
||||
* @param string $source valid CSP source types like 'script-src', 'style-src', 'connect-src', 'frame-src', ...
|
||||
* @param string|array $attrs 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
|
||||
*/
|
||||
public static function add($source, $attrs)
|
||||
{
|
||||
if (!isset(self::$sources[$source]))
|
||||
{
|
||||
// set frame-src attrs of API and apps via hook
|
||||
if ($source == 'frame-src' && !isset($attrs))
|
||||
{
|
||||
$attrs = array('manual.egroupware.org', 'www.egroupware.org');
|
||||
if (($app_additional = $GLOBALS['egw']->hooks->process('csp-frame-src')))
|
||||
{
|
||||
foreach($app_additional as $addtional)
|
||||
{
|
||||
if ($addtional) $attrs = array_unique(array_merge($attrs, $addtional));
|
||||
}
|
||||
}
|
||||
}
|
||||
self::$sources[$source] = array();
|
||||
}
|
||||
foreach((array)$attrs as $attr)
|
||||
{
|
||||
if (in_array($attr, array('none', 'self', 'unsafe-eval', 'unsafe-inline')))
|
||||
{
|
||||
$attr = "'$attr'"; // automatic add quotes
|
||||
}
|
||||
if (!in_array($attr, self::$sources[$source]))
|
||||
{
|
||||
self::$sources[$source][] = $attr;
|
||||
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Content-Security-Policy attributes for script-src: 'unsafe-eval' and/or 'unsafe-inline'
|
||||
*
|
||||
* Using CK-Editor currently requires both to be set :(
|
||||
*
|
||||
* Old pre-et2 apps might need to call Api\Headers::script_src_attrs(array('unsafe-eval','unsafe-inline'))
|
||||
*
|
||||
* EGroupware itself currently still requires 'unsafe-eval'!
|
||||
*
|
||||
* @param string|array $set =array() 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
|
||||
*/
|
||||
public static function add_script_src($set=null)
|
||||
{
|
||||
self::add('script-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Content-Security-Policy attributes for style-src: 'unsafe-inline'
|
||||
*
|
||||
* EGroupware itself currently still requires 'unsafe-inline'!
|
||||
*
|
||||
* @param string|array $set =array() 'unsafe-inline' (without quotes!) and/or URL (incl. protocol!)
|
||||
*/
|
||||
public static function add_style_src($set=null)
|
||||
{
|
||||
self::add('style-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Content-Security-Policy attributes for connect-src:
|
||||
*
|
||||
* @param string|array $set =array() URL (incl. protocol!)
|
||||
*/
|
||||
public static function add_connect_src($set=null)
|
||||
{
|
||||
self::add('connect-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/get Content-Security-Policy attributes for frame-src:
|
||||
*
|
||||
* Calling this method with an empty array sets no frame-src, but "'self'"!
|
||||
*
|
||||
* @param string|array $set =array() URL (incl. protocol!)
|
||||
* @return string with attributes eg. "'unsafe-inline'"
|
||||
*/
|
||||
public static function add_frame_src($set=null)
|
||||
{
|
||||
self::add('frame-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Content-Security-Policy header
|
||||
*
|
||||
* @link http://content-security-policy.com/
|
||||
*/
|
||||
public static function send()
|
||||
{
|
||||
self::add('frame-src', null); // set defaults for frame-src
|
||||
|
||||
$policies = array();
|
||||
foreach(self::$sources as $source => $urls)
|
||||
{
|
||||
$policies[] = "$source 'self' ".implode(' ', $urls);
|
||||
}
|
||||
$csp = implode('; ', $policies);
|
||||
|
||||
//$csp = "default-src * 'unsafe-eval' 'unsafe-inline'"; // allow everything
|
||||
|
||||
$user_agent = UserAgent::type();
|
||||
$version = UserAgent::version();
|
||||
|
||||
// recommendaton ist to not send regular AND deprecated headers together, as they can cause unexpected behavior
|
||||
if ($user_agent == 'chrome' && $version < 25 || $user_agent == 'safari' && $version < 7)
|
||||
{
|
||||
header("X-Webkit-CSP: $csp"); // Chrome: <= 24, Safari incl. iOS
|
||||
}
|
||||
elseif ($user_agent == 'firefox' && $version < 23 || $user_agent == 'msie') // Edge is reported as 'edge'!
|
||||
{
|
||||
header("X-Content-Security-Policy: $csp");
|
||||
}
|
||||
else
|
||||
{
|
||||
header("Content-Security-Policy: $csp");
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ class UserAgent
|
||||
}
|
||||
|
||||
/**
|
||||
* user-agent: 'firefox', 'msie', 'safari' (incl. iPhone), 'chrome', 'opera', 'konqueror', 'mozilla'
|
||||
* user-agent: 'firefox', 'msie', 'edge', 'safari' (incl. iPhone), 'chrome', 'opera', 'konqueror', 'mozilla'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -16,7 +16,6 @@ namespace EGroupware\Api;
|
||||
|
||||
// explicitly import classes not yet imported into Api
|
||||
use egw_framework; // validate_file, includeCSS
|
||||
use egw_ckeditor_config;
|
||||
use egw; // link
|
||||
|
||||
/**
|
||||
@ -437,7 +436,7 @@ class Html
|
||||
|
||||
// User preferences
|
||||
$font = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font'];
|
||||
$font_size = egw_ckeditor_config::font_size_from_prefs();
|
||||
$font_size = Html\CkEditorConfing::font_size_from_prefs();
|
||||
$font_span = '<span '.($font||$font_size?'style=\"':'').($font?'font-family:'.$font.'; ':'').($font_size?'font-size:'.$font_size.'; ':'').'\">';
|
||||
if (empty($font) && empty($font_size)) $font_span = '';
|
||||
|
||||
@ -449,7 +448,7 @@ class Html
|
||||
<script type="text/javascript">
|
||||
window.CKEDITOR_BASEPATH="'.$GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/js/ckeditor/";
|
||||
egw_LAB.wait(function() {
|
||||
CKEDITOR.replace("'.$_name.'", '.egw_ckeditor_config::get_ckeditor_config($_mode,
|
||||
CKEDITOR.replace("'.$_name.'", '.Html\CkEditorConfing::get_ckeditor_config($_mode,
|
||||
$pxheight, $expanded, $_start_path).');
|
||||
CKEDITOR.addCss("body { margin: 5px; }");
|
||||
CKEDITOR.instances["'.$_name.'"].on(
|
||||
|
518
api/src/Html/CkEditorConfig.php
Normal file
518
api/src/Html/CkEditorConfig.php
Normal file
@ -0,0 +1,518 @@
|
||||
<?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 html
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Html;
|
||||
|
||||
use EGroupware\Api\Header\ContentSecurityPolicy;
|
||||
|
||||
/**
|
||||
* CK-Editor configuration
|
||||
*/
|
||||
class CkEditorConfig
|
||||
{
|
||||
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
|
||||
*
|
||||
* Only return skins existing in filesystem, as we disable / remove them if not compatible with supported browsers.
|
||||
*
|
||||
* @return array skin => label pairs alphabetical sorted with default moono first
|
||||
*/
|
||||
public static function getAvailableCKEditorSkins()
|
||||
{
|
||||
$labels = 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'),
|
||||
'minimalist' => lang('Minimalist theme'),
|
||||
'flat' => lang('Flat theme')
|
||||
);
|
||||
$skins = array();
|
||||
|
||||
foreach(scandir(EGW_SERVER_ROOT.'/phpgwapi/js/ckeditor/skins') as $skin)
|
||||
{
|
||||
if ($skin[0] == '.') continue;
|
||||
|
||||
if (isset($labels[$skin]))
|
||||
{
|
||||
$skins[$skin] = $labels[$skin];
|
||||
}
|
||||
else
|
||||
{
|
||||
$skins[$skin] = str_replace('_', '-', $skin).' '.lang('Theme');
|
||||
}
|
||||
}
|
||||
uasort($skins, 'strcasecmp');
|
||||
|
||||
// return our default "moono" first
|
||||
return isset($skins['moono']) ? array('moono' => $skins['moono'])+$skins : $skins;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 'moono-dark':
|
||||
$skin = "moono-dark";
|
||||
break;
|
||||
case 'icy_orange':
|
||||
$skin = "icy_orange";
|
||||
break;
|
||||
case 'bootstrapck': // no longer support by egw
|
||||
case 'Moono_blue':
|
||||
case 'office2013':
|
||||
case 'office2003':
|
||||
case 'moonocolor':
|
||||
$skin = "moonocolor";
|
||||
break;
|
||||
case 'minimalist':
|
||||
$skin = "minimalist";
|
||||
break;
|
||||
case 'flat':
|
||||
$skin = "flat";
|
||||
break;
|
||||
case 'moono':
|
||||
case 'default':
|
||||
default:
|
||||
$skin = "moono";
|
||||
}
|
||||
|
||||
//Check whether the skin actually exists, if not, switch to a default
|
||||
if (!file_exists(EGW_SERVER_ROOT.'/phpgwapi/js/ckeditor/skins/'.$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;
|
||||
|
||||
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';
|
||||
self::append_extraPlugins_config_array($config, array("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);
|
||||
$scayt_button = null;
|
||||
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));
|
||||
// Add extra plugins
|
||||
self::append_extraPlugins_config_array($config, array('uploadimage','uploadwidget','widget','notification','notificationaggregator','lineutils'));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extra
|
||||
* @param array $config
|
||||
* @param array $plugins plugins name which needs to be appended into extraPlugins
|
||||
*/
|
||||
public static function append_extraPlugins_config_array (&$config, $plugins)
|
||||
{
|
||||
if (is_array($plugins))
|
||||
{
|
||||
foreach ($plugins as &$plugin)
|
||||
{
|
||||
if (!empty($config['extraPlugins']) && $config['extraPlugins'] !== '')
|
||||
{
|
||||
$config['extraPlugins'] .= ',' . $plugin;
|
||||
}
|
||||
else
|
||||
{
|
||||
$config['extraPlugins'] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
ContentSecurityPolicy::add('style-src', $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 :(
|
||||
ContentSecurityPolicy::add('script-src', $attrs);
|
||||
}
|
||||
}
|
@ -11,504 +11,9 @@
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Only return skins existing in filesystem, as we disable / remove them if not compatible with supported browsers.
|
||||
*
|
||||
* @return array skin => label pairs alphabetical sorted with default moono first
|
||||
*/
|
||||
public static function getAvailableCKEditorSkins()
|
||||
{
|
||||
$labels = 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'),
|
||||
'minimalist' => lang('Minimalist theme'),
|
||||
'flat' => lang('Flat theme')
|
||||
);
|
||||
$skins = array();
|
||||
|
||||
foreach(scandir(EGW_SERVER_ROOT.'/phpgwapi/js/ckeditor/skins') as $skin)
|
||||
{
|
||||
if ($skin[0] == '.') continue;
|
||||
|
||||
if (isset($labels[$skin]))
|
||||
{
|
||||
$skins[$skin] = $labels[$skin];
|
||||
}
|
||||
else
|
||||
{
|
||||
$skins[$skin] = str_replace('_', '-', $skin).' '.lang('Theme');
|
||||
}
|
||||
}
|
||||
uasort($skins, 'strcasecmp');
|
||||
|
||||
// return our default "moono" first
|
||||
return isset($skins['moono']) ? array('moono' => $skins['moono'])+$skins : $skins;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 'moono-dark':
|
||||
$skin = "moono-dark";
|
||||
break;
|
||||
case 'icy_orange':
|
||||
$skin = "icy_orange";
|
||||
break;
|
||||
case 'bootstrapck': // no longer support by egw
|
||||
case 'Moono_blue':
|
||||
case 'office2013':
|
||||
case 'office2003':
|
||||
case 'moonocolor':
|
||||
$skin = "moonocolor";
|
||||
break;
|
||||
case 'minimalist':
|
||||
$skin = "minimalist";
|
||||
break;
|
||||
case 'flat':
|
||||
$skin = "flat";
|
||||
break;
|
||||
case 'moono':
|
||||
case 'default':
|
||||
default:
|
||||
$skin = "moono";
|
||||
}
|
||||
|
||||
//Check whether the skin actually exists, if not, switch to a default
|
||||
if (!file_exists(EGW_SERVER_ROOT.'/phpgwapi/js/ckeditor/skins/'.$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;
|
||||
|
||||
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';
|
||||
self::append_extraPlugins_config_array($config, array("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);
|
||||
$scayt_button = null;
|
||||
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));
|
||||
// Add extra plugins
|
||||
self::append_extraPlugins_config_array($config, array('uploadimage','uploadwidget','widget','notification','notificationaggregator','lineutils'));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extra
|
||||
* @param array $config
|
||||
* @param array $plugins plugins name which needs to be appended into extraPlugins
|
||||
*/
|
||||
public static function append_extraPlugins_config_array (&$config, $plugins)
|
||||
{
|
||||
if (is_array($plugins))
|
||||
{
|
||||
foreach ($plugins as &$plugin)
|
||||
{
|
||||
if (!empty($config['extraPlugins']) && $config['extraPlugins'] !== '')
|
||||
{
|
||||
$config['extraPlugins'] .= ',' . $plugin;
|
||||
}
|
||||
else
|
||||
{
|
||||
$config['extraPlugins'] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
class egw_ckeditor_config extends Api\Html\CkEditorConfig{}
|
||||
|
@ -12,6 +12,8 @@
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* eGW API - framework: virtual base class for all template sets
|
||||
*
|
||||
@ -127,15 +129,6 @@ abstract class egw_framework
|
||||
return new $class($GLOBALS['egw_info']['server']['template_set']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional attributes or urls for CSP script-src 'self'
|
||||
*
|
||||
* 'unsafe-eval' is currently allways added, as it is used in a couple of places.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $csp_script_src_attrs = array("'unsafe-eval'");
|
||||
|
||||
/**
|
||||
* Set/get Content-Security-Policy attributes for script-src: 'unsafe-eval' and/or 'unsafe-inline'
|
||||
*
|
||||
@ -145,132 +138,49 @@ abstract class egw_framework
|
||||
*
|
||||
* EGroupware itself currently still requires 'unsafe-eval'!
|
||||
*
|
||||
* @param string|array $set =array() 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
|
||||
* @return string with attributes eg. "'unsafe-eval' 'unsafe-inline'"
|
||||
* @param string|array $set =null 'unsafe-eval' and/or 'unsafe-inline' (without quotes!) or URL (incl. protocol!)
|
||||
* @deprecated use Api\Header\ContentSecurityPolicy::add('script-src', $set);
|
||||
*/
|
||||
public static function csp_script_src_attrs($set=null)
|
||||
{
|
||||
foreach((array)$set as $attr)
|
||||
{
|
||||
if (in_array($attr, array('none', 'self', 'unsafe-eval', 'unsafe-inline')))
|
||||
{
|
||||
$attr = "'$attr'"; // automatic add quotes
|
||||
}
|
||||
if (!in_array($attr, self::$csp_script_src_attrs))
|
||||
{
|
||||
self::$csp_script_src_attrs[] = $attr;
|
||||
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
|
||||
}
|
||||
}
|
||||
//error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_script_src_attrs)).' '.function_backtrace());
|
||||
return implode(' ', self::$csp_script_src_attrs);
|
||||
Api\Header\ContentSecurityPolicy::add('script-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional attributes or urls for CSP style-src 'self'
|
||||
*
|
||||
* 'unsafe-inline' is currently allways added, as it is used in a couple of places.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $csp_style_src_attrs = array("'unsafe-inline'");
|
||||
|
||||
/**
|
||||
* Set/get Content-Security-Policy attributes for style-src: 'unsafe-inline'
|
||||
*
|
||||
* EGroupware itself currently still requires 'unsafe-inline'!
|
||||
*
|
||||
* @param string|array $set =array() 'unsafe-inline' (without quotes!) and/or URL (incl. protocol!)
|
||||
* @return string with attributes eg. "'unsafe-inline'"
|
||||
* @param string|array $set =null 'unsafe-inline' (without quotes!) and/or URL (incl. protocol!)
|
||||
* @deprecated use Api\Header\ContentSecurityPolicy::add('style-src', $set);
|
||||
*/
|
||||
public static function csp_style_src_attrs($set=null)
|
||||
{
|
||||
foreach((array)$set as $attr)
|
||||
{
|
||||
if (in_array($attr, array('none', 'self', 'unsafe-inline')))
|
||||
{
|
||||
$attr = "'$attr'"; // automatic add quotes
|
||||
}
|
||||
if (!in_array($attr, self::$csp_style_src_attrs))
|
||||
{
|
||||
self::$csp_style_src_attrs[] = $attr;
|
||||
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
|
||||
}
|
||||
}
|
||||
//error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_script_src_attrs)).' '.function_backtrace());
|
||||
return implode(' ', self::$csp_style_src_attrs);
|
||||
Api\Header\ContentSecurityPolicy::add('style-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional attributes or urls for CSP connect-src 'self'
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $csp_connect_src_attrs = array();
|
||||
|
||||
/**
|
||||
* Set/get Content-Security-Policy attributes for connect-src:
|
||||
*
|
||||
* @param string|array $set =array() URL (incl. protocol!)
|
||||
* @return string with attributes eg. "'unsafe-inline'"
|
||||
* @deprecated use Api\Header\ContentSecurityPolicy::add('connect-src', $set);
|
||||
*/
|
||||
public static function csp_connect_src_attrs($set=null)
|
||||
{
|
||||
foreach((array)$set as $attr)
|
||||
{
|
||||
if (!in_array($attr, self::$csp_connect_src_attrs))
|
||||
{
|
||||
self::$csp_connect_src_attrs[] = $attr;
|
||||
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
|
||||
}
|
||||
}
|
||||
//error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_connect_src_attrs)).' '.function_backtrace());
|
||||
return implode(' ', self::$csp_connect_src_attrs);
|
||||
Api\Header\ContentSecurityPolicy::add('connect-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional attributes or urls for CSP frame-src 'self'
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $csp_frame_src_attrs;
|
||||
|
||||
/**
|
||||
* Set/get Content-Security-Policy attributes for frame-src:
|
||||
*
|
||||
* Calling this method with an empty array sets no frame-src, but "'self'"!
|
||||
*
|
||||
* @param string|array $set =array() URL (incl. protocol!)
|
||||
* @return string with attributes eg. "'unsafe-inline'"
|
||||
* @param string|array $set =null URL (incl. protocol!)
|
||||
* @deprecated use Api\Header\ContentSecurityPolicy::add('frame-src', $set);
|
||||
*/
|
||||
public static function csp_frame_src_attrs($set=null)
|
||||
{
|
||||
// set frame-src attrs of API and apps via hook
|
||||
if (!isset(self::$csp_frame_src_attrs) && !isset($set))
|
||||
{
|
||||
$frame_src = array('manual.egroupware.org', 'www.egroupware.org');
|
||||
if (($app_additional = $GLOBALS['egw']->hooks->process('csp-frame-src')))
|
||||
{
|
||||
foreach($app_additional as $addtional)
|
||||
{
|
||||
if ($addtional) $frame_src = array_unique(array_merge($frame_src, $addtional));
|
||||
}
|
||||
}
|
||||
return self::csp_frame_src_attrs($frame_src);
|
||||
}
|
||||
|
||||
if (!isset(self::$csp_frame_src_attrs)) self::$csp_frame_src_attrs = array();
|
||||
|
||||
foreach((array)$set as $attr)
|
||||
{
|
||||
if (!in_array($attr, self::$csp_frame_src_attrs))
|
||||
{
|
||||
self::$csp_frame_src_attrs[] = $attr;
|
||||
//error_log(__METHOD__."() setting CSP script-src $attr ".function_backtrace());
|
||||
}
|
||||
}
|
||||
//error_log(__METHOD__."(".array2string($set).") returned ".array2string(implode(' ', self::$csp_frame_src_attrs)).' '.function_backtrace());
|
||||
return implode(' ', self::$csp_frame_src_attrs);
|
||||
Api\Header\ContentSecurityPolicy::add('frame-src', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,20 +191,7 @@ abstract class egw_framework
|
||||
// add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv)
|
||||
header('Content-type: text/html; charset='.translation::charset());
|
||||
|
||||
// content-security-policy header:
|
||||
// - "script-src 'self' 'unsafe-eval'" allows only self and eval (eg. ckeditor), but forbids inline scripts, onchange, etc
|
||||
// - "connect-src 'self'" allows ajax requests only to self
|
||||
// - "style-src 'self' 'unsave-inline'" allows only self and inline style, which we need
|
||||
// - "frame-src 'self' manual.egroupware.org" allows frame and iframe content only for self or manual.egroupware.org
|
||||
$csp = "script-src 'self' ".self::csp_script_src_attrs().
|
||||
"; connect-src 'self' ".self::csp_connect_src_attrs().
|
||||
"; style-src 'self' ".self::csp_style_src_attrs().
|
||||
"; frame-src 'self' ".self::csp_frame_src_attrs();
|
||||
|
||||
//$csp = "default-src * 'unsafe-eval' 'unsafe-inline'"; // allow everything
|
||||
header("Content-Security-Policy: $csp");
|
||||
header("X-Webkit-CSP: $csp"); // Chrome: <= 24, Safari incl. iOS
|
||||
header("X-Content-Security-Policy: $csp"); // FF <= 22
|
||||
Api\Header\ContentSecurityPolicy::send();
|
||||
|
||||
// allow client-side to detect first load aka just logged in
|
||||
$reload_count =& egw_cache::getSession(__CLASS__, 'framework-reload');
|
||||
|
Loading…
Reference in New Issue
Block a user