accounts: add "extend from previous expiry"

If the expiry time of an expired user is still in the activity log,
extending and re-enabling a user with this option checked will extend
the expiry from this time, rather than the current time. For #379, i
think this is basically what they wanted.
This commit is contained in:
Harvey Tindall
2025-11-26 15:30:45 +00:00
parent 875387166e
commit 5e653c51f3
7 changed files with 124 additions and 63 deletions

View File

@@ -836,6 +836,18 @@ interface UsersDTO extends paginatedDTO {
users: User[];
}
declare interface ExtendExpiryDTO {
users: string[];
months?: number;
days?: number;
hours?: number;
minutes?: number;
timestamp?: number;
notify: boolean;
reason?: string;
try_extend_from_previous_expiry?: boolean;
}
export class accountsList extends PaginatedList {
protected _container = document.getElementById("accounts-list") as HTMLTableSectionElement;
@@ -856,6 +868,7 @@ export class accountsList extends PaginatedList {
private _extendExpiryForm = document.getElementById("form-extend-expiry") as HTMLFormElement;
private _extendExpiryTextInput = document.getElementById("extend-expiry-text") as HTMLInputElement;
private _extendExpiryFieldInputs = document.getElementById("extend-expiry-field-inputs") as HTMLElement;
private _extendExpiryFromPreviousExpiry = document.getElementById("expiry-use-previous") as HTMLInputElement;
private _usingExtendExpiryTextInput = true;
private _extendExpiryDate = document.getElementById("extend-expiry-date") as HTMLElement;
@@ -1117,14 +1130,14 @@ export class accountsList extends PaginatedList {
this._extendExpiryDate.classList.add("unfocused");
this._extendExpiryTextInput.onkeyup = () => {
this._extendExpiryTextInput.parentElement.parentElement.classList.remove("opacity-60");
this._extendExpiryTextInput.parentElement.classList.remove("opacity-60");
this._extendExpiryFieldInputs.classList.add("opacity-60");
this._usingExtendExpiryTextInput = true;
this._displayExpiryDate();
}
this._extendExpiryTextInput.onclick = () => {
this._extendExpiryTextInput.parentElement.parentElement.classList.remove("opacity-60");
this._extendExpiryTextInput.parentElement.classList.remove("opacity-60");
this._extendExpiryFieldInputs.classList.add("opacity-60");
this._usingExtendExpiryTextInput = true;
this._displayExpiryDate();
@@ -1132,15 +1145,17 @@ export class accountsList extends PaginatedList {
this._extendExpiryFieldInputs.onclick = () => {
this._extendExpiryFieldInputs.classList.remove("opacity-60");
this._extendExpiryTextInput.parentElement.parentElement.classList.add("opacity-60");
this._extendExpiryTextInput.parentElement.classList.add("opacity-60");
this._usingExtendExpiryTextInput = false;
this._displayExpiryDate();
};
this._extendExpiryFromPreviousExpiry.onclick = this._displayExpiryDate;
for (let field of ["months", "days", "hours", "minutes"]) {
(document.getElementById("extend-expiry-"+field) as HTMLSelectElement).onchange = () => {
this._extendExpiryFieldInputs.classList.remove("opacity-60");
this._extendExpiryTextInput.parentElement.parentElement.classList.add("opacity-60");
this._extendExpiryTextInput.parentElement.classList.add("opacity-60");
this._usingExtendExpiryTextInput = false;
this._displayExpiryDate();
};
@@ -2008,45 +2023,54 @@ export class accountsList extends PaginatedList {
_displayExpiryDate = () => {
let date: Date;
let invalid = false;
let cantShow = false;
let users = this._collectUsers();
if (this._usingExtendExpiryTextInput) {
date = (Date as any).fromString(this._extendExpiryTextInput.value) as Date;
invalid = "invalid" in (date as any);
} else {
let fields: Array<HTMLSelectElement> = [
document.getElementById("extend-expiry-months") as HTMLSelectElement,
document.getElementById("extend-expiry-days") as HTMLSelectElement,
document.getElementById("extend-expiry-hours") as HTMLSelectElement,
document.getElementById("extend-expiry-minutes") as HTMLSelectElement
];
invalid = fields[0].value == "0" && fields[1].value == "0" && fields[2].value == "0" && fields[3].value == "0";
let id = users.length > 0 ? users[0] : "";
if (!id) invalid = true;
else {
date = new Date(this.users[id].expiry*1000);
if (this.users[id].expiry == 0) date = new Date();
date.setMonth(date.getMonth() + (+fields[0].value))
date.setDate(date.getDate() + (+fields[1].value));
date.setHours(date.getHours() + (+fields[2].value));
date.setMinutes(date.getMinutes() + (+fields[3].value));
if (this._extendExpiryFromPreviousExpiry.checked) {
cantShow = true;
} else {
let fields: Array<HTMLSelectElement> = [
document.getElementById("extend-expiry-months") as HTMLSelectElement,
document.getElementById("extend-expiry-days") as HTMLSelectElement,
document.getElementById("extend-expiry-hours") as HTMLSelectElement,
document.getElementById("extend-expiry-minutes") as HTMLSelectElement
];
invalid = fields[0].value == "0" && fields[1].value == "0" && fields[2].value == "0" && fields[3].value == "0";
let id = users.length > 0 ? users[0] : "";
if (!id) invalid = true;
else {
date = new Date(this.users[id].expiry*1000);
if (this.users[id].expiry == 0) date = new Date();
date.setMonth(date.getMonth() + (+fields[0].value))
date.setDate(date.getDate() + (+fields[1].value));
date.setHours(date.getHours() + (+fields[2].value));
date.setMinutes(date.getMinutes() + (+fields[3].value));
}
}
}
const submit = this._extendExpiryForm.querySelector(`input[type="submit"]`) as HTMLInputElement;
const submitSpan = submit.nextElementSibling;
if (invalid || cantShow) {
this._extendExpiryDate.classList.add("unfocused");
}
if (invalid) {
submit.disabled = true;
submitSpan.classList.add("opacity-60");
this._extendExpiryDate.classList.add("unfocused");
} else {
submit.disabled = false;
submitSpan.classList.remove("opacity-60");
this._extendExpiryDate.innerHTML = `
<div class="flex flex-col">
<span>${window.lang.strings("accountWillExpire").replace("{date}", toDateString(date))}</span>
${users.length > 1 ? "<span>"+window.lang.strings("expirationBasedOn")+"</span>" : ""}
</div>
`;
this._extendExpiryDate.classList.remove("unfocused");
if (!cantShow) {
this._extendExpiryDate.innerHTML = `
<div class="flex flex-col">
<span>${window.lang.strings("accountWillExpire").replace("{date}", toDateString(date))}</span>
${users.length > 1 ? "<span>"+window.lang.strings("expirationBasedOn")+"</span>" : ""}
</div>
`;
this._extendExpiryDate.classList.remove("unfocused");
}
}
}
@@ -2072,18 +2096,25 @@ export class accountsList extends PaginatedList {
}
document.getElementById("header-extend-expiry").textContent = header;
const extend = () => {
let send = { "users": applyList, "timestamp": 0, "notify": this._enableExpiryNotify.checked }
let send: ExtendExpiryDTO = {
users: applyList,
timestamp: 0,
notify: this._enableExpiryNotify.checked
}
if (this._enableExpiryNotify.checked) {
send["reason"] = this._enableExpiryReason.value;
send.reason = this._enableExpiryReason.value;
}
if (this._usingExtendExpiryTextInput) {
let date = (Date as any).fromString(this._extendExpiryTextInput.value) as Date;
send["timestamp"] = Math.floor(date.getTime() / 1000);
send.timestamp = Math.floor(date.getTime() / 1000);
if ("invalid" in (date as any)) {
window.notifications.customError("extendExpiryError", window.lang.notif("errorInvalidDate"));
return;
}
} else {
if (this._extendExpiryFromPreviousExpiry.checked) {
send.try_extend_from_previous_expiry = true;
}
for (let field of ["months", "days", "hours", "minutes"]) {
send[field] = +(document.getElementById("extend-expiry-"+field) as HTMLSelectElement).value;
}