mirror of
https://github.com/webmin/webmin.git
synced 2026-03-20 16:50:24 +00:00
Add support for awesome replies using new HTML editor
This commit is contained in:
@@ -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("e_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.
|
||||
|
||||
@@ -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, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
};
|
||||
|
||||
<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
|
||||
|
||||
Reference in New Issue
Block a user