| let admin_current_email = null; |
| let admin_email_meta = {}; |
| let audit_page = 0; |
| let audit_size = 30; |
| let mgmt_prefs = {} |
| |
| async function POST(url, formdata, state) { |
| const resp = await fetch(url, { |
| credentials: "same-origin", |
| mode: "same-origin", |
| method: "post", |
| headers: { |
| "Content-Type": "application/json" |
| }, |
| body: formdata |
| }); |
| return resp |
| } |
| |
| // Removes an attachment from the archives |
| async function admin_del_attachment(hash) { |
| if (!confirm("Are you sure you wish remove this attachment from the archives?")) { |
| return |
| } |
| // rewrite attachments for email |
| let new_attach = []; |
| for (let el of admin_email_meta.attachments) { |
| if (el.hash != hash) { |
| new_attach.push(el); |
| } |
| } |
| admin_email_meta.attachments = new_attach; |
| let formdata = JSON.stringify({ |
| action: "delatt", |
| document: hash |
| }); |
| // remove attachment |
| let rv = await POST('%sapi/mgmt.json'.format(G_apiURL), formdata, {}); |
| let response = await rv.text(); |
| |
| // Edit email in place |
| admin_save_email(true); |
| |
| if (rv.status == 200) { |
| modal("Attachment removed", "Server responded with: " + response, "help"); |
| } else { |
| modal("Something went wrong!", "Server responded with: " + response, "error"); |
| } |
| } |
| |
| // 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 |
| } |
| let formdata = JSON.stringify({ |
| action: "hide", |
| document: admin_current_email |
| }); |
| let rv = await POST('%sapi/mgmt.json'.format(G_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 |
| } |
| let formdata = JSON.stringify({ |
| action: "unhide", |
| document: admin_current_email |
| }); |
| let rv = await POST('%sapi/mgmt.json'.format(G_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 |
| } |
| let formdata = JSON.stringify({ |
| action: "delete", |
| document: admin_current_email |
| }); |
| let rv = await POST('%sapi/mgmt.json'.format(G_apiURL), formdata, {}); |
| let response = await rv.text(); |
| if (rv.status == 200) { |
| modal("Email removed", "Server responded with: " + response, "help"); |
| } else { |
| modal("Something went wrong!", "Server responded with: " + response, "error"); |
| } |
| } |
| |
| // Saves an email with edits |
| async function admin_save_email(edit_attachment = false) { |
| let from = document.getElementById('email_from').value; |
| let subject = document.getElementById('email_subject').value; |
| let listname = document.getElementById('email_listname').value; |
| let is_private = document.getElementById('email_private').value; |
| let body = document.getElementById('email_body').value; |
| let attach = null; |
| if (edit_attachment) { |
| attach = admin_email_meta.attachments; |
| } |
| let formdata = JSON.stringify({ |
| action: "edit", |
| document: admin_current_email, |
| from: from, |
| subject: subject, |
| list: listname, |
| private: is_private, |
| body: body, |
| attachments: attach |
| }) |
| let rv = await POST('%sapi/mgmt.json'.format(G_apiURL), formdata, {}); |
| let response = await rv.text(); |
| if (edit_attachment && rv.status == 200) return |
| if (rv.status == 200) { |
| modal("Email changed", "Server responded with: " + response, "help"); |
| } else { |
| modal("Something went wrong!", "Server responded with: " + response, "error"); |
| } |
| } |
| |
| 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: { |
| margin: '5px' |
| } |
| }); |
| cp.inject(div); |
| |
| div.inject(new HTML('h1', {}, "Editing email " + json.mid + ":")); |
| |
| // Author |
| let author_field = new HTML('div', { |
| class: 'email_kv_edit' |
| }); |
| let author_key = new HTML('div', { |
| class: 'email_key' |
| }, "From: "); |
| let author_value = new HTML('input', { |
| id: 'email_from', |
| style: { |
| width: "480px" |
| }, |
| value: json.from |
| }); |
| author_field.inject([author_key, author_value]); |
| div.inject(author_field); |
| |
| // Subject |
| let subject_field = new HTML('div', { |
| class: 'email_kv_edit' |
| }); |
| let subject_key = new HTML('div', { |
| class: 'email_key' |
| }, "Subject: "); |
| let subject_value = new HTML('input', { |
| id: 'email_subject', |
| style: { |
| width: "480px" |
| }, |
| value: json.subject |
| }); |
| subject_field.inject([subject_key, subject_value]); |
| div.inject(subject_field); |
| |
| // Date |
| let date_field = new HTML('div', { |
| class: 'email_kv_edit' |
| }); |
| let date_key = new HTML('div', { |
| class: 'email_key' |
| }, "Date: "); |
| let date_value = new HTML('div', { |
| class: 'email_value' |
| }, new Date(json.epoch * 1000.0).ISOBare()); |
| date_field.inject([date_key, date_value]); |
| div.inject(date_field); |
| |
| // List |
| let listname = json.list_raw.replace(".", "@", 1).replace(/[<>]/g, ""); |
| let list_field = new HTML('div', { |
| class: 'email_kv_edit' |
| }); |
| let list_key = new HTML('div', { |
| class: 'email_key' |
| }, "List: "); |
| let list_value = new HTML('input', { |
| id: 'email_listname', |
| style: { |
| width: "480px" |
| }, |
| value: listname |
| }); |
| list_field.inject([list_key, list_value]); |
| div.inject(list_field); |
| |
| // Private email? |
| let priv_field = new HTML('div', { |
| class: 'email_kv_edit' |
| }); |
| let priv_key = new HTML('div', { |
| class: 'email_key' |
| }, "Visibility: "); |
| let priv_value = new HTML('select', { |
| id: 'email_private' |
| }); |
| priv_value.inject(new HTML('option', { |
| value: 'no', |
| style: { |
| color: 'green' |
| }, |
| selected: json.private ? null : "selected" |
| }, "Public")); |
| priv_value.inject(new HTML('option', { |
| value: 'yes', |
| style: { |
| color: 'red' |
| }, |
| selected: json.private ? "selected" : null |
| }, "Private")); |
| priv_field.inject([priv_key, priv_value]); |
| div.inject(priv_field); |
| |
| // Attachments? |
| if (json.attachments && json.attachments.length > 0) { |
| let attach_field = new HTML('div', { |
| class: 'email_kv' |
| }); |
| let attach_key = new HTML('div', { |
| class: 'email_key' |
| }, "Attachment(s): "); |
| let alinks = []; |
| for (let attachment of json.attachments) { |
| let link = `${G_apiURL}api/email.lua?attachment=true&id=${encodeURIComponent(json.mid)}&file=${encodeURIComponent(attachment.hash)}`; |
| let a = new HTML('a', { |
| href: link, |
| target: '_blank' |
| }, attachment.filename); |
| alinks.push(a); |
| let fs = ` ${attachment.size} bytes`; |
| if (attachment.size >= 1024) fs = ` ${Math.floor(attachment.size/1024)} KB`; |
| if (attachment.size >= 1024 * 1024) fs = ` ${Math.floor(attachment.size/(1024*10.24))/100} MB`; |
| alinks.push(fs); |
| let adel = new HTML('a', { |
| onclick: `admin_del_attachment('${attachment.hash}');`, |
| href: "javascript:void(0);" |
| }, "Delete attachment"); |
| alinks.push(adel); |
| alinks.push(new HTML('br')); |
| } |
| let attach_value = new HTML('div', { |
| class: 'email_value' |
| }, alinks); |
| attach_field.inject([attach_key, attach_value]); |
| div.inject(attach_field); |
| } |
| |
| let text = new HTML('textarea', { |
| id: 'email_body', |
| style: { |
| width: "100%", |
| height: "480px" |
| } |
| }, json.body); |
| div.inject(text); |
| |
| 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: 'purple' |
| } |
| }, "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 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 full delete enforcement is enabled on this server, emails are removed forever from the archive when deleted, and cannot be recovered.")) |
| } |
| } |
| |
| function admin_audit_view(state, json) { |
| let headers = ['Date', 'Author', 'Remote', 'Action', 'Target', 'Log']; |
| let cp = document.getElementById("panel"); |
| let div = document.getElementById('auditlog_entries'); |
| if (!div) { |
| div = new HTML('div', { |
| id: "auditlog", |
| style: { |
| margin: '5px' |
| } |
| }); |
| cp.inject(div); |
| div.inject(new HTML('h1', {}, "Audit log:")); |
| } |
| let table = document.getElementById('auditlog_entries'); |
| if (json.entries && json.entries.length > 0 || table) { |
| if (!table) { |
| table = new HTML('table', { |
| border: "1", |
| id: "auditlog_entries", |
| class: "auditlog_entries" |
| }); |
| let trh = new HTML('tr'); |
| for (let header of headers) { |
| let th = new HTML('th', {}, header + ":"); |
| trh.inject(th); |
| } |
| table.inject(trh) |
| div.inject(table); |
| let btn = new HTML('button', { |
| onclick: "admin_audit_next();" |
| }, "Load more entries"); |
| div.inject(btn); |
| } |
| for (let entry of json.entries) { |
| let tr = new HTML('tr', { |
| class: "auditlog_entry" |
| }); |
| for (let header of headers) { |
| let key = header.toLowerCase(); |
| let value = entry[key]; |
| if (key == 'target') { |
| value = new HTML('a', { |
| href: "/admin/" + value |
| }, value); |
| } |
| if (key == 'action') { |
| let action_colors = { |
| edit: 'blue', |
| delete: 'red', |
| default: 'black' |
| }; |
| value = new HTML('spam', { |
| style: { |
| color: action_colors[value] ? action_colors[value] : action_colors['default'] |
| } |
| }, value); |
| } |
| let th = new HTML('td', {}, value); |
| tr.inject(th); |
| } |
| table.inject(tr); |
| } |
| } else { |
| div.inject("Audit log is empty"); |
| } |
| } |
| |
| function admin_audit_next() { |
| audit_page++; |
| GET('%sapi/mgmt.json?action=log&page=%u&size=%u'.format(G_apiURL, audit_page, audit_size), admin_audit_view, null); |
| } |
| |
| // Onload function for admin.html |
| function admin_init() { |
| init_preferences(); // blank call to load defaults like social rendering |
| GET('%sapi/preferences.lua'.format(G_apiURL), (state, json) => { |
| mgmt_prefs = json |
| init_preferences(state, json); |
| }, null); |
| let mid = decodeURIComponent(location.href.split('/').pop()); |
| // Specific email/list handling? |
| if (mid.length > 0) { |
| // List handling? |
| if (mid.match(/^<.+>$/)) { |
| |
| } |
| // Email handling? |
| else { |
| GET('%sapi/email.json?id=%s'.format(G_apiURL, encodeURIComponent(mid)), admin_email_preview, null); |
| } |
| } else { // View audit log |
| GET('%sapi/mgmt.json?action=log&page=%s&size=%u'.format(G_apiURL, audit_page, audit_size), admin_audit_view, null); |
| } |
| } |