WIP get TinyMCE to respect users preferred font and -size

This commit is contained in:
ralf 2022-05-10 18:46:12 +02:00
parent ced6c77f46
commit b7d3b7408e
4 changed files with 188 additions and 17 deletions

View File

@ -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 // setting p (and below also the preferred formatblock) to the users font and -size preference
p: { block: 'p', styles: { p: { block: 'p', styles: {
"font-family": (egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif'), "font-family": (egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif'),
"font-size": (egw.preference('rte_font_size', 'common') || '10')+'pt' "font-size": (<string>egw.preference('rte_font_size', 'common') || '10')+
(<string>egw.preference('rte_font_unit', 'common') || 'pt')
}}, }},
customparagraph: { block: 'p', styles: {"margin-block-start": "0px", "margin-block-end": "0px"}} 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;"+ "MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;"+
"Wingdings=wingdings,zapf dingbats", "Wingdings=wingdings,zapf dingbats",
fontsize_formats: '8pt 10pt 12pt 14pt 18pt 24pt 36pt', 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_css: egw.webserverUrl+'/api/tinymce.php?'+ // use the 3 prefs as cache-buster
content_style: (egw.preference('rte_formatblock', 'common') || 'p')+',li'+ btoa(egw.preference('rte_font', 'common')+':'+
' { font-family: '+(egw.preference('rte_font', 'common') || 'arial, helvetica, sans-serif')+ egw.preference('rte_font_size', 'common')+':'+
'; font-size: '+(egw.preference('rte_font_size', 'common') || '10')+'pt }', egw.preference('rte_font_unit', 'common')),
setup : function(ed) /*setup : function(ed)
{ {
ed.on('init', function() ed.on('init', function()
{ {
@ -288,7 +289,7 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable
this.execCommand('fontSize', false, <string><unknown>egw.preference('rte_font_size', 'common') this.execCommand('fontSize', false, <string><unknown>egw.preference('rte_font_size', 'common')
+ egw.preference('rte_font_unit', 'common')); + egw.preference('rte_font_unit', 'common'));
}); });
} }*/
}; };
const rte_formatblock = <string>(egw.preference('rte_formatblock', 'common') || 'p'); const rte_formatblock = <string>(egw.preference('rte_formatblock', 'common') || 'p');
if (rte_formatblock !== 'p') if (rte_formatblock !== 'p')
@ -308,9 +309,9 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable
{ {
self.editor.formatter.toggle(<string><unknown>egw.preference('rte_formatblock', 'common')); self.editor.formatter.toggle(<string><unknown>egw.preference('rte_formatblock', 'common'));
jQuery(self.editor.editorContainer).height(self.options.height); 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, <string><unknown>egw.preference('rte_font_size', 'common') self.editor.execCommand('fontSize', false, <string><unknown>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(){ jQuery(self.editor.iframeElement.contentWindow.document).on('dragenter', function(){
if (jQuery('#dragover-tinymce').length < 1) jQuery("<style id='dragover-tinymce'>.dragover:after {height:calc(100% - "+jQuery(this).height()+"px) !important;}</style>").appendTo('head'); if (jQuery('#dragover-tinymce').length < 1) jQuery("<style id='dragover-tinymce'>.dragover:after {height:calc(100% - "+jQuery(this).height()+"px) !important;}</style>").appendTo('head');
}); });
@ -533,9 +534,18 @@ export class et2_htmlarea extends et2_editableWidget implements et2_IResizeable
getValue() getValue()
{ {
return this.editor ? this.editor.getContent() : ( if (this.editor)
this.options.readonly ? this.value : this.htmlNode.val() {
); /* 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();
} }
/** /**

View File

@ -182,5 +182,108 @@ class HtmlArea extends Etemplate\Widget
} }
return $size.($size?$unit:''); 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 <<<EOF
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body {
font-family: $font_family;
font-size: $font_size;
line-height: 1.4;
margin: 1rem;
} }
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\HtmlArea', 'htmlarea'); table {
border-collapse: collapse;
}
/* Apply a default padding if legacy cellpadding attribute is missing */
table:not([cellpadding]) th,
table:not([cellpadding]) td {
padding: 0.4rem;
}
/* Set default table styles if a table has a positive border attribute
and no inline css */
table[border]:not([border="0"]):not([style*="border-width"]) th,
table[border]:not([border="0"]):not([style*="border-width"]) td {
border-width: 1px;
}
/* Set default table styles if a table has a positive border attribute
and no inline css */
table[border]:not([border="0"]):not([style*="border-style"]) th,
table[border]:not([border="0"]):not([style*="border-style"]) td {
border-style: solid;
}
/* Set default table styles if a table has a positive border attribute
and no inline css */
table[border]:not([border="0"]):not([style*="border-color"]) th,
table[border]:not([border="0"]):not([style*="border-color"]) td {
border-color: #ccc;
}
figure {
display: table;
margin: 1rem auto;
}
figure figcaption {
color: #999;
display: block;
margin-top: 0.25rem;
text-align: center;
}
hr {
border-color: #ccc;
border-style: solid;
border-width: 1px 0 0 0;
}
code {
background-color: #e8e8e8;
border-radius: 3px;
padding: 0.1rem 0.2rem;
}
.mce-content-body:not([dir=rtl]) blockquote {
border-left: 2px solid #ccc;
margin-left: 1.5rem;
padding-left: 1rem;
}
.mce-content-body[dir=rtl] blockquote {
border-right: 2px solid #ccc;
margin-right: 1.5rem;
padding-right: 1rem;
}
fieldset {
border: 2px solid silver;
border-left: none;
border-right: none;
font-family: $font_family;
font-size: $font_size;
margin: .5rem 0 .5rem 0;
}
/* EGroupware users preferred font and -size */
h1:not([style*="font-family"]),h2:not([style*="font-family"]),h3:not([style*="font-family"]),h4:not([style*="font-family"]),h5:not([style*="font-family"]),h6:not([style*="font-family"]),
div:not([style*="font-family"]),li:not([style*="font-family"]),p:not([style*="font-family"]),blockquote:not([style*="font-family"]),
td:not([style*="font-family"]),th:not([style*="font-family"]) {
font-family: $font_family;
}
div:not([style*="font-size"]),li:not([style*="font-size"]),p:not([style*="font-size"]),blockquote:not([style*="font-size"]),
td:not([style*="font-size"]),th:not([style*="font-size"]) {
font-size: $font_size;
}
EOF;
}
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\HtmlArea', 'htmlarea');

57
api/tinymce.php Normal file
View File

@ -0,0 +1,57 @@
<?php
/**
* API: loading user preferences and data
*
* Usage: /egroupware/api/user.php?user=123
*
* @link www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @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;

View File

@ -2382,11 +2382,11 @@ class mail_compose
*/ */
static function wrapBlockWithPreferredFont($content, $legend, $class=null) 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-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); return Api\Html::fieldset($content, $legend, $options);
} }
@ -2530,7 +2530,8 @@ class mail_compose
switch ($_formData['mimeType']) switch ($_formData['mimeType'])
{ {
case 'html': case 'html':
$body = $_formData['body']; $body = "<style>\n".preg_replace('#/\*.*?\*/\s*#sm', '', Etemplate\Widget\HtmlArea::contentCss()).
"</style>\n".$_formData['body'];
if (!empty($attachment_links)) if (!empty($attachment_links))
{ {
if (strpos($body, '<!-- HTMLSIGBEGIN -->') !== false) if (strpos($body, '<!-- HTMLSIGBEGIN -->') !== false)