Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
56 changes: 56 additions & 0 deletions app/dashboard/views/alias_detail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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 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