mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-16 23:14:15 -06:00
feat(frontend): user permissions
This commit is contained in:
@@ -11,6 +11,7 @@ import { ref } from 'vue'
|
||||
import { Button } from 'primevue'
|
||||
import { onMounted } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import type { UserPermission } from '@/services/api-schema'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -29,7 +30,9 @@ const menuItems = ref([
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
if (userStore.class === 'staff') {
|
||||
// if the user can do one of those actions, they can access the staff dashboard
|
||||
const permissionsToSeeStaffDashboard: UserPermission[] = ['create_css_sheet', 'edit_css_sheet', 'get_user_application', 'read_staff_pm']
|
||||
if (permissionsToSeeStaffDashboard.some((x: UserPermission) => userStore.permissions.includes(x))) {
|
||||
menuItems.value.push({ label: 'Staff Dashboard', route: '/staff-dashboard' })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="actions">
|
||||
<i
|
||||
class="pi pi-pen-to-square"
|
||||
v-if="userStore.class === 'staff' || artist.created_by_id === userStore.id"
|
||||
v-if="userStore.permissions.includes('edit_artist') || artist.created_by_id === userStore.id"
|
||||
v-tooltip.top="t('artist.edit')"
|
||||
@click="editArtist"
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="actions">
|
||||
<i
|
||||
class="pi pi-pen-to-square"
|
||||
v-if="(userStore.id === comment.created_by.id && 'locked' in comment && comment.locked === false) || userStore.class === 'staff'"
|
||||
v-if="(userStore.id === comment.created_by.id && 'locked' in comment && comment.locked === false) || hasEditPermission"
|
||||
@click="editCommentDialogVisible = true"
|
||||
/>
|
||||
<RouterLink
|
||||
@@ -55,6 +55,7 @@ const props = defineProps<{
|
||||
comment: TitleGroupCommentHierarchy | ForumPostHierarchy | ConversationMessageHierarchy
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
editCommentMethod?: Function
|
||||
hasEditPermission: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
:key="message.id"
|
||||
:comment="message"
|
||||
:class="`message ${userStore.id === message.created_by.id ? 'sent' : 'received'}`"
|
||||
:hasEditPermission="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<label for="subcategory">{{ t('forum.subcategory') }}</label>
|
||||
</FloatLabel>
|
||||
|
||||
<div v-if="userStore.class === 'staff'" class="staff-options">
|
||||
<div v-if="userStore.permissions.includes('edit_forum_thread')" class="staff-options">
|
||||
<div class="checkbox-row">
|
||||
<Checkbox v-model="editedThread.locked" binary inputId="locked" />
|
||||
<label for="locked">{{ t('general.locked') }}</label>
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
<div class="top">
|
||||
<div class="title">{{ forumCategory.name }}</div>
|
||||
<div class="actions">
|
||||
<RouterLink :to="`/forum/category/${forumCategory.id}/edit`" v-if="userStore.class === 'staff'" v-tooltip.top="t('forum.edit_category')">
|
||||
<RouterLink
|
||||
:to="`/forum/category/${forumCategory.id}/edit`"
|
||||
v-if="userStore.permissions.includes('edit_forum_category')"
|
||||
v-tooltip.top="t('forum.edit_category')"
|
||||
>
|
||||
<i class="pi pi-pen-to-square" />
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
:to="{ path: '/forum/sub-category/new', query: { categoryId: forumCategory.id, categoryName: forumCategory.name } }"
|
||||
v-if="userStore.class === 'staff'"
|
||||
v-if="userStore.permissions.includes('create_forum_sub_category')"
|
||||
v-tooltip.top="t('forum.create_sub_category')"
|
||||
>
|
||||
<i class="pi pi-plus" />
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
:comment="comment"
|
||||
@commentEdited="commentEdited($event, comment.id)"
|
||||
:editCommentMethod="(post: EditedTitleGroupComment) => editTitleGroupComment({ EditedTitleGroupComment: post, id: comment.id })"
|
||||
:hasEditPermission="useUserStore().permissions.includes('edit_title_group_comment')"
|
||||
/>
|
||||
</div>
|
||||
<Form v-slot="$form" :initialValues="new_comment" :resolver @submit="onFormSubmit" validateOnSubmit :validateOnValueUpdate="false">
|
||||
|
||||
@@ -49,11 +49,11 @@
|
||||
<i
|
||||
v-tooltip.top="t('general.delete')"
|
||||
class="action pi pi-trash"
|
||||
v-if="showActionBtns && (user.id === slotProps.data.created_by_id || user.class === 'staff')"
|
||||
v-if="showActionBtns && (user.id === slotProps.data.created_by_id || user.permissions.includes('delete_torrent'))"
|
||||
@click="deleteTorrent(slotProps.data.id)"
|
||||
/>
|
||||
<i
|
||||
v-if="showActionBtns && (user.id === slotProps.data.created_by_id || user.class === 'staff')"
|
||||
v-if="showActionBtns && (user.id === slotProps.data.created_by_id || user.permissions.includes('edit_torrent'))"
|
||||
v-tooltip.top="t('general.edit')"
|
||||
@click="editTorrent(slotProps.data)"
|
||||
class="action pi pi-pen-to-square"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{{ t('user.last_seen') }}:
|
||||
<span v-tooltip.top="formatDate(user.last_seen)">{{ timeAgo(user.last_seen) }}</span>
|
||||
<br />
|
||||
{{ t('user.class') }}: {{ user.class }}
|
||||
{{ t('user.class') }}: {{ user.class_name }}
|
||||
<br />
|
||||
{{ t('user.bonus_points') }}: {{ user.bonus_points }}
|
||||
<br />
|
||||
|
||||
@@ -1049,7 +1049,7 @@ export interface PublicUser {
|
||||
'average_seeding_time': number;
|
||||
'banned': boolean;
|
||||
'bonus_points': number;
|
||||
'class': UserClass;
|
||||
'class_name': string;
|
||||
'collages_started': number;
|
||||
'created_at': string;
|
||||
'description': string;
|
||||
@@ -1076,8 +1076,6 @@ export interface PublicUser {
|
||||
'username': string;
|
||||
'warned': boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface RefreshToken {
|
||||
'refresh_token': string;
|
||||
}
|
||||
@@ -1495,11 +1493,6 @@ export interface TorrentHierarchyLite {
|
||||
}
|
||||
|
||||
|
||||
export interface TorrentMinimal {
|
||||
'created_at': string;
|
||||
'id': number;
|
||||
'info_hash'?: string | null;
|
||||
}
|
||||
export interface TorrentReport {
|
||||
'description': string;
|
||||
'id': number;
|
||||
@@ -1669,7 +1662,8 @@ export interface User {
|
||||
'average_seeding_time': number;
|
||||
'banned': boolean;
|
||||
'bonus_points': number;
|
||||
'class': UserClass;
|
||||
'class_locked': boolean;
|
||||
'class_name': string;
|
||||
'collages_started': number;
|
||||
'created_at': string;
|
||||
'css_sheet_name': string;
|
||||
@@ -1686,6 +1680,7 @@ export interface User {
|
||||
'leeching': number;
|
||||
'passkey': string;
|
||||
'password_hash': string;
|
||||
'permissions': Array<UserPermission>;
|
||||
'ratio': number;
|
||||
'real_downloaded': number;
|
||||
'real_uploaded': number;
|
||||
@@ -1703,8 +1698,6 @@ export interface User {
|
||||
'username': string;
|
||||
'warned': boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface UserApplication {
|
||||
'applied_from_ip': string;
|
||||
'body': string;
|
||||
@@ -1727,16 +1720,6 @@ export const UserApplicationStatus = {
|
||||
export type UserApplicationStatus = typeof UserApplicationStatus[keyof typeof UserApplicationStatus];
|
||||
|
||||
|
||||
|
||||
export const UserClass = {
|
||||
Newbie: 'newbie',
|
||||
Staff: 'staff',
|
||||
Tracker: 'tracker'
|
||||
} as const;
|
||||
|
||||
export type UserClass = typeof UserClass[keyof typeof UserClass];
|
||||
|
||||
|
||||
export interface UserCreatedAffiliatedArtist {
|
||||
'artist_id': number;
|
||||
'nickname'?: string | null;
|
||||
@@ -1924,6 +1907,50 @@ export interface UserLiteAvatar {
|
||||
'username': string;
|
||||
'warned': boolean;
|
||||
}
|
||||
|
||||
export const UserPermission = {
|
||||
UploadTorrent: 'upload_torrent',
|
||||
DownloadTorrent: 'download_torrent',
|
||||
CreateTorrentRequest: 'create_torrent_request',
|
||||
ImmuneActivityPruning: 'immune_activity_pruning',
|
||||
EditTitleGroup: 'edit_title_group',
|
||||
EditTitleGroupComment: 'edit_title_group_comment',
|
||||
EditEditionGroup: 'edit_edition_group',
|
||||
EditTorrent: 'edit_torrent',
|
||||
EditArtist: 'edit_artist',
|
||||
EditCollage: 'edit_collage',
|
||||
EditSeries: 'edit_series',
|
||||
EditTorrentRequest: 'edit_torrent_request',
|
||||
EditForumPost: 'edit_forum_post',
|
||||
EditForumThread: 'edit_forum_thread',
|
||||
EditForumSubCategory: 'edit_forum_sub_category',
|
||||
EditForumCategory: 'edit_forum_category',
|
||||
CreateForumCategory: 'create_forum_category',
|
||||
CreateForumSubCategory: 'create_forum_sub_category',
|
||||
CreateForumThread: 'create_forum_thread',
|
||||
CreateForumPost: 'create_forum_post',
|
||||
SendPm: 'send_pm',
|
||||
CreateCssSheet: 'create_css_sheet',
|
||||
EditCssSheet: 'edit_css_sheet',
|
||||
SetDefaultCssSheet: 'set_default_css_sheet',
|
||||
ReadStaffPm: 'read_staff_pm',
|
||||
ReplyStaffPm: 'reply_staff_pm',
|
||||
ResolveStaffPm: 'resolve_staff_pm',
|
||||
UnresolveStaffPm: 'unresolve_staff_pm',
|
||||
DeleteTitleGroupTag: 'delete_title_group_tag',
|
||||
EditTitleGroupTag: 'edit_title_group_tag',
|
||||
DeleteTorrent: 'delete_torrent',
|
||||
GetUserApplication: 'get_user_application',
|
||||
UpdateUserApplication: 'update_user_application',
|
||||
WarnUser: 'warn_user',
|
||||
EditUser: 'edit_user',
|
||||
CreateWikiArticle: 'create_wiki_article',
|
||||
EditWikiArticle: 'edit_wiki_article'
|
||||
} as const;
|
||||
|
||||
export type UserPermission = typeof UserPermission[keyof typeof UserPermission];
|
||||
|
||||
|
||||
export interface UserSettings {
|
||||
'css_sheet_name': string;
|
||||
}
|
||||
@@ -9172,39 +9199,6 @@ export const TorrentApiAxiosParamCreator = function (configuration?: Configurati
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRegisteredTorrents: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/torrents/registered`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication http required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} period
|
||||
@@ -9370,17 +9364,6 @@ export const TorrentApiFp = function(configuration?: Configuration) {
|
||||
const localVarOperationServerBasePath = operationServerMap['TorrentApi.editTorrent']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getRegisteredTorrents(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TorrentMinimal>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getRegisteredTorrents(options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['TorrentApi.getRegisteredTorrents']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} period
|
||||
@@ -9479,14 +9462,6 @@ export const TorrentApiFactory = function (configuration?: Configuration, basePa
|
||||
editTorrent(editedTorrent: EditedTorrent, options?: RawAxiosRequestConfig): AxiosPromise<Torrent> {
|
||||
return localVarFp.editTorrent(editedTorrent, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRegisteredTorrents(options?: RawAxiosRequestConfig): AxiosPromise<Array<TorrentMinimal>> {
|
||||
return localVarFp.getRegisteredTorrents(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} period
|
||||
@@ -9582,15 +9557,6 @@ export class TorrentApi extends BaseAPI {
|
||||
return TorrentApiFp(this.configuration).editTorrent(editedTorrent, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public getRegisteredTorrents(options?: RawAxiosRequestConfig) {
|
||||
return TorrentApiFp(this.configuration).getRegisteredTorrents(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} period
|
||||
@@ -9691,12 +9657,6 @@ export const editTorrent = async (editedTorrent: EditedTorrent, options?: RawAxi
|
||||
return response.data;
|
||||
};
|
||||
|
||||
|
||||
export const getRegisteredTorrents = async (options?: RawAxiosRequestConfig): Promise<Array<TorrentMinimal>> => {
|
||||
const response = await torrentApi.getRegisteredTorrents(options);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export interface GetTopTorrentRequest {
|
||||
/** */
|
||||
'period': string;
|
||||
@@ -10365,7 +10325,7 @@ export const UserApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
warnUser: async (userCreatedUserWarning: UserCreatedUserWarning, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'userCreatedUserWarning' is not null or undefined
|
||||
assertParamExists('warnUser', 'userCreatedUserWarning', userCreatedUserWarning)
|
||||
const localVarPath = `/api/users/warnings`;
|
||||
const localVarPath = `/api/users/warn`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
|
||||
@@ -6,7 +6,9 @@ const initialState: User = {
|
||||
avatar: null,
|
||||
average_seeding_time: 0,
|
||||
bonus_points: 0,
|
||||
class: 'newbie',
|
||||
class_name: 'newbie',
|
||||
class_locked: false,
|
||||
permissions: [],
|
||||
collages_started: 0,
|
||||
created_at: '',
|
||||
description: '',
|
||||
|
||||
@@ -64,10 +64,20 @@
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column :header="t('general.action', 2)" class="actions" v-if="userStore.class === 'staff'">
|
||||
<Column :header="t('general.action', 2)" class="actions">
|
||||
<template #body="slotProps">
|
||||
<i class="pi pi-pen-to-square cursor-pointer" v-tooltip.top="t('general.edit')" @click="editTag(slotProps.data)" />
|
||||
<i class="pi pi-trash cursor-pointer" v-tooltip.top="t('general.delete')" @click="deleteTag(slotProps.data)" />
|
||||
<i
|
||||
class="pi pi-pen-to-square cursor-pointer"
|
||||
v-if="userStore.permissions.includes('edit_title_group_tag')"
|
||||
v-tooltip.top="t('general.edit')"
|
||||
@click="editTag(slotProps.data)"
|
||||
/>
|
||||
<i
|
||||
class="pi pi-trash cursor-pointer"
|
||||
v-if="userStore.permissions.includes('delete_title_group_tag')"
|
||||
v-tooltip.top="t('general.delete')"
|
||||
@click="deleteTag(slotProps.data)"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<i
|
||||
v-if="titleGroupAndAssociatedData.title_group.created_by_id === userStore.id || userStore.class === 'staff'"
|
||||
v-if="titleGroupAndAssociatedData.title_group.created_by_id === userStore.id || userStore.permissions.includes('edit_title_group')"
|
||||
v-tooltip.top="t('general.edit')"
|
||||
class="pi pi-pen-to-square"
|
||||
@click="editTitleGroupDialogVisible = true"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<RouterLink :to="`/conversation/new?receiverId=${user.id}&username=${user.username}`" class="no-color" v-if="userStore.id !== user.id">
|
||||
<i v-tooltip.top="t('user.message_user', [user.username])" class="pi pi-envelope" />
|
||||
</RouterLink>
|
||||
<template v-if="userStore.class === 'staff' && userStore.id !== user.id">
|
||||
<template v-if="userStore.permissions.includes('warn_user') && userStore.id !== user.id">
|
||||
<i v-tooltip.top="t('user.warn')" class="cursor-pointer pi pi-exclamation-triangle" @click="warnUserDialogVisible = true" />
|
||||
</template>
|
||||
<template v-if="userStore.id === user.id">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<RouterLink to="/forum/search">
|
||||
<i class="pi pi-search" v-tooltip.top="t('forum.search')" />
|
||||
</RouterLink>
|
||||
<RouterLink to="/forum/category/new" v-if="userStore.class === 'staff'">
|
||||
<RouterLink to="/forum/category/new" v-if="userStore.permissions.includes('create_forum_category')">
|
||||
<i class="pi pi-plus" v-tooltip.top="t('forum.create_category')" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RouterLink to="">{{ forumSubCategory.name }}</RouterLink>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<RouterLink :to="`/forum/sub-category/${route.params.id}/edit`" v-if="forumSubCategory && userStore.class === 'staff'">
|
||||
<RouterLink :to="`/forum/sub-category/${route.params.id}/edit`" v-if="forumSubCategory && userStore.permissions.includes('edit_forum_sub_category')">
|
||||
<i v-tooltip.top="t('forum.edit_subcategory')" class="pi pi-pen-to-square cursor-pointer" />
|
||||
</RouterLink>
|
||||
<RouterLink :to="`/forum/thread/new?subCategoryId=${route.params.id}`">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="actions">
|
||||
<i
|
||||
v-if="userStore.class === 'staff' || forumThread.created_by_id === userStore.id"
|
||||
v-if="userStore.permissions.includes('edit_forum_thread') || forumThread.created_by_id === userStore.id"
|
||||
class="pi pi-pen-to-square"
|
||||
v-tooltip.top="t('forum.edit_thread')"
|
||||
@click="editThreadDialogVisible = true"
|
||||
@@ -36,6 +36,7 @@
|
||||
:comment="post"
|
||||
:editCommentMethod="editForumPostMethod"
|
||||
@commentEdited="postEdited($event as EditedForumPost)"
|
||||
:hasEditPermission="userStore.permissions.includes('edit_forum_post')"
|
||||
/>
|
||||
</PaginatedResults>
|
||||
<Form v-slot="$form" :initialValues="newPost" :resolver @submit="onFormSubmit" validateOnSubmit :validateOnValueUpdate="false">
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<Tab value="2">{{ t('css_sheet.css_sheet', 2) }}</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel value="0">
|
||||
<TabPanel value="0" v-if="userStore.permissions.includes('get_user_application')">
|
||||
<UserApplications />
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<TabPanel value="1" v-if="userStore.permissions.includes('read_staff_pm')">
|
||||
<StaffPmsTable />
|
||||
</TabPanel>
|
||||
<TabPanel value="2">
|
||||
<TabPanel value="2" v-if="userStore.permissions.includes('edit_css_sheet') || userStore.permissions.includes('create_css_sheet')">
|
||||
<CssSheetList showStaffActions />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
@@ -31,8 +31,10 @@ import UserApplications from '@/components/staff/UserApplications.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import StaffPmsTable from '@/components/staff_pm/StaffPmsTable.vue'
|
||||
import CssSheetList from '@/components/CssSheetList.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="actions wrapper-center">
|
||||
<RouterLink to="/wiki/create-article">
|
||||
<RouterLink to="/wiki/create-article" v-if="userStore.permissions.includes('create_wiki_article')">
|
||||
<i class="pi pi-plus" v-tooltip.top="t('wiki.create_article')" />
|
||||
</RouterLink>
|
||||
<RouterLink to="/wiki/search">
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div v-if="wikiArticle" class="wiki-article">
|
||||
<ContentContainer :containerTitle="wikiArticle.title">
|
||||
<template v-if="userStore.class === 'staff'" #top-right>
|
||||
<template v-if="userStore.permissions.includes('edit_wiki_article')" #top-right>
|
||||
<RouterLink :to="`/wiki/article/${wikiArticle.id}/edit`" v-tooltip.top="t('wiki.edit_article')">
|
||||
<i class="pi pi-pen-to-square" style="color: white" />
|
||||
</RouterLink>
|
||||
|
||||
Reference in New Issue
Block a user