[Xfce4-commits] <postler:master> Implement message draft saving and auto-saving
Christian Dywan
noreply at xfce.org
Mon Feb 21 00:28:02 CET 2011
Updating branch refs/heads/master
to 30b168b9f8c736ccb0d00787db9b603619f27b22 (commit)
from 2ac7116ef871a322f18519a4aeca4a2d3f29feb9 (commit)
commit 30b168b9f8c736ccb0d00787db9b603619f27b22
Author: Sergio Spinatelli <spinatelli.sergio at gmail.com>
Date: Mon Feb 21 00:23:50 2011 +0100
Implement message draft saving and auto-saving
Generation of the message is refactored into generate_message()
and used for both sending and saving drafts.
Fixes: https://bugs.launchpad.net/postler/+bug/690505
postler/postler-composer.vala | 260 +++++++++++++++++++++++------------------
postler/postler-content.vala | 5 +-
2 files changed, 152 insertions(+), 113 deletions(-)
diff --git a/postler/postler-composer.vala b/postler/postler-composer.vala
index c240a7e..0b2c18e 100644
--- a/postler/postler-composer.vala
+++ b/postler/postler-composer.vala
@@ -29,6 +29,8 @@ public class Postler.Composer : Gtk.Window {
Gtk.Button button_send;
uint grace_period_timer = 0;
uint progress_timer = 0;
+ bool message_saved = false;
+ string? draft_filename = null;
public string subject { get { return entry_subject.text; } }
int part = 0;
@@ -128,6 +130,146 @@ public class Postler.Composer : Gtk.Window {
grace_period_timer = GLib.Timeout.add_seconds (1, send_grace_period_timer);
}
+ void action_mail_save () {
+ save_draft ();
+ if (message_saved)
+ destroy();
+ }
+
+ string generate_message (string sender, AccountInfo info) {
+ var now = new Soup.Date.from_now (0);
+ string copy = entry_copy.text;
+ string header = ("From: %s\nTo: %s\n%s%s"
+ + "MIME-Version: 1.0\n"
+ + "Subject: %s\n"
+ + "Date: %s\n"
+ + "X-Mailer: %s\n"
+ + (info.reply != null ? "Reply-To: " + info.reply + "\n" : "")
+ + (info.organization != null ?
+ "Organization: " + info.organization + "\n" : "")).printf (
+ sender,
+ entry_to.text,
+ copy != "" ? "CC: " : "", copy != "" ? copy + "\n" : "",
+ entry_subject.text,
+ now.to_string (Soup.DateFormat.RFC2822),
+ Postler.App.get_user_agent ());
+
+ if ((actions.get_action ("MarkImportant") as Gtk.ToggleAction).active)
+ header += "X-Priority: 1\n";
+
+ Gtk.TreeIter iter;
+ if (!(attachments.model.get_iter_first (out iter))) {
+ header = header
+ + "Content-Transfer-Encoding: 8bit\n"
+ + "Content-Type: text/plain; charset=UTF-8\n";
+ }
+
+ var clipboard = content.get_clipboard (Gdk.SELECTION_CLIPBOARD);
+ clipboard.set_text ("",0);
+ content.select_all ();
+ content.copy_clipboard ();
+ string body = clipboard.wait_for_text ();
+ clear_selection ();
+
+ if (attachments.model.get_iter_first (out iter)) {
+ string boundary = GLib.Checksum.compute_for_string (
+ GLib.ChecksumType.MD5,
+ body + now.to_string (Soup.DateFormat.RFC2822));
+ header = header
+ + "Content-Type: multipart/mixed; "
+ + "boundary=" + boundary + "\n";
+ StringBuilder body_builder = new StringBuilder (body);
+ body_builder.prepend ("Content-Type: text/plain; charset=UTF-8\n\n");
+ body_builder.prepend ("--" + boundary + "\n");
+ body_builder.append_c ('\n');
+ MessagePart part;
+ attachments.model.get (iter, 0, out part);
+ try {
+ body_builder.append ("--" + boundary + "\n");
+ uchar[] data = {};
+ FileUtils.get_data (part.filename, out data);
+ bool uncertain;
+ string mime_type = g_content_type_guess (part.filename, data,
+ out uncertain);
+ var fname_parts = part.filename.split ("/");
+ var filename = fname_parts[fname_parts.length-1];
+ body_builder.append ("Content-Type: " + mime_type + "\n"
+ + "Content-Disposition: attachment; "
+ + "filename=\"" + filename + "\"\n"
+ + "Content-Transfer-Encoding: base64\n\n"
+ + GLib.Base64.encode (data));
+ while (attachments.model.iter_next (ref iter)) {
+ attachments.model.get (iter, 0, out part);
+ body_builder.append ("\n--" + boundary + "\n");
+ FileUtils.get_data (part.filename, out data);
+ mime_type = g_content_type_guess (part.filename, data,
+ out uncertain);
+ fname_parts = part.filename.split ("/");
+ filename = fname_parts[fname_parts.length - 1];
+ body_builder.append ("Content-Type: " + mime_type + "\n"
+ + "Content-Disposition: attachment; "
+ + "filename=\"" + filename + "\"\n"
+ + "Content-Transfer-Encoding: base64\n\n"
+ + GLib.Base64.encode (data));
+ }
+ body_builder.append ("\n--" + boundary + "--");
+ body = body_builder.str;
+ } catch (Error e) {
+ GLib.message (_("Failed to add attachment: %s"), part.filename);
+ }
+ }
+ return header + "\n" + body;
+ }
+
+ bool save_draft () {
+ button_send.sensitive = false;
+
+ string? sender = combo_from.get_active_text ();
+ if (sender == null) {
+ var dialog = new Gtk.MessageDialog (this, 0,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.NONE,
+ _("You have not configured any accounts for saving."));
+ dialog.add_buttons (Gtk.STOCK_OK, Gtk.ResponseType.OK);
+ dialog.run ();
+ dialog.destroy ();
+ return true;
+ }
+
+ var info = selected_account_for_address (sender);
+ string drafts = info.path + "/" + info.get_folder (FolderType.DRAFTS);
+ if (drafts.has_suffix ("/") || !Postler.Messages.ensure_folder (drafts)) {
+ var dialog = new Gtk.MessageDialog (this, 0,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.NONE,
+ _("Folder for drafts saving can't be created. "
+ + "You need to fetch mail at least once.\n"
+ + "If you see this error after fetching, right-click "
+ + "the folder for drafts, \"Use as...\" and "
+ + "select \"Drafts\"."));
+ dialog.add_buttons (Gtk.STOCK_OK, Gtk.ResponseType.OK);
+ dialog.run ();
+ dialog.destroy ();
+ return true;
+ }
+
+ string message = generate_message (sender, info);
+
+ try {
+ if (draft_filename == null) {
+ draft_filename = drafts + "/cur/"
+ + Postler.Messages.generate_maildir_filename ("D");
+ } else {
+ FileUtils.remove (draft_filename);
+ }
+ FileUtils.set_contents (draft_filename, message, -1);
+ FileUtils.chmod (draft_filename, 0700);
+ } catch (GLib.Error error) {
+ GLib.critical (_("Failed to save message: %s"), error.message);
+ toggle_sensitivity (true);
+ }
+ message_saved = true;
+ return true;
+ }
+
void send_message () {
progress_timer = GLib.Timeout.add_seconds (1, send_progress_timer);
button_send.sensitive = false;
@@ -177,118 +319,13 @@ public class Postler.Composer : Gtk.Window {
return;
}
- var now = new Soup.Date.from_now (0);
- string copy = entry_copy.text;
- string header = ("From: %s\nTo: %s\n%s%s"
- + "MIME-Version: 1.0\n"
- + "Subject: %s\n"
- + "Date: %s\n"
- + "X-Mailer: %s\n"
- + (info.reply != null ? "Reply-To: " + info.reply + "\n" : "")
- + (info.organization != null ?
- "Organization: " + info.organization + "\n" : "")).printf (
- sender,
- entry_to.text,
- copy != "" ? "CC: " : "", copy != "" ? copy + "\n" : "",
- entry_subject.text,
- now.to_string (Soup.DateFormat.RFC2822),
- Postler.App.get_user_agent ());
-
- if ((actions.get_action ("MarkImportant") as Gtk.ToggleAction).active)
- header += "X-Priority: 1\n";
-
- Gtk.TreeIter iter;
- if (!(attachments.model.get_iter_first (out iter))) {
- header = header
- + "Content-Transfer-Encoding: 8bit\n"
- + "Content-Type: text/plain; charset=UTF-8\n";
- }
-
- content.select_all ();
- content.copy_clipboard ();
- var clipboard = content.get_clipboard (Gdk.SELECTION_CLIPBOARD);
- string body = clipboard.wait_for_text ();
- clear_selection ();
-
- if (attachments.model.iter_n_children (null) == 0) {
- /* i18n: A warning is displayed when writing a new message
- and there are no attachments but the text mentions
- attachments. Words are separated by |. */
- string[] attachments = _("attachment|attach").split ("|");
- foreach (string keyword in attachments)
- if (keyword in body) {
- var dialog = new Gtk.MessageDialog (this, 0,
- Gtk.MessageType.ERROR, Gtk.ButtonsType.NONE,
- _("An attachment is mentioned in the text, but no files were attached."));
- dialog.format_secondary_text (
- _("Do you want to send the message without attachments?"));
- dialog.add_buttons (
- Gtk.STOCK_CANCEL,
- Gtk.ResponseType.CANCEL,
- _("_Send message without attachments"),
- Gtk.ResponseType.OK);
- dialog.set_default_response (Gtk.ResponseType.CANCEL);
- int response = dialog.run ();
- dialog.destroy ();
- if (response != Gtk.ResponseType.OK)
- return;
- break;
- }
- }
-
- if (attachments.model.get_iter_first (out iter)) {
- string boundary = GLib.Checksum.compute_for_string (
- GLib.ChecksumType.MD5,
- body + now.to_string (Soup.DateFormat.RFC2822));
- header = header
- + "Content-Type: multipart/mixed; "
- + "boundary=" + boundary + "\n";
- StringBuilder body_builder = new StringBuilder (body);
- body_builder.prepend ("Content-Type: text/plain; charset=UTF-8\n\n");
- body_builder.prepend ("--" + boundary + "\n");
- body_builder.append_c ('\n');
- MessagePart part;
- attachments.model.get (iter, 0, out part);
- try {
- body_builder.append ("--" + boundary + "\n");
- uchar[] data = {};
- FileUtils.get_data (part.filename, out data);
- bool uncertain;
- string mime_type = g_content_type_guess (part.filename, data,
- out uncertain);
- var fname_parts = part.filename.split ("/");
- var filename = fname_parts[fname_parts.length-1];
- body_builder.append ("Content-Type: " + mime_type + "\n"
- + "Content-Disposition: attachment; "
- + "filename=\"" + filename + "\"\n"
- + "Content-Transfer-Encoding: base64\n\n"
- + GLib.Base64.encode (data));
- while (attachments.model.iter_next (ref iter)) {
- attachments.model.get (iter, 0, out part);
- body_builder.append ("\n--" + boundary + "\n");
- FileUtils.get_data (part.filename, out data);
- mime_type = g_content_type_guess (part.filename, data,
- out uncertain);
- fname_parts = part.filename.split ("/");
- filename = fname_parts[fname_parts.length - 1];
- body_builder.append ("Content-Type: " + mime_type + "\n"
- + "Content-Disposition: attachment; "
- + "filename=\"" + filename + "\"\n"
- + "Content-Transfer-Encoding: base64\n\n"
- + GLib.Base64.encode (data));
- }
- body_builder.append ("\n--" + boundary + "--");
- body = body_builder.str;
- } catch (Error e) {
- GLib.message (_("Failed to add attachment: %s"), part.filename);
- }
- }
+ string message = generate_message (sender, info);
try {
/* TODO: Put in /Queue/new/ first and move on success */
string filename = sent + "/cur/"
+ Postler.Messages.generate_maildir_filename ("S");
- FileUtils.set_contents (filename, header + "\n" + body, -1);
+ FileUtils.set_contents (filename, message, -1);
FileUtils.chmod (filename, 0700);
client.sent.connect ((account, file, message) => {
if (account != info.name || file != filename)
@@ -318,7 +355,7 @@ public class Postler.Composer : Gtk.Window {
}
bool window_not_closed () {
- if (content.can_undo ()) {
+ if (content.can_undo () && !message_saved) {
var dialog = new Gtk.MessageDialog (this, 0,
Gtk.MessageType.WARNING, Gtk.ButtonsType.NONE,
_("Do you want to discard the unsaved message?"));
@@ -452,6 +489,8 @@ public class Postler.Composer : Gtk.Window {
public Composer () {
GLib.Object (icon_name: STOCK_MAIL_MESSAGE_NEW,
title: _("Compose message"));
+ /* Autosave every 5 minutes */
+ GLib.Timeout.add_seconds (350, save_draft);
var screen = get_screen ();
Gdk.Rectangle monitor;
@@ -507,10 +546,7 @@ public class Postler.Composer : Gtk.Window {
button.name = "PostlerWideButton";
toolitem.add (button);
actions.get_action ("MessageSave").connect_proxy (button);
- button.sensitive = false;
- button.clicked.connect ((widget) => {
- /* TODO: action_mail_save (); */
- });
+ button.clicked.connect (action_mail_save);
toolbar.insert (toolitem, -1);
toolitem = new Gtk.ToolItem ();
toolitem.set_border_width (4);
diff --git a/postler/postler-content.vala b/postler/postler-content.vala
index 7cbcf82..71509a7 100644
--- a/postler/postler-content.vala
+++ b/postler/postler-content.vala
@@ -649,7 +649,10 @@ public class Postler.Content : WebKit.WebView {
if (info.address != null && from in info.address)
return from;
}
- return selected_address ().split (",")[0];
+ if (selected_account == null || selected_account.address == null)
+ return null;
+
+ return selected_account.address.split (",")[0];
}
void parse_message (string location) throws GLib.FileError {
More information about the Xfce4-commits
mailing list