From b7d3b7408e02552170c0a6b3047d334574934e47 Mon Sep 17 00:00:00 2001 From: ralf Date: Tue, 10 May 2022 18:46:12 +0200 Subject: [PATCH] WIP get TinyMCE to respect users preferred font and -size --- api/js/etemplate/et2_widget_htmlarea.ts | 34 +++++--- api/src/Etemplate/Widget/HtmlArea.php | 105 +++++++++++++++++++++++- api/tinymce.php | 57 +++++++++++++ mail/inc/class.mail_compose.inc.php | 9 +- 4 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 api/tinymce.php diff --git a/api/js/etemplate/et2_widget_htmlarea.ts b/api/js/etemplate/et2_widget_htmlarea.ts index a6c47c2abc..5fe0779072 100644 --- a/api/js/etemplate/et2_widget_htmlarea.ts +++ b/api/js/etemplate/et2_widget_htmlarea.ts @@ -238,7 +238,8 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable // setting p (and below also the preferred formatblock) to the users font and -size preference p: { block: 'p', styles: { "font-family": (egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif'), - "font-size": (egw.preference('rte_font_size', 'common') || '10')+'pt' + "font-size": (egw.preference('rte_font_size', 'common') || '10')+ + (egw.preference('rte_font_unit', 'common') || 'pt') }}, customparagraph: { block: 'p', styles: {"margin-block-start": "0px", "margin-block-end": "0px"}} }, @@ -276,11 +277,11 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable "MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;"+ "Wingdings=wingdings,zapf dingbats", fontsize_formats: '8pt 10pt 12pt 14pt 18pt 24pt 36pt', - // this displays all p and li with the users default font and -size (only kosmetik, as TinyMCE does not return or set these styles!) - content_style: (egw.preference('rte_formatblock', 'common') || 'p')+',li'+ - ' { font-family: '+(egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif')+ - '; font-size: '+(egw.preference('rte_font_size', 'common') || '10')+'pt }', - setup : function(ed) + content_css: egw.webserverUrl+'/api/tinymce.php?'+ // use the 3 prefs as cache-buster + btoa(egw.preference('rte_font', 'common')+':'+ + egw.preference('rte_font_size', 'common')+':'+ + egw.preference('rte_font_unit', 'common')), + /*setup : function(ed) { ed.on('init', function() { @@ -288,7 +289,7 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable this.execCommand('fontSize', false, egw.preference('rte_font_size', 'common') + egw.preference('rte_font_unit', 'common')); }); - } + }*/ }; const rte_formatblock = (egw.preference('rte_formatblock', 'common') || 'p'); if (rte_formatblock !== 'p') @@ -308,9 +309,9 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable { self.editor.formatter.toggle(egw.preference('rte_formatblock', 'common')); jQuery(self.editor.editorContainer).height(self.options.height); - self.editor.execCommand('fontName', false, egw.preference('rte_font', 'common')); + /*self.editor.execCommand('fontName', false, egw.preference('rte_font', 'common')); self.editor.execCommand('fontSize', false, egw.preference('rte_font_size', 'common') - + egw.preference('rte_font_unit', 'common')); + + egw.preference('rte_font_unit', 'common'));*/ jQuery(self.editor.iframeElement.contentWindow.document).on('dragenter', function(){ if (jQuery('#dragover-tinymce').length < 1) jQuery("").appendTo('head'); }); @@ -533,9 +534,18 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable getValue() { - return this.editor ? this.editor.getContent() : ( - this.options.readonly ? this.value : this.htmlNode.val() - ); + if (this.editor) + { + /* set users preferred font and -size explicit for all these elements without any style set + this.editor.getDoc().querySelectorAll('div:not([style]),li:not([style]),p:not([style]),blockquote:not([style]),fieldset:not([style]),td:not([style])') + .forEach(element => { + element.style.fontFamily = (egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif'); + element.style.fontSize = (egw.preference('rte_font_size', 'common') || '10')+'pt'; + });*/ + + return this.editor.getContent(); + } + return this.options.readonly ? this.value : this.htmlNode.val(); } /** diff --git a/api/src/Etemplate/Widget/HtmlArea.php b/api/src/Etemplate/Widget/HtmlArea.php index a058ec3d83..b6ed91c0cb 100644 --- a/api/src/Etemplate/Widget/HtmlArea.php +++ b/api/src/Etemplate/Widget/HtmlArea.php @@ -182,5 +182,108 @@ class HtmlArea extends Etemplate\Widget } return $size.($size?$unit:''); } + + /** + * Content CSS für TinyMCE + * + * Can/should also be added to mails, to ensure identical display on the receiving MUA. + * + * @return string + */ + public static function contentCss() + { + $font_family = $GLOBALS['egw_info']['user']['preferences']['common']['rtf_font'] ?? 'arial, helvetica, sans-serif'; + $font_size = ($GLOBALS['egw_info']['user']['preferences']['common']['rtf_font_size'] ?? '10'). + ($GLOBALS['egw_info']['user']['preferences']['common']['rtf_font_unit'] ?? 'pt'); + + return << + * @package api + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +use EGroupware\Api; + +// switch evtl. set output-compression off, as we cant calculate a Content-Length header with transparent compression +ini_set('zlib.output_compression', 0); + +$GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => 'api', + 'noheader' => true, + 'nocachecontrol' => true, + ) +); + +include '../header.inc.php'; + +// release session, as we don't need it, and it blocks parallel requests +$GLOBALS['egw']->session->commit_session(); + +// use an etag over output +$content = Api\Etemplate\Widget\HtmlArea::contentCss(); +$etag = '"'.md5($content).'"'; + +// headers to allow caching, egw_framework specifies etag on url to force reload, even with Expires header +Api\Session::cache_control(86400); // cache for 1 day +Header('Content-Type: text/css'); +Header('ETag: '.$etag); + +// if servers send a If-None-Match header, response with 304 Not Modified, if etag matches +if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag) +{ + header("HTTP/1.1 304 Not Modified"); + exit; +} + +// we run our own gzip compression, to set a correct Content-Length of the encoded content +if (in_array('gzip', explode(',',$_SERVER['HTTP_ACCEPT_ENCODING'])) && function_exists('gzencode')) +{ + $content = gzencode($content); + header('Content-Encoding: gzip'); +} + +// Content-Lenght header is important, otherwise browsers dont cache! +Header('Content-Length: '.bytes($content)); +echo $content; \ No newline at end of file diff --git a/mail/inc/class.mail_compose.inc.php b/mail/inc/class.mail_compose.inc.php index 083788373b..d8f6ce15de 100644 --- a/mail/inc/class.mail_compose.inc.php +++ b/mail/inc/class.mail_compose.inc.php @@ -2382,11 +2382,11 @@ class mail_compose */ static function wrapBlockWithPreferredFont($content, $legend, $class=null) { - $options = ' style="border: 2px solid silver; border-left: none; border-right: none;'. + $options = '';/*' style="border: 2px solid silver; border-left: none; border-right: none;'. 'font-family: '.($GLOBALS['egw_info']['user']['preferences']['common']['rtf_font'] ?? 'arial, helvetica, sans-serif'). - '; font-size: '.($GLOBALS['egw_info']['user']['preferences']['common']['rtf_size'] ?? '10').'pt"'; + '; font-size: '.($GLOBALS['egw_info']['user']['preferences']['common']['rtf_size'] ?? '10').'pt"';*/ - if (!empty($class)) $options .= ' class="'.htmlspecialchars($class).'"'; + if (!empty($class)) $options = ' class="'.htmlspecialchars($class).'"'; return Api\Html::fieldset($content, $legend, $options); } @@ -2530,7 +2530,8 @@ class mail_compose switch ($_formData['mimeType']) { case 'html': - $body = $_formData['body']; + $body = "\n".$_formData['body']; if (!empty($attachment_links)) { if (strpos($body, '') !== false)