Add support for awesome replies using new HTML editor

This commit is contained in:
iliajie
2023-06-15 18:17:14 +03:00
parent 45d6c8d235
commit e70d5ce764
2 changed files with 245 additions and 24 deletions

View File

@@ -2945,6 +2945,104 @@ EOF
return &trim($iframe_body);
}
# iframe_quote(quote)
# Returns quoted message in an iframe HTML element
sub iframe_quote
{
my ($quote) = @_;
return $quote if (!$quote);
# Quote mail iframe inner styles
my $iframe_styles =
'<style>
html,body { overflow-y: hidden; }
blockquote:not([style]) {
margin: 0px 0px 0px 0.8ex;
border-left: 1px solid #ccc;
padding-left: 1ex;
}
</style>';
# Add inner styles to the email body
if ($quote =~ /<\/body>/) {
$quote =~ s/<\/body>/$iframe_styles<\/body>/;
}
else {
$quote .= $iframe_styles;
}
$quote = &trim(&quote_escape($quote, '"'));
# Email iframe stuff
my $iframe_body = <<EOF;
<style>
#quote-mail-iframe {
border: none;
width: calc(100% - 12px);
}
details.iframe_quote_details summary {
display: block;
width: fit-content;
outline: none;
margin-left: 6px;
margin-bottom: 6px;
}
details.iframe_quote_details iframe {
padding-left: 6px;
padding-bottom: 6px;
}
details.iframe_quote_details summary::after {
border: 1px solid #666;
border-radius: 18px;
content: "...";
cursor: pointer;
display: inline-block;
line-height: 0;
padding: 2px 2px 1px 9px;
width: 18px;
height: 9px;
}
details.iframe_quote_details[open] summary::after {
background-color: #d1dffc;
border: 1px solid #97abd4;
}
</style>
<script>
function quote_mail_iframe_onload(iframe) {
if (typeof theme_quote_mail_iframe_onload === 'function') {
theme_quote_mail_iframe_onload(iframe);
return;
}
const iframe_resize = function() {
const iframeobj = document.querySelector('#quote-mail-iframe'),
iframe_height_bound = iframeobj.contentWindow.document.body.getBoundingClientRect().bottom,
iframe_scroll_height = iframeobj.contentWindow.document.body.scrollHeight,
iframe_height =
iframe_height_bound > iframe_scroll_height ?
iframe_height_bound : iframe_scroll_height;
iframeobj.style.height = Math.ceil(iframe_height) + "px";
};
iframe.classList.add("loaded");
setTimeout(iframe_resize);
document.querySelector('.iframe_quote_details').addEventListener("click", function() {
quote_mail_iframe_onload(this)
}, { once: true });
}
</script>
<iframe
id="quote-mail-iframe"
class="quote-mail-iframe"
onload="quote_mail_iframe_onload(this)"
sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"
src="about:blank" srcdoc="<div contenteditable='true' id='webmin-iframe-quote' class='iframe_quote'>$quote</div>">
</iframe>
EOF
$iframe_body = &ui_details({
html => 1,
content => $iframe_body,
class => 'iframe_quote_details'
});
return &trim($iframe_body);
}
# remove_body_attachments(&mail, &attach)
# Returns attachments except for those that make up the message body, and those
# that have sub-attachments.

View File

@@ -442,35 +442,158 @@ if ($in{'new'}) {
# Output message body input
print &ui_table_start($text{'reply_body'}, "width=100%", 2, undef,
&ui_links_row(\@bodylinks));
my $html_editor_quote = &iframe_quote($quote);
my $html_editor_template;
my $html_editor_scripts;
if ($html_edit) {
if ($current_theme !~ /authentic-theme/) {
# Output HTML editor textarea
print <<EOF;
<script type="text/javascript">
_editor_url = "@{[&get_webprefix()]}/$module_name/xinha/";
_editor_lang = "en";
</script>
<script type="text/javascript" src="xinha/XinhaCore.js"></script>
my %tinfo = &get_theme_info($current_theme);
if (!$tinfo{'spa'}) {
$html_editor_template = <<EOF;
<div class="ql-compose-container">
<div class="ql-toolbar">
<span class="ql-formats">
<select class="ql-font">
<option value="initial" selected>$text{'editor_default'}</option>
<option value="sans-serif">Sans Serif</option>
<option value="serif">Serif</option>
<option value="monospace">Monospace</option>
</select>
<select class="ql-size">
<option value="0.75em">$text{'editor_small'}</option>
<option selected>$text{'editor_normal'}</option>
<option value="1.2em">$text{'editor_medium'}</option>
<option value="1.5em">$text{'editor_large'}</option>
<option value="2.5em">$text{'editor_huge'}</option>
</select>
</span>
<span class="ql-formats">
<button class="ql-bold"></button>
<button class="ql-italic"></button>
<button class="ql-underline"></button>
<button class="ql-strike"></button>
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
<span class="ql-formats">
<select class="ql-align"></select>
</span>
<span class="ql-formats">
<button class="ql-list" value="ordered"></button>
<button class="ql-list" value="bullet"></button>
<button class="ql-indent" value="-1"></button>
<button class="ql-indent" value="+1"></button>
</span>
<span class="ql-formats">
<button class="ql-blockquote"></button>
<button class="ql-code-block"></button>
</span>
<span class="ql-formats">
<button class="ql-link"></button>
<button class="ql-image"></button>
</span>
<span class="ql-formats">
<button class="ql-clean"></button>
</span>
</div>
<div data-composer="html" class="ql-compose ql-container ql-container-toolbar-bottom"></div>
$html_editor_quote
</div>
EOF
# Output HTML editor textarea
my $ts = &get_webmin_version();
$ts =~ s/[.-]+//g;
$html_editor_scripts = <<EOF;
<link href="quill/quill.min.css?$ts" rel="stylesheet">
<link href="quill/quill-custom.css?$ts" rel="stylesheet">
<script type="text/javascript" src="quill/quill.min.js?$ts"></script>
<script type="text/javascript">
(function() {
var targ = document.querySelector('[name="body"]');
var qs = Quill.import('attributors/style/size'),
qf = Quill.import('attributors/style/font');
qs.whitelist = ["0.75em", "1.2em", "1.5em", "2.5em"];
qf.whitelist = ["initial", "sans-serif", "serif", "monospace"],
escapeHTML_ = function(htmlStr) {
return htmlStr.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
};
<script type="text/javascript">
xinha_init = function()
{
xinha_editors = [ "body" ];
xinha_plugins = [ ];
xinha_config = new Xinha.Config();
xinha_config.hideSomeButtons(" print showhelp about killword toggleborders ");
xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);
Xinha.startEditors(xinha_editors);
}
</script>
var Parchment = Quill.import('parchment');
// Whitelist attrs (useful on preserving)
const attrs_whitelist = [
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'border', 'border-right', 'border-left',
'font-size', 'font-family', 'href', 'target',
]
attrs_whitelist.forEach(function(attr) {
Quill.register(new Parchment.Attributor.Style(attr, attr, {}));
});
Quill.register(qs, true);
Quill.register(qf, true);
var editor = new Quill('.ql-container', {
modules: {
formula: false,
syntax: false,
imageDrop: true,
imageResize: {
modules: [
'DisplaySize',
'Resize',
],
},
toolbar: '.ql-toolbar',
},
bounds: '.ql-compose-container',
theme: 'snow'
});
// Google Mail editor like keybind for quoting
var isMacOS = navigator.userAgent.toLowerCase().includes('mac');
editor.keyboard.addBinding({
key: '9',
shiftKey: true,
ctrlKey: !isMacOS,
metaKey: isMacOS,
format: ['blockquote'],
}, function(range, context) {
this.quill.format('blockquote', false);
});
editor.keyboard.addBinding({
key: '9',
shiftKey: true,
ctrlKey: !isMacOS,
metaKey: isMacOS,
}, function(range, context) {
this.quill.format('blockquote', true);
});
editor.on('text-change', function() {
targ.value = escapeHTML_(editor.root.innerHTML + "<br><br>");
var quoteHTML = String(), err = false;
try {
quoteHTML = document.querySelector('#quote-mail-iframe').contentWindow.document.querySelector('.iframe_quote[contenteditable]#webmin-iframe-quote').innerHTML;
} catch(e) {
err = true;
}
if (!err) {
targ.value = targ.value + escapeHTML_(quoteHTML);
}
});
editor.pasteHTML(targ.value);
})();
</script>
EOF
}
else {
print '<script type="text/javascript">xinha_init = function(){}</script>';
}
$sig =~ s/\n/<br>\n/g,
$sig =~ s/^\s+//g
if ($sig);
print &ui_table_row(undef,
&ui_textarea("body", $quote, 16, 80, undef, 0,
"style='width:99%' id=body"), 2);
&ui_textarea("body", "$sig", 16, 80, undef, 0,
"style='display: none' id=body").$html_editor_template.$html_editor_scripts, 2);
}
else {
# Show text editing area