mirror of
https://github.com/webmin/webmin.git
synced 2026-08-31 18:50:39 +01:00
Add HTML signature support to shared mail libraries
[no-build]
This commit is contained in:
@@ -242,6 +242,9 @@ my $iframe_styles =
|
||||
$iframe_styles =~ s/\n/ /g;
|
||||
my $navigation_type = $ENV{'HTTP_X_NAVIGATION_TYPE'};
|
||||
$navigation_type ||= 'reload';
|
||||
|
||||
# Key for preserving unsaved editor content across page reloads
|
||||
my $storage_key = $opts->{'storage'} || 'quill=last-message';
|
||||
my $html_editor_init_script =
|
||||
<<EOF;
|
||||
<script type="text/javascript">
|
||||
@@ -263,6 +266,60 @@ my $html_editor_init_script =
|
||||
qf.whitelist = ["monospace"],
|
||||
Quill.register(qf, true);
|
||||
|
||||
// Register signature class attributor, so signature blocks keep their
|
||||
// marking class across editor round trips, e.g. when editing a draft
|
||||
const qp = Quill.import('parchment');
|
||||
Quill.register(new qp.Attributor.Class('signature', 'x-signature',
|
||||
{ scope: qp.Scope.BLOCK }), true);
|
||||
|
||||
// Convert the signature container into per-block marking classes,
|
||||
// which the editor preserves via the registered signature attributor.
|
||||
// Block children are marked directly, while runs of inline content,
|
||||
// like plain text with line breaks, become marked paragraphs
|
||||
const signature_marks = function(html) {
|
||||
if (!html || html.indexOf('x-signature') < 0) {
|
||||
return html;
|
||||
}
|
||||
const doc = document.createElement('div'),
|
||||
blocks = ['P', 'DIV', 'UL', 'OL', 'TABLE',
|
||||
'BLOCKQUOTE', 'PRE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
|
||||
doc.innerHTML = html;
|
||||
doc.querySelectorAll('div.x-signature').forEach(function(sig) {
|
||||
const frag = document.createDocumentFragment();
|
||||
let run = [];
|
||||
const run_empty = function() {
|
||||
return !run.some(function(n) {
|
||||
return n.nodeType === 1 || (n.textContent || '').trim();
|
||||
});
|
||||
};
|
||||
const flush = function() {
|
||||
if (run.length && !run_empty()) {
|
||||
const p = document.createElement('p');
|
||||
p.className = 'x-signature-mark';
|
||||
run.forEach(function(n) { p.appendChild(n); });
|
||||
frag.appendChild(p);
|
||||
}
|
||||
run = [];
|
||||
};
|
||||
Array.from(sig.childNodes).forEach(function(node) {
|
||||
if (node.nodeType === 1 && blocks.includes(node.tagName)) {
|
||||
// Mark block level parts of the signature directly
|
||||
flush();
|
||||
node.classList.add('x-signature-mark');
|
||||
frag.appendChild(node);
|
||||
} else if (node.nodeType === 1 && node.tagName === 'BR') {
|
||||
// Line break ends the current inline line
|
||||
flush();
|
||||
} else {
|
||||
run.push(node);
|
||||
}
|
||||
});
|
||||
flush();
|
||||
sig.replaceWith(frag);
|
||||
});
|
||||
return doc.innerHTML;
|
||||
};
|
||||
|
||||
const editor = new Quill('.ql-container', {
|
||||
modules: {
|
||||
syntax: typeof hljs === 'object',
|
||||
@@ -332,7 +389,7 @@ my $html_editor_init_script =
|
||||
editor.on('text-change', function() {
|
||||
// This should most probably go to onSubmit event
|
||||
targ.value = editor.root.innerHTML + "<br>";
|
||||
sessionStorage.setItem('$module_name/quill=last-message', editor.root.innerHTML);
|
||||
sessionStorage.setItem('$module_name/$storage_key', editor.root.innerHTML);
|
||||
let extraValue = String(),
|
||||
sync = JSON.parse('@{[&convert_to_json($opts->{'textarea'}->{'sync'}->{'data'})]}'),
|
||||
position = '@{[$opts->{'textarea'}->{'sync'}->{'position'}]}',
|
||||
@@ -386,7 +443,7 @@ my $html_editor_init_script =
|
||||
restore_message = false;
|
||||
}
|
||||
if (restore_message) {
|
||||
const quill_last_message = sessionStorage.getItem('$module_name/quill=last-message');
|
||||
const quill_last_message = sessionStorage.getItem('$module_name/$storage_key');
|
||||
if (quill_last_message) {
|
||||
editor.pasteHTML(quill_last_message);
|
||||
return;
|
||||
@@ -394,8 +451,8 @@ my $html_editor_init_script =
|
||||
}
|
||||
|
||||
// Update editor on initial load
|
||||
editor.pasteHTML(targ.value);
|
||||
sessionStorage.setItem('$module_name/quill=last-message', editor.root.innerHTML);
|
||||
editor.pasteHTML(signature_marks(targ.value));
|
||||
sessionStorage.setItem('$module_name/$storage_key', editor.root.innerHTML);
|
||||
}
|
||||
@{[$opts->{'load'} ? "fn_${module_name}_html_editor_init()" : '']}
|
||||
</script>
|
||||
@@ -506,13 +563,13 @@ if ($document_styles_string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
# Fill tags with our inline styles
|
||||
# Fill tags with our inline styles, preserving existing tag attributes
|
||||
my (@document_styles_tag_names) = $styled_html_email =~ /<(?!style)(?!script)(?!s)(\w+)\s*.*?>/migx;
|
||||
foreach my $tag_name (&unique(@document_styles_tag_names)) {
|
||||
my (%document_styles_tag_names) = $document_styles_string =~ /(\Q$tag_name\E)\s*\{\s*([^}]*?)\s*\}/migx;
|
||||
foreach my $tag (keys %document_styles_tag_names) {
|
||||
my $tag_style = &$style_format($document_styles_tag_names{$tag});
|
||||
$styled_html_email =~ s/(<$tag)(?![^>]+style).*?>/$1 style="$tag_style">/mig;
|
||||
$styled_html_email =~ s/(<$tag\b)(?![^>]+style)([^>]*)>/$1$2 style="$tag_style">/mig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3285,6 +3285,14 @@ foreach my $a (@$attach) {
|
||||
return @rv;
|
||||
}
|
||||
|
||||
# signature_is_html(sig)
|
||||
# Returns 1 if the given signature contains HTML markup
|
||||
sub signature_is_html
|
||||
{
|
||||
local ($sig) = @_;
|
||||
return 0 if (!$sig);
|
||||
return $sig =~ /<\/?(?:a|b|i|u|s|p|em|img|br|hr|div|span|font|code|pre|sub|sup|small|big|strong|strike|blockquote|table|tbody|thead|tr|td|th|ul|ol|li|h[1-6]|style)\b[^>]*>/i ? 1 : 0;
|
||||
}
|
||||
# quoted_message(&mail, quote-mode, sig, 0=any,1=text,2=html, sig-at-top?)
|
||||
# Returns the quoted text, html-flag and body attachment
|
||||
sub quoted_message
|
||||
@@ -3315,20 +3323,22 @@ local $qm = %userconfig ? $userconfig{'html_quote'} : $config{'html_quote'};
|
||||
if (($cfg->{'html_edit'} == 2 ||
|
||||
$cfg->{'html_edit'} == 1 && $htmlbody) &&
|
||||
$bodymode != 1) {
|
||||
# Create quoted body HTML
|
||||
# Create quoted body HTML, with the signature wrapped in a container
|
||||
# with a known class so that it can be styled later if needed
|
||||
$sig =~ s/\n/<br>\n/g if (!&signature_is_html($sig));
|
||||
$sig = "<div class=\"x-signature\">$sig</div>" if ($sig);
|
||||
if ($htmlbody) {
|
||||
$body = $htmlbody;
|
||||
$sig =~ s/\n/<br>\n/g;
|
||||
if ($qu && $qm == 0) {
|
||||
# Quoted HTML as cite
|
||||
$quote = &html_escape($writer)."\n".
|
||||
"<blockquote type=cite>\n".
|
||||
&safe_html($htmlbody->{'data'}).
|
||||
"</blockquote>";
|
||||
if ($sigtop) {
|
||||
if ($sig && $sigtop) {
|
||||
$quote = $sig."<br>\n".$quote;
|
||||
}
|
||||
else {
|
||||
elsif ($sig) {
|
||||
$quote = $quote.$sig."<br>\n";
|
||||
}
|
||||
}
|
||||
@@ -3341,10 +3351,10 @@ if (($cfg->{'html_edit'} == 2 ||
|
||||
else {
|
||||
# Un-quoted HTML
|
||||
$quote = &safe_html($htmlbody->{'data'});
|
||||
if ($sigtop) {
|
||||
if ($sig && $sigtop) {
|
||||
$quote = $sig."<br>\n".$quote;
|
||||
}
|
||||
else {
|
||||
elsif ($sig) {
|
||||
$quote = $quote.$sig."<br>\n";
|
||||
}
|
||||
}
|
||||
@@ -3360,10 +3370,10 @@ if (($cfg->{'html_edit'} == 2 ||
|
||||
"<blockquote type=cite>\n".
|
||||
"<pre>$pd</pre>".
|
||||
"</blockquote>";
|
||||
if ($sigtop) {
|
||||
if ($sig && $sigtop) {
|
||||
$quote = $sig."<br>\n".$quote;
|
||||
}
|
||||
else {
|
||||
elsif ($sig) {
|
||||
$quote = $quote.$sig."<br>\n";
|
||||
}
|
||||
}
|
||||
@@ -3376,10 +3386,10 @@ if (($cfg->{'html_edit'} == 2 ||
|
||||
else {
|
||||
# Un-quoted plain text as HTML
|
||||
$quote = "<pre>$pd</pre>";
|
||||
if ($sigtop) {
|
||||
if ($sig && $sigtop) {
|
||||
$quote = $sig."<br>\n".$quote;
|
||||
}
|
||||
else {
|
||||
elsif ($sig) {
|
||||
$quote = $quote.$sig."<br>\n";
|
||||
}
|
||||
}
|
||||
@@ -3388,6 +3398,7 @@ if (($cfg->{'html_edit'} == 2 ||
|
||||
}
|
||||
else {
|
||||
# Create quoted body text
|
||||
$sig = &html_to_text($sig) if (&signature_is_html($sig));
|
||||
if ($plainbody) {
|
||||
$body = $plainbody;
|
||||
$quote = $plainbody->{'data'};
|
||||
|
||||
@@ -21,10 +21,12 @@ if ($in{'new'}) {
|
||||
}
|
||||
$sig = &get_signature($in{'user'});
|
||||
if ($html_edit && $sig) {
|
||||
$sig =~ s/\n/<br>\n/g;
|
||||
$quote = "<html><body>$sig</body></html>";
|
||||
$sig =~ s/\n/<br>\n/g if (!&signature_is_html($sig));
|
||||
$quote = "<html><body><div class=\"x-signature\">$sig</div>".
|
||||
"</body></html>";
|
||||
}
|
||||
else {
|
||||
$sig = &html_to_text($sig) if (&signature_is_html($sig));
|
||||
$quote = "\n\n$sig" if ($sig);
|
||||
}
|
||||
$to = $in{'to'};
|
||||
@@ -313,8 +315,9 @@ else {
|
||||
$subject = "Fwd: ".$subject if ($subject !~ /^Fwd:/i &&
|
||||
($in{'forward'} || @mailforward));
|
||||
|
||||
# Construct the initial mail text
|
||||
$sig = &get_signature($in{'user'});
|
||||
# Construct the initial mail text, without adding a signature when
|
||||
# editing an existing draft which already contains one
|
||||
$sig = &get_signature($in{'user'}) if (!$in{'enew'});
|
||||
($quote, $html_edit, $body) = "ed_message($mail, $qu, $sig);
|
||||
# Load images using server in replies
|
||||
$quote = &disable_html_images($quote, 3);
|
||||
@@ -476,10 +479,11 @@ if ($html_edit) {
|
||||
after =>
|
||||
{ editor => $iframe_quote }
|
||||
});
|
||||
$sig =~ s/\n/<br>/g,
|
||||
$sig =~ s/^\s+//g,
|
||||
$sig = "<br><br>$sig<br><br>"
|
||||
if ($sig);
|
||||
if ($sig) {
|
||||
$sig =~ s/\n/<br>/g if (!&signature_is_html($sig));
|
||||
$sig =~ s/^\s+//g;
|
||||
$sig = "<br><br><div class=\"x-signature\">$sig</div><br><br>";
|
||||
}
|
||||
print &ui_table_row(undef,
|
||||
&ui_textarea("body", $sig, 12, 80, undef, 0,
|
||||
"style='display: none' id=body data-html-mode='$config{'html_edit_mode'}'").
|
||||
@@ -487,6 +491,7 @@ if ($html_edit) {
|
||||
}
|
||||
else {
|
||||
# Show text editing area
|
||||
$sig = &html_to_text($sig) if (&signature_is_html($sig));
|
||||
$wm = $config{'wrap_mode'};
|
||||
$wm =~ s/^wrap=//g;
|
||||
$wcols = $config{'wrap_compose'};
|
||||
|
||||
Reference in New Issue
Block a user