mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-20 08:49:36 -06:00
feat(frontend): user permissions
This commit is contained in:
@@ -11,6 +11,7 @@ import { ref } from 'vue'
|
|||||||
import { Button } from 'primevue'
|
import { Button } from 'primevue'
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import type { UserPermission } from '@/services/api-schema'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
@@ -29,7 +30,9 @@ const menuItems = ref([
|
|||||||
])
|
])
|
||||||
|
|
||||||
onMounted(() => {
|
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' })
|
menuItems.value.push({ label: 'Staff Dashboard', route: '/staff-dashboard' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<i
|
<i
|
||||||
class="pi pi-pen-to-square"
|
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')"
|
v-tooltip.top="t('artist.edit')"
|
||||||
@click="editArtist"
|
@click="editArtist"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<i
|
<i
|
||||||
class="pi pi-pen-to-square"
|
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"
|
@click="editCommentDialogVisible = true"
|
||||||
/>
|
/>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
@@ -55,6 +55,7 @@ const props = defineProps<{
|
|||||||
comment: TitleGroupCommentHierarchy | ForumPostHierarchy | ConversationMessageHierarchy
|
comment: TitleGroupCommentHierarchy | ForumPostHierarchy | ConversationMessageHierarchy
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
editCommentMethod?: Function
|
editCommentMethod?: Function
|
||||||
|
hasEditPermission: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
:key="message.id"
|
:key="message.id"
|
||||||
:comment="message"
|
:comment="message"
|
||||||
:class="`message ${userStore.id === message.created_by.id ? 'sent' : 'received'}`"
|
:class="`message ${userStore.id === message.created_by.id ? 'sent' : 'received'}`"
|
||||||
|
:hasEditPermission="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<label for="subcategory">{{ t('forum.subcategory') }}</label>
|
<label for="subcategory">{{ t('forum.subcategory') }}</label>
|
||||||
</FloatLabel>
|
</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">
|
<div class="checkbox-row">
|
||||||
<Checkbox v-model="editedThread.locked" binary inputId="locked" />
|
<Checkbox v-model="editedThread.locked" binary inputId="locked" />
|
||||||
<label for="locked">{{ t('general.locked') }}</label>
|
<label for="locked">{{ t('general.locked') }}</label>
|
||||||
|
|||||||
@@ -3,12 +3,16 @@
|
|||||||
<div class="top">
|
<div class="top">
|
||||||
<div class="title">{{ forumCategory.name }}</div>
|
<div class="title">{{ forumCategory.name }}</div>
|
||||||
<div class="actions">
|
<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" />
|
<i class="pi pi-pen-to-square" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="{ path: '/forum/sub-category/new', query: { categoryId: forumCategory.id, categoryName: forumCategory.name } }"
|
: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')"
|
v-tooltip.top="t('forum.create_sub_category')"
|
||||||
>
|
>
|
||||||
<i class="pi pi-plus" />
|
<i class="pi pi-plus" />
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
:comment="comment"
|
:comment="comment"
|
||||||
@commentEdited="commentEdited($event, comment.id)"
|
@commentEdited="commentEdited($event, comment.id)"
|
||||||
:editCommentMethod="(post: EditedTitleGroupComment) => editTitleGroupComment({ EditedTitleGroupComment: post, id: comment.id })"
|
:editCommentMethod="(post: EditedTitleGroupComment) => editTitleGroupComment({ EditedTitleGroupComment: post, id: comment.id })"
|
||||||
|
:hasEditPermission="useUserStore().permissions.includes('edit_title_group_comment')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Form v-slot="$form" :initialValues="new_comment" :resolver @submit="onFormSubmit" validateOnSubmit :validateOnValueUpdate="false">
|
<Form v-slot="$form" :initialValues="new_comment" :resolver @submit="onFormSubmit" validateOnSubmit :validateOnValueUpdate="false">
|
||||||
|
|||||||
@@ -49,11 +49,11 @@
|
|||||||
<i
|
<i
|
||||||
v-tooltip.top="t('general.delete')"
|
v-tooltip.top="t('general.delete')"
|
||||||
class="action pi pi-trash"
|
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)"
|
@click="deleteTorrent(slotProps.data.id)"
|
||||||
/>
|
/>
|
||||||
<i
|
<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')"
|
v-tooltip.top="t('general.edit')"
|
||||||
@click="editTorrent(slotProps.data)"
|
@click="editTorrent(slotProps.data)"
|
||||||
class="action pi pi-pen-to-square"
|
class="action pi pi-pen-to-square"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{{ t('user.last_seen') }}:
|
{{ t('user.last_seen') }}:
|
||||||
<span v-tooltip.top="formatDate(user.last_seen)">{{ timeAgo(user.last_seen) }}</span>
|
<span v-tooltip.top="formatDate(user.last_seen)">{{ timeAgo(user.last_seen) }}</span>
|
||||||
<br />
|
<br />
|
||||||
{{ t('user.class') }}: {{ user.class }}
|
{{ t('user.class') }}: {{ user.class_name }}
|
||||||
<br />
|
<br />
|
||||||
{{ t('user.bonus_points') }}: {{ user.bonus_points }}
|
{{ t('user.bonus_points') }}: {{ user.bonus_points }}
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -1049,7 +1049,7 @@ export interface PublicUser {
|
|||||||
'average_seeding_time': number;
|
'average_seeding_time': number;
|
||||||
'banned': boolean;
|
'banned': boolean;
|
||||||
'bonus_points': number;
|
'bonus_points': number;
|
||||||
'class': UserClass;
|
'class_name': string;
|
||||||
'collages_started': number;
|
'collages_started': number;
|
||||||
'created_at': string;
|
'created_at': string;
|
||||||
'description': string;
|
'description': string;
|
||||||
@@ -1076,8 +1076,6 @@ export interface PublicUser {
|
|||||||
'username': string;
|
'username': string;
|
||||||
'warned': boolean;
|
'warned': boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface RefreshToken {
|
export interface RefreshToken {
|
||||||
'refresh_token': string;
|
'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 {
|
export interface TorrentReport {
|
||||||
'description': string;
|
'description': string;
|
||||||
'id': number;
|
'id': number;
|
||||||
@@ -1669,7 +1662,8 @@ export interface User {
|
|||||||
'average_seeding_time': number;
|
'average_seeding_time': number;
|
||||||
'banned': boolean;
|
'banned': boolean;
|
||||||
'bonus_points': number;
|
'bonus_points': number;
|
||||||
'class': UserClass;
|
'class_locked': boolean;
|
||||||
|
'class_name': string;
|
||||||
'collages_started': number;
|
'collages_started': number;
|
||||||
'created_at': string;
|
'created_at': string;
|
||||||
'css_sheet_name': string;
|
'css_sheet_name': string;
|
||||||
@@ -1686,6 +1680,7 @@ export interface User {
|
|||||||
'leeching': number;
|
'leeching': number;
|
||||||
'passkey': string;
|
'passkey': string;
|
||||||
'password_hash': string;
|
'password_hash': string;
|
||||||
|
'permissions': Array<UserPermission>;
|
||||||
'ratio': number;
|
'ratio': number;
|
||||||
'real_downloaded': number;
|
'real_downloaded': number;
|
||||||
'real_uploaded': number;
|
'real_uploaded': number;
|
||||||
@@ -1703,8 +1698,6 @@ export interface User {
|
|||||||
'username': string;
|
'username': string;
|
||||||
'warned': boolean;
|
'warned': boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface UserApplication {
|
export interface UserApplication {
|
||||||
'applied_from_ip': string;
|
'applied_from_ip': string;
|
||||||
'body': string;
|
'body': string;
|
||||||
@@ -1727,16 +1720,6 @@ export const UserApplicationStatus = {
|
|||||||
export type UserApplicationStatus = typeof UserApplicationStatus[keyof typeof 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 {
|
export interface UserCreatedAffiliatedArtist {
|
||||||
'artist_id': number;
|
'artist_id': number;
|
||||||
'nickname'?: string | null;
|
'nickname'?: string | null;
|
||||||
@@ -1924,6 +1907,50 @@ export interface UserLiteAvatar {
|
|||||||
'username': string;
|
'username': string;
|
||||||
'warned': boolean;
|
'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 {
|
export interface UserSettings {
|
||||||
'css_sheet_name': string;
|
'css_sheet_name': string;
|
||||||
}
|
}
|
||||||
@@ -9172,39 +9199,6 @@ export const TorrentApiAxiosParamCreator = function (configuration?: Configurati
|
|||||||
options: localVarRequestOptions,
|
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
|
* @param {string} period
|
||||||
@@ -9370,17 +9364,6 @@ export const TorrentApiFp = function(configuration?: Configuration) {
|
|||||||
const localVarOperationServerBasePath = operationServerMap['TorrentApi.editTorrent']?.[localVarOperationServerIndex]?.url;
|
const localVarOperationServerBasePath = operationServerMap['TorrentApi.editTorrent']?.[localVarOperationServerIndex]?.url;
|
||||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
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
|
* @param {string} period
|
||||||
@@ -9479,14 +9462,6 @@ export const TorrentApiFactory = function (configuration?: Configuration, basePa
|
|||||||
editTorrent(editedTorrent: EditedTorrent, options?: RawAxiosRequestConfig): AxiosPromise<Torrent> {
|
editTorrent(editedTorrent: EditedTorrent, options?: RawAxiosRequestConfig): AxiosPromise<Torrent> {
|
||||||
return localVarFp.editTorrent(editedTorrent, options).then((request) => request(axios, basePath));
|
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
|
* @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));
|
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
|
* @param {string} period
|
||||||
@@ -9691,12 +9657,6 @@ export const editTorrent = async (editedTorrent: EditedTorrent, options?: RawAxi
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getRegisteredTorrents = async (options?: RawAxiosRequestConfig): Promise<Array<TorrentMinimal>> => {
|
|
||||||
const response = await torrentApi.getRegisteredTorrents(options);
|
|
||||||
return response.data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface GetTopTorrentRequest {
|
export interface GetTopTorrentRequest {
|
||||||
/** */
|
/** */
|
||||||
'period': string;
|
'period': string;
|
||||||
@@ -10365,7 +10325,7 @@ export const UserApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
warnUser: async (userCreatedUserWarning: UserCreatedUserWarning, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
warnUser: async (userCreatedUserWarning: UserCreatedUserWarning, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
// verify required parameter 'userCreatedUserWarning' is not null or undefined
|
// verify required parameter 'userCreatedUserWarning' is not null or undefined
|
||||||
assertParamExists('warnUser', 'userCreatedUserWarning', userCreatedUserWarning)
|
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.
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
let baseOptions;
|
let baseOptions;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ const initialState: User = {
|
|||||||
avatar: null,
|
avatar: null,
|
||||||
average_seeding_time: 0,
|
average_seeding_time: 0,
|
||||||
bonus_points: 0,
|
bonus_points: 0,
|
||||||
class: 'newbie',
|
class_name: 'newbie',
|
||||||
|
class_locked: false,
|
||||||
|
permissions: [],
|
||||||
collages_started: 0,
|
collages_started: 0,
|
||||||
created_at: '',
|
created_at: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
|||||||
@@ -64,10 +64,20 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</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">
|
<template #body="slotProps">
|
||||||
<i class="pi pi-pen-to-square cursor-pointer" v-tooltip.top="t('general.edit')" @click="editTag(slotProps.data)" />
|
<i
|
||||||
<i class="pi pi-trash cursor-pointer" v-tooltip.top="t('general.delete')" @click="deleteTag(slotProps.data)" />
|
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>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<i
|
<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')"
|
v-tooltip.top="t('general.edit')"
|
||||||
class="pi pi-pen-to-square"
|
class="pi pi-pen-to-square"
|
||||||
@click="editTitleGroupDialogVisible = true"
|
@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">
|
<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" />
|
<i v-tooltip.top="t('user.message_user', [user.username])" class="pi pi-envelope" />
|
||||||
</RouterLink>
|
</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" />
|
<i v-tooltip.top="t('user.warn')" class="cursor-pointer pi pi-exclamation-triangle" @click="warnUserDialogVisible = true" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="userStore.id === user.id">
|
<template v-if="userStore.id === user.id">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<RouterLink to="/forum/search">
|
<RouterLink to="/forum/search">
|
||||||
<i class="pi pi-search" v-tooltip.top="t('forum.search')" />
|
<i class="pi pi-search" v-tooltip.top="t('forum.search')" />
|
||||||
</RouterLink>
|
</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')" />
|
<i class="pi pi-plus" v-tooltip.top="t('forum.create_category')" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RouterLink to="">{{ forumSubCategory.name }}</RouterLink>
|
<RouterLink to="">{{ forumSubCategory.name }}</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<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" />
|
<i v-tooltip.top="t('forum.edit_subcategory')" class="pi pi-pen-to-square cursor-pointer" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink :to="`/forum/thread/new?subCategoryId=${route.params.id}`">
|
<RouterLink :to="`/forum/thread/new?subCategoryId=${route.params.id}`">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<i
|
<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"
|
class="pi pi-pen-to-square"
|
||||||
v-tooltip.top="t('forum.edit_thread')"
|
v-tooltip.top="t('forum.edit_thread')"
|
||||||
@click="editThreadDialogVisible = true"
|
@click="editThreadDialogVisible = true"
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
:comment="post"
|
:comment="post"
|
||||||
:editCommentMethod="editForumPostMethod"
|
:editCommentMethod="editForumPostMethod"
|
||||||
@commentEdited="postEdited($event as EditedForumPost)"
|
@commentEdited="postEdited($event as EditedForumPost)"
|
||||||
|
:hasEditPermission="userStore.permissions.includes('edit_forum_post')"
|
||||||
/>
|
/>
|
||||||
</PaginatedResults>
|
</PaginatedResults>
|
||||||
<Form v-slot="$form" :initialValues="newPost" :resolver @submit="onFormSubmit" validateOnSubmit :validateOnValueUpdate="false">
|
<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>
|
<Tab value="2">{{ t('css_sheet.css_sheet', 2) }}</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel value="0">
|
<TabPanel value="0" v-if="userStore.permissions.includes('get_user_application')">
|
||||||
<UserApplications />
|
<UserApplications />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="1">
|
<TabPanel value="1" v-if="userStore.permissions.includes('read_staff_pm')">
|
||||||
<StaffPmsTable />
|
<StaffPmsTable />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="2">
|
<TabPanel value="2" v-if="userStore.permissions.includes('edit_css_sheet') || userStore.permissions.includes('create_css_sheet')">
|
||||||
<CssSheetList showStaffActions />
|
<CssSheetList showStaffActions />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
@@ -31,8 +31,10 @@ import UserApplications from '@/components/staff/UserApplications.vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import StaffPmsTable from '@/components/staff_pm/StaffPmsTable.vue'
|
import StaffPmsTable from '@/components/staff_pm/StaffPmsTable.vue'
|
||||||
import CssSheetList from '@/components/CssSheetList.vue'
|
import CssSheetList from '@/components/CssSheetList.vue'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const userStore = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="actions wrapper-center">
|
<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')" />
|
<i class="pi pi-plus" v-tooltip.top="t('wiki.create_article')" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink to="/wiki/search">
|
<RouterLink to="/wiki/search">
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="wikiArticle" class="wiki-article">
|
<div v-if="wikiArticle" class="wiki-article">
|
||||||
<ContentContainer :containerTitle="wikiArticle.title">
|
<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')">
|
<RouterLink :to="`/wiki/article/${wikiArticle.id}/edit`" v-tooltip.top="t('wiki.edit_article')">
|
||||||
<i class="pi pi-pen-to-square" style="color: white" />
|
<i class="pi pi-pen-to-square" style="color: white" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|||||||
Reference in New Issue
Block a user