Skip to content

Commit

Permalink
quota & admin notes
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria committed Oct 15, 2024
1 parent 2fb0982 commit 0e3dcd7
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 22 deletions.
2 changes: 1 addition & 1 deletion app/Actions/Photo/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function __construct(?ImportMode $importMode, int $intendedOwnerId)
* @return Photo the newly created or updated photo
*
* @throws ModelNotFoundException
* @throws
* @throws QuotaExceededException
* @throws LycheeException
*/
public function add(NativeLocalFile $sourceFile, ?AbstractAlbum $album, ?int $fileLastModifiedTime = null): Photo
Expand Down
5 changes: 5 additions & 0 deletions app/Actions/User/Save.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public function do(User $user,
throw new ConflictingPropertyException('Username already exists');
}

if ($quota_kb === 0) {
$default = \Configs::getValueAsInt('default_user_quota');
$quota_kb = $default === 0 ? null : $default;
}

$user->username = $username;
$user->may_upload = $mayUpload;
$user->may_edit_own_settings = $mayEditOwnSettings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function rules(): array
RequestAttribute::MAY_EDIT_OWN_SETTINGS_ATTRIBUTE => 'present|boolean',
RequestAttribute::HAS_QUOTA_ATTRIBUTE => ['sometimes', 'boolean', new BooleanRequireSupportRule(false, $this->verify)],
RequestAttribute::QUOTA_ATTRIBUTE => ['sometimes', 'int', new IntegerRequireSupportRule(0, $this->verify)],
RequestAttribute::NOTE_ATTRIBUTE => ['sometimes', 'string', new StringRequireSupportRule('', $this->verify)],
RequestAttribute::NOTE_ATTRIBUTE => ['sometimes', 'nullable', 'string', new StringRequireSupportRule('', $this->verify)],
];
}

Expand Down
1 change: 0 additions & 1 deletion app/Rules/ConfigKeyRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class ConfigKeyRule implements ValidationRule
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if (is_string($value) === false) {
dd($value);
$fail($attribute . ' is not a string');

return;
Expand Down
3 changes: 0 additions & 3 deletions resources/js/components/forms/album/AlbumProperties.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
<InputText id="title" type="text" v-model="title" />
<label for="title">{{ $t("lychee.ALBUM_TITLE") }}</label>
</FloatLabel>
<!-- <x-forms.error-message field='title' /> -->
</div>
<div class="my-4 h-48">
<FloatLabel variant="on">
<Textarea id="description" class="w-full h-48" v-model="description" rows="6" cols="30" />
<!-- <x-forms.error-message field='description' /> -->
<label for="description">{{ $t("lychee.ALBUM_DESCRIPTION") }}</label>
</FloatLabel>
</div>
Expand Down Expand Up @@ -136,7 +134,6 @@
<label for="header">{{ $t("lychee.SET_HEADER") }}</label>
</FloatLabel>
</div>
<!-- <livewire:forms.album.set-header :album_id="$this->albumID" lazy="on-load" /> -->
<div class="h-10 my-2">
<FloatLabel variant="on">
<Select id="license" class="w-72 border-none" v-model="license" :options="licenseOptions" optionLabel="label" showClear>
Expand Down
33 changes: 31 additions & 2 deletions resources/js/components/forms/users/CreateEditUser.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<Dialog v-model:visible="visible" class="border-none">
<Dialog v-model:visible="visible" class="border-none max-w-lg w-full">
<template #container="{ closeCallback }">
<div class="p-9 w-full flex flex-col gap-2 justify-center">
<FloatLabel class="w-full" variant="on">
Expand All @@ -18,6 +18,20 @@
<Checkbox inputId="mayEdit" v-model="may_edit_own_settings" :binary="true" />
<label for="mayEdit" class="ml-2 cursor-pointer">User can modify their profile (username, password).</label>
</div>
<div class="w-full items-center text-muted-color" v-if="is_se_enabled || is_se_preview_enabled">
<Checkbox inputId="hasQuota" v-model="has_quota" :binary="true" />
<label for="hasQuota" class="ml-2 cursor-pointer">User has quota limit.</label>
</div>
<div class="w-full flex items-center text-muted-color" v-if="has_quota">
<InputText id="quota_kb" v-model="quota_kb" aria-label="quota_kb" class="!w-1/2" />
<label class="pl-4 w-1/2" for="username">{{ "quota in kB (0 for default)" }}</label>
</div>
<div class="w-full flex items-center text-muted-color pt-2" v-if="is_se_enabled">
<FloatLabel variant="on">
<Textarea id="note" class="w-full h-18" v-model="note" rows="2" cols="40" />
<label for="note">{{ "Admin note (not publically visible)" }}</label>
</FloatLabel>
</div>
</div>
<div class="flex">
<Button @click="closeCallback" severity="secondary" class="w-full border-0 rounded-none rounded-bl-lg font-bold">
Expand Down Expand Up @@ -56,9 +70,9 @@ import UserManagementService from "@/services/user-management-service";
import Dialog from "primevue/dialog";
import { useLycheeStateStore } from "@/stores/LycheeState";
import { storeToRefs } from "pinia";
import Textarea from "../basic/Textarea.vue";
const lycheeStore = useLycheeStateStore();
lycheeStore.init();
const { is_se_preview_enabled, is_se_enabled } = storeToRefs(lycheeStore);
const visible = defineModel("visible") as Ref<boolean>;
Expand All @@ -69,9 +83,12 @@ const props = defineProps<{
const id = ref(props.user?.id);
const username = ref(props.user?.username);
const note = ref(props.user?.note ?? undefined);
const password = ref(undefined as string | undefined);
const may_edit_own_settings = ref(props.user?.may_edit_own_settings ?? false);
const may_upload = ref(props.user?.may_upload ?? false);
const has_quota = ref(props.user?.quota_kb !== null);
const quota_kb = ref(props.user?.quota_kb ?? 0);
const toast = useToast();
const emits = defineEmits<{
Expand All @@ -88,13 +105,18 @@ function createUser() {
password: password.value,
may_edit_own_settings: may_edit_own_settings.value,
may_upload: may_upload.value,
has_quota: is_se_enabled ? has_quota.value : undefined,
quota_kb: is_se_enabled ? quota_kb.value : undefined,
note: is_se_enabled ? note.value : undefined,
})
.then(() => {
visible.value = false;
password.value = undefined;
may_upload.value = false;
may_edit_own_settings.value = false;
username.value = undefined;
has_quota.value = false;
quota_kb.value = 0;
toast.add({ severity: "success", summary: "Success", detail: "User created", life: 3000 });
emits("refresh");
})
Expand All @@ -114,13 +136,18 @@ function editUser() {
password: password.value,
may_edit_own_settings: may_edit_own_settings.value,
may_upload: may_upload.value,
has_quota: is_se_enabled ? has_quota.value : undefined,
quota_kb: is_se_enabled ? quota_kb.value : undefined,
note: is_se_enabled ? note.value : undefined,
})
.then(() => {
visible.value = false;
password.value = undefined;
may_upload.value = false;
may_edit_own_settings.value = false;
username.value = undefined;
has_quota.value = false;
quota_kb.value = 0;
toast.add({ severity: "success", summary: "Change saved!", detail: "User updated", life: 3000 });
emits("refresh");
})
Expand All @@ -136,6 +163,8 @@ watch(
username.value = newUser?.username;
may_edit_own_settings.value = newUser?.may_edit_own_settings ?? false;
may_upload.value = newUser?.may_upload ?? false;
has_quota.value = newUser?.quota_kb !== null;
quota_kb.value = newUser?.quota_kb ?? 0;
},
);
</script>
29 changes: 21 additions & 8 deletions resources/js/components/forms/users/ListUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
{{ props.user.username }}
<i class="pi pi-crown text-orange-400" v-if="props.user.may_administrate" v-tooltip.top="'admin user'"></i>
</div>
<div class="w-1/6 flex justify-center items-center">
<i v-if="props.user.may_upload" class="pi pi-check text-create-600"></i>
<i v-else class="pi pi-times text-muted-color"></i>
</div>
<div class="w-1/6 flex justify-center items-center">
<i v-if="props.user.may_edit_own_settings" class="pi pi-check text-create-600"></i>
<i v-else class="pi pi-times text-muted-color"></i>
<div class="w-1/3 flex items-center justify-evenly">
<div class="w-full text-center">
<i v-if="props.user.may_upload" class="pi pi-check text-create-600"></i>
<i v-else class="pi pi-times text-muted-color opacity-30"></i>
</div>
<div class="w-full text-center">
<i v-if="props.user.may_edit_own_settings" class="pi pi-check text-create-600"></i>
<i v-else class="pi pi-times text-muted-color opacity-30"></i>
</div>
<div class="w-full text-center" v-if="isQuotaEnabled">
<i v-if="props.user.quota_kb !== null" class="pi pi-chart-pie text-muted-color" v-tooltip.right="formattedQuota"></i>
</div>
</div>
<template v-if="showMetterBar">
<ProgressBar
Expand Down Expand Up @@ -45,15 +50,23 @@ import { sizeToUnit } from "@/utils/StatsSizeVariantToColours";
const props = defineProps<{
user: App.Http.Resources.Models.UserManagementResource;
totalUsedSpace: number;
isQuotaEnabled: boolean;
}>();
const value = computed(() => {
if (props.user.quota_kb !== null) {
return ((props.user.space ?? 0) * 100) / props.user.quota_kb;
return ((props.user.space ?? 0) * 100) / (props.user.quota_kb * 1024);
}
return ((props.user.space ?? 0) * 100) / (props.totalUsedSpace ?? 1);
});
const formattedQuota = computed(() => {
if (props.user.quota_kb !== null) {
return `${sizeToUnit(props.user.quota_kb * 1024)}`;
}
return "";
});
const formattedSpace = computed(() => {
if (props.user.quota_kb !== null) {
return `${sizeToUnit(props.user.space ?? 0)} / ${sizeToUnit(props.user.quota_kb * 1024)}`;
Expand Down
3 changes: 3 additions & 0 deletions resources/js/services/user-management-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ type UserManagementCreateRequest = {
password: string | null | undefined;
may_upload: boolean;
may_edit_own_settings: boolean;
has_quota?: boolean;
quota_kb?: number;
note?: string;
};

type HasId = {
Expand Down
34 changes: 28 additions & 6 deletions resources/js/views/Users.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
<template #end> </template>
</Toolbar>
<Panel class="border-0 max-w-3xl mx-auto">
<div class="w-full mb-10 text-muted-color flex items-center gap-4">
<div class="w-full mb-10 text-muted-color-emphasis flex items-center gap-4">
<div>
<p>This pages allows you to manage users.</p>
<ul class="mt-1">
<li class="ml-4 pt-2"><i class="pi pi-upload" /> : When selected, the user can upload content.</li>
<li class="ml-4 pt-2"><i class="pi pi-lock-open" /> : When selected, the user can modify their profile (username, password).</li>
<li class="ml-4 pt-2" v-if="is_se_enabled">
<i class="pi pi-chart-pie" /> : When set, the user has a space quota for pictures (in kB).
</li>
</ul>
</div>
<Button @click="createUser" severity="primary" class="border-none p-3">Create a new user</Button>
Expand All @@ -28,10 +31,21 @@
<div class="flex flex-wrap md:flex-nowrap gap-2 justify-center border-b border-solid border-b-surface-700 mb-4 pb-4">
<div class="w-3/6 flex">
<span class="w-2/3 font-bold">{{ $t("lychee.USERNAME") }}</span>
<span class="w-1/6 text-center" v-tooltip.top="'When selected, the user can upload content.'"><i class="pi pi-upload" /></span>
<span class="w-1/6 text-center" v-tooltip.top="'When selected, the user can modify their profile (username, password).'"
><i class="pi pi-lock-open"
/></span>
<div class="w-1/3 flex justify-evenly">
<span class="w-full text-center" v-tooltip.top="'When selected, the user can upload content.'"
><i class="pi pi-upload"
/></span>
<span class="w-full text-center" v-tooltip.top="'When selected, the user can modify their profile (username, password).'">
<i class="pi pi-lock-open" />
</span>
<span
v-if="isQuotaEnabled"
class="w-full text-center"
v-tooltip.top="'When selected, the user is limited in the quatity of picture they can upload (in kB).'"
>
<i class="pi pi-chart-pie" />
</span>
</div>
</div>
<span class="w-1/6"></span>
<span class="w-1/6"></span>
Expand All @@ -44,19 +58,26 @@
@delete-user="deleteUser"
@edit-user="editUser"
:total-used-space="totalUsedSpace"
:is-quota-enabled="isQuotaEnabled"
/>
</div>
</Panel>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { computed, ref } from "vue";
import Button from "primevue/button";
import Panel from "primevue/panel";
import Toolbar from "primevue/toolbar";
import CreateEditUser from "@/components/forms/users/CreateEditUser.vue";
import UserManagementService from "@/services/user-management-service";
import ListUser from "@/components/forms/users/ListUser.vue";
import { useToast } from "primevue/usetoast";
import { storeToRefs } from "pinia";
import { useLycheeStateStore } from "@/stores/LycheeState";
const lycheeStore = useLycheeStateStore();
lycheeStore.init();
const { is_se_preview_enabled, is_se_enabled } = storeToRefs(lycheeStore);
const users = ref([] as App.Http.Resources.Models.UserManagementResource[]);
const isCreateUserVisible = ref(false);
Expand All @@ -66,6 +87,7 @@ const toast = useToast();
const selectedUser = ref<App.Http.Resources.Models.UserManagementResource | undefined>(undefined);
const isEdit = ref(false);
const isQuotaEnabled = computed(() => is_se_enabled && users.value.reduce((acc, user) => acc || user.quota_kb !== null, false));
function load() {
UserManagementService.get().then((response) => {
Expand Down

0 comments on commit 0e3dcd7

Please sign in to comment.