Closes #5303 Attachment reminder

This commit is contained in:
Avi Levy 2013-06-20 19:13:39 -07:00 committed by Eric Gregory
parent 833066746c
commit 34236be3d5
2 changed files with 118 additions and 2 deletions

View file

@ -80,6 +80,10 @@ public class ComposerWindow : Gtk.Window {
</style>
</head><body id="message-body"></body></html>""";
public const string ATTACHMENT_KEYWORDS_GENERIC = ".doc|.pdf|.xls|.ppt|.rtf|.pps";
/// A list of keywords, separated by pipe ("|") characters, that suggest an attachment
public const string ATTACHMENT_KEYWORDS_LOCALIZED = _("attach|enclosed|enclosing|cover letter");
// Signal sent when the "Send" button is clicked.
public signal void send(ComposerWindow composer);
@ -304,7 +308,7 @@ public class ComposerWindow : Gtk.Window {
try {
body_html = referred.get_message().get_body(true);
} catch (Error error) {
debug("Error getting messae body: %s", error.message);
debug("Error getting message body: %s", error.message);
}
add_attachments(referred.attachments);
break;
@ -659,9 +663,56 @@ public class ComposerWindow : Gtk.Window {
on_discard();
}
private bool email_contains_attachment_keywords() {
// Filter out all content contained in block quotes
string filtered = @"$subject\n";
filtered += Util.DOM.get_text_representation(editor.get_dom_document(), "blockquote");
Regex url_regex = null;
try {
// Prepare to ignore urls later
url_regex = new Regex(URL_REGEX, RegexCompileFlags.CASELESS);
} catch (Error error) {
debug("Error building regex in keyword checker: %s", error.message);
}
string[] keys = ATTACHMENT_KEYWORDS_GENERIC.casefold().split("|");
foreach (string key in ATTACHMENT_KEYWORDS_LOCALIZED.casefold().split("|")) {
keys += key;
}
string folded;
foreach (string line in filtered.split("\n")) {
// Stop looking once we hit forwarded content
if (line.has_prefix("--")) {
break;
}
folded = line.casefold();
foreach (string key in keys) {
if (key in folded) {
try {
// Make sure the match isn't coming from a url
if (key in url_regex.replace(folded, -1, 0, "")) {
return true;
}
} catch (Error error) {
debug("Regex replacement error in keyword checker: %s", error.message);
return true;
}
}
}
}
return false;
}
private bool should_send() {
bool has_subject = !Geary.String.is_empty(subject.strip());
bool has_body_or_attachment = !Geary.String.is_empty(get_html()) || attachment_files.size > 0;
bool has_body = !Geary.String.is_empty(get_html());
bool has_attachment = attachment_files.size > 0;
bool has_body_or_attachment = has_body || has_attachment;
string? confirmation = null;
if (!has_subject && !has_body_or_attachment) {
confirmation = _("Send message with an empty subject and body?");
@ -669,6 +720,8 @@ public class ComposerWindow : Gtk.Window {
confirmation = _("Send message with an empty subject?");
} else if (!has_body_or_attachment) {
confirmation = _("Send message with an empty body?");
} else if (!has_attachment && email_contains_attachment_keywords()) {
confirmation = _("Send message without an attachment?");
}
if (confirmation != null) {
ConfirmationDialog dialog = new ConfirmationDialog(this,

View file

@ -42,6 +42,69 @@ namespace Util.DOM {
class_list.remove(clas);
}
}
// Returns the text contained in the DOM document, after ignoring tags of type "exclude"
// and padding newlines where appropriate. Used to scan for attachment keywords.
public string get_text_representation(WebKit.DOM.Document doc, string exclude) {
WebKit.DOM.HTMLElement? copy = Util.DOM.clone_node(doc.get_body());
if (copy == null) {
return "";
}
// Keep deleting the next excluded element until there are none left
while (true) {
WebKit.DOM.HTMLElement? current = Util.DOM.select(copy, exclude);
if (current == null) {
break;
}
WebKit.DOM.Node parent = current.get_parent_node();
try {
parent.remove_child(current);
} catch (Error error) {
debug("Error removing blockquotes: %s", error.message);
break;
}
}
WebKit.DOM.NodeList node_list;
try {
node_list = copy.query_selector_all("br");
} catch (Error error) {
debug("Error finding <br>s: %s", error.message);
return copy.get_inner_text();
}
// Replace <br> tags with newlines
for (int i = 0; i < node_list.length; ++i) {
WebKit.DOM.Node br = node_list.item(i);
WebKit.DOM.Node parent = br.get_parent_node();
try {
parent.replace_child(doc.create_text_node("\n"), br);
} catch (Error error) {
debug("Error replacing <br>: %s", error.message);
}
}
try {
node_list = copy.query_selector_all("div");
} catch (Error error) {
debug("Error finding <div>s: %s", error.message);
return copy.get_inner_text();
}
// Pad each <div> with newlines
for (int i = 0; i < node_list.length; ++i) {
WebKit.DOM.Node div = node_list.item(i);
try {
div.insert_before(doc.create_text_node("\n"), div.first_child);
div.append_child(doc.create_text_node("\n"));
} catch (Error error) {
debug("Error padding <div> with newlines: %s", error.message);
}
}
return copy.get_inner_text();
}
}
public void bind_event(WebKit.WebView view, string selector, string event, Callback callback,