mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
accounts: allow setting exact expiry date
set with a text input field which uses the same date parsing library as the search function. Parsed expiry date will appear once you've typed something in, so you can make sure it's right.
This commit is contained in:
parent
10c8d4ad2f
commit
213b1e7f9e
@ -719,7 +719,7 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
||||
var req extendExpiryDTO
|
||||
gc.BindJSON(&req)
|
||||
app.info.Printf("Expiry extension requested for %d user(s)", len(req.Users))
|
||||
if req.Months <= 0 && req.Days <= 0 && req.Hours <= 0 && req.Minutes <= 0 {
|
||||
if req.Months <= 0 && req.Days <= 0 && req.Hours <= 0 && req.Minutes <= 0 && req.Timestamp <= 0 {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
@ -731,7 +731,12 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
||||
} else {
|
||||
app.debug.Printf("Created expiry for \"%s\"", id)
|
||||
}
|
||||
expiry := UserExpiry{Expiry: base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)}
|
||||
expiry := UserExpiry{}
|
||||
if req.Timestamp != 0 {
|
||||
expiry.Expiry = time.Unix(req.Timestamp, 0)
|
||||
} else {
|
||||
expiry.Expiry = base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
|
||||
}
|
||||
app.storage.SetUserExpiryKey(id, expiry)
|
||||
}
|
||||
respondBool(204, true, gc)
|
||||
|
@ -181,39 +181,49 @@
|
||||
<form class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3" id="form-extend-expiry" href="">
|
||||
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">×</span></span>
|
||||
<div class="content mt-8">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-months">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-days">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
<aside class="aside sm ~urge dark:~d_info mb-2 @low row unfocused" id="extend-expiry-date"></aside>
|
||||
<div>
|
||||
<span class="text-xl supra row py-1">{{ .strings.setExpiry }}</span>
|
||||
<div class="row">
|
||||
<input type="text" id="extend-expiry-text" class="input ~neutral @low mb-2 mt-4" placeholder="{{ .strings.enterExpiry }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-hours">
|
||||
<option>0</option>
|
||||
</select>
|
||||
<div id="extend-expiry-field-inputs">
|
||||
<span class="text-xl supra row py-1">{{ .strings.extendExpiry }}</span>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-months">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-days">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-minutes">
|
||||
<option>0</option>
|
||||
</select>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-hours">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-minutes">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,6 +64,7 @@
|
||||
"extendExpiry": "Extend expiry",
|
||||
"setExpiry": "Set expiry",
|
||||
"removeExpiry": "Remove expiry",
|
||||
"enterExpiry": "Enter an expiry",
|
||||
"sendPWRManual": "User {n} has no method of contact, press copy to get a link to send to them.",
|
||||
"sendPWRSuccess": "Password reset link sent.",
|
||||
"sendPWRSuccessManual": "If the user hasn't received it, press copy to get a link to manually send to them.",
|
||||
@ -149,6 +150,7 @@
|
||||
"accountDisabled": "Account disabled: {user}",
|
||||
"accountReEnabled": "Account re-enabled: {user}",
|
||||
"accountExpired": "Account expired: {user}",
|
||||
"accountWillExpire": "Account will expire on {date}",
|
||||
"userDeleted": "User was deleted.",
|
||||
"userDisabled": "User was disabled",
|
||||
"inviteCreated": "Invite created: {invite}",
|
||||
@ -219,6 +221,7 @@
|
||||
"errorCheckUpdate": "Failed to check for update.",
|
||||
"errorNoReferralTemplate": "Profile doesn't contain referral template, add one in settings.",
|
||||
"errorLoadActivities": "Failed to load activities.",
|
||||
"errorInvalidDate": "Date is invalid.",
|
||||
"updateAvailable": "A new update is available, check settings.",
|
||||
"noUpdatesAvailable": "No new updates available."
|
||||
},
|
||||
|
11
models.go
11
models.go
@ -261,11 +261,12 @@ type customEmailDTO struct {
|
||||
}
|
||||
|
||||
type extendExpiryDTO struct {
|
||||
Users []string `json:"users"` // List of user IDs to apply to.
|
||||
Months int `json:"months" example:"1"` // Number of months to add.
|
||||
Days int `json:"days" example:"1"` // Number of days to add.
|
||||
Hours int `json:"hours" example:"2"` // Number of hours to add.
|
||||
Minutes int `json:"minutes" example:"3"` // Number of minutes to add.
|
||||
Users []string `json:"users"` // List of user IDs to apply to.
|
||||
Months int `json:"months" example:"1"` // Number of months to add.
|
||||
Days int `json:"days" example:"1"` // Number of days to add.
|
||||
Hours int `json:"hours" example:"2"` // Number of hours to add.
|
||||
Minutes int `json:"minutes" example:"3"` // Number of minutes to add.
|
||||
Timestamp int64 `json:"timestamp"` // Optional, exact time to expire at. Overrides other fields.
|
||||
}
|
||||
|
||||
type checkUpdateDTO struct {
|
||||
|
@ -771,6 +771,12 @@ export class accountsList {
|
||||
private _deleteReason = document.getElementById("textarea-delete-user") as HTMLTextAreaElement;
|
||||
private _expiryDropdown = document.getElementById("accounts-expiry-dropdown") as HTMLElement;
|
||||
private _extendExpiry = document.getElementById("accounts-extend-expiry") as HTMLSpanElement;
|
||||
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 _usingExtendExpiryTextInput = true;
|
||||
|
||||
private _extendExpiryDate = document.getElementById("extend-expiry-date") as HTMLElement;
|
||||
private _removeExpiry = document.getElementById("accounts-remove-expiry") as HTMLSpanElement;
|
||||
private _enableExpiryNotify = document.getElementById("expiry-extend-enable") as HTMLInputElement;
|
||||
private _enableExpiryReason = document.getElementById("textarea-extend-enable") as HTMLTextAreaElement;
|
||||
@ -1625,6 +1631,45 @@ export class accountsList {
|
||||
this.reload();
|
||||
}
|
||||
|
||||
_displayExpiryDate = () => {
|
||||
let date: Date;
|
||||
let invalid = false;
|
||||
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 = this._collectUsers().length == 1 ? this._collectUsers()[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) {
|
||||
submit.disabled = true;
|
||||
submitSpan.classList.add("opacity-60");
|
||||
this._extendExpiryDate.classList.add("unfocused");
|
||||
} else {
|
||||
submit.disabled = false;
|
||||
submitSpan.classList.remove("opacity-60");
|
||||
this._extendExpiryDate.textContent = window.lang.strings("accountWillExpire").replace("{date}", toDateString(date));
|
||||
this._extendExpiryDate.classList.remove("unfocused");
|
||||
}
|
||||
}
|
||||
|
||||
extendExpiry = (enableUser?: boolean) => {
|
||||
const list = this._collectUsers();
|
||||
let applyList: string[] = [];
|
||||
@ -1647,10 +1692,20 @@ export class accountsList {
|
||||
}
|
||||
document.getElementById("header-extend-expiry").textContent = header;
|
||||
const extend = () => {
|
||||
let send = { "users": applyList }
|
||||
for (let field of ["months", "days", "hours", "minutes"]) {
|
||||
send[field] = +(document.getElementById("extend-expiry-"+field) as HTMLSelectElement).value;
|
||||
let send = { "users": applyList, "timestamp": 0 }
|
||||
if (this._usingExtendExpiryTextInput) {
|
||||
let date = (Date as any).fromString(this._extendExpiryTextInput.value) as Date;
|
||||
send["timestamp"] = Math.floor(date.getTime() / 1000);
|
||||
if ("invalid" in (date as any)) {
|
||||
window.notifications.customError("extendExpiryError", window.lang.notif("errorInvalidDate"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (let field of ["months", "days", "hours", "minutes"]) {
|
||||
send[field] = +(document.getElementById("extend-expiry-"+field) as HTMLSelectElement).value;
|
||||
}
|
||||
}
|
||||
|
||||
_post("/users/extend", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status != 200 && req.status != 204) {
|
||||
@ -1663,8 +1718,7 @@ export class accountsList {
|
||||
}
|
||||
});
|
||||
};
|
||||
const form = document.getElementById("form-extend-expiry") as HTMLFormElement;
|
||||
form.onsubmit = (event: Event) => {
|
||||
this._extendExpiryForm.onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (enableUser) {
|
||||
this._enableDisableUsers(applyList, true, this._enableExpiryNotify.checked, this._enableExpiryNotify ? this._enableExpiryReason.value : null, (req: XMLHttpRequest) => {
|
||||
@ -1685,6 +1739,7 @@ export class accountsList {
|
||||
extend();
|
||||
}
|
||||
}
|
||||
this._extendExpiryTextInput.value = "";
|
||||
window.modals.extendExpiry.show();
|
||||
}
|
||||
|
||||
@ -1821,6 +1876,37 @@ export class accountsList {
|
||||
this._extendExpiry.onclick = () => { this.extendExpiry(); };
|
||||
this._removeExpiry.onclick = () => { this.removeExpiry(); };
|
||||
this._expiryDropdown.classList.add("unfocused");
|
||||
this._extendExpiryDate.classList.add("unfocused");
|
||||
|
||||
this._extendExpiryTextInput.onkeyup = () => {
|
||||
this._extendExpiryTextInput.parentElement.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._extendExpiryFieldInputs.classList.add("opacity-60");
|
||||
this._usingExtendExpiryTextInput = true;
|
||||
this._displayExpiryDate();
|
||||
};
|
||||
|
||||
this._extendExpiryFieldInputs.onclick = () => {
|
||||
this._extendExpiryFieldInputs.classList.remove("opacity-60");
|
||||
this._extendExpiryTextInput.parentElement.parentElement.classList.add("opacity-60");
|
||||
this._usingExtendExpiryTextInput = false;
|
||||
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._usingExtendExpiryTextInput = false;
|
||||
this._displayExpiryDate();
|
||||
};
|
||||
}
|
||||
|
||||
this._disableEnable.onclick = this.enableDisableUsers;
|
||||
this._disableEnable.parentElement.classList.add("unfocused");
|
||||
|
Loading…
Reference in New Issue
Block a user