From c87bbea3ab06121f5fbb23658cb2320efd55aad8 Mon Sep 17 00:00:00 2001 From: Robert Schroll Date: Mon, 1 Dec 2014 20:14:21 -0500 Subject: [PATCH] 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 "" to ""), 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 --- .../account-dialog-add-edit-pane.vala | 2 +- src/client/accounts/add-edit-page.vala | 42 +++++++++++++++---- src/client/composer/composer-widget.vala | 10 ++--- src/engine/util/util-html.vala | 15 +++++++ ui/login.glade | 24 +++++------ 5 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/client/accounts/account-dialog-add-edit-pane.vala b/src/client/accounts/account-dialog-add-edit-pane.vala index 80ad80e2..4a90f456 100644 --- a/src/client/accounts/account-dialog-add-edit-pane.vala +++ b/src/client/accounts/account-dialog-add-edit-pane.vala @@ -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); diff --git a/src/client/accounts/add-edit-page.vala b/src/client/accounts/add-edit-page.vala index 567b5296..a78d5df6 100644 --- a/src/client/accounts/add-edit-page.vala +++ b/src/client/accounts/add-edit-page.vala @@ -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() { diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 09a48cf1..78a6a3d4 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -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 + "

" + signature; else if (top_posting) - body_html = CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature) + body_html; + body_html = CURSOR + "

" + signature + body_html; else - body_html = body_html + CURSOR + Geary.HTML.preserve_whitespace("\n\n" + signature); + body_html = body_html + CURSOR + "

" + signature; } private void set_cursor() { diff --git a/src/engine/util/util-html.vala b/src/engine/util/util-html.vala index ce34ec56..561c9d99 100644 --- a/src/engine/util/util-html.vala +++ b/src/engine/util/util-html.vala @@ -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]*)[^>]*>.*|<[^>]*/>", res, + RegexCompileFlags.CASELESS)) { + res = escape_markup(res); + preserve_whitespace_in_html = true; + } + if (preserve_whitespace_in_html) + res = @"
$res
"; + 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. diff --git a/ui/login.glade b/ui/login.glade index 390e7895..1e85ad0c 100644 --- a/ui/login.glade +++ b/ui/login.glade @@ -7,7 +7,6 @@ 1 10 - True False @@ -960,7 +959,7 @@ - Si_gn emails: + Si_gn emails (HTML allowed): True True False @@ -976,23 +975,20 @@ - + True - True + False + False 12 - in - - True - False - True - word - buffer: email_signature - + + + + - False + True True 3 @@ -1002,7 +998,7 @@ - False + True True 4