[geary/wip/714809-empty] Basic plumbing and implementation inside the Engine



commit 34e7599fd05fd1b3d1d57c8a40d5a79a91de013e
Author: Jim Nelson <jim yorba org>
Date:   Mon Jan 12 18:00:53 2015 -0800

    Basic plumbing and implementation inside the Engine

 src/CMakeLists.txt                                 |    2 +
 src/engine/api/geary-folder-supports-empty.vala    |   25 +++++++
 src/engine/imap-db/imap-db-folder.vala             |   27 ++++++-
 .../gmail/imap-engine-gmail-spam-trash-folder.vala |    7 ++-
 .../imap-engine/imap-engine-generic-folder.vala    |    6 ++-
 .../imap-engine/imap-engine-minimal-folder.vala    |    9 +++
 .../imap-engine/imap-engine-replay-operation.vala  |    2 +-
 .../replay-ops/imap-engine-empty-folder.vala       |   76 ++++++++++++++++++++
 8 files changed, 148 insertions(+), 6 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 052131d..0d04959 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -29,6 +29,7 @@ engine/api/geary-folder-properties.vala
 engine/api/geary-folder-supports-archive.vala
 engine/api/geary-folder-supports-copy.vala
 engine/api/geary-folder-supports-create.vala
+engine/api/geary-folder-supports-empty.vala
 engine/api/geary-folder-supports-mark.vala
 engine/api/geary-folder-supports-move.vala
 engine/api/geary-folder-supports-remove.vala
@@ -205,6 +206,7 @@ engine/imap-engine/outlook/imap-engine-outlook-drafts-folder.vala
 engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
 engine/imap-engine/replay-ops/imap-engine-copy-email.vala
 engine/imap-engine/replay-ops/imap-engine-create-email.vala
+engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
 engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
 engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
 engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
diff --git a/src/engine/api/geary-folder-supports-empty.vala b/src/engine/api/geary-folder-supports-empty.vala
new file mode 100644
index 0000000..f4c9d45
--- /dev/null
+++ b/src/engine/api/geary-folder-supports-empty.vala
@@ -0,0 +1,25 @@
+/* Copyright 2015 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+/**
+ * The addition of the Geary.FolderSupport.Empty interface to a { link Geary.Folder}
+ * indicates that it supports removing (deleting) all email quickly.
+ *
+ * This generally means that the message is deleted from the server and is not recoverable.
+ * It does ''not'' mean the message is moved to a Trash folder where it may or may not be
+ * automatically deleted some time later.  Users invoking empty are expecting all contents on the
+ * server to be removed entirely, whether or not any or all of them have been synchronized locally.
+ *
+ * @see FolderSupport.Remove
+ */
+
+public interface Geary.FolderSupport.Empty : Geary.Folder {
+    /**
+     * Removes all email from the folder.
+     */
+    public abstract async void empty_folder_async(Cancellable? cancellable = null) throws Error;
+}
+
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index 91d50a8..f8c41fb 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -935,16 +935,22 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
     // on most operations unless ListFlags.INCLUDE_MARKED_REMOVED is true.  Use detach_email_async()
     // to formally remove the messages from the folder.
     //
+    // If ids is null, all messages are marked for removal.
+    //
     // Returns a collection of ImapDB.EmailIdentifiers *with the UIDs set* for this folder.
     // Supplied EmailIdentifiers not in this Folder will not be included.
     public async Gee.Set<ImapDB.EmailIdentifier>? mark_removed_async(
-        Gee.Collection<ImapDB.EmailIdentifier> ids, bool mark_removed, Cancellable? cancellable)
+        Gee.Collection<ImapDB.EmailIdentifier>? ids, bool mark_removed, Cancellable? cancellable)
         throws Error {
         int unread_count = 0;
         Gee.Set<ImapDB.EmailIdentifier> removed_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
         yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
-            Gee.List<LocationIdentifier?> locs = do_get_locations_for_ids(cx, ids,
-                ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
+            Gee.List<LocationIdentifier?> locs;
+            if (ids != null)
+                locs = do_get_locations_for_ids(cx, ids, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
+            else
+                locs = do_get_all_locations(cx, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
+            
             if (locs == null || locs.size == 0)
                 return Db.TransactionOutcome.DONE;
             
@@ -2256,6 +2262,21 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         return (locs.size > 0) ? locs : null;
     }
     
+    private Gee.List<LocationIdentifier>? do_get_all_locations(Db.Connection cx, ListFlags flags,
+        Cancellable? cancellable) throws Error {
+        Db.Statement stmt = cx.prepare("""
+            SELECT message_id, ordering, remove_marker
+            FROM MessageLocationTable
+            WHERE folder_id = ?
+        """);
+        stmt.bind_rowid(0, folder_id);
+        
+        Gee.List<LocationIdentifier> locs = do_results_to_locations(stmt.exec(cancellable), flags,
+            cancellable);
+        
+        return (locs.size > 0) ? locs : null;
+    }
+    
     private int do_get_unread_count_for_ids(Db.Connection cx,
         Gee.Collection<ImapDB.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
         // Fetch flags for each email and update this folder's unread count.
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
index 8583eaf..e9bb552 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
@@ -9,7 +9,8 @@
  * IMAP STORE/EXPUNGE operation.
  */
 
-private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSupport.Remove {
+private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSupport.Remove,
+    FolderSupport.Empty {
     public GmailSpamTrashFolder(GmailAccount account, Imap.Account remote, ImapDB.Account local,
         ImapDB.Folder local_folder, SpecialFolderType special_folder_type) {
         base (account, remote, local, local_folder, special_folder_type);
@@ -19,5 +20,9 @@ private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSuppo
         Cancellable? cancellable = null) throws Error {
         yield expunge_email_async(email_ids, cancellable);
     }
+    
+    public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
+        yield expunge_all_async(cancellable);
+    }
 }
 
diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala 
b/src/engine/imap-engine/imap-engine-generic-folder.vala
index b09f034..2f234af 100644
--- a/src/engine/imap-engine/imap-engine-generic-folder.vala
+++ b/src/engine/imap-engine/imap-engine-generic-folder.vala
@@ -5,7 +5,7 @@
  */
 
 private class Geary.ImapEngine.GenericFolder : MinimalFolder, Geary.FolderSupport.Remove,
-    Geary.FolderSupport.Create {
+    Geary.FolderSupport.Create, Geary.FolderSupport.Empty {
     public GenericFolder(GenericAccount account, Imap.Account remote, ImapDB.Account local,
         ImapDB.Folder local_folder, SpecialFolderType special_folder_type) {
         base (account, remote, local, local_folder, special_folder_type);
@@ -16,6 +16,10 @@ private class Geary.ImapEngine.GenericFolder : MinimalFolder, Geary.FolderSuppor
         yield expunge_email_async(email_ids, cancellable);
     }
     
+    public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
+        yield expunge_all_async(cancellable);
+    }
+    
     public new async Geary.EmailIdentifier? create_email_async(
         RFC822.Message rfc822, Geary.EmailFlags? flags, DateTime? date_received,
         Geary.EmailIdentifier? id, Cancellable? cancellable = null) throws Error {
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index 62bd816..0f5213d 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -1231,6 +1231,15 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         yield remove.wait_for_ready_async(cancellable);
     }
     
+    protected async void expunge_all_async(Cancellable? cancellable = null) throws Error {
+        check_open("expunge_all_async");
+        
+        EmptyFolder empty_folder = new EmptyFolder(this, cancellable);
+        replay_queue.schedule(empty_folder);
+        
+        yield empty_folder.wait_for_ready_async(cancellable);
+    }
+    
     private void check_open(string method) throws EngineError {
         if (open_count == 0)
             throw new EngineError.OPEN_REQUIRED("%s failed: folder %s is not open", method, to_string());
diff --git a/src/engine/imap-engine/imap-engine-replay-operation.vala 
b/src/engine/imap-engine/imap-engine-replay-operation.vala
index 0dd62e2..5662521 100644
--- a/src/engine/imap-engine/imap-engine-replay-operation.vala
+++ b/src/engine/imap-engine/imap-engine-replay-operation.vala
@@ -59,7 +59,7 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject {
     public abstract void notify_remote_removed_position(Imap.SequenceNumber removed);
     
     /**
-     * Notify the operation that a message has been removed by position (SequenceNumber).
+     * Notify the operation that a message has been removed by UID (EmailIdentifier).
      *
      * This method is called only when the ReplayOperation is blocked waiting to execute and it's
      * discovered that the supplied email(s) are no longer on the server.
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
new file mode 100644
index 0000000..9944d74
--- /dev/null
+++ b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
@@ -0,0 +1,76 @@
+/* Copyright 2015 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+/**
+ * Similar to RemoveEmail, except this command ''always'' issues the command to remove all mail,
+ * ensuring the entire folder is emptied even if only a portion of it is synchronized locally.
+ */
+
+private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperation {
+    private MinimalFolder engine;
+    private Cancellable? cancellable;
+    private Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
+    private int original_count = 0;
+    
+    public EmptyFolder(MinimalFolder engine, Cancellable? cancellable) {
+        base("EmptyFolder");
+        
+        this.engine = engine;
+        this.cancellable = cancellable;
+    }
+    
+    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+    }
+    
+    public override async ReplayOperation.Status replay_local_async() throws Error {
+        int remote_count;
+        int last_seen_remote_count;
+        original_count = engine.get_remote_counts(out remote_count, out last_seen_remote_count);
+        
+        // because this value is only used for reporting count changes, offer best-possible service
+        if (original_count < 0)
+            original_count = 0;
+        
+        removed_ids = yield engine.local_folder.mark_removed_async(null, true, cancellable);
+        
+        if (removed_ids != null && removed_ids.size > 0)
+            engine.replay_notify_email_removed(removed_ids);
+        
+        int new_count = Numeric.int_floor(original_count - removed_ids.size, 0);
+        if (new_count != original_count)
+            engine.replay_notify_email_count_changed(new_count, Geary.Folder.CountChangeReason.REMOVED);
+        
+        return ReplayOperation.Status.CONTINUE;
+    }
+    
+    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+        if (removed_ids != null)
+            ids.add_all(removed_ids);
+    }
+    
+    public override async ReplayOperation.Status replay_remote_async() throws Error {
+        Imap.MessageSet msg_set = new Imap.MessageSet.range_to_highest(
+            new Imap.SequenceNumber(Imap.SequenceNumber.MIN));
+        
+        yield engine.remote_folder.remove_email_async(msg_set.to_list(), cancellable);
+        
+        return ReplayOperation.Status.COMPLETED;
+    }
+    
+    public override async void backout_local_async() throws Error {
+        if (removed_ids != null && removed_ids.size > 0) {
+            yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
+            engine.replay_notify_email_inserted(removed_ids);
+        }
+        
+        engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
+    }
+    
+    public override string describe_state() {
+        return "removed_ids.size=%d".printf((removed_ids != null) ? removed_ids.size : 0);
+    }
+}
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]