mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-28 20:10:11 +00:00
Compare commits
7 Commits
729552a827
...
311ecb7030
Author | SHA1 | Date | |
---|---|---|---|
311ecb7030 | |||
0a82f889f3 | |||
00e6da520d | |||
0b830e9b5e | |||
468b2f3284 | |||
db21131185 | |||
7d9555fdf7 |
@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
REFERRAL_EXPIRY_DAYS = 365
|
REFERRAL_EXPIRY_DAYS = 90
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Summary Returns the logged-in user's Jellyfin ID & Username, and other details.
|
// @Summary Returns the logged-in user's Jellyfin ID & Username, and other details.
|
||||||
@ -81,6 +81,25 @@ func (app *appContext) MyDetails(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.config.Section("user_page").Key("referrals").MustBool(false) {
|
||||||
|
// 1. Look for existing template bound to this Jellyfin ID
|
||||||
|
// If one exists, that means its just for us and so we
|
||||||
|
// can use it directly.
|
||||||
|
inv := Invite{}
|
||||||
|
err := app.storage.db.FindOne(&inv, badgerhold.Where("ReferrerJellyfinID").Eq(resp.Id))
|
||||||
|
if err == nil {
|
||||||
|
resp.HasReferrals = true
|
||||||
|
} else {
|
||||||
|
// 2. Look for a template matching the key found in the user storage
|
||||||
|
// Since this key is shared between users in a profile, we make a copy.
|
||||||
|
user, ok := app.storage.GetEmailsKey(gc.GetString("jfId"))
|
||||||
|
err = app.storage.db.Get(user.ReferralTemplateKey, &inv)
|
||||||
|
if ok && err == nil {
|
||||||
|
resp.HasReferrals = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,6 +704,6 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
|
|||||||
Code: inv.Code,
|
Code: inv.Code,
|
||||||
RemainingUses: inv.RemainingUses,
|
RemainingUses: inv.RemainingUses,
|
||||||
NoLimit: inv.NoLimit,
|
NoLimit: inv.NoLimit,
|
||||||
Expiry: inv.ValidTill,
|
Expiry: inv.ValidTill.Unix(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
38
api-users.go
38
api-users.go
@ -304,6 +304,12 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
id := user.ID
|
id := user.ID
|
||||||
|
|
||||||
|
emailStore := EmailAddress{
|
||||||
|
Addr: req.Email,
|
||||||
|
Contact: (req.Email != ""),
|
||||||
|
}
|
||||||
|
|
||||||
var profile Profile
|
var profile Profile
|
||||||
if invite.Profile != "" {
|
if invite.Profile != "" {
|
||||||
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
||||||
@ -325,10 +331,15 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
if !((status == 200 || status == 204) && err == nil) {
|
if !((status == 200 || status == 204) && err == nil) {
|
||||||
app.err.Printf("%s: Failed to set configuration template (%d): %v", req.Code, status, err)
|
app.err.Printf("%s: Failed to set configuration template (%d): %v", req.Code, status, err)
|
||||||
}
|
}
|
||||||
|
if app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false) && profile.ReferralTemplateKey != "" {
|
||||||
|
emailStore.ReferralTemplateKey = profile.ReferralTemplateKey
|
||||||
|
// Store here, just incase email are disabled (whether this is even possible, i don't know)
|
||||||
|
app.storage.SetEmailsKey(id, emailStore)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
// if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
||||||
if req.Email != "" {
|
if req.Email != "" {
|
||||||
app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true})
|
app.storage.SetEmailsKey(id, emailStore)
|
||||||
}
|
}
|
||||||
expiry := time.Time{}
|
expiry := time.Time{}
|
||||||
if invite.UserExpiry {
|
if invite.UserExpiry {
|
||||||
@ -634,6 +645,7 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
|||||||
|
|
||||||
// @Summary Enable referrals for the given user(s) based on the rules set in the given invite code, or profile.
|
// @Summary Enable referrals for the given user(s) based on the rules set in the given invite code, or profile.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
|
// @Param EnableDisableReferralDTO body EnableDisableReferralDTO true "List of users"
|
||||||
// @Param mode path string true "mode of template sourcing from 'invite' or 'profile'."
|
// @Param mode path string true "mode of template sourcing from 'invite' or 'profile'."
|
||||||
// @Param source path string true "invite code or profile name, depending on what mode is."
|
// @Param source path string true "invite code or profile name, depending on what mode is."
|
||||||
// @Success 200 {object} boolResponse
|
// @Success 200 {object} boolResponse
|
||||||
@ -689,6 +701,30 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Disable referrals for the given user(s).
|
||||||
|
// @Produce json
|
||||||
|
// @Param EnableDisableReferralDTO body EnableDisableReferralDTO true "List of users"
|
||||||
|
// @Success 200 {object} boolResponse
|
||||||
|
// @Router /users/referral [delete]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Users
|
||||||
|
func (app *appContext) DisableReferralForUsers(gc *gin.Context) {
|
||||||
|
var req EnableDisableReferralDTO
|
||||||
|
gc.BindJSON(&req)
|
||||||
|
for _, u := range req.Users {
|
||||||
|
// 1. Delete directly bound template
|
||||||
|
app.storage.db.DeleteMatching(Invite{}, badgerhold.Where("ReferrerJellyfinID").Eq(u))
|
||||||
|
// 2. Check for and delete profile-attached template
|
||||||
|
user, ok := app.storage.GetEmailsKey(u)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
user.ReferralTemplateKey = ""
|
||||||
|
app.storage.SetEmailsKey(u, user)
|
||||||
|
}
|
||||||
|
respondBool(200, true, gc)
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Send an announcement via email to a given list of users.
|
// @Summary Send an announcement via email to a given list of users.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param announcementDTO body announcementDTO true "Announcement request object"
|
// @Param announcementDTO body announcementDTO true "Announcement request object"
|
||||||
|
@ -10,7 +10,7 @@ func (app *appContext) clearEmails() {
|
|||||||
emails := app.storage.GetEmails()
|
emails := app.storage.GetEmails()
|
||||||
for _, email := range emails {
|
for _, email := range emails {
|
||||||
_, status, err := app.jf.UserByID(email.JellyfinID, false)
|
_, status, err := app.jf.UserByID(email.JellyfinID, false)
|
||||||
if status == 200 && err != nil {
|
if status == 200 && err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app.storage.DeleteEmailsKey(email.JellyfinID)
|
app.storage.DeleteEmailsKey(email.JellyfinID)
|
||||||
@ -23,7 +23,7 @@ func (app *appContext) clearDiscord() {
|
|||||||
discordUsers := app.storage.GetDiscord()
|
discordUsers := app.storage.GetDiscord()
|
||||||
for _, discordUser := range discordUsers {
|
for _, discordUser := range discordUsers {
|
||||||
_, status, err := app.jf.UserByID(discordUser.JellyfinID, false)
|
_, status, err := app.jf.UserByID(discordUser.JellyfinID, false)
|
||||||
if status == 200 && err != nil {
|
if status == 200 && err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app.storage.DeleteDiscordKey(discordUser.JellyfinID)
|
app.storage.DeleteDiscordKey(discordUser.JellyfinID)
|
||||||
@ -36,7 +36,7 @@ func (app *appContext) clearMatrix() {
|
|||||||
matrixUsers := app.storage.GetMatrix()
|
matrixUsers := app.storage.GetMatrix()
|
||||||
for _, matrixUser := range matrixUsers {
|
for _, matrixUser := range matrixUsers {
|
||||||
_, status, err := app.jf.UserByID(matrixUser.JellyfinID, false)
|
_, status, err := app.jf.UserByID(matrixUser.JellyfinID, false)
|
||||||
if status == 200 && err != nil {
|
if status == 200 && err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app.storage.DeleteMatrixKey(matrixUser.JellyfinID)
|
app.storage.DeleteMatrixKey(matrixUser.JellyfinID)
|
||||||
@ -49,7 +49,7 @@ func (app *appContext) clearTelegram() {
|
|||||||
telegramUsers := app.storage.GetTelegram()
|
telegramUsers := app.storage.GetTelegram()
|
||||||
for _, telegramUser := range telegramUsers {
|
for _, telegramUser := range telegramUsers {
|
||||||
_, status, err := app.jf.UserByID(telegramUser.JellyfinID, false)
|
_, status, err := app.jf.UserByID(telegramUser.JellyfinID, false)
|
||||||
if status == 200 && err != nil {
|
if status == 200 && err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app.storage.DeleteTelegramKey(telegramUser.JellyfinID)
|
app.storage.DeleteTelegramKey(telegramUser.JellyfinID)
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
window.matrixRequired = {{ .matrixRequired }};
|
window.matrixRequired = {{ .matrixRequired }};
|
||||||
window.matrixUserID = "{{ .matrixUser }}";
|
window.matrixUserID = "{{ .matrixUser }}";
|
||||||
window.validationStrings = JSON.parse({{ .validationStrings }});
|
window.validationStrings = JSON.parse({{ .validationStrings }});
|
||||||
|
window.referralsEnabled = {{ .referralsEnabled }};
|
||||||
</script>
|
</script>
|
||||||
{{ template "header.html" . }}
|
{{ template "header.html" . }}
|
||||||
<title>{{ .strings.myAccount }}</title>
|
<title>{{ .strings.myAccount }}</title>
|
||||||
@ -150,6 +151,20 @@
|
|||||||
<div class="user-expiry-countdown"></div>
|
<div class="user-expiry-countdown"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ if .referralsEnabled }}
|
||||||
|
<div>
|
||||||
|
<div class="card @low dark:~d_neutral unfocused" id="card-referrals">
|
||||||
|
<span class="heading mb-2">{{ .strings.referrals }}</span>
|
||||||
|
<aside class="aside ~neutral my-4 col">{{ .strings.referralsDescription }}</aside>
|
||||||
|
<div class="row flex-expand">
|
||||||
|
<div class="user-referrals-info"></div>
|
||||||
|
<div class="grid my-2">
|
||||||
|
<button type="button" class="user-referrals-button button ~info dark:~d_info @low" title="Copy">{{ .strings.copyReferral }}<i class="ri-file-copy-line ml-2"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{ .urlBase }}/js/user.js" type="module"></script>
|
<script src="{{ .urlBase }}/js/user.js" type="module"></script>
|
||||||
|
@ -79,7 +79,6 @@
|
|||||||
"inviteUsersCreated": "Oprettet brugere",
|
"inviteUsersCreated": "Oprettet brugere",
|
||||||
"inviteNoProfile": "Ingen Profil",
|
"inviteNoProfile": "Ingen Profil",
|
||||||
"inviteDateCreated": "Oprettet",
|
"inviteDateCreated": "Oprettet",
|
||||||
"inviteRemainingUses": "Resterende anvendelser",
|
|
||||||
"inviteNoInvites": "Ingen",
|
"inviteNoInvites": "Ingen",
|
||||||
"inviteExpiresInTime": "Udløber om {n}",
|
"inviteExpiresInTime": "Udløber om {n}",
|
||||||
"notifyEvent": "Meddel den:",
|
"notifyEvent": "Meddel den:",
|
||||||
|
@ -53,7 +53,6 @@
|
|||||||
"inviteUsersCreated": "Erstellte Benutzer",
|
"inviteUsersCreated": "Erstellte Benutzer",
|
||||||
"inviteNoProfile": "Kein Profil",
|
"inviteNoProfile": "Kein Profil",
|
||||||
"inviteDateCreated": "Erstellt",
|
"inviteDateCreated": "Erstellt",
|
||||||
"inviteRemainingUses": "Verbleibende Verwendungen",
|
|
||||||
"inviteNoInvites": "Keine",
|
"inviteNoInvites": "Keine",
|
||||||
"inviteExpiresInTime": "Läuft in {n} ab",
|
"inviteExpiresInTime": "Läuft in {n} ab",
|
||||||
"notifyEvent": "Benachrichtigen bei:",
|
"notifyEvent": "Benachrichtigen bei:",
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
"inviteUsersCreated": "Δημιουργηθέντες χρήστες",
|
"inviteUsersCreated": "Δημιουργηθέντες χρήστες",
|
||||||
"inviteNoProfile": "Κανένα Προφίλ",
|
"inviteNoProfile": "Κανένα Προφίλ",
|
||||||
"inviteDateCreated": "Δημιουργηθέντα",
|
"inviteDateCreated": "Δημιουργηθέντα",
|
||||||
"inviteRemainingUses": "Εναπομείναντες χρήσεις",
|
|
||||||
"inviteNoInvites": "Καμία",
|
"inviteNoInvites": "Καμία",
|
||||||
"inviteExpiresInTime": "Λήγει σε {n}",
|
"inviteExpiresInTime": "Λήγει σε {n}",
|
||||||
"notifyEvent": "Ενημέρωση όταν:",
|
"notifyEvent": "Ενημέρωση όταν:",
|
||||||
|
@ -124,7 +124,6 @@
|
|||||||
"addProfileStoreHomescreenLayout": "Store homescreen layout",
|
"addProfileStoreHomescreenLayout": "Store homescreen layout",
|
||||||
"inviteNoUsersCreated": "None yet!",
|
"inviteNoUsersCreated": "None yet!",
|
||||||
"inviteUsersCreated": "Created users",
|
"inviteUsersCreated": "Created users",
|
||||||
"inviteRemainingUses": "Remaining uses",
|
|
||||||
"inviteNoInvites": "None",
|
"inviteNoInvites": "None",
|
||||||
"inviteExpiresInTime": "Expires in {n}",
|
"inviteExpiresInTime": "Expires in {n}",
|
||||||
"notifyEvent": "Notify on:",
|
"notifyEvent": "Notify on:",
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
"modifySettings": "Modify Settings",
|
"modifySettings": "Modify Settings",
|
||||||
"modifySettingsDescription": "Apply settings from an existing profile, or source them directly from a user.",
|
"modifySettingsDescription": "Apply settings from an existing profile, or source them directly from a user.",
|
||||||
"enableReferrals": "Enable Referrals",
|
"enableReferrals": "Enable Referrals",
|
||||||
|
"disableReferrals": "Disable Referrals",
|
||||||
"enableReferralsDescription": "Give users a personal referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an existing invite.",
|
"enableReferralsDescription": "Give users a personal referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an existing invite.",
|
||||||
"enableReferralsProfileDescription": "Give users created with this profile a personal referral link similiar to an invite, to send to friends/family. Create an invite with the desired settings, then select it here. Each referral will then be based on this invite. You can delete the invite once complete.",
|
"enableReferralsProfileDescription": "Give users created with this profile a personal referral link similiar to an invite, to send to friends/family. Create an invite with the desired settings, then select it here. Each referral will then be based on this invite. You can delete the invite once complete.",
|
||||||
"applyHomescreenLayout": "Apply homescreen layout",
|
"applyHomescreenLayout": "Apply homescreen layout",
|
||||||
@ -94,7 +95,6 @@
|
|||||||
"inviteUsersCreated": "Created users",
|
"inviteUsersCreated": "Created users",
|
||||||
"inviteNoProfile": "No Profile",
|
"inviteNoProfile": "No Profile",
|
||||||
"inviteDateCreated": "Created",
|
"inviteDateCreated": "Created",
|
||||||
"inviteRemainingUses": "Remaining uses",
|
|
||||||
"inviteNoInvites": "None",
|
"inviteNoInvites": "None",
|
||||||
"inviteExpiresInTime": "Expires in {n}",
|
"inviteExpiresInTime": "Expires in {n}",
|
||||||
"notifyEvent": "Notify on:",
|
"notifyEvent": "Notify on:",
|
||||||
@ -157,6 +157,7 @@
|
|||||||
"errorSendWelcomeEmail": "Failed to send welcome message (check console/logs)",
|
"errorSendWelcomeEmail": "Failed to send welcome message (check console/logs)",
|
||||||
"errorApplyUpdate": "Failed to apply update, try manually.",
|
"errorApplyUpdate": "Failed to apply update, try manually.",
|
||||||
"errorCheckUpdate": "Failed to check for update.",
|
"errorCheckUpdate": "Failed to check for update.",
|
||||||
|
"errorNoReferralTemplate": "Profile doesn't contain referral template, add one in settings.",
|
||||||
"updateAvailable": "A new update is available, check settings.",
|
"updateAvailable": "A new update is available, check settings.",
|
||||||
"noUpdatesAvailable": "No new updates available."
|
"noUpdatesAvailable": "No new updates available."
|
||||||
},
|
},
|
||||||
@ -222,4 +223,4 @@
|
|||||||
"plural": "Extended expiry for {n} users."
|
"plural": "Extended expiry for {n} users."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -75,7 +75,6 @@
|
|||||||
"inviteUsersCreated": "Usuarios creados",
|
"inviteUsersCreated": "Usuarios creados",
|
||||||
"inviteNoProfile": "Sin perfil",
|
"inviteNoProfile": "Sin perfil",
|
||||||
"inviteDateCreated": "Creado",
|
"inviteDateCreated": "Creado",
|
||||||
"inviteRemainingUses": "Usos restantes",
|
|
||||||
"inviteNoInvites": "Ninguno",
|
"inviteNoInvites": "Ninguno",
|
||||||
"inviteExpiresInTime": "Caduca en {n}",
|
"inviteExpiresInTime": "Caduca en {n}",
|
||||||
"notifyEvent": "Notificar en:",
|
"notifyEvent": "Notificar en:",
|
||||||
|
@ -55,7 +55,6 @@
|
|||||||
"inviteUsersCreated": "Utilisateurs créés",
|
"inviteUsersCreated": "Utilisateurs créés",
|
||||||
"inviteNoProfile": "Aucun profil",
|
"inviteNoProfile": "Aucun profil",
|
||||||
"inviteDateCreated": "Créer",
|
"inviteDateCreated": "Créer",
|
||||||
"inviteRemainingUses": "Utilisations restantes",
|
|
||||||
"inviteNoInvites": "Aucune",
|
"inviteNoInvites": "Aucune",
|
||||||
"inviteExpiresInTime": "Expires dans {n}",
|
"inviteExpiresInTime": "Expires dans {n}",
|
||||||
"notifyEvent": "Notifier sur :",
|
"notifyEvent": "Notifier sur :",
|
||||||
|
@ -87,7 +87,6 @@
|
|||||||
"inviteUsersCreated": "",
|
"inviteUsersCreated": "",
|
||||||
"inviteNoProfile": "",
|
"inviteNoProfile": "",
|
||||||
"inviteDateCreated": "",
|
"inviteDateCreated": "",
|
||||||
"inviteRemainingUses": "",
|
|
||||||
"inviteNoInvites": "",
|
"inviteNoInvites": "",
|
||||||
"inviteExpiresInTime": "",
|
"inviteExpiresInTime": "",
|
||||||
"notifyEvent": "",
|
"notifyEvent": "",
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
"inviteUsersCreated": "Pengguna yang telah dibuat",
|
"inviteUsersCreated": "Pengguna yang telah dibuat",
|
||||||
"inviteNoProfile": "Tidak ada profil",
|
"inviteNoProfile": "Tidak ada profil",
|
||||||
"inviteDateCreated": "Dibuat",
|
"inviteDateCreated": "Dibuat",
|
||||||
"inviteRemainingUses": "Penggunaan yang tersisa",
|
|
||||||
"inviteNoInvites": "Tidak ada",
|
"inviteNoInvites": "Tidak ada",
|
||||||
"inviteExpiresInTime": "Kadaluarsa dalam {n}",
|
"inviteExpiresInTime": "Kadaluarsa dalam {n}",
|
||||||
"notifyEvent": "Beritahu pada:",
|
"notifyEvent": "Beritahu pada:",
|
||||||
|
@ -53,7 +53,6 @@
|
|||||||
"inviteUsersCreated": "Aangemaakte gebruikers",
|
"inviteUsersCreated": "Aangemaakte gebruikers",
|
||||||
"inviteNoProfile": "Geen profiel",
|
"inviteNoProfile": "Geen profiel",
|
||||||
"inviteDateCreated": "Aangemaakt",
|
"inviteDateCreated": "Aangemaakt",
|
||||||
"inviteRemainingUses": "Resterend aantal keer te gebruiken",
|
|
||||||
"inviteNoInvites": "Geen",
|
"inviteNoInvites": "Geen",
|
||||||
"inviteExpiresInTime": "Verloopt over {n}",
|
"inviteExpiresInTime": "Verloopt over {n}",
|
||||||
"notifyEvent": "Meldingen:",
|
"notifyEvent": "Meldingen:",
|
||||||
|
@ -87,7 +87,6 @@
|
|||||||
"inviteUsersCreated": "",
|
"inviteUsersCreated": "",
|
||||||
"inviteNoProfile": "",
|
"inviteNoProfile": "",
|
||||||
"inviteDateCreated": "Utworzone",
|
"inviteDateCreated": "Utworzone",
|
||||||
"inviteRemainingUses": "",
|
|
||||||
"inviteNoInvites": "",
|
"inviteNoInvites": "",
|
||||||
"inviteExpiresInTime": "",
|
"inviteExpiresInTime": "",
|
||||||
"notifyEvent": "",
|
"notifyEvent": "",
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
"inviteUsersCreated": "Usuários criado",
|
"inviteUsersCreated": "Usuários criado",
|
||||||
"inviteNoProfile": "Sem Perfil",
|
"inviteNoProfile": "Sem Perfil",
|
||||||
"inviteDateCreated": "Criado",
|
"inviteDateCreated": "Criado",
|
||||||
"inviteRemainingUses": "Uso restantes",
|
|
||||||
"inviteNoInvites": "Nenhum",
|
"inviteNoInvites": "Nenhum",
|
||||||
"inviteExpiresInTime": "Expira em {n}",
|
"inviteExpiresInTime": "Expira em {n}",
|
||||||
"notifyEvent": "Notificar em:",
|
"notifyEvent": "Notificar em:",
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
"inviteUsersCreated": "Skapade användare",
|
"inviteUsersCreated": "Skapade användare",
|
||||||
"inviteNoProfile": "Ingen profil",
|
"inviteNoProfile": "Ingen profil",
|
||||||
"inviteDateCreated": "Skapad",
|
"inviteDateCreated": "Skapad",
|
||||||
"inviteRemainingUses": "Återstående användningar",
|
|
||||||
"inviteNoInvites": "Ingen",
|
"inviteNoInvites": "Ingen",
|
||||||
"inviteExpiresInTime": "Går ut om {n}",
|
"inviteExpiresInTime": "Går ut om {n}",
|
||||||
"notifyEvent": "Meddela den:",
|
"notifyEvent": "Meddela den:",
|
||||||
|
@ -86,7 +86,6 @@
|
|||||||
"inviteUsersCreated": "Người dùng đã tạo",
|
"inviteUsersCreated": "Người dùng đã tạo",
|
||||||
"inviteNoProfile": "Không có Tài khoản mẫu",
|
"inviteNoProfile": "Không có Tài khoản mẫu",
|
||||||
"inviteDateCreated": "Tạo",
|
"inviteDateCreated": "Tạo",
|
||||||
"inviteRemainingUses": "Số lần sử dụng còn lại",
|
|
||||||
"inviteNoInvites": "Không có",
|
"inviteNoInvites": "Không có",
|
||||||
"inviteExpiresInTime": "Hết hạn trong {n}",
|
"inviteExpiresInTime": "Hết hạn trong {n}",
|
||||||
"notifyEvent": "Thông báo khi:",
|
"notifyEvent": "Thông báo khi:",
|
||||||
|
@ -80,7 +80,6 @@
|
|||||||
"inviteUsersCreated": "已创建的用户",
|
"inviteUsersCreated": "已创建的用户",
|
||||||
"inviteNoProfile": "没有个人资料",
|
"inviteNoProfile": "没有个人资料",
|
||||||
"inviteDateCreated": "已创建",
|
"inviteDateCreated": "已创建",
|
||||||
"inviteRemainingUses": "剩余使用次数",
|
|
||||||
"inviteNoInvites": "无",
|
"inviteNoInvites": "无",
|
||||||
"inviteExpiresInTime": "在 {n} 到期",
|
"inviteExpiresInTime": "在 {n} 到期",
|
||||||
"notifyEvent": "通知:",
|
"notifyEvent": "通知:",
|
||||||
|
@ -87,7 +87,6 @@
|
|||||||
"inviteUsersCreated": "創建的帳戶",
|
"inviteUsersCreated": "創建的帳戶",
|
||||||
"inviteNoProfile": "無資料",
|
"inviteNoProfile": "無資料",
|
||||||
"inviteDateCreated": "已創建",
|
"inviteDateCreated": "已創建",
|
||||||
"inviteRemainingUses": "剩餘使用次數",
|
|
||||||
"inviteNoInvites": "無",
|
"inviteNoInvites": "無",
|
||||||
"inviteExpiresInTime": "在 {n} 到期",
|
"inviteExpiresInTime": "在 {n} 到期",
|
||||||
"notifyEvent": "通知:",
|
"notifyEvent": "通知:",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Udløb",
|
"expiry": "Udløb",
|
||||||
"add": "Tilføj",
|
"add": "Tilføj",
|
||||||
"edit": "Rediger",
|
"edit": "Rediger",
|
||||||
"delete": "Slet"
|
"delete": "Slet",
|
||||||
|
"inviteRemainingUses": "Resterende anvendelser"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Brugernavnet og/eller adgangskoden blev efterladt tomme.",
|
"errorLoginBlank": "Brugernavnet og/eller adgangskoden blev efterladt tomme.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Ablaufdatum",
|
"expiry": "Ablaufdatum",
|
||||||
"add": "Hinzufügen",
|
"add": "Hinzufügen",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"delete": "Löschen"
|
"delete": "Löschen",
|
||||||
|
"inviteRemainingUses": "Verbleibende Verwendungen"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Der Benutzername und/oder das Passwort wurden nicht ausgefüllt.",
|
"errorLoginBlank": "Der Benutzername und/oder das Passwort wurden nicht ausgefüllt.",
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
"disable": "Απενεργοποίηση",
|
"disable": "Απενεργοποίηση",
|
||||||
"expiry": "Λήξη",
|
"expiry": "Λήξη",
|
||||||
"edit": "Επεξεργασία",
|
"edit": "Επεξεργασία",
|
||||||
"delete": "Διαγραφή"
|
"delete": "Διαγραφή",
|
||||||
|
"inviteRemainingUses": "Εναπομείναντες χρήσεις"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Το όνομα χρήστη και/ή ο κωδικός ήταν κενά.",
|
"errorLoginBlank": "Το όνομα χρήστη και/ή ο κωδικός ήταν κενά.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Expiry",
|
"expiry": "Expiry",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Delete"
|
"delete": "Delete",
|
||||||
|
"inviteRemainingUses": "Remaining uses"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "The username and/or password was left blank.",
|
"errorLoginBlank": "The username and/or password was left blank.",
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"myAccount": "My Account",
|
"myAccount": "My Account",
|
||||||
"referrals": "Referrals"
|
"referrals": "Referrals",
|
||||||
|
"inviteRemainingUses": "Remaining uses"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "The username and/or password were left blank.",
|
"errorLoginBlank": "The username and/or password were left blank.",
|
||||||
@ -63,4 +64,4 @@
|
|||||||
"plural": "{n} Days"
|
"plural": "{n} Days"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Expiración",
|
"expiry": "Expiración",
|
||||||
"add": "Agregar",
|
"add": "Agregar",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"delete": "Eliminar"
|
"delete": "Eliminar",
|
||||||
|
"inviteRemainingUses": "Usos restantes"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "El nombre de usuario y/o la contraseña se dejaron en blanco.",
|
"errorLoginBlank": "El nombre de usuario y/o la contraseña se dejaron en blanco.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Expiration",
|
"expiry": "Expiration",
|
||||||
"add": "Ajouter",
|
"add": "Ajouter",
|
||||||
"edit": "Éditer",
|
"edit": "Éditer",
|
||||||
"delete": "Effacer"
|
"delete": "Effacer",
|
||||||
|
"inviteRemainingUses": "Utilisations restantes"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Le nom d'utilisateur et/ou le mot de passe sont vides.",
|
"errorLoginBlank": "Le nom d'utilisateur et/ou le mot de passe sont vides.",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"login": "Masuk",
|
"login": "Masuk",
|
||||||
"logout": "Keluar",
|
"logout": "Keluar",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Hapus"
|
"delete": "Hapus",
|
||||||
|
"inviteRemainingUses": "Penggunaan yang tersisa"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Nama pengguna dan / atau sandi kosong.",
|
"errorLoginBlank": "Nama pengguna dan / atau sandi kosong.",
|
||||||
|
@ -28,4 +28,4 @@
|
|||||||
},
|
},
|
||||||
"notifications": {},
|
"notifications": {},
|
||||||
"quantityStrings": {}
|
"quantityStrings": {}
|
||||||
}
|
}
|
8
lang/common/nds.json
Normal file
8
lang/common/nds.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"name": "Nedderdütsch (NDS)"
|
||||||
|
},
|
||||||
|
"strings": {},
|
||||||
|
"notifications": {},
|
||||||
|
"quantityStrings": {}
|
||||||
|
}
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Verloop",
|
"expiry": "Verloop",
|
||||||
"add": "Voeg toe",
|
"add": "Voeg toe",
|
||||||
"edit": "Bewerken",
|
"edit": "Bewerken",
|
||||||
"delete": "Verwijderen"
|
"delete": "Verwijderen",
|
||||||
|
"inviteRemainingUses": "Resterend aantal keer te gebruiken"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "De gebruikersnaam en/of wachtwoord is leeg.",
|
"errorLoginBlank": "De gebruikersnaam en/of wachtwoord is leeg.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "Expira",
|
"expiry": "Expira",
|
||||||
"add": "Adicionar",
|
"add": "Adicionar",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"delete": "Deletar"
|
"delete": "Deletar",
|
||||||
|
"inviteRemainingUses": "Uso restantes"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "O nome de usuário e/ou senha foram deixados em branco.",
|
"errorLoginBlank": "O nome de usuário e/ou senha foram deixados em branco.",
|
||||||
|
@ -28,4 +28,4 @@
|
|||||||
},
|
},
|
||||||
"notifications": {},
|
"notifications": {},
|
||||||
"quantityStrings": {}
|
"quantityStrings": {}
|
||||||
}
|
}
|
@ -22,7 +22,8 @@
|
|||||||
"disabled": "Inaktiverad",
|
"disabled": "Inaktiverad",
|
||||||
"expiry": "Löper ut",
|
"expiry": "Löper ut",
|
||||||
"edit": "Redigera",
|
"edit": "Redigera",
|
||||||
"delete": "Radera"
|
"delete": "Radera",
|
||||||
|
"inviteRemainingUses": "Återstående användningar"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "Användarnamnet och/eller lösenordet lämnades tomt.",
|
"errorLoginBlank": "Användarnamnet och/eller lösenordet lämnades tomt.",
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"expiry": "Hết hạn",
|
"expiry": "Hết hạn",
|
||||||
"add": "Thêm",
|
"add": "Thêm",
|
||||||
"edit": "Chỉnh sửa",
|
"edit": "Chỉnh sửa",
|
||||||
"delete": "Xóa"
|
"delete": "Xóa",
|
||||||
|
"inviteRemainingUses": "Số lần sử dụng còn lại"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorConnection": "Không thể kết nối với jfa-go.",
|
"errorConnection": "Không thể kết nối với jfa-go.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "到期",
|
"expiry": "到期",
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"delete": "删除"
|
"delete": "删除",
|
||||||
|
"inviteRemainingUses": "剩余使用次数"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "用户名/密码留空。",
|
"errorLoginBlank": "用户名/密码留空。",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"expiry": "到期",
|
"expiry": "到期",
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
"edit": "編輯",
|
"edit": "編輯",
|
||||||
"delete": "刪除"
|
"delete": "刪除",
|
||||||
|
"inviteRemainingUses": "剩餘使用次數"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "帳戶名稱和/或密碼留空。",
|
"errorLoginBlank": "帳戶名稱和/或密碼留空。",
|
||||||
|
@ -49,4 +49,4 @@
|
|||||||
"clickBelow": "",
|
"clickBelow": "",
|
||||||
"confirmEmail": ""
|
"confirmEmail": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,9 @@
|
|||||||
"resetPasswordThroughLink": "To reset your password, enter your username, email address or a linked contact method username, and submit. A link will be sent to reset your password.",
|
"resetPasswordThroughLink": "To reset your password, enter your username, email address or a linked contact method username, and submit. A link will be sent to reset your password.",
|
||||||
"resetSent": "Reset Sent.",
|
"resetSent": "Reset Sent.",
|
||||||
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.",
|
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.",
|
||||||
"changePassword": "Change Password"
|
"changePassword": "Change Password",
|
||||||
|
"referralsDescription": "Invite friends & family to Jellyfin with this link. Come back here for a new one if it expires.",
|
||||||
|
"copyReferral": "Copy Link"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorUserExists": "User already exists.",
|
"errorUserExists": "User already exists.",
|
||||||
@ -76,4 +78,4 @@
|
|||||||
"plural": "Must have at least {n} special characters"
|
"plural": "Must have at least {n} special characters"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -48,4 +48,4 @@
|
|||||||
"plural": "Deve avere almeno {n} caratteri speciali"
|
"plural": "Deve avere almeno {n} caratteri speciali"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -57,4 +57,4 @@
|
|||||||
"plural": "Potrebnih je vsaj {n} posebnih znakov"
|
"plural": "Potrebnih je vsaj {n} posebnih znakov"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"changeYourPassword": "Spremenite svoje geslo po prijavi.",
|
"changeYourPassword": "Spremenite svoje geslo po prijavi.",
|
||||||
"enterYourPassword": "Vnesite svoje novo geslo spodaj."
|
"enterYourPassword": "Vnesite svoje novo geslo spodaj."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -157,4 +157,4 @@
|
|||||||
"emailMessage": "Email Message",
|
"emailMessage": "Email Message",
|
||||||
"emailMessageNotice": "Displays at the bottom of emails."
|
"emailMessageNotice": "Displays at the bottom of emails."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -149,4 +149,4 @@
|
|||||||
"emailMessage": "",
|
"emailMessage": "",
|
||||||
"emailMessageNotice": ""
|
"emailMessageNotice": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -146,4 +146,4 @@
|
|||||||
"emailMessage": "",
|
"emailMessage": "",
|
||||||
"emailMessageNotice": ""
|
"emailMessageNotice": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"languageSet": "",
|
"languageSet": "",
|
||||||
"discordDMs": ""
|
"discordDMs": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"languageSet": "",
|
"languageSet": "",
|
||||||
"discordDMs": ""
|
"discordDMs": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"languageSet": "Jezik nastavljen na {language}.",
|
"languageSet": "Jezik nastavljen na {language}.",
|
||||||
"discordDMs": "Prosimo preverite svoja zasebna sporočila za odziv."
|
"discordDMs": "Prosimo preverite svoja zasebna sporočila za odziv."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -390,6 +390,7 @@ type MyDetailsDTO struct {
|
|||||||
Discord *MyDetailsContactMethodsDTO `json:"discord,omitempty"`
|
Discord *MyDetailsContactMethodsDTO `json:"discord,omitempty"`
|
||||||
Telegram *MyDetailsContactMethodsDTO `json:"telegram,omitempty"`
|
Telegram *MyDetailsContactMethodsDTO `json:"telegram,omitempty"`
|
||||||
Matrix *MyDetailsContactMethodsDTO `json:"matrix,omitempty"`
|
Matrix *MyDetailsContactMethodsDTO `json:"matrix,omitempty"`
|
||||||
|
HasReferrals bool `json:"has_referrals,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyDetailsContactMethodsDTO struct {
|
type MyDetailsContactMethodsDTO struct {
|
||||||
@ -418,10 +419,10 @@ type ChangeMyPasswordDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetMyReferralRespDTO struct {
|
type GetMyReferralRespDTO struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
RemainingUses int `json:"remaining-uses"`
|
RemainingUses int `json:"remaining_uses"`
|
||||||
NoLimit bool `json:"no-limit"`
|
NoLimit bool `json:"no_limit"`
|
||||||
Expiry time.Time `json:"expiry"` // Come back after this time to get a new referral
|
Expiry int64 `json:"expiry"` // Come back after this time to get a new referral
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnableDisableReferralDTO struct {
|
type EnableDisableReferralDTO struct {
|
||||||
|
@ -228,6 +228,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
api.POST(p+"/matrix/login", app.MatrixLogin)
|
api.POST(p+"/matrix/login", app.MatrixLogin)
|
||||||
if app.config.Section("user_page").Key("referrals").MustBool(false) {
|
if app.config.Section("user_page").Key("referrals").MustBool(false) {
|
||||||
api.POST(p+"/users/referral/:mode/:source", app.EnableReferralForUsers)
|
api.POST(p+"/users/referral/:mode/:source", app.EnableReferralForUsers)
|
||||||
|
api.DELETE(p+"/users/referral", app.DisableReferralForUsers)
|
||||||
api.POST(p+"/profiles/referral/:profile/:invite", app.EnableReferralForProfile)
|
api.POST(p+"/profiles/referral/:profile/:invite", app.EnableReferralForProfile)
|
||||||
api.DELETE(p+"/profiles/referral/:profile", app.DisableReferralForProfile)
|
api.DELETE(p+"/profiles/referral/:profile", app.DisableReferralForProfile)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,10 @@
|
|||||||
"expiry": "common",
|
"expiry": "common",
|
||||||
"add": "common",
|
"add": "common",
|
||||||
"edit": "common",
|
"edit": "common",
|
||||||
"delete": "admin"
|
"delete": "common",
|
||||||
|
"myAccount": "common",
|
||||||
|
"referrals": "common",
|
||||||
|
"inviteRemainingUses": "admin"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"errorLoginBlank": "common",
|
"errorLoginBlank": "common",
|
||||||
|
@ -935,6 +935,14 @@ export class accountsList {
|
|||||||
bool: true,
|
bool: true,
|
||||||
string: false,
|
string: false,
|
||||||
date: true
|
date: true
|
||||||
|
},
|
||||||
|
"referrals-enabled": {
|
||||||
|
name: window.lang.strings("referrals"),
|
||||||
|
getter: "referrals_enabled",
|
||||||
|
bool: true,
|
||||||
|
string: false,
|
||||||
|
date: false,
|
||||||
|
dependsOnTableHeader: "accounts-header-referrals"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1217,6 +1225,7 @@ export class accountsList {
|
|||||||
let anyNonExpiries = list.length == 0 ? true : false;
|
let anyNonExpiries = list.length == 0 ? true : false;
|
||||||
let allNonExpiries = true;
|
let allNonExpiries = true;
|
||||||
let noContactCount = 0;
|
let noContactCount = 0;
|
||||||
|
let referralState = Number(this._users[list[0]].referrals_enabled); // -1 = hide, 0 = show "enable", 1 = show "disable"
|
||||||
// Only show enable/disable button if all selected have the same state.
|
// Only show enable/disable button if all selected have the same state.
|
||||||
this._shouldEnable = this._users[list[0]].disabled
|
this._shouldEnable = this._users[list[0]].disabled
|
||||||
let showDisableEnable = true;
|
let showDisableEnable = true;
|
||||||
@ -1236,6 +1245,9 @@ export class accountsList {
|
|||||||
if (!this._users[id].lastNotifyMethod()) {
|
if (!this._users[id].lastNotifyMethod()) {
|
||||||
noContactCount++;
|
noContactCount++;
|
||||||
}
|
}
|
||||||
|
if (window.referralsEnabled && referralState != -1 && Number(this._users[id].referrals_enabled) != referralState) {
|
||||||
|
referralState = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._settingExpiry = false;
|
this._settingExpiry = false;
|
||||||
if (!anyNonExpiries && !allNonExpiries) {
|
if (!anyNonExpiries && !allNonExpiries) {
|
||||||
@ -1269,6 +1281,22 @@ export class accountsList {
|
|||||||
this._disableEnable.parentElement.classList.remove("unfocused");
|
this._disableEnable.parentElement.classList.remove("unfocused");
|
||||||
this._disableEnable.textContent = message;
|
this._disableEnable.textContent = message;
|
||||||
}
|
}
|
||||||
|
if (window.referralsEnabled) {
|
||||||
|
if (referralState == -1) {
|
||||||
|
this._enableReferrals.classList.add("unfocused");
|
||||||
|
} else {
|
||||||
|
this._enableReferrals.classList.remove("unfocused");
|
||||||
|
}
|
||||||
|
if (referralState == 0) {
|
||||||
|
this._enableReferrals.classList.add("~urge");
|
||||||
|
this._enableReferrals.classList.remove("~warning");
|
||||||
|
this._enableReferrals.textContent = window.lang.strings("enableReferrals");
|
||||||
|
} else if (referralState == 1) {
|
||||||
|
this._enableReferrals.classList.add("~warning");
|
||||||
|
this._enableReferrals.classList.remove("~urge");
|
||||||
|
this._enableReferrals.textContent = window.lang.strings("disableReferrals");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1700,6 +1728,17 @@ export class accountsList {
|
|||||||
const modalHeader = document.getElementById("header-enable-referrals-user");
|
const modalHeader = document.getElementById("header-enable-referrals-user");
|
||||||
modalHeader.textContent = window.lang.quantity("enableReferralsFor", this._collectUsers().length)
|
modalHeader.textContent = window.lang.quantity("enableReferralsFor", this._collectUsers().length)
|
||||||
let list = this._collectUsers();
|
let list = this._collectUsers();
|
||||||
|
|
||||||
|
// Check if we're disabling or enabling
|
||||||
|
if (this._users[list[0]].referrals_enabled) {
|
||||||
|
_delete("/users/referral", {"users": list}, (req: XMLHttpRequest) => {
|
||||||
|
if (req.readyState != 4 || req.status != 200) return;
|
||||||
|
window.notifications.customSuccess("disabledReferralsSuccess", window.lang.quantity("appliedSettings", list.length));
|
||||||
|
this.reload();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
_get("/invites", null, (req: XMLHttpRequest) => {
|
_get("/invites", null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState != 4 || req.status != 200) return;
|
if (req.readyState != 4 || req.status != 200) return;
|
||||||
@ -1755,9 +1794,9 @@ export class accountsList {
|
|||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
toggleLoader(button);
|
toggleLoader(button);
|
||||||
if (req.status == 400) {
|
if (req.status == 400) {
|
||||||
window.notifications.customError("unknownError", window.lang.notif("errorUnknown"));
|
window.notifications.customError("noReferralTemplateError", window.lang.notif("errorNoReferralTemplate"));
|
||||||
} else if (req.status == 200 || req.status == 204) {
|
} else if (req.status == 200 || req.status == 204) {
|
||||||
window.notifications.customSuccess("enableReferralsSuccess", window.lang.quantity("appliedSettings", this._collectUsers().length));
|
window.notifications.customSuccess("enableReferralsSuccess", window.lang.quantity("appliedSettings", list.length));
|
||||||
}
|
}
|
||||||
this.reload();
|
this.reload();
|
||||||
window.modals.enableReferralsUser.close();
|
window.modals.enableReferralsUser.close();
|
||||||
|
87
ts/user.ts
87
ts/user.ts
@ -1,7 +1,7 @@
|
|||||||
import { ThemeManager } from "./modules/theme.js";
|
import { ThemeManager } from "./modules/theme.js";
|
||||||
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
||||||
import { Modal } from "./modules/modal.js";
|
import { Modal } from "./modules/modal.js";
|
||||||
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader, addLoader, removeLoader } from "./modules/common.js";
|
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader, addLoader, removeLoader, toClipboard } from "./modules/common.js";
|
||||||
import { Login } from "./modules/login.js";
|
import { Login } from "./modules/login.js";
|
||||||
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
|
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
|
||||||
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
|
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
|
||||||
@ -18,6 +18,7 @@ interface userWindow extends Window {
|
|||||||
matrixUserID: string;
|
matrixUserID: string;
|
||||||
discordSendPINMessage: string;
|
discordSendPINMessage: string;
|
||||||
pwrEnabled: string;
|
pwrEnabled: string;
|
||||||
|
referralsEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare var window: userWindow;
|
declare var window: userWindow;
|
||||||
@ -107,6 +108,14 @@ interface MyDetails {
|
|||||||
discord?: MyDetailsContactMethod;
|
discord?: MyDetailsContactMethod;
|
||||||
telegram?: MyDetailsContactMethod;
|
telegram?: MyDetailsContactMethod;
|
||||||
matrix?: MyDetailsContactMethod;
|
matrix?: MyDetailsContactMethod;
|
||||||
|
has_referrals: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MyReferral {
|
||||||
|
code: string;
|
||||||
|
remaining_uses: string;
|
||||||
|
no_limit: boolean;
|
||||||
|
expiry: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContactDTO {
|
interface ContactDTO {
|
||||||
@ -363,7 +372,8 @@ const discordConf: ServiceConfiguration = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let discord = new Discord(discordConf);
|
let discord: Discord;
|
||||||
|
if (window.discordEnabled) discord = new Discord(discordConf);
|
||||||
|
|
||||||
const telegramConf: ServiceConfiguration = {
|
const telegramConf: ServiceConfiguration = {
|
||||||
modal: window.modals.telegram as Modal,
|
modal: window.modals.telegram as Modal,
|
||||||
@ -378,7 +388,8 @@ const telegramConf: ServiceConfiguration = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let telegram = new Telegram(telegramConf);
|
let telegram: Telegram;
|
||||||
|
if (window.telegramEnabled) telegram = new Telegram(telegramConf);
|
||||||
|
|
||||||
const matrixConf: MatrixConfiguration = {
|
const matrixConf: MatrixConfiguration = {
|
||||||
modal: window.modals.matrix as Modal,
|
modal: window.modals.matrix as Modal,
|
||||||
@ -393,7 +404,8 @@ const matrixConf: MatrixConfiguration = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let matrix = new Matrix(matrixConf);
|
let matrix: Matrix;
|
||||||
|
if (window.matrixEnabled) matrix = new Matrix(matrixConf);
|
||||||
|
|
||||||
|
|
||||||
const oldPasswordField = document.getElementById("user-old-password") as HTMLInputElement;
|
const oldPasswordField = document.getElementById("user-old-password") as HTMLInputElement;
|
||||||
@ -468,14 +480,15 @@ document.addEventListener("details-reload", () => {
|
|||||||
// Note the weird format of the functions for discord/telegram:
|
// Note the weird format of the functions for discord/telegram:
|
||||||
// "this" was being redefined within the onclick() method, so
|
// "this" was being redefined within the onclick() method, so
|
||||||
// they had to be wrapped in an anonymous function.
|
// they had to be wrapped in an anonymous function.
|
||||||
const contactMethods: { name: string, icon: string, f: (add: boolean) => void, required: boolean }[] = [
|
const contactMethods: { name: string, icon: string, f: (add: boolean) => void, required: boolean, enabled: boolean }[] = [
|
||||||
{name: "email", icon: `<i class="ri-mail-fill ri-lg"></i>`, f: addEditEmail, required: true},
|
{name: "email", icon: `<i class="ri-mail-fill ri-lg"></i>`, f: addEditEmail, required: true, enabled: true},
|
||||||
{name: "discord", icon: `<i class="ri-discord-fill ri-lg"></i>`, f: (add: boolean) => { discord.onclick(); }, required: window.discordRequired},
|
{name: "discord", icon: `<i class="ri-discord-fill ri-lg"></i>`, f: (add: boolean) => { discord.onclick(); }, required: window.discordRequired, enabled: window.discordEnabled},
|
||||||
{name: "telegram", icon: `<i class="ri-telegram-fill ri-lg"></i>`, f: (add: boolean) => { telegram.onclick() }, required: window.telegramRequired},
|
{name: "telegram", icon: `<i class="ri-telegram-fill ri-lg"></i>`, f: (add: boolean) => { telegram.onclick() }, required: window.telegramRequired, enabled: window.telegramEnabled},
|
||||||
{name: "matrix", icon: `<span class="font-bold">[m]</span>`, f: (add: boolean) => { matrix.show(); }, required: window.matrixRequired}
|
{name: "matrix", icon: `<span class="font-bold">[m]</span>`, f: (add: boolean) => { matrix.show(); }, required: window.matrixRequired, enabled: window.matrixEnabled}
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let method of contactMethods) {
|
for (let method of contactMethods) {
|
||||||
|
if (!(method.enabled)) continue;
|
||||||
if (method.name in details) {
|
if (method.name in details) {
|
||||||
contactMethodList.append(method.name, details[method.name], method.icon, method.f, method.required);
|
contactMethodList.append(method.name, details[method.name], method.icon, method.f, method.required);
|
||||||
}
|
}
|
||||||
@ -509,6 +522,62 @@ document.addEventListener("details-reload", () => {
|
|||||||
} else if (!statusCard.classList.contains("unfocused")) {
|
} else if (!statusCard.classList.contains("unfocused")) {
|
||||||
setBestRowSpan(passwordCard, true);
|
setBestRowSpan(passwordCard, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let referralCard = document.getElementById("card-referrals");
|
||||||
|
if (window.referralsEnabled && typeof(referralCard) != "undefined" && referralCard != null) {
|
||||||
|
if (details.has_referrals) {
|
||||||
|
_get("/my/referral", null, (req: XMLHttpRequest) => {
|
||||||
|
if (req.readyState != 4 || req.status != 200) return;
|
||||||
|
const referral: MyReferral = req.response as MyReferral;
|
||||||
|
const infoArea = referralCard.querySelector(".user-referrals-info") as HTMLDivElement;
|
||||||
|
|
||||||
|
infoArea.innerHTML = `
|
||||||
|
<div class="row my-3">
|
||||||
|
<div class="inline baseline">
|
||||||
|
<span class="text-2xl">${referral.no_limit ? "∞" : referral.remaining_uses}</span> <span class="text-gray-400 text-lg">${window.lang.strings("inviteRemainingUses")}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-3">
|
||||||
|
<div class="inline baseline">
|
||||||
|
<span class="text-gray-400 text-lg">${window.lang.strings("expiry")}</span> <span class="text-2xl">${toDateString(new Date(referral.expiry * 1000))}</span>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const linkButton = referralCard.querySelector(".user-referrals-button") as HTMLButtonElement;
|
||||||
|
|
||||||
|
let codeLink = window.location.href;
|
||||||
|
for (let split of ["#", "?", "account", "my"]) {
|
||||||
|
codeLink = codeLink.split(split)[0];
|
||||||
|
}
|
||||||
|
if (codeLink.slice(-1) != "/") { codeLink += "/"; }
|
||||||
|
codeLink = codeLink + "invite/" + referral.code;
|
||||||
|
|
||||||
|
linkButton.addEventListener("click", () => {
|
||||||
|
toClipboard(codeLink);
|
||||||
|
const content = linkButton.innerHTML;
|
||||||
|
linkButton.innerHTML = `
|
||||||
|
${window.lang.strings("copied")} <i class="ri-check-line ml-2"></i>
|
||||||
|
`;
|
||||||
|
linkButton.classList.add("~positive");
|
||||||
|
linkButton.classList.remove("~info");
|
||||||
|
setTimeout(() => {
|
||||||
|
linkButton.classList.add("~info");
|
||||||
|
linkButton.classList.remove("~positive");
|
||||||
|
linkButton.innerHTML = content;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
referralCard.classList.remove("unfocused");
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
referralCard.classList.add("unfocused");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
1
views.go
1
views.go
@ -204,6 +204,7 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
|
|||||||
"langName": lang,
|
"langName": lang,
|
||||||
"jfLink": app.config.Section("ui").Key("redirect_url").String(),
|
"jfLink": app.config.Section("ui").Key("redirect_url").String(),
|
||||||
"requirements": app.validator.getCriteria(),
|
"requirements": app.validator.getCriteria(),
|
||||||
|
"referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false),
|
||||||
}
|
}
|
||||||
if telegramEnabled {
|
if telegramEnabled {
|
||||||
data["telegramUsername"] = app.telegram.username
|
data["telegramUsername"] = app.telegram.username
|
||||||
|
Loading…
Reference in New Issue
Block a user