You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ponymail.apache.org by hu...@apache.org on 2021/09/27 18:50:05 UTC

[incubator-ponymail-foal] branch master updated (30a4932 -> b9f0817)

This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git.


    from 30a4932  Unused imports
     new 50295f2  add in hide/"unhide" functionality
     new 61b1865  Allow for hide/unhide
     new aa9a137  rename variable
     new 7c72a18  admins should be able to see hidden emails
     new b9f0817  regen JS

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 INSTALL.md                      |  7 +++--
 server/endpoints/email.py       |  5 ++-
 server/endpoints/mgmt.py        | 38 ++++++++++++++++++++---
 server/plugins/configuration.py |  2 +-
 webui/js/ponymail.js            | 67 ++++++++++++++++++++++++++++++++++++++---
 webui/js/source/mgmt.js         | 67 ++++++++++++++++++++++++++++++++++++++---
 6 files changed, 167 insertions(+), 19 deletions(-)

[incubator-ponymail-foal] 01/05: add in hide/"unhide" functionality

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit 50295f2a2a6dc5958fd6ba63556993e1cfa1c57d
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Mon Sep 27 13:43:57 2021 -0500

    add in hide/"unhide" functionality
---
 webui/js/source/mgmt.js | 67 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 62 insertions(+), 5 deletions(-)

diff --git a/webui/js/source/mgmt.js b/webui/js/source/mgmt.js
index bc8f563..ad75382 100644
--- a/webui/js/source/mgmt.js
+++ b/webui/js/source/mgmt.js
@@ -1,4 +1,5 @@
 let admin_current_email = null;
+let admin_email_meta = {};
 let audit_entries = []
 let audit_page = 0;
 let audit_size = 30;
@@ -17,8 +18,44 @@ async function POST(url, formdata, state) {
     return resp
 }
 
-// Deletes (hides) an email from the archives
+// Hides an email from the archives
 async function admin_hide_email() {
+    if (!confirm("Are you sure you wish to hide this email from the archives?")) {
+        return
+    }
+    formdata = JSON.stringify({
+        action: "hide",
+        document: admin_current_email
+    });
+    let rv = await POST('%sapi/mgmt.json'.format(apiURL), formdata, {});
+    let response = await rv.text();
+    if (rv.status == 200) {
+        modal("Email hidden", "Server responded with: " + response, "help");
+    } else {
+        modal("Something went wrong!", "Server responded with: " + response, "error");
+    }
+}
+
+async function admin_unhide_email() {
+    if (!confirm("Are you sure you wish to unhide this email?")) {
+        return
+    }
+    formdata = JSON.stringify({
+        action: "unhide",
+        document: admin_current_email
+    });
+    let rv = await POST('%sapi/mgmt.json'.format(apiURL), formdata, {});
+    let response = await rv.text();
+    if (rv.status == 200) {
+        modal("Email unhidden", "Server responded with: " + response, "help");
+    } else {
+        modal("Something went wrong!", "Server responded with: " + response, "error");
+    }
+}
+
+
+// Fully deletes an email from the archives
+async function admin_delete_email() {
     if (!confirm("Are you sure you wish to remove this email from the archives?")) {
         return
     }
@@ -62,6 +99,7 @@ async function admin_save_email() {
 
 function admin_email_preview(stats, json) {
     admin_current_email = json.mid;
+    admin_email_meta = json;
     let cp = document.getElementById("panel");
     let div = new HTML('div', {
         style: {
@@ -206,23 +244,42 @@ function admin_email_preview(stats, json) {
     let btn_edit = new HTML('button', {
         onclick: "admin_save_email();"
     }, "Save changes to archive");
+    let btn_del = new HTML('button', {
+        onclick: "admin_delete_email();",
+        style: {
+            marginLeft: "36px",
+            color: 'red'
+        }
+    }, "Delete email from archives");
+
     let btn_hide = new HTML('button', {
         onclick: "admin_hide_email();",
         style: {
             marginLeft: "36px",
-            color: 'red'
+            color: 'purple'
         }
-    }, "Remove email from archives");
+    }, "Hide email from archives");
+    if (admin_email_meta.deleted) {
+        btn_hide = new HTML('button', {
+            onclick: "admin_unhide_email();",
+            style: {
+                marginLeft: "36px",
+                color: 'purple'
+            }
+        }, "Unhide email from archives");
+    }
+
     div.inject(new HTML('br'));
     div.inject(btn_edit);
     div.inject(btn_hide);
+    div.inject(btn_del);
     div.inject(new HTML('br'));
     div.inject(new HTML('small', {}, "Modifying emails will remove the option to view their sources via the web interface, as the source may contain traces that reveal the edit."))
     div.inject(new HTML('br'));
     if (!mgmt_prefs.login.credentials.fully_delete) {
-        div.inject(new HTML('small', {}, "Emails that are removed may still be recovered by the base system administrator. For complete expungement, please contact the system administrator."))
+        div.inject(new HTML('small', {}, "Emails that are deleted may still be recovered by the base system administrator. For complete expungement, please contact the system administrator."))
     } else {
-        div.inject(new HTML('small', {style:{color: 'red'}}, "As GDPR enforcement is enabled on this server, emails are removed forever from the archive when deleted, and cannot be recovered."))
+        div.inject(new HTML('small', {style:{color: 'red'}}, "As full delete enforcement is enabled on this server, emails are removed forever from the archive when deleted, and cannot be recovered."))
     }
 }
 

[incubator-ponymail-foal] 02/05: Allow for hide/unhide

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit 61b1865dcbb810b2f9e9b854d0bd2ba3d4b3ac3f
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Mon Sep 27 13:44:21 2021 -0500

    Allow for hide/unhide
---
 server/endpoints/mgmt.py | 38 ++++++++++++++++++++++++++++++++++----
 1 file changed, 34 insertions(+), 4 deletions(-)

diff --git a/server/endpoints/mgmt.py b/server/endpoints/mgmt.py
index 3c761f5..b2ad597 100644
--- a/server/endpoints/mgmt.py
+++ b/server/endpoints/mgmt.py
@@ -42,13 +42,13 @@ async def process(
         numentries = int(indata.get("size", 50))
         page = int(indata.get("page", 0))
         out = []
-        async for entry in plugins.auditlog.view(session, page=page, num_entries=numentries, raw=True, filter=("edit", "delete",)):
+        async for entry in plugins.auditlog.view(session, page=page, num_entries=numentries, raw=True, filter=("edit","delete","hide","unhide")):
             out.append(entry)
         return {
             "entries": out
         }
 
-    # Deleting/hiding a document?
+    # Deleting a document?
     elif action == "delete":
         delcount = 0
         for doc in docs:
@@ -64,13 +64,43 @@ async def process(
                         index=session.database.dbs.source, id=email["dbid"],
                     )
                 else:  # Standard behavior: hide the email from everyone.
-                    await session.database.index(
-                        index=session.database.dbs.mbox, body=email, id=email["id"],
+                    await session.database.update(
+                        index=session.database.dbs.mbox, body={"doc": email}, id=email["id"],
                     )
                 lid = email.get("list_raw", "??")
                 await plugins.auditlog.add_entry(session, action="delete", target=doc, lid=lid, log=f"Removed email {doc} from {lid} archives")
                 delcount += 1
         return aiohttp.web.Response(headers={}, status=200, text=f"Removed {delcount} emails from archives.")
+    # Hiding one or more emails?
+    elif action == "hide":
+        hidecount = 0
+        for doc in docs:
+            assert isinstance(doc, str), "Document ID must be a string"
+            email = await plugins.messages.get_email(session, permalink=doc)
+            if email and isinstance(email, dict) and plugins.aaa.can_access_email(session, email):
+                email["deleted"] = True
+                await session.database.update(
+                    index=session.database.dbs.mbox, body={"doc": email}, id=email["id"],
+                )
+                lid = email.get("list_raw", "??")
+                await plugins.auditlog.add_entry(session, action="hide", target=doc, lid=lid, log=f"Hid email {doc} from {lid} archives")
+                hidecount += 1
+        return aiohttp.web.Response(headers={}, status=200, text=f"Hid {hidecount} emails from archives.")
+    # Exposing (unhiding) one or more emails?
+    elif action == "unhide":
+        hidecount = 0
+        for doc in docs:
+            assert isinstance(doc, str), "Document ID must be a string"
+            email = await plugins.messages.get_email(session, permalink=doc)
+            if email and isinstance(email, dict) and plugins.aaa.can_access_email(session, email):
+                email["deleted"] = False
+                await session.database.update(
+                    index=session.database.dbs.mbox, body={"doc": email}, id=email["id"],
+                )
+                lid = email.get("list_raw", "??")
+                await plugins.auditlog.add_entry(session, action="unhide", target=doc, lid=lid, log=f"Unhid email {doc} from {lid} archives")
+                hidecount += 1
+        return aiohttp.web.Response(headers={}, status=200, text=f"Unhid {hidecount} emails from archives.")
     # Editing an email in place
     elif action == "edit":
         new_from = indata.get("from")

[incubator-ponymail-foal] 04/05: admins should be able to see hidden emails

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit 7c72a1890ee3f8c2ff4a6dd3a7cc121f9f59d167
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Mon Sep 27 13:49:44 2021 -0500

    admins should be able to see hidden emails
---
 server/endpoints/email.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/server/endpoints/email.py b/server/endpoints/email.py
index 0e8e6dd..dcfad60 100644
--- a/server/endpoints/email.py
+++ b/server/endpoints/email.py
@@ -40,7 +40,10 @@ async def process(
         email = await plugins.messages.get_email(session, messageid=indata.get("id"))
 
     # If email was found, process the request if we are allowed to display it
-    if email and isinstance(email, dict) and not email.get("deleted"):
+    cannot_view = email.get("deleted", False)
+    if session.credentials and session.credentials.admin:
+        cannot_view = False
+    if email and isinstance(email, dict) and not cannot_view:
         if plugins.aaa.can_access_email(session, email):
             # Are we fetching an attachment?
             if not indata.get("attachment"):

[incubator-ponymail-foal] 05/05: regen JS

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit b9f0817ea132832a8bee4e9ae0694078810a3afb
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Mon Sep 27 13:49:53 2021 -0500

    regen JS
---
 webui/js/ponymail.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 62 insertions(+), 5 deletions(-)

diff --git a/webui/js/ponymail.js b/webui/js/ponymail.js
index c5e0579..4b409e5 100644
--- a/webui/js/ponymail.js
+++ b/webui/js/ponymail.js
@@ -2687,6 +2687,7 @@ function listview_threaded_element(thread, idx) {
 ******************************************/
 
 let admin_current_email = null;
+let admin_email_meta = {};
 let audit_entries = []
 let audit_page = 0;
 let audit_size = 30;
@@ -2705,8 +2706,44 @@ async function POST(url, formdata, state) {
     return resp
 }
 
-// Deletes (hides) an email from the archives
+// Hides an email from the archives
 async function admin_hide_email() {
+    if (!confirm("Are you sure you wish to hide this email from the archives?")) {
+        return
+    }
+    formdata = JSON.stringify({
+        action: "hide",
+        document: admin_current_email
+    });
+    let rv = await POST('%sapi/mgmt.json'.format(apiURL), formdata, {});
+    let response = await rv.text();
+    if (rv.status == 200) {
+        modal("Email hidden", "Server responded with: " + response, "help");
+    } else {
+        modal("Something went wrong!", "Server responded with: " + response, "error");
+    }
+}
+
+async function admin_unhide_email() {
+    if (!confirm("Are you sure you wish to unhide this email?")) {
+        return
+    }
+    formdata = JSON.stringify({
+        action: "unhide",
+        document: admin_current_email
+    });
+    let rv = await POST('%sapi/mgmt.json'.format(apiURL), formdata, {});
+    let response = await rv.text();
+    if (rv.status == 200) {
+        modal("Email unhidden", "Server responded with: " + response, "help");
+    } else {
+        modal("Something went wrong!", "Server responded with: " + response, "error");
+    }
+}
+
+
+// Fully deletes an email from the archives
+async function admin_delete_email() {
     if (!confirm("Are you sure you wish to remove this email from the archives?")) {
         return
     }
@@ -2750,6 +2787,7 @@ async function admin_save_email() {
 
 function admin_email_preview(stats, json) {
     admin_current_email = json.mid;
+    admin_email_meta = json;
     let cp = document.getElementById("panel");
     let div = new HTML('div', {
         style: {
@@ -2894,23 +2932,42 @@ function admin_email_preview(stats, json) {
     let btn_edit = new HTML('button', {
         onclick: "admin_save_email();"
     }, "Save changes to archive");
+    let btn_del = new HTML('button', {
+        onclick: "admin_delete_email();",
+        style: {
+            marginLeft: "36px",
+            color: 'red'
+        }
+    }, "Delete email from archives");
+
     let btn_hide = new HTML('button', {
         onclick: "admin_hide_email();",
         style: {
             marginLeft: "36px",
-            color: 'red'
+            color: 'purple'
         }
-    }, "Remove email from archives");
+    }, "Hide email from archives");
+    if (admin_email_meta.deleted) {
+        btn_hide = new HTML('button', {
+            onclick: "admin_unhide_email();",
+            style: {
+                marginLeft: "36px",
+                color: 'purple'
+            }
+        }, "Unhide email from archives");
+    }
+
     div.inject(new HTML('br'));
     div.inject(btn_edit);
     div.inject(btn_hide);
+    div.inject(btn_del);
     div.inject(new HTML('br'));
     div.inject(new HTML('small', {}, "Modifying emails will remove the option to view their sources via the web interface, as the source may contain traces that reveal the edit."))
     div.inject(new HTML('br'));
     if (!mgmt_prefs.login.credentials.fully_delete) {
-        div.inject(new HTML('small', {}, "Emails that are removed may still be recovered by the base system administrator. For complete expungement, please contact the system administrator."))
+        div.inject(new HTML('small', {}, "Emails that are deleted may still be recovered by the base system administrator. For complete expungement, please contact the system administrator."))
     } else {
-        div.inject(new HTML('small', {style:{color: 'red'}}, "As GDPR enforcement is enabled on this server, emails are removed forever from the archive when deleted, and cannot be recovered."))
+        div.inject(new HTML('small', {style:{color: 'red'}}, "As full delete enforcement is enabled on this server, emails are removed forever from the archive when deleted, and cannot be recovered."))
     }
 }
 

[incubator-ponymail-foal] 03/05: rename variable

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit aa9a1374601e7481eff3853aa39d0d450d69765d
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Mon Sep 27 13:49:25 2021 -0500

    rename variable
---
 INSTALL.md                      | 7 ++++---
 server/plugins/configuration.py | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/INSTALL.md b/INSTALL.md
index 3d808f2..ad1bea5 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -178,13 +178,14 @@ web interface.
 ## Online Management Console
 the `ui` paragraph of the server configuration allows for enabling an administrative interface
 for editing or removing emails from the archives. To enable this, set `mgmtconsole` to `true`.
-For true GDPR compliance (deleting an email deletes from disk), set `true_gdpr` to `true`. 
-If left out or set to false, deleted emails are merely hidden, and can be recovered at a later stage.
+For GDPR compliance (deleting an email deletes from disk), set `allow_delete` to `true`. 
+If left out or set to false, deleted emails are merely hidden, and can be recovered at a later 
+stage by an administrator.
 
 ~~~yaml
 ui:
   mgmtconsole:     true
-  true_gdpr:       true
+  allow_delete:       true
 ~~~
 
 The administrative interface can be accessed by clicking on the yellow cog in the context menu 
diff --git a/server/plugins/configuration.py b/server/plugins/configuration.py
index 66abfab..7fd47a5 100644
--- a/server/plugins/configuration.py
+++ b/server/plugins/configuration.py
@@ -52,7 +52,7 @@ class UIConfig:
         # Set to false in yaml to redirect to stderr instead.
         self.traceback = subyaml.get("traceback", True)
         self.mgmt_enabled = bool(subyaml.get("mgmtconsole", False))  # Whether to enable online mgmt component or not
-        self.fully_delete = bool(subyaml.get("true_gdpr", False))  # Whether to enforce full expunging of deleted emails
+        self.fully_delete = bool(subyaml.get("allow_delete", False))  # Whether to enforce full expunging of deleted emails
         # Default to all lists, "*". Use "" for host. Wildcard subdomain globs also supported
         self.focus_domain = subyaml.get("focus_domain", "*")