Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/dashboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
custom_alias,
subdomain,
billing,
alias_detail,
alias_log,
alias_export,
unsubscribe,
Expand Down
60 changes: 60 additions & 0 deletions app/dashboard/views/alias_detail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from flask import render_template, flash, redirect, url_for, request
from flask_login import login_required, current_user

from app import alias_utils
from app.api.serializer import get_alias_info_v3
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.log import LOG
from app.utils import CSRFValidationForm


@dashboard_bp.route("/aliases/<int:alias_id>", methods=["GET", "POST"])
@login_required
def aliases(alias_id):
alias_info = get_alias_info_v3(current_user, alias_id)

# sanity check
if not alias_info:
flash("You do not have access to this page", "warning")
return redirect(url_for("dashboard.index"))

alias = alias_info.alias

if alias.user_id != current_user.id:
flash("You do not have access to this page", "warning")
return redirect(url_for("dashboard.index"))

mailboxes = current_user.mailboxes()

csrf_form = CSRFValidationForm()

if request.method == "POST":
if not csrf_form.validate():
flash("Invalid request", "warning")
return redirect(request.url)
if request.form.get("form-name") in ("delete-alias", "disable-alias"):
if not alias or alias.user_id != current_user.id:
Comment thread
D-Bao marked this conversation as resolved.
Outdated
flash("Unknown error, sorry for the inconvenience", "error")
return redirect(url_for("dashboard.index"))

if request.form.get("form-name") == "delete-alias":
LOG.d("delete alias %s", alias)
email = alias.email
alias_utils.delete_alias(alias, current_user)
flash(f"Alias {email} has been deleted", "success")
return redirect(url_for("dashboard.index"))

elif request.form.get("form-name") == "disable-alias":
alias.enabled = False
Session.commit()
flash(f"Alias {alias.email} has been disabled", "success")

return redirect(url_for("dashboard.aliases", alias_id=alias.id))

return render_template(
"dashboard/alias_detail.html",
alias_info=alias_info,
mailboxes=mailboxes,
csrf_form=csrf_form,
)
2 changes: 1 addition & 1 deletion app/dashboard/views/alias_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def alias_transfer_receive_route():
Session.commit()

flash(f"You are now owner of {alias.email}", "success")
return redirect(url_for("dashboard.index", highlight_alias_id=alias.id))
return redirect(url_for("dashboard.aliases", alias_id=alias.id))

return render_template(
"dashboard/alias_transfer_receive.html",
Expand Down
218 changes: 218 additions & 0 deletions static/js/alias-detail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
$('.mailbox-select').multipleSelect();

function confirmDeleteAlias() {
let that = $(this);
let alias = that.data("alias-email");
let aliasDomainTrashUrl = that.data("custom-domain-trash-url");

let message = `Maybe you want to disable the alias instead? Please note once deleted, it <b>can't</b> be restored.`;
if (aliasDomainTrashUrl !== undefined) {
message = `Maybe you want to disable the alias instead? When it's deleted, it's moved to the domain
<a href="${aliasDomainTrashUrl}">trash</a>`;
}

bootbox.dialog({
title: `Delete ${alias}`,
message: message,
size: 'large',
onEscape: true,
backdrop: true,
buttons: {
disable: {
label: 'Disable it',
className: 'btn-primary',
callback: function () {
that.closest("form").find('input[name="form-name"]').val("disable-alias");
that.closest("form").submit();
}
},

delete: {
label: "Delete it, I don't need it anymore",
className: 'btn-outline-danger',
callback: function () {
that.closest("form").submit();
}
},

cancel: {
label: 'Cancel',
className: 'btn-outline-primary'
},

}
});
}

$(".enable-disable-alias").change(async function () {
let aliasId = $(this).data("alias");
let alias = $(this).data("alias-email");

await disableAlias(aliasId, alias);
});

async function disableAlias(aliasId, alias) {
let oldValue;
try {
let res = await fetch(`/api/aliases/${aliasId}/toggle`, {
method: "POST",
headers: {
"Content-Type": "application/json",
}
});

if (res.ok) {
let json = await res.json();

if (json.enabled) {
toastr.success(`${alias} is enabled`);
$(`#send-email-${aliasId}`).removeClass("disabled");
} else {
toastr.success(`${alias} is disabled`);
$(`#send-email-${aliasId}`).addClass("disabled");
}
} else {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
// reset to the original value
oldValue = !$(this).prop("checked");
$(this).prop("checked", oldValue);
}
} catch (e) {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
// reset to the original value
oldValue = !$(this).prop("checked");
$(this).prop("checked", oldValue);
}
}

$(".enable-disable-pgp").change(async function (e) {
let aliasId = $(this).data("alias");
let alias = $(this).data("alias-email");
const oldValue = !$(this).prop("checked");
let newValue = !oldValue;

try {
let res = await fetch(`/api/aliases/${aliasId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
disable_pgp: oldValue,
}),
});

if (res.ok) {
if (newValue) {
toastr.success(`PGP is enabled for ${alias}`);
} else {
toastr.info(`PGP is disabled for ${alias}`);
}
} else {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
// reset to the original value
$(this).prop("checked", oldValue);
}
} catch (err) {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
// reset to the original value
$(this).prop("checked", oldValue);
}
});

async function handleNoteChange(aliasId, aliasEmail) {
const note = document.getElementById(`note-${aliasId}`).value;

try {
let res = await fetch(`/api/aliases/${aliasId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
note: note,
}),
});

if (res.ok) {
toastr.success(`Description saved for ${aliasEmail}`);
} else {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}
} catch (e) {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}

}

function handleNoteFocus(aliasId) {
document.getElementById(`note-focus-message-${aliasId}`).classList.remove('d-none');
}

function handleNoteBlur(aliasId) {
document.getElementById(`note-focus-message-${aliasId}`).classList.add('d-none');
}

async function handleMailboxChange(aliasId, aliasEmail) {
const selectedOptions = document.getElementById(`mailbox-${aliasId}`).selectedOptions;
const mailbox_ids = Array.from(selectedOptions).map((selectedOption) => selectedOption.value);

if (mailbox_ids.length === 0) {
toastr.error("You must select at least a mailbox", "Error");
return;
}

try {
let res = await fetch(`/api/aliases/${aliasId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
mailbox_ids: mailbox_ids,
}),
});

if (res.ok) {
toastr.success(`Mailbox updated for ${aliasEmail}`);
} else {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}
} catch (e) {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}

}

async function handleDisplayNameChange(aliasId, aliasEmail) {
const name = document.getElementById(`alias-name-${aliasId}`).value;

try {
let res = await fetch(`/api/aliases/${aliasId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: name,
}),
});

if (res.ok) {
toastr.success(`Display name saved for ${aliasEmail}`);
} else {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}
} catch (e) {
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}

}

function handleDisplayNameFocus(aliasId) {
document.getElementById(`display-name-focus-message-${aliasId}`).classList.remove('d-none');
}

function handleDisplayNameBlur(aliasId) {
document.getElementById(`display-name-focus-message-${aliasId}`).classList.add('d-none');
}
Loading