Allow HTML signatures

We search for HTML-like tags in signatures, and don't do much as much
escaping if we find one.  For .signature files detected to be HTML, we
insert them without any change.  User-entered signatures get their
whitespace protected, even when HTML is detected. The existing code for
preserving whitespace doesn't work when there's already HTML code in the
text (it converts "<a b>" to "<a&nbsp;b>"), so instead we preserve the
whitespace with CSS.

A preview of the signature is added to the the UI.  There's a TextView
and a WebView in a Stack, and we swap between them with a StackSwitcher.
Some of the packing details are changed so that these views are the
thing that expands when the dialog size changes.

https://bugzilla.gnome.org/show_bug.cgi?id=738895
This commit is contained in:
Robert Schroll 2014-12-01 20:14:21 -05:00
parent 11d8038422
commit c87bbea3ab
5 changed files with 65 additions and 28 deletions

View file

@ -38,7 +38,7 @@ public class AccountDialogAddEditPane : AccountDialogPane {
add_edit_page.size_changed.connect(() => { size_changed(); } );
pack_start(add_edit_page);
pack_start(button_box);
pack_start(button_box, false, false);
// Default mode is Welcome.
set_mode(AddEditPage.PageMode.WELCOME);

View file

@ -173,7 +173,9 @@ public class AddEditPage : Gtk.Box {
// Signature
private Gtk.Box composer_container;
private Gtk.CheckButton check_use_email_signature;
private Gtk.Stack signature_stack;
private Gtk.TextView textview_email_signature;
private StylishWebView preview_webview;
private Gtk.Alignment other_info;
@ -257,7 +259,32 @@ public class AddEditPage : Gtk.Box {
// composer options
composer_container = (Gtk.Box) builder.get_object("composer container");
check_use_email_signature = (Gtk.CheckButton) builder.get_object("check: use_email_signature");
textview_email_signature = (Gtk.TextView) builder.get_object("textview: email_signature");
Gtk.ScrolledWindow edit_window = new Gtk.ScrolledWindow(null, null);
edit_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
edit_window.set_shadow_type(Gtk.ShadowType.IN);
textview_email_signature = new Gtk.TextView();
edit_window.add(textview_email_signature);
Gtk.ScrolledWindow preview_window = new Gtk.ScrolledWindow(null, null);
preview_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
preview_window.set_shadow_type(Gtk.ShadowType.IN);
preview_webview = new StylishWebView();
preview_window.add(preview_webview);
signature_stack = new Gtk.Stack();
signature_stack.add_titled(edit_window, "edit_window", _("Edit"));
signature_stack.child_set_property(edit_window, "icon-name", "text-editor-symbolic");
signature_stack.add_titled(preview_window, "preview_window", _("Preview"));
signature_stack.child_set_property(preview_window, "icon-name", "text-x-generic-symbolic");
Gtk.StackSwitcher switcher = new Gtk.StackSwitcher();
switcher.set_stack(signature_stack);
Gtk.Box signature_box = (Gtk.Box) builder.get_object("signature box");
signature_box.set_spacing(4);
signature_box.pack_start(signature_stack);
switcher.valign = Gtk.Align.START;
signature_box.pack_start(switcher, false, false);
// IMAP info widgets.
entry_imap_host = (Gtk.Entry) builder.get_object("entry: imap host");
@ -316,7 +343,8 @@ public class AddEditPage : Gtk.Box {
entry_nickname.insert_text.connect(on_nickname_insert_text);
check_use_email_signature.toggled.connect(() => on_use_signature_changed());
check_use_email_signature.bind_property("active", signature_box, "sensitive");
signature_stack.notify["visible-child-name"].connect(on_signature_stack_changed);
// Reset the "first update" flag when the window is mapped.
map.connect(() => { first_ui_update = true; });
@ -394,6 +422,7 @@ public class AddEditPage : Gtk.Box {
combo_smtp_encryption.active = Encryption.NONE;
use_email_signature = initial_use_email_signature;
email_signature = initial_email_signature;
signature_stack.set_visible_child_name("edit_window");
// Set defaults for IMAP info
imap_host = initial_default_imap_host ?? "";
@ -550,12 +579,9 @@ public class AddEditPage : Gtk.Box {
}
}
private void on_use_signature_changed() {
if(check_use_email_signature.active == true) {
textview_email_signature.sensitive = true;
} else {
textview_email_signature.sensitive = false;
}
private void on_signature_stack_changed() {
if (signature_stack.visible_child_name == "preview_window")
preview_webview.load_html_string(Geary.HTML.smart_escape(email_signature, true), "");
}
private uint16 get_default_smtp_port() {

View file

@ -993,6 +993,7 @@ public class ComposerWidget : Gtk.EventBox {
set_cursor();
return;
}
signature = Geary.HTML.smart_escape(signature, false);
} catch (Error error) {
debug("Error reading signature file %s: %s", signature_file.get_path(), error.message);
set_cursor();
@ -1004,16 +1005,15 @@ public class ComposerWidget : Gtk.EventBox {
set_cursor();
return;
}
signature = Geary.HTML.smart_escape(signature, true);
}
signature = Geary.HTML.escape_markup(signature);
if (body_html == null)
body_html = CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature);
body_html = CURSOR + "<br /><br />" + signature;
else if (top_posting)
body_html = CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature) + body_html;
body_html = CURSOR + "<br /><br />" + signature + body_html;
else
body_html = body_html + CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature);
body_html = body_html + CURSOR + "<br /><br />" + signature;
}
private void set_cursor() {

View file

@ -88,6 +88,21 @@ public string preserve_whitespace(string? text) {
return output;
}
public string smart_escape(string? text, bool preserve_whitespace_in_html) {
if (text == null)
return text;
string res = text;
if (!Regex.match_simple("<([A-Z]*)[^>]*>.*</(\\1)>|<[^>]*/>", res,
RegexCompileFlags.CASELESS)) {
res = escape_markup(res);
preserve_whitespace_in_html = true;
}
if (preserve_whitespace_in_html)
res = @"<div style='white-space: pre;'>$res</div>";
return res;
}
// Removes any text between < and >. Additionally, if input terminates in the middle of a tag,
// the tag will be removed.
// If the HTML is invalid, the original string will be returned.

View file

@ -7,7 +7,6 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkTextBuffer" id="buffer: email_signature"/>
<object class="GtkBox" id="container">
<property name="visible">True</property>
<property name="can_focus">False</property>
@ -960,7 +959,7 @@
</child>
<child>
<object class="GtkCheckButton" id="check: use_email_signature">
<property name="label" translatable="yes">Si_gn emails:</property>
<property name="label" translatable="yes">Si_gn emails (HTML allowed):</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -976,23 +975,20 @@
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<object class="GtkBox" id="signature box">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTextView" id="textview: email_signature">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="wrap_mode">word</property>
<property name="buffer">buffer: email_signature</property>
</object>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
@ -1002,7 +998,7 @@
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>