mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-02-17 15:01:42 +08:00
work on version page
This commit is contained in:
parent
2ba0abe3d4
commit
7af8e89398
@ -268,6 +268,7 @@ const msgs: LocaleMessageObject = {
|
||||
required: '(required)',
|
||||
adminMsg: '{0} approved this version on {1}',
|
||||
reviewLogs: 'Review logs',
|
||||
reviewStart: 'Start review',
|
||||
delete: 'Delete',
|
||||
download: 'Download',
|
||||
downloadExternal: 'Download External',
|
||||
|
@ -11,8 +11,8 @@ export default async ({ app: { $cookies }, $auth, $api, store, redirect }: Conte
|
||||
path: '/',
|
||||
});
|
||||
redirect(returnRoute);
|
||||
}
|
||||
if ($cookies.get('HangarAuth_REFRESH', { parseJSON: false })) {
|
||||
// TODO if not running hangarauth locally, this needs to just be a regular if not an else-if (idk what a good fix for that is)
|
||||
} else if ($cookies.get('HangarAuth_REFRESH', { parseJSON: false })) {
|
||||
const token = await $api.getToken(true);
|
||||
if (token != null) {
|
||||
if (store.state.auth.authenticated) {
|
||||
|
@ -58,31 +58,33 @@
|
||||
<FlagModal :project="project" />
|
||||
<v-menu v-if="isStaff" bottom offset-y>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn v-bind="attrs" v-on="on">
|
||||
<v-btn v-bind="attrs" class="ml-1" v-on="on">
|
||||
{{ $t('project.actions.adminActions') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list-item :to="slug + '/flags'">
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.flagHistory', [project.info.flagCount]) }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :to="slug + '/notes'">
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.staffNotes', [project.info.noteCount]) }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :to="'/admin/log/?projectFilter=' + slug">
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.userActionLogs') }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :href="$util.forumUrl(project.namespace.owner)">
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.forum') }}
|
||||
<v-icon>mdi-open-in-new</v-icon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list>
|
||||
<v-list-item :to="slug + '/flags'" nuxt>
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.flagHistory', [project.info.flagCount]) }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :to="slug + '/notes'" nuxt>
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.staffNotes', [project.info.noteCount]) }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :to="'/admin/log/?projectFilter=' + slug" nuxt>
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.userActionLogs') }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :href="$util.forumUrl(project.namespace.owner)">
|
||||
<v-list-item-title>
|
||||
{{ $t('project.actions.forum') }}
|
||||
<v-icon>mdi-open-in-new</v-icon>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
@ -1,46 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row v-if="version">
|
||||
<div style="float: left">
|
||||
<v-col>
|
||||
<h1>{{ version.name }}</h1>
|
||||
<TagComponent :tag="channel" :short-form="true" />
|
||||
<v-subheader>{{ $t('version.page.subheader', [version.author, $util.prettyDate(version.createdAt)]) }}</v-subheader>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<v-subheader>
|
||||
<!-- todo approver and stuff, perm -->
|
||||
<i v-if="true">{{ $t('version.page.adminMsg', [version.author, $util.prettyDate(version.createdAt)]) }}</i>
|
||||
<!-- todo check if recommended -->
|
||||
<v-icon v-if="true" :title="$t('version.page.recommended')">mdi-diamond-stone</v-icon>
|
||||
<v-icon v-if="isChecked" :title="approvalTooltip">mdi-check-circle-outline</v-icon>
|
||||
</v-col>
|
||||
<v-col class="text-right">
|
||||
<v-subheader style="justify-content: end">
|
||||
<i v-if="isReviewer && version.approvedBy">{{ $t('version.page.adminMsg', [version.approvedBy, $util.prettyDate(version.createdAt)]) }}</i>
|
||||
<v-icon v-if="version.recommended" :title="$t('version.page.recommended')">mdi-diamond-stone</v-icon>
|
||||
<v-icon v-if="isReviewStateChecked" :title="approvalTooltip">mdi-check-circle-outline</v-icon>
|
||||
</v-subheader>
|
||||
<!-- todo perms -->
|
||||
<v-btn color="secondary" :to="$route.path + '/reviews'">{{ $t('version.page.reviewLogs') }}</v-btn>
|
||||
<v-btn color="error" @click="deleteVersion">{{ $t('version.page.delete') }}</v-btn>
|
||||
<!-- todo check recommended -->
|
||||
<v-btn v-if="true" color="primary" :to="$route.path + '/download'">{{ $t('version.page.download') }}</v-btn>
|
||||
<!-- todo maybe move the review logs to the admin actions dropdown? -->
|
||||
<template v-if="isReviewer">
|
||||
<v-btn v-if="isReviewStateChecked" color="secondary" :to="$route.path + '/reviews'" nuxt>{{ $t('version.page.reviewLogs') }}</v-btn>
|
||||
<v-btn v-else color="secondary" :to="$route.path + '/reviews'" nuxt>
|
||||
<v-icon left>mdi-play</v-icon>
|
||||
{{ $t('version.page.reviewStart') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-btn v-if="canDeleteVersion" color="error" @click="deleteVersion">{{ $t('version.page.delete') }}</v-btn>
|
||||
<v-btn v-if="!version.externalUrl" color="primary" :to="$route.path + '/download'">{{ $t('version.page.download') }}</v-btn>
|
||||
<v-btn v-else color="primary" :to="$route.path + '/download'">{{ $t('version.page.downloadExternal') }}</v-btn>
|
||||
<!-- todo perms -->
|
||||
<v-menu offset-y>
|
||||
<v-menu v-if="canViewLogs || isReviewer || canHardDeleteVersion" offset-y open-on-hover>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn plain dark v-bind="attrs" v-on="on">{{ $t('version.page.adminActions') }}</v-btn>
|
||||
<v-btn v-ripple="false" plain v-bind="attrs" v-on="on">
|
||||
{{ $t('version.page.adminActions') }}
|
||||
<v-icon right>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<!--todo route for user action log-->
|
||||
<v-list-item nuxt :to="`ddd`">
|
||||
<v-list-item-title>
|
||||
<nuxt-link to="ddd" class="text-decoration-none">
|
||||
{{ $t('version.page.userAdminLogs') }}
|
||||
</nuxt-link>
|
||||
{{ $t('version.page.userAdminLogs') }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="isReviewer && version.visibility === 'softDelete'">
|
||||
<!--todo i18n & restore modal-->
|
||||
<v-list-item-title>Undo delete</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="canHardDeleteVersion && !version.recommended && (project.info.publicVersions > 1 || version.visibility === 'softDelete')"
|
||||
>
|
||||
<!--todo i18n & hard delete modal-->
|
||||
<v-list-item-title>Hard delete</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
<v-divider />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="version">
|
||||
<v-col cols="12" md="8">
|
||||
<MarkdownEditor v-if="canEdit" ref="editor" :raw="version.description" :editing.sync="editingPage" :deletable="false" @save="save" />
|
||||
<MarkdownEditor v-if="canEdit" ref="editor" :raw="version.description" :editing.sync="editingPage" :deletable="false" @save="savePage" />
|
||||
<Markdown v-else :raw="version.description" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
@ -53,30 +67,28 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'nuxt-property-decorator';
|
||||
import { Component } from 'nuxt-property-decorator';
|
||||
import { Context } from '@nuxt/types';
|
||||
import { Project, Tag, Version } from 'hangar-api';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
import { Tag } from 'hangar-api';
|
||||
import { HangarVersion } from 'hangar-internal';
|
||||
import MarkdownEditor from '~/components/MarkdownEditor.vue';
|
||||
import Markdown from '~/components/Markdown.vue';
|
||||
import { NamedPermission, ReviewState } from '~/types/enums';
|
||||
import TagComponent from '~/components/Tag.vue';
|
||||
import { HangarProjectMixin } from '~/components/mixins';
|
||||
|
||||
// TODO implement ProjectVersionsVersionPage
|
||||
@Component({
|
||||
components: { TagComponent, Markdown, MarkdownEditor },
|
||||
})
|
||||
export default class ProjectVersionsVersionPage extends Vue {
|
||||
@Prop()
|
||||
project!: Project;
|
||||
|
||||
versions!: Version[];
|
||||
|
||||
export default class ProjectVersionsVersionPage extends HangarProjectMixin {
|
||||
versions!: HangarVersion[];
|
||||
version!: HangarVersion;
|
||||
editingPage: boolean = false;
|
||||
|
||||
get version(): Version | null {
|
||||
return this.versions && this.versions.length > 0 ? this.versions[0] : null;
|
||||
}
|
||||
// get version(): Version | null {
|
||||
// return this.versions && this.versions.length > 0 ? this.versions[0] : null;
|
||||
// }
|
||||
|
||||
get channel(): Tag | null {
|
||||
return this.version?.tags?.find((t) => t.name === 'Channel') || null;
|
||||
@ -86,7 +98,23 @@ export default class ProjectVersionsVersionPage extends Vue {
|
||||
return this.$util.hasPerms(NamedPermission.EDIT_VERSION);
|
||||
}
|
||||
|
||||
get isChecked() {
|
||||
get canDeleteVersion() {
|
||||
return this.$util.hasPerms(NamedPermission.DELETE_VERSION);
|
||||
}
|
||||
|
||||
get canHardDeleteVersion() {
|
||||
return this.$util.hasPerms(NamedPermission.HARD_DELETE_VERSION);
|
||||
}
|
||||
|
||||
get canViewLogs() {
|
||||
return this.$util.hasPerms(NamedPermission.VIEW_LOGS);
|
||||
}
|
||||
|
||||
get isReviewer() {
|
||||
return this.$util.hasPerms(NamedPermission.REVIEWER);
|
||||
}
|
||||
|
||||
get isReviewStateChecked() {
|
||||
return this.version?.reviewState === ReviewState.PARTIALLY_REVIEWED || this.version?.reviewState === ReviewState.REVIEWED;
|
||||
}
|
||||
|
||||
@ -94,16 +122,40 @@ export default class ProjectVersionsVersionPage extends Vue {
|
||||
return this.version?.reviewState === ReviewState.PARTIALLY_REVIEWED ? this.$t('version.page.partiallyApproved') : this.$t('version.page.approved');
|
||||
}
|
||||
|
||||
async asyncData({ $api, $util, params }: Context) {
|
||||
async asyncData({ $api, $util, params, error }: Context) {
|
||||
const versions = await $api
|
||||
.request<Version[]>(`projects/${params.author}/${params.slug}/versions/${params.version}`)
|
||||
.catch($util.handlePageRequestError);
|
||||
return { versions };
|
||||
.requestInternal<HangarVersion[]>(`versions/version/${params.author}/${params.slug}/versions/${params.version}`)
|
||||
.catch<any>($util.handlePageRequestError);
|
||||
if (versions.length < 1) {
|
||||
return error({
|
||||
statusCode: 404,
|
||||
});
|
||||
}
|
||||
// TODO maybe select default version differently?
|
||||
return { versions, version: versions[0] };
|
||||
}
|
||||
|
||||
$refs!: {
|
||||
editor: MarkdownEditor;
|
||||
};
|
||||
|
||||
savePage(content: string) {
|
||||
this.$api
|
||||
.requestInternal(`versions/version/${this.project.id}/${this.version.id}/saveDescription`, true, 'post', {
|
||||
content,
|
||||
})
|
||||
.then(() => {
|
||||
this.version.description = content;
|
||||
this.editingPage = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$refs.editor.loading.save = false;
|
||||
// TODO i18n for version desc save?
|
||||
this.$util.handleRequestError(err, 'page.new.error.save');
|
||||
});
|
||||
}
|
||||
|
||||
// TODO implement all of the below
|
||||
save() {}
|
||||
|
||||
deleteVersion() {}
|
||||
}
|
||||
</script>
|
||||
|
@ -11,11 +11,11 @@
|
||||
<v-row>
|
||||
<v-col cols="12">{{ version.name }}</v-col>
|
||||
<!-- todo is this order always this way? -->
|
||||
<Tag :tag="version.tags[version.tags.length - 1]" />
|
||||
<Tag :tag="getChannelTag(version)" />
|
||||
</v-row>
|
||||
</v-col>
|
||||
<v-col cols="8" md="6" lg="4">
|
||||
<Tag v-for="(tag, index) in version.tags.slice(0, version.tags.length - 1)" :key="index" :tag="tag" />
|
||||
<Tag v-for="(tag, index) in getNonChannelTags(version)" :key="index" :tag="tag" />
|
||||
</v-col>
|
||||
<v-col cols="0" md="4" lg="3">
|
||||
<v-row>
|
||||
@ -67,7 +67,7 @@ import { Component, Prop, Vue } from 'nuxt-property-decorator';
|
||||
import { PropType } from 'vue';
|
||||
import { HangarProject } from 'hangar-internal';
|
||||
import { Context } from '@nuxt/types';
|
||||
import { PaginatedResult, Version } from 'hangar-api';
|
||||
import { PaginatedResult, Tag as ApiTag, Version } from 'hangar-api';
|
||||
import { NamedPermission } from '~/types/enums';
|
||||
import Tag from '~/components/Tag.vue';
|
||||
|
||||
@ -94,6 +94,18 @@ export default class ProjectVersionsPage extends Vue {
|
||||
get canUpload() {
|
||||
return this.$util.hasPerms(NamedPermission.CREATE_VERSION);
|
||||
}
|
||||
|
||||
getChannelTag(version: Version): ApiTag {
|
||||
const channelTag = version.tags.find((t) => t.name === 'Channel');
|
||||
if (typeof channelTag === 'undefined') {
|
||||
throw new TypeError('Version missing a channel tag');
|
||||
}
|
||||
return channelTag;
|
||||
}
|
||||
|
||||
getNonChannelTags(version: Version): ApiTag[] {
|
||||
return version.tags.filter((t) => t.name !== 'Channel');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
2
frontend/types/api/versions.d.ts
vendored
2
frontend/types/api/versions.d.ts
vendored
@ -30,8 +30,10 @@ declare module 'hangar-api' {
|
||||
description: string;
|
||||
stats: VersionStats;
|
||||
fileInfo: FileInfo;
|
||||
externalUrl: string | null;
|
||||
author: String;
|
||||
reviewState: ReviewState;
|
||||
tags: Tag[];
|
||||
recommended: boolean;
|
||||
}
|
||||
}
|
||||
|
7
frontend/types/internal/versions.d.ts
vendored
7
frontend/types/internal/versions.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
declare module 'hangar-internal' {
|
||||
import { FileInfo, Named } from 'hangar-api';
|
||||
import { FileInfo, Named, Version } from 'hangar-api';
|
||||
import { Platform } from '~/types/enums';
|
||||
|
||||
interface PlatformDependency {
|
||||
@ -30,4 +30,9 @@ declare module 'hangar-internal' {
|
||||
nonReviewed: boolean;
|
||||
temp?: boolean;
|
||||
}
|
||||
|
||||
interface HangarVersion extends Version {
|
||||
id: number;
|
||||
approvedBy?: string;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ export function GlobalPermission(...permissions: NamedPermission[]) {
|
||||
permissions,
|
||||
})
|
||||
.then((check) => {
|
||||
console.log(check);
|
||||
if (check.type !== PermissionType.GLOBAL || !check.result) {
|
||||
error({
|
||||
message: 'Not Found',
|
||||
|
@ -3,6 +3,7 @@ package io.papermc.hangar.controller;
|
||||
import io.papermc.hangar.config.hangar.HangarConfig;
|
||||
import io.papermc.hangar.security.HangarAuthenticationToken;
|
||||
import io.papermc.hangar.security.HangarPrincipal;
|
||||
import io.papermc.hangar.service.internal.UserActionLogService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -27,6 +28,9 @@ public abstract class HangarController {
|
||||
@Autowired
|
||||
protected HttpServletResponse response;
|
||||
|
||||
@Autowired
|
||||
protected UserActionLogService userActionLogService;
|
||||
|
||||
protected final HangarPrincipal getHangarPrincipal() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (!(authentication instanceof HangarAuthenticationToken)) {
|
||||
|
@ -3,8 +3,8 @@ package io.papermc.hangar.controller.internal;
|
||||
import io.papermc.hangar.controller.HangarController;
|
||||
import io.papermc.hangar.model.db.projects.ProjectChannelTable;
|
||||
import io.papermc.hangar.security.annotations.Anyone;
|
||||
import io.papermc.hangar.security.annotations.visibility.Type;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
|
||||
import io.papermc.hangar.service.internal.projects.ChannelService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -7,8 +7,8 @@ import io.papermc.hangar.model.internal.HangarProject;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProject;
|
||||
import io.papermc.hangar.model.internal.api.responses.PossibleProjectOwner;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.security.annotations.visibility.Type;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
|
||||
import io.papermc.hangar.service.internal.OrganizationService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectFactory;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
|
@ -9,8 +9,8 @@ import io.papermc.hangar.model.internal.api.requests.projects.NewProjectPage;
|
||||
import io.papermc.hangar.security.annotations.Anyone;
|
||||
import io.papermc.hangar.security.annotations.permission.PermissionRequired;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.security.annotations.visibility.Type;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
|
||||
import io.papermc.hangar.service.internal.MarkdownService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectPageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -54,7 +54,7 @@ public class ProjectPageController extends HangarController {
|
||||
|
||||
@Unlocked
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_PAGE, type = PermissionType.PROJECT, args = "{#projectId}")
|
||||
@PostMapping("/create/{projectId}")
|
||||
@PostMapping(value = "/create/{projectId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ResponseEntity<String> createProjectPage(@PathVariable long projectId, @RequestBody @Valid NewProjectPage newProjectPage) {
|
||||
return ResponseEntity.ok(projectPageService.createProjectPage(projectId, newProjectPage));
|
||||
@ -62,7 +62,7 @@ public class ProjectPageController extends HangarController {
|
||||
|
||||
@Unlocked
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_PAGE, type = PermissionType.PROJECT, args = "{#projectId}")
|
||||
@PostMapping("/save/{projectId}/{pageId}")
|
||||
@PostMapping(value = "/save/{projectId}/{pageId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public void saveProjectPage(@PathVariable long projectId, @PathVariable long pageId, @RequestBody @Valid StringContent content) {
|
||||
projectPageService.saveProjectPage(projectId, pageId, content.getContent());
|
||||
|
@ -1,18 +1,28 @@
|
||||
package io.papermc.hangar.controller.internal;
|
||||
|
||||
import io.papermc.hangar.controller.HangarController;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType.VersionContext;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.PermissionType;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.internal.api.requests.StringContent;
|
||||
import io.papermc.hangar.model.internal.versions.HangarVersion;
|
||||
import io.papermc.hangar.model.internal.versions.PendingVersion;
|
||||
import io.papermc.hangar.security.annotations.permission.PermissionRequired;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
|
||||
import io.papermc.hangar.service.internal.versions.VersionFactory;
|
||||
import io.papermc.hangar.service.internal.versions.VersionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@ -22,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@Secured("ROLE_USER")
|
||||
@ -29,10 +40,18 @@ import javax.validation.Valid;
|
||||
public class VersionController extends HangarController {
|
||||
|
||||
private final VersionFactory versionFactory;
|
||||
private final VersionService versionService;
|
||||
|
||||
@Autowired
|
||||
public VersionController(VersionFactory versionFactory) {
|
||||
public VersionController(VersionFactory versionFactory, VersionService versionService) {
|
||||
this.versionFactory = versionFactory;
|
||||
this.versionService = versionService;
|
||||
}
|
||||
|
||||
@VisibilityRequired(type = Type.PROJECT, args = "{#author, #slug}")
|
||||
@GetMapping(path = "/version/{author}/{slug}/versions/{versionString}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<List<HangarVersion>> getVersions(@PathVariable String author, @PathVariable String slug, @PathVariable String versionString) {
|
||||
return ResponseEntity.ok(versionService.getHangarVersions(author, slug, versionString));
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ -59,4 +78,20 @@ public class VersionController extends HangarController {
|
||||
System.out.println(pendingVersion);
|
||||
versionFactory.publishPendingVersion(projectId, pendingVersion);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_VERSION, args = "{#projectId}")
|
||||
@PostMapping(path = "/version/{projectId}/{versionId}/saveDescription", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveDescription(@PathVariable long projectId, @PathVariable long versionId, @Valid @RequestBody StringContent stringContent) {
|
||||
ProjectVersionTable projectVersionTable = versionService.getProjectVersionTable(versionId);
|
||||
if (projectVersionTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
String oldDesc = projectVersionTable.getDescription();
|
||||
String newDesc = stringContent.getContent().trim();
|
||||
projectVersionTable.setDescription(newDesc);
|
||||
versionService.updateProjectVersionTable(projectVersionTable);
|
||||
userActionLogService.version(LoggedActionType.VERSION_DESCRIPTION_CHANGED.with(VersionContext.of(projectId, versionId)), newDesc, oldDesc);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
package io.papermc.hangar.db.dao.internal;
|
||||
|
||||
import io.papermc.hangar.db.dao.v1.VersionsApiDAO.VersionReducer;
|
||||
import io.papermc.hangar.model.api.project.version.PluginDependency;
|
||||
import io.papermc.hangar.model.internal.versions.HangarVersion;
|
||||
import org.jdbi.v3.core.enums.EnumStrategy;
|
||||
import org.jdbi.v3.core.result.LinkedHashMapRowReducer;
|
||||
import org.jdbi.v3.core.result.RowView;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
|
||||
import org.jdbi.v3.sqlobject.config.UseEnumStrategy;
|
||||
import org.jdbi.v3.sqlobject.customizer.Define;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlQuery;
|
||||
import org.jdbi.v3.sqlobject.statement.UseRowReducer;
|
||||
import org.jdbi.v3.stringtemplate4.UseStringTemplateEngine;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Repository
|
||||
@RegisterConstructorMapper(HangarVersion.class)
|
||||
public interface HangarVersionsDAO {
|
||||
@UseEnumStrategy(EnumStrategy.BY_ORDINAL)
|
||||
@UseRowReducer(HangarVersionReducer.class)
|
||||
@RegisterConstructorMapper(value = PluginDependency.class, prefix = "pd_")
|
||||
@UseStringTemplateEngine
|
||||
@SqlQuery("SELECT pv.id," +
|
||||
" pv.created_at," +
|
||||
" pv.version_string," +
|
||||
" pv.visibility," +
|
||||
" pv.description," +
|
||||
" coalesce((SELECT sum(pvd.downloads) FROM project_versions_downloads pvd WHERE p.id = pvd.project_id AND pv.id = pvd.version_id), 0) vs_downloads," +
|
||||
" pv.file_name fi_name," +
|
||||
" pv.file_size fi_size_bytes," +
|
||||
" pv.hash fi_md5_hash," +
|
||||
" pv.external_url," +
|
||||
" u.name author," +
|
||||
" pv.review_state," +
|
||||
" pvt.name AS tag_name," +
|
||||
" pvt.data AS tag_data," +
|
||||
" pvt.color AS tag_color," +
|
||||
" 'Channel' AS ch_tag_name," +
|
||||
" pc.name AS ch_tag_data," +
|
||||
" pc.color AS ch_tag_color," +
|
||||
" d.platform pd_platform," +
|
||||
" d.name pd_name," +
|
||||
" d.required pd_required," +
|
||||
" d.project_id pd_project_id," +
|
||||
" d.external_url pd_external_url," +
|
||||
" plv.platform p_platform," +
|
||||
" plv.version p_version," +
|
||||
" exists(SELECT 1 FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id) as recommended," +
|
||||
" ru.name approved_by" +
|
||||
" FROM project_versions pv" +
|
||||
" JOIN projects p ON pv.project_id = p.id" +
|
||||
" LEFT JOIN users u ON pv.author_id = u.id" +
|
||||
" LEFT JOIN project_version_tags pvt ON pv.id = pvt.version_id" +
|
||||
" LEFT JOIN project_channels pc ON pv.channel_id = pc.id " +
|
||||
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
|
||||
" JOIN platform_versions plv ON pvpd.platform_version_id = plv.id" +
|
||||
" LEFT JOIN project_version_dependencies d ON pv.id = d.version_id" +
|
||||
" LEFT JOIN users ru ON pv.reviewer_id = ru.id" +
|
||||
" WHERE <if(!canSeeHidden)>(pv.visibility = 0 " +
|
||||
" <if(userId)>OR (<userId> IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4) <endif>) AND <endif> " +
|
||||
" pvt.name IS NOT NULL AND" +
|
||||
" lower(p.owner_name) = lower(:author) AND" +
|
||||
" lower(p.slug) = lower(:slug) AND" +
|
||||
" lower(pv.version_string) = lower(:versionString)" +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id, ru.id" +
|
||||
" ORDER BY pv.created_at DESC")
|
||||
List<HangarVersion> getVersions(String author, String slug, String versionString, @Define boolean canSeeHidden, @Define Long userId);
|
||||
|
||||
class HangarVersionReducer implements LinkedHashMapRowReducer<Long, HangarVersion> {
|
||||
@Override
|
||||
public void accumulate(Map<Long, HangarVersion> container, RowView rowView) {
|
||||
final HangarVersion version = container.computeIfAbsent(rowView.getColumn("id", Long.class), id -> rowView.getRow(HangarVersion.class));
|
||||
VersionReducer._accumulateVersion(rowView, version.getPluginDependencies(), version.getPlatformDependencies(), version.getTags(), version);
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,9 @@ public interface ProjectVersionsDAO {
|
||||
@SqlQuery("SELECT * FROM project_versions WHERE project_id = :projectId AND hash = :hash AND version_string = :versionString")
|
||||
ProjectVersionTable getProjectVersionTable(long projectId, String hash, String versionString);
|
||||
|
||||
@SqlQuery("SELECT * FROM project_versions pv WHERE pv.id = :versionId")
|
||||
ProjectVersionTable getProjectVersionTable(long versionId);
|
||||
|
||||
@Timestamped
|
||||
@SqlBatch("INSERT INTO project_version_tags (created_at, version_id, name, data, color) VALUES (:now, :versionId, :name, :data, :color)")
|
||||
void insertTags(@BindBean Collection<ProjectVersionTagTable> projectVersionTagTables);
|
||||
|
@ -27,6 +27,7 @@ import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Repository
|
||||
@UseStringTemplateEngine
|
||||
@ -45,6 +46,7 @@ public interface VersionsApiDAO {
|
||||
" pv.file_name fi_name," +
|
||||
" pv.file_size fi_size_bytes," +
|
||||
" pv.hash fi_md5_hash," +
|
||||
" pv.external_url," +
|
||||
" u.name author," +
|
||||
" pv.review_state," +
|
||||
" pvt.name AS tag_name," +
|
||||
@ -59,7 +61,9 @@ public interface VersionsApiDAO {
|
||||
" d.project_id pd_project_id," +
|
||||
" d.external_url pd_external_url," +
|
||||
" plv.platform p_platform," +
|
||||
" plv.version p_version" +
|
||||
" plv.version p_version," +
|
||||
" exists(SELECT 1 FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id) as recommended," +
|
||||
" ru.name approved_by" +
|
||||
" FROM project_versions pv" +
|
||||
" JOIN projects p ON pv.project_id = p.id" +
|
||||
" LEFT JOIN users u ON pv.author_id = u.id" +
|
||||
@ -68,6 +72,7 @@ public interface VersionsApiDAO {
|
||||
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
|
||||
" JOIN platform_versions plv ON pvpd.platform_version_id = plv.id" +
|
||||
" LEFT JOIN project_version_dependencies d ON pv.id = d.version_id" +
|
||||
" LEFT JOIN users ru ON pv.reviewer_id = ru.id" +
|
||||
" WHERE <if(!canSeeHidden)>(pv.visibility = 0 " +
|
||||
" <if(userId)>OR (<userId> IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4) <endif>) AND <endif> " +
|
||||
" plv.platform = :platform AND" +
|
||||
@ -75,7 +80,7 @@ public interface VersionsApiDAO {
|
||||
" lower(p.owner_name) = lower(:author) AND" +
|
||||
" lower(p.slug) = lower(:slug) AND" +
|
||||
" lower(pv.version_string) = lower(:versionString)" +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id " +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id, ru.id " +
|
||||
" ORDER BY pv.created_at DESC")
|
||||
Version getVersion(String author, String slug, String versionString, @EnumByOrdinal Platform platform, @Define boolean canSeeHidden, @Define Long userId);
|
||||
|
||||
@ -91,6 +96,7 @@ public interface VersionsApiDAO {
|
||||
" pv.file_name fi_name," +
|
||||
" pv.file_size fi_size_bytes," +
|
||||
" pv.hash fi_md5_hash," +
|
||||
" pv.external_url," +
|
||||
" u.name author," +
|
||||
" pv.review_state," +
|
||||
" pvt.name AS tag_name," +
|
||||
@ -105,7 +111,9 @@ public interface VersionsApiDAO {
|
||||
" d.project_id pd_project_id," +
|
||||
" d.external_url pd_external_url," +
|
||||
" plv.platform p_platform," +
|
||||
" plv.version p_version" +
|
||||
" plv.version p_version," +
|
||||
" exists(SELECT 1 FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id) as recommended," +
|
||||
" ru.name approved_by" +
|
||||
" FROM project_versions pv" +
|
||||
" JOIN projects p ON pv.project_id = p.id" +
|
||||
" LEFT JOIN users u ON pv.author_id = u.id" +
|
||||
@ -114,37 +122,41 @@ public interface VersionsApiDAO {
|
||||
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
|
||||
" JOIN platform_versions plv ON pvpd.platform_version_id = plv.id" +
|
||||
" LEFT JOIN project_version_dependencies d ON pv.id = d.version_id" +
|
||||
" LEFT JOIN users ru ON pv.reviewer_id = ru.id" +
|
||||
" WHERE <if(!canSeeHidden)>(pv.visibility = 0 " +
|
||||
" <if(userId)>OR (<userId> IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4) <endif>) AND <endif> " +
|
||||
" pvt.name IS NOT NULL AND" +
|
||||
" lower(p.owner_name) = lower(:author) AND" +
|
||||
" lower(p.slug) = lower(:slug) AND" +
|
||||
" lower(pv.version_string) = lower(:versionString)" +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id " +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id, ru.id" +
|
||||
" ORDER BY pv.created_at DESC")
|
||||
List<Version> getVersions(String author, String slug, String versionString, @Define boolean canSeeHidden, @Define Long userId);
|
||||
|
||||
class VersionReducer implements LinkedHashMapRowReducer<Long, Version> { // What a mess really
|
||||
class VersionReducer implements LinkedHashMapRowReducer<Long, Version> {
|
||||
@Override
|
||||
public void accumulate(Map<Long, Version> container, RowView rowView) {
|
||||
final Version version = container.computeIfAbsent(rowView.getColumn("id", Long.class), id -> rowView.getRow(Version.class));
|
||||
VersionReducer._accumulateVersion(rowView, version.getPluginDependencies(), version.getPlatformDependencies(), version.getTags(), version);
|
||||
}
|
||||
|
||||
public static <T extends Version> void _accumulateVersion(RowView rowView, Map<Platform, Set<PluginDependency>> pluginDependencies, Map<Platform, Set<String>> platformDependencies, Set<Tag> tags, T version) { // What a mess really
|
||||
Platform pluginPlatform = rowView.getColumn("pd_platform", Platform.class);
|
||||
if (pluginPlatform != null) {
|
||||
version.getPluginDependencies().computeIfAbsent(pluginPlatform, _pl -> new HashSet<>());
|
||||
version.getPluginDependencies().get(pluginPlatform).add(rowView.getRow(PluginDependency.class));
|
||||
pluginDependencies.computeIfAbsent(pluginPlatform, _pl -> new HashSet<>());
|
||||
pluginDependencies.get(pluginPlatform).add(rowView.getRow(PluginDependency.class));
|
||||
}
|
||||
|
||||
Platform platformPlatform = rowView.getColumn("p_platform", Platform.class);
|
||||
version.getPlatformDependencies().computeIfAbsent(platformPlatform, _pl -> new HashSet<>());
|
||||
version.getPlatformDependencies().get(platformPlatform).add(rowView.getColumn("p_version", String.class));
|
||||
platformDependencies.computeIfAbsent(platformPlatform, _pl -> new HashSet<>());
|
||||
platformDependencies.get(platformPlatform).add(rowView.getColumn("p_version", String.class));
|
||||
|
||||
if (rowView.getColumn("ch_tag_name", String.class) != null) {
|
||||
version.getTags().add(new Tag(rowView.getColumn("ch_tag_name", String.class), rowView.getColumn("ch_tag_data", String.class), new TagColor(null, rowView.getColumn("ch_tag_color", Color.class).getHex())));
|
||||
tags.add(new Tag(rowView.getColumn("ch_tag_name", String.class), rowView.getColumn("ch_tag_data", String.class), new TagColor(null, rowView.getColumn("ch_tag_color", Color.class).getHex())));
|
||||
}
|
||||
|
||||
if (rowView.getColumn("tag_name", String.class) != null) {
|
||||
version.getTags().add(new Tag(
|
||||
tags.add(new Tag(
|
||||
rowView.getColumn("tag_name", String.class),
|
||||
StringUtils.formatVersionNumbers(Arrays.asList(rowView.getColumn("tag_data", String[].class))),
|
||||
rowView.getColumn("tag_color", io.papermc.hangar.model.common.TagColor.class).toTagColor()
|
||||
@ -165,6 +177,7 @@ public interface VersionsApiDAO {
|
||||
" pv.file_name fi_name," +
|
||||
" pv.file_size fi_size_bytes," +
|
||||
" pv.hash fi_md5_hash," +
|
||||
" pv.external_url," +
|
||||
" u.name author," +
|
||||
" pv.review_state," +
|
||||
" pvt.name AS tag_name," +
|
||||
@ -179,7 +192,9 @@ public interface VersionsApiDAO {
|
||||
" d.project_id pd_project_id," +
|
||||
" d.external_url pd_external_url," +
|
||||
" plv.platform p_platform," +
|
||||
" plv.version p_version" +
|
||||
" plv.version p_version," +
|
||||
" exists(SELECT 1 FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id) as recommended," +
|
||||
" ru.name approved_by" +
|
||||
" FROM project_versions pv" +
|
||||
" JOIN projects p ON pv.project_id = p.id" +
|
||||
" LEFT JOIN users u ON pv.author_id = u.id" +
|
||||
@ -188,12 +203,13 @@ public interface VersionsApiDAO {
|
||||
" JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id" +
|
||||
" JOIN platform_versions plv ON pvpd.platform_version_id = plv.id" +
|
||||
" LEFT JOIN project_version_dependencies d ON pv.id = d.version_id" +
|
||||
" LEFT JOIN users ru ON pv.reviewer_id = ru.id" +
|
||||
" WHERE <if(!canSeeHidden)>(pv.visibility = 0 " +
|
||||
" <if(userId)>OR (<userId> IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4) <endif>) AND <endif> " +
|
||||
" pvt.name IS NOT NULL AND" +
|
||||
" lower(p.owner_name) = lower(:author) AND" +
|
||||
" lower(p.slug) = lower(:slug)" +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id " +
|
||||
" GROUP BY p.id, pv.id, u.id, pc.id, d.id, plv.id, pvt.id, ru.id " +
|
||||
" ORDER BY pv.created_at DESC LIMIT :limit OFFSET :offset")
|
||||
List<Version> getVersions(String author, String slug, @BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) List<String> tags, @Define boolean canSeeHidden, @Define Long userId, long limit, long offset);
|
||||
|
||||
|
@ -25,13 +25,17 @@ public class Version extends Model implements Named, Visible {
|
||||
private final String description;
|
||||
private final VersionStats stats;
|
||||
private final FileInfo fileInfo;
|
||||
private final String externalUrl;
|
||||
private final String author;
|
||||
private final ReviewState reviewState;
|
||||
private final Set<Tag> tags;
|
||||
private final boolean recommended;
|
||||
|
||||
public Version(OffsetDateTime createdAt, @ColumnName("version_string") String name, Visibility visibility, String description, @Nested("vs") VersionStats stats, @Nested("fi") FileInfo fileInfo, String author, @EnumByOrdinal ReviewState reviewState) {
|
||||
public Version(OffsetDateTime createdAt, @ColumnName("version_string") String name, Visibility visibility, String description, @Nested("vs") VersionStats stats, @Nested("fi") FileInfo fileInfo, String externalUrl, String author, @EnumByOrdinal ReviewState reviewState, boolean recommended) {
|
||||
super(createdAt);
|
||||
this.name = name;
|
||||
this.externalUrl = externalUrl;
|
||||
this.recommended = recommended;
|
||||
this.tags = new HashSet<>();
|
||||
this.pluginDependencies = new EnumMap<>(Platform.class);
|
||||
this.platformDependencies = new EnumMap<>(Platform.class);
|
||||
@ -73,6 +77,10 @@ public class Version extends Model implements Named, Visible {
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
public String getExternalUrl() {
|
||||
return externalUrl;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
@ -85,19 +93,25 @@ public class Version extends Model implements Named, Visible {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public boolean isRecommended() {
|
||||
return recommended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Version{" +
|
||||
"versionString='" + name + '\'' +
|
||||
"name='" + name + '\'' +
|
||||
", pluginDependencies=" + pluginDependencies +
|
||||
", platformDependencies=" + platformDependencies +
|
||||
", visibility=" + visibility +
|
||||
", description='" + description + '\'' +
|
||||
", stats=" + stats +
|
||||
", fileInfo=" + fileInfo +
|
||||
", externalUrl='" + externalUrl + '\'' +
|
||||
", author='" + author + '\'' +
|
||||
", reviewState=" + reviewState +
|
||||
", tags=" + tags +
|
||||
", recommended=" + recommended +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ public class ProjectVersionTable extends Table implements Named, ModelVisible, P
|
||||
}
|
||||
|
||||
@Override
|
||||
@EnumByOrdinal
|
||||
public Visibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
@ -129,6 +130,7 @@ public class ProjectVersionTable extends Table implements Named, ModelVisible, P
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
@EnumByOrdinal
|
||||
public ReviewState getReviewState() {
|
||||
return reviewState;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
|
||||
", lastVisibilityChangeComment='" + lastVisibilityChangeComment + '\'' +
|
||||
", lastVisibilityChangeUserName='" + lastVisibilityChangeUserName + '\'' +
|
||||
", info=" + info +
|
||||
", pages=" + pages +
|
||||
"} " + super.toString();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package io.papermc.hangar.model.internal.versions;
|
||||
|
||||
import io.papermc.hangar.config.jackson.RequiresPermission;
|
||||
import io.papermc.hangar.model.Identified;
|
||||
import io.papermc.hangar.model.api.project.version.FileInfo;
|
||||
import io.papermc.hangar.model.api.project.version.Version;
|
||||
import io.papermc.hangar.model.api.project.version.VersionStats;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.projects.ReviewState;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import org.jdbi.v3.core.enums.EnumByOrdinal;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
import org.jdbi.v3.core.mapper.reflect.ColumnName;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class HangarVersion extends Version implements Identified {
|
||||
|
||||
private final long id;
|
||||
private final String approvedBy;
|
||||
|
||||
public HangarVersion(OffsetDateTime createdAt, @ColumnName("version_string") String name, Visibility visibility, String description, @Nested("vs") VersionStats stats, @Nested("fi") FileInfo fileInfo, String externalUrl, String author, @EnumByOrdinal ReviewState reviewState, boolean recommended, long id, String approvedBy) {
|
||||
super(createdAt, name, visibility, description, stats, fileInfo, externalUrl, author, reviewState, recommended);
|
||||
this.id = id;
|
||||
this.approvedBy = approvedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@RequiresPermission(NamedPermission.REVIEWER)
|
||||
public String getApprovedBy() {
|
||||
return approvedBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HangarVersion{" +
|
||||
"id=" + id +
|
||||
", approvedBy='" + approvedBy + '\'' +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package io.papermc.hangar.security.annotations.visibility;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public enum Type {
|
||||
PROJECT(1, 2),
|
||||
VERSION(1, 3);
|
||||
|
||||
private final Set<Integer> argCount;
|
||||
|
||||
Type(Integer...argCounts) {
|
||||
this.argCount = Set.of(argCounts);
|
||||
}
|
||||
|
||||
public Set<Integer> getArgCount() {
|
||||
return argCount;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Set;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ -13,4 +14,19 @@ public @interface VisibilityRequired {
|
||||
Type type();
|
||||
|
||||
String args();
|
||||
|
||||
enum Type {
|
||||
PROJECT(1, 2),
|
||||
VERSION(1, 3);
|
||||
|
||||
private final Set<Integer> argCount;
|
||||
|
||||
Type(Integer...argCounts) {
|
||||
this.argCount = Set.of(argCounts);
|
||||
}
|
||||
|
||||
public Set<Integer> getArgCount() {
|
||||
return argCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,15 +22,15 @@ public class VisibilityRequiredMetadataExtractor implements AnnotationMetadataEx
|
||||
|
||||
static class VisibilityRequiredAttribute implements ConfigAttribute {
|
||||
|
||||
private final Type type;
|
||||
private final VisibilityRequired.Type type;
|
||||
private final Expression expression;
|
||||
|
||||
VisibilityRequiredAttribute(Type type, Expression expression) {
|
||||
VisibilityRequiredAttribute(VisibilityRequired.Type type, Expression expression) {
|
||||
this.type = type;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
public VisibilityRequired.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -30,16 +30,20 @@ public class VersionsApiService extends HangarService {
|
||||
}
|
||||
|
||||
public Version getVersion(String author, String slug, String versionString, Platform platform) {
|
||||
return versionsApiDAO.getVersion(author, slug, versionString, platform, getHangarPrincipal().getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
return versionsApiDAO.getVersion(author, slug, versionString, platform, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
}
|
||||
|
||||
public List<Version> getVersions(String author, String slug, String versionString) {
|
||||
return versionsApiDAO.getVersions(author, slug, versionString, getHangarPrincipal().getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
List<Version> versions = versionsApiDAO.getVersions(author, slug, versionString, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
if (versions.isEmpty()) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
public PaginatedResult<Version> getVersions(String author, String slug, List<String> tags, RequestPagination pagination) {
|
||||
List<Version> versions = versionsApiDAO.getVersions(author, slug, tags, getHangarPrincipal().getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId(), pagination.getLimit(), pagination.getOffset());
|
||||
Long versionCount = versionsApiDAO.getVersionCount(author, slug, tags, getHangarPrincipal().getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
List<Version> versions = versionsApiDAO.getVersions(author, slug, tags, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId(), pagination.getLimit(), pagination.getOffset());
|
||||
Long versionCount = versionsApiDAO.getVersionCount(author, slug, tags, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
return new PaginatedResult<>(new Pagination(versionCount == null ? 0 : versionCount, pagination), versions);
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,56 @@
|
||||
package io.papermc.hangar.service.internal.versions;
|
||||
|
||||
import io.papermc.hangar.db.dao.HangarDao;
|
||||
import io.papermc.hangar.db.dao.internal.HangarVersionsDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.versions.ProjectVersionsDAO;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.TagColor;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTagTable;
|
||||
import io.papermc.hangar.model.internal.versions.HangarVersion;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVersionVisibilityService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class VersionService extends HangarService {
|
||||
|
||||
private final ProjectVersionsDAO projectVersionsDAO;
|
||||
private final HangarVersionsDAO hangarVersionsDAO;
|
||||
private final ProjectVersionVisibilityService projectVersionVisibilityService;
|
||||
|
||||
@Autowired
|
||||
public VersionService(HangarDao<ProjectVersionsDAO> projectVersionDAO) {
|
||||
public VersionService(HangarDao<ProjectVersionsDAO> projectVersionDAO, HangarDao<HangarVersionsDAO> hangarProjectsDAO, ProjectVersionVisibilityService projectVersionVisibilityService) {
|
||||
this.projectVersionsDAO = projectVersionDAO.get();
|
||||
this.hangarVersionsDAO = hangarProjectsDAO.get();
|
||||
this.projectVersionVisibilityService = projectVersionVisibilityService;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProjectVersionTable getProjectVersionTable(Long versionId) {
|
||||
if (versionId == null) {
|
||||
return null;
|
||||
}
|
||||
return projectVersionVisibilityService.checkVisibility(projectVersionsDAO.getProjectVersionTable(versionId));
|
||||
}
|
||||
|
||||
public void updateProjectVersionTable(ProjectVersionTable projectVersionTable) {
|
||||
projectVersionsDAO.update(projectVersionTable);
|
||||
}
|
||||
|
||||
public List<HangarVersion> getHangarVersions(String author, String slug, String versionString) {
|
||||
List<HangarVersion> versions = hangarVersionsDAO.getVersions(author, slug, versionString, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
|
||||
if (versions.isEmpty()) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
public void addUnstableTag(long versionId) {
|
||||
|
@ -252,7 +252,9 @@ CREATE TABLE project_version_platform_dependencies
|
||||
CONSTRAINT project_version_platform_dependencies_platform_version_id_fkey
|
||||
FOREIGN KEY (platform_version_id)
|
||||
REFERENCES platform_versions
|
||||
ON DELETE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT project_version_platform_dependencies_unique
|
||||
UNIQUE (version_id, platform_version_id)
|
||||
);
|
||||
|
||||
CREATE TABLE roles
|
||||
|
Loading…
Reference in New Issue
Block a user