[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