mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-03-13 15:39:18 +08:00
project visibility stuff
This commit is contained in:
parent
783cde2c87
commit
4101ce9fbf
@ -99,4 +99,8 @@
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
@ -6,15 +6,16 @@
|
||||
<v-card>
|
||||
<v-card-title>{{ title }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="modalForm" v-model="validForm">
|
||||
<v-form v-if="!noForm" ref="modalForm" v-model="validForm">
|
||||
<slot />
|
||||
</v-form>
|
||||
<slot v-else />
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn text color="warning" @click.stop="close">{{ $t('general.close') }}</v-btn>
|
||||
<slot name="other-btns" />
|
||||
<v-btn color="success" :disabled="!validForm || submitDisabled" :loading="loading" @click.stop="submit0">
|
||||
{{ submitLabel }}
|
||||
<v-btn color="success" :disabled="(!noForm && !validForm) || submitDisabled" :loading="loading" @click.stop="submit0">
|
||||
{{ submitLabel || $t('general.submit') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@ -35,8 +36,8 @@ export default class HangarModal extends HangarFormModal {
|
||||
@Prop({ type: String as PropType<TranslateResult>, required: true })
|
||||
title!: TranslateResult;
|
||||
|
||||
@Prop({ type: String as PropType<TranslateResult>, required: true })
|
||||
submitLabel!: TranslateResult;
|
||||
@Prop({ type: String as PropType<TranslateResult> })
|
||||
submitLabel!: TranslateResult | null;
|
||||
|
||||
@Prop({ type: Function as PropType<() => Promise<void>>, required: true })
|
||||
submit!: () => Promise<void>;
|
||||
@ -44,12 +45,18 @@ export default class HangarModal extends HangarFormModal {
|
||||
@Prop({ type: Boolean, default: false })
|
||||
submitDisabled!: boolean;
|
||||
|
||||
@Prop({ type: Boolean, default: false })
|
||||
noForm!: boolean;
|
||||
|
||||
$refs!: {
|
||||
modalForm: any;
|
||||
};
|
||||
|
||||
close() {
|
||||
this.$refs.modalForm.reset();
|
||||
if (!this.noForm) {
|
||||
this.$refs.modalForm.reset();
|
||||
}
|
||||
|
||||
this.dialog = false;
|
||||
this.$emit('close');
|
||||
}
|
||||
@ -59,7 +66,9 @@ export default class HangarModal extends HangarFormModal {
|
||||
this.submit()
|
||||
.then(() => {
|
||||
this.dialog = false;
|
||||
this.$refs.modalForm.reset();
|
||||
if (!this.noForm) {
|
||||
this.$refs.modalForm.reset();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
|
98
frontend/components/modals/VisibilityChangerModal.vue
Normal file
98
frontend/components/modals/VisibilityChangerModal.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<HangarModal :title="$t('visibility.modal.title', [type])" :submit="submit" no-form :submit-disabled="disableSubmit">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn :small="smallBtn" v-bind="attrs" color="warning" class="mr-1" :class="activatorClass" v-on="on">
|
||||
<v-icon :small="smallBtn" left>mdi-eye</v-icon>
|
||||
{{ $t('visibility.modal.activatorBtn') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-radio-group v-model="formVisibility" mandatory>
|
||||
<v-radio v-for="vis in visibilities" :key="vis.name" :value="vis.name" :label="$t(vis.title)" />
|
||||
</v-radio-group>
|
||||
<v-form ref="reasonForm" v-model="validForm">
|
||||
<!-- TODO this should be a markdown editor since the reason is rendered in markdown-->
|
||||
<v-textarea
|
||||
v-if="showTextarea"
|
||||
v-model.trim="reason"
|
||||
filled
|
||||
hide-details
|
||||
auto-grow
|
||||
rows="2"
|
||||
:rules="[$util.$vc.require()]"
|
||||
:label="$t('visibility.modal.reason')"
|
||||
/>
|
||||
</v-form>
|
||||
</HangarModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Watch } from 'nuxt-property-decorator';
|
||||
import { IVisibility } from 'hangar-internal';
|
||||
import { PropType } from 'vue';
|
||||
import { HangarFormModal } from '~/components/mixins';
|
||||
import { Visibility } from '~/types/enums';
|
||||
import HangarModal from '~/components/modals/HangarModal.vue';
|
||||
|
||||
@Component({
|
||||
components: { HangarModal },
|
||||
})
|
||||
export default class VisibilityChangerModal extends HangarFormModal {
|
||||
@Prop({ type: String as PropType<Visibility>, required: true })
|
||||
propVisibility!: Visibility;
|
||||
|
||||
@Prop({ type: String as PropType<'project' | 'version'>, required: true })
|
||||
type!: 'project' | 'version';
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
postUrl!: string;
|
||||
|
||||
@Prop({ type: Boolean, default: false })
|
||||
smallBtn!: boolean;
|
||||
|
||||
visibilities: IVisibility[] = [];
|
||||
formVisibility: Visibility = this.propVisibility;
|
||||
reason: string = '';
|
||||
|
||||
$refs!: {
|
||||
reasonForm: any;
|
||||
};
|
||||
|
||||
submit(): Promise<void> {
|
||||
return this.$api
|
||||
.requestInternal(this.postUrl, true, 'post', {
|
||||
visibility: this.formVisibility,
|
||||
comment: this.currentIVis.showModal ? this.reason : null,
|
||||
})
|
||||
.then(() => {
|
||||
this.reason = '';
|
||||
this.$util.success(this.$t('visibility.modal.success', [this.type, this.$t(this.currentIVis?.title)]));
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError);
|
||||
}
|
||||
|
||||
@Watch('currentVisibility')
|
||||
onPropChange() {
|
||||
this.formVisibility = this.propVisibility;
|
||||
this.$refs.reasonForm.resetValidation();
|
||||
}
|
||||
|
||||
get disableSubmit(): boolean {
|
||||
return this.propVisibility === this.formVisibility || !this.validForm;
|
||||
}
|
||||
|
||||
get showTextarea(): boolean {
|
||||
return this.currentIVis?.showModal && this.propVisibility !== this.formVisibility;
|
||||
}
|
||||
|
||||
get currentIVis(): IVisibility {
|
||||
return this.visibilities.find((v) => v.name === this.formVisibility)!;
|
||||
}
|
||||
|
||||
async fetch() {
|
||||
this.visibilities = (await this.$api.requestInternal<IVisibility[]>('data/visibilities', false).catch<any>(this.$util.handlePageRequestError)) || [];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -455,18 +455,28 @@ const msgs: LocaleMessageObject = {
|
||||
notice: {
|
||||
new:
|
||||
'This project is new, and will not be shown to others until a version has been uploaded. If a version is not uploaded over a longer time the project will be deleted.',
|
||||
needsChanges: 'This project requires changes: {0}',
|
||||
needsChanges: 'This project requires changes',
|
||||
needsApproval: 'You have sent the project for review',
|
||||
softDelete: 'Project deleted by {0}',
|
||||
},
|
||||
name: {
|
||||
new: 'New',
|
||||
public: 'Public',
|
||||
needsChanges: 'Needs Changes',
|
||||
needsApproval: 'Needs Approval',
|
||||
softDelete: 'Soft Delete',
|
||||
},
|
||||
changes: {
|
||||
version: {
|
||||
reviewed: 'due to approved reviews',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
activatorBtn: 'Visibility Actions',
|
||||
title: "Change {0}'s visibility",
|
||||
reason: 'Reason for change',
|
||||
success: "You changed the {0}'s visibility to {1}",
|
||||
},
|
||||
},
|
||||
author: {
|
||||
watching: 'Watching',
|
||||
@ -601,7 +611,6 @@ const msgs: LocaleMessageObject = {
|
||||
msgUser: 'Message user',
|
||||
msgProjectOwner: 'Message owner',
|
||||
markResolved: 'Mark resolved',
|
||||
visibilityActions: 'Visibility actions',
|
||||
line1: '{0} reported {1} on {2}',
|
||||
line2: 'Reason: {0}',
|
||||
line3: 'Comment: {0}',
|
||||
|
@ -2,11 +2,18 @@
|
||||
<div>
|
||||
<template v-if="!isPublic">
|
||||
<!-- todo alert for visibility stuff -->
|
||||
<v-alert v-if="needsChanges" type="error">
|
||||
<v-btn v-if="$perms.canEditPage" type="primary" :to="'/' + slug + '/manage/sendforapproval'">{{ $t('project.sendForApproval') }} </v-btn>
|
||||
<strong>{{ $t('visibility.notice.' + project.visibility) }}</strong>
|
||||
<br />
|
||||
<Markdown :raw="project.lastVisibilityChangeComment || 'Unknown'" />
|
||||
<v-alert v-if="needsChanges" type="error" text>
|
||||
<v-row>
|
||||
<v-col class="grow">
|
||||
<strong>{{ $t('visibility.notice.' + project.visibility) }}</strong>
|
||||
</v-col>
|
||||
<v-col v-if="$perms.canEditPage" class="shrink">
|
||||
<v-btn v-if="$perms.canEditPage" :loading="loading.approval" color="warning" @click.stop="sendForApproval">
|
||||
{{ $t('project.sendForApproval') }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<Markdown :raw="project.lastVisibilityChangeComment || 'Unknown'" style="z-index: 10; position: relative" class="mt-2" />
|
||||
</v-alert>
|
||||
<v-alert v-else-if="isSoftDeleted" type="error">
|
||||
{{ $t('visibility.notice.' + project.visibility, [project.lastVisibilityChangeUserName]) }}
|
||||
@ -111,7 +118,7 @@ import { HangarProject } from 'hangar-internal';
|
||||
import { NavigationGuardNext, Route } from 'vue-router';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
import { Markdown } from '~/components/markdown';
|
||||
import FlagModal from '~/components/modals/FlagModal.vue';
|
||||
import FlagModal from '~/components/modals/projects/FlagModal.vue';
|
||||
import { UserAvatar } from '~/components/users';
|
||||
import { Visibility } from '~/types/enums';
|
||||
import { HangarComponent } from '~/components/mixins';
|
||||
@ -133,6 +140,9 @@ interface Tab {
|
||||
})
|
||||
export default class ProjectPage extends HangarComponent {
|
||||
project!: HangarProject;
|
||||
loading = {
|
||||
approval: false,
|
||||
};
|
||||
|
||||
head() {
|
||||
return {
|
||||
@ -212,6 +222,20 @@ export default class ProjectPage extends HangarComponent {
|
||||
.catch((err) => this.$util.handleRequestError(err, 'project.error.watch'));
|
||||
}
|
||||
|
||||
sendForApproval() {
|
||||
this.loading.approval = true;
|
||||
this.$api
|
||||
.requestInternal(`projects/visibility/${this.project.id}/sendforapproval`, true, 'post')
|
||||
.then(() => {
|
||||
this.$util.success('SUCCESS');
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError)
|
||||
.finally(() => {
|
||||
this.loading.approval = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Need to refresh the project if anything has changed. idk if this is the best way to do this
|
||||
async beforeRouteUpdate(to: Route, _from: Route, next: NavigationGuardNext) {
|
||||
this.project = await this.$api
|
||||
|
@ -4,7 +4,21 @@
|
||||
<v-card class="settings-card">
|
||||
<v-card-title class="sticky">
|
||||
{{ $t('project.settings.title') }}
|
||||
<v-btn class="flex-right" color="success" :loading="loading.save" :disabled="!validForm.settings" @click="save">
|
||||
<VisibilityChangerModal
|
||||
v-if="$perms.canSeeHidden"
|
||||
type="project"
|
||||
:prop-visibility="project.visibility"
|
||||
activator-class="flex-right"
|
||||
:post-url="`projects/visibility/${project.id}`"
|
||||
/>
|
||||
<v-btn
|
||||
class="flex-right"
|
||||
:class="{ 'ml-1': $perms.canSeeHidden }"
|
||||
color="success"
|
||||
:loading="loading.save"
|
||||
:disabled="!validForm.settings"
|
||||
@click="save"
|
||||
>
|
||||
<v-icon left>mdi-check</v-icon>
|
||||
{{ $t('project.settings.save') }}
|
||||
</v-btn>
|
||||
@ -292,9 +306,10 @@ import { NamedPermission, ProjectCategory } from '~/types/enums';
|
||||
import { RootState } from '~/store';
|
||||
import { HangarProjectMixin } from '~/components/mixins';
|
||||
import { MemberList } from '~/components/projects';
|
||||
import VisibilityChangerModal from '~/components/modals/VisibilityChangerModal.vue';
|
||||
|
||||
@Component({
|
||||
components: { MemberList },
|
||||
components: { VisibilityChangerModal, MemberList },
|
||||
})
|
||||
@ProjectPermission(NamedPermission.EDIT_SUBJECT_SETTINGS)
|
||||
export default class ProjectManagePage extends HangarProjectMixin {
|
||||
|
@ -2,7 +2,6 @@
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('flagReview.title') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<!-- TODO link to project -->
|
||||
<v-list v-if="flags.length > 0">
|
||||
<v-list-item v-for="flag in flags" :key="flag.id">
|
||||
<v-list-item-avatar>
|
||||
@ -17,6 +16,9 @@
|
||||
$util.prettyDateTime(flag.createdAt),
|
||||
])
|
||||
}}
|
||||
<v-btn small icon color="primary" :to="`/${flag.projectNamespace.owner}/${flag.projectNamespace.slug}`" nuxt target="_blank">
|
||||
<v-icon small>mdi-open-in-new</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ $t('flagReview.line2', [$t(flag.reason)]) }}</v-list-item-subtitle>
|
||||
<v-list-item-subtitle>{{ $t('flagReview.line3', [flag.comment]) }}</v-list-item-subtitle>
|
||||
@ -30,22 +32,16 @@
|
||||
<v-icon small left>mdi-reply</v-icon>
|
||||
{{ $t('flagReview.msgProjectOwner') }}
|
||||
</v-btn>
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn small v-bind="attrs" color="warning" class="mr-1" v-on="on">
|
||||
<v-icon small left>mdi-eye</v-icon>
|
||||
{{ $t('flagReview.visibilityActions') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item v-for="(v, index) in visibilities" :key="index" @click="visibility(flag, v)">
|
||||
<v-list-item-title>{{ v }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-btn small color="success" :loading="loading[flag.id]" @click="resolve(flag)"
|
||||
><v-icon small left>mdi-check</v-icon>{{ $t('flagReview.markResolved') }}</v-btn
|
||||
>
|
||||
<VisibilityChangerModal
|
||||
:prop-visibility="flag.projectVisibility"
|
||||
type="project"
|
||||
:post-url="`projects/visibility/${flag.projectId}`"
|
||||
small-btn
|
||||
/>
|
||||
<v-btn small color="success" :loading="loading[flag.id]" @click="resolve(flag)">
|
||||
<v-icon small left>mdi-check</v-icon>
|
||||
{{ $t('flagReview.markResolved') }}
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@ -59,26 +55,18 @@ import { Component, Vue } from 'nuxt-property-decorator';
|
||||
import { Flag } from 'hangar-internal';
|
||||
import { Context } from '@nuxt/types';
|
||||
import UserAvatar from '~/components/users/UserAvatar.vue';
|
||||
import { NamedPermission, Visibility } from '~/types/enums';
|
||||
import { NamedPermission } from '~/types/enums';
|
||||
import { GlobalPermission } from '~/utils/perms';
|
||||
import VisibilityChangerModal from '~/components/modals/VisibilityChangerModal.vue';
|
||||
|
||||
@Component({
|
||||
components: { UserAvatar },
|
||||
components: { VisibilityChangerModal, UserAvatar },
|
||||
})
|
||||
@GlobalPermission(NamedPermission.MOD_NOTES_AND_FLAGS)
|
||||
export default class AdminFlagsPage extends Vue {
|
||||
flags!: Flag[];
|
||||
loading: { [key: number]: boolean } = {};
|
||||
|
||||
get visibilities(): Visibility[] {
|
||||
return Object.keys(Visibility) as Visibility[];
|
||||
}
|
||||
|
||||
// todo send to server
|
||||
visibility(flag: Flag, visibility: Visibility) {
|
||||
console.log('changing visibility of ', flag, 'to ', visibility);
|
||||
}
|
||||
|
||||
resolve(flag: Flag) {
|
||||
this.loading[flag.id] = true;
|
||||
this.$api
|
||||
|
6
frontend/types/api.d.ts
vendored
6
frontend/types/api.d.ts
vendored
@ -1,4 +1,6 @@
|
||||
declare module 'hangar-api' {
|
||||
import { Visibility } from '~/types/enums';
|
||||
|
||||
interface Model {
|
||||
createdAt: string;
|
||||
}
|
||||
@ -33,4 +35,8 @@ declare module 'hangar-api' {
|
||||
name: String;
|
||||
link: String;
|
||||
}
|
||||
|
||||
interface Visible {
|
||||
visibility: Visibility;
|
||||
}
|
||||
}
|
||||
|
7
frontend/types/api/projects.d.ts
vendored
7
frontend/types/api/projects.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
declare module 'hangar-api' {
|
||||
import { Model, Named, TagColor } from 'hangar-api';
|
||||
import { ProjectCategory, Visibility } from '~/types/enums';
|
||||
import { Model, Named, TagColor, Visible } from 'hangar-api';
|
||||
import { ProjectCategory } from '~/types/enums';
|
||||
|
||||
interface ProjectNamespace {
|
||||
owner: string;
|
||||
@ -48,11 +48,10 @@ declare module 'hangar-api' {
|
||||
tags: PromotedVersionTag[];
|
||||
}
|
||||
|
||||
interface ProjectCompact extends Model, Named {
|
||||
interface ProjectCompact extends Model, Named, Visible {
|
||||
namespace: ProjectNamespace;
|
||||
stats: ProjectStats;
|
||||
category: ProjectCategory;
|
||||
visibility: Visibility;
|
||||
}
|
||||
|
||||
interface Project extends ProjectCompact {
|
||||
|
7
frontend/types/api/versions.d.ts
vendored
7
frontend/types/api/versions.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
declare module 'hangar-api' {
|
||||
import { Model, Named, ProjectNamespace, TagColor } from 'hangar-api';
|
||||
import { Platform, ReviewState, Visibility } from '~/types/enums';
|
||||
import { Model, Named, ProjectNamespace, TagColor, Visible } from 'hangar-api';
|
||||
import { Platform, ReviewState } from '~/types/enums';
|
||||
|
||||
interface PluginDependency extends Named {
|
||||
required: boolean;
|
||||
@ -27,9 +27,8 @@ declare module 'hangar-api' {
|
||||
pluginDependencies: Record<Platform, PluginDependency[]>;
|
||||
}
|
||||
|
||||
interface Version extends Model, Named, DependencyVersion {
|
||||
interface Version extends Model, Named, DependencyVersion, Visible {
|
||||
platformDependencies: Record<Platform, string[]>;
|
||||
visibility: Visibility;
|
||||
description: string;
|
||||
stats: VersionStats;
|
||||
fileInfo: FileInfo;
|
||||
|
9
frontend/types/internal/data.d.ts
vendored
9
frontend/types/internal/data.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
declare module 'hangar-internal' {
|
||||
import { Model, TagColor } from 'hangar-api';
|
||||
import { Platform, ProjectCategory } from '~/types/enums';
|
||||
import { Platform, ProjectCategory, Visibility } from '~/types/enums';
|
||||
|
||||
interface Table extends Model {
|
||||
id: number;
|
||||
@ -31,4 +31,11 @@ declare module 'hangar-internal' {
|
||||
tagColor: TagColor;
|
||||
possibleVersions: string[];
|
||||
}
|
||||
|
||||
interface IVisibility {
|
||||
name: Visibility;
|
||||
showModal: boolean;
|
||||
cssClass: string;
|
||||
title: string;
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.papermc.hangar.config.hangar.HangarConfig;
|
||||
import io.papermc.hangar.model.Announcement;
|
||||
import io.papermc.hangar.model.api.project.ProjectLicense;
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
import io.papermc.hangar.model.common.projects.FlagReason;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import io.papermc.hangar.model.common.roles.OrganizationRole;
|
||||
import io.papermc.hangar.model.common.roles.ProjectRole;
|
||||
import io.papermc.hangar.security.annotations.Anyone;
|
||||
@ -142,6 +142,20 @@ public class BackendDataController {
|
||||
return ResponseEntity.ok(config.getLicences());
|
||||
}
|
||||
|
||||
@GetMapping("/visibilities")
|
||||
public ResponseEntity<ArrayNode> getVisibilities() {
|
||||
ArrayNode arrayNode = mapper.createArrayNode();
|
||||
for (Visibility value : Visibility.getValues()) {
|
||||
ObjectNode objectNode = mapper.createObjectNode();
|
||||
objectNode.put("name", value.getName())
|
||||
.put("showModal", value.getShowModal())
|
||||
.put("cssClass", value.getCssClass())
|
||||
.put("title", value.getTitle());
|
||||
arrayNode.add(objectNode);
|
||||
}
|
||||
return ResponseEntity.ok(arrayNode);
|
||||
}
|
||||
|
||||
@GetMapping("/validations")
|
||||
public ResponseEntity<ObjectNode> getValidations() {
|
||||
ObjectNode validations = mapper.createObjectNode();
|
||||
|
@ -10,6 +10,7 @@ import io.papermc.hangar.model.internal.api.requests.EditMembersForm;
|
||||
import io.papermc.hangar.model.internal.api.requests.StringContent;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProjectForm;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.ProjectSettingsForm;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.VisibilityChangeForm;
|
||||
import io.papermc.hangar.model.internal.api.responses.PossibleProjectOwner;
|
||||
import io.papermc.hangar.model.internal.projects.HangarProject;
|
||||
import io.papermc.hangar.model.internal.projects.HangarProjectNote;
|
||||
@ -22,6 +23,7 @@ import io.papermc.hangar.service.internal.projects.ProjectFactory;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectNoteService;
|
||||
import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVisibilityService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -51,14 +53,16 @@ public class ProjectController extends HangarController {
|
||||
private final UserService userService;
|
||||
private final OrganizationService organizationService;
|
||||
private final ProjectNoteService projectNoteService;
|
||||
private final ProjectVisibilityService projectVisibilityService;
|
||||
|
||||
@Autowired
|
||||
public ProjectController(ProjectFactory projectFactory, ProjectService projectService, UserService userService, OrganizationService organizationService, ProjectNoteService projectNoteService) {
|
||||
public ProjectController(ProjectFactory projectFactory, ProjectService projectService, UserService userService, OrganizationService organizationService, ProjectNoteService projectNoteService, ProjectVisibilityService projectVisibilityService) {
|
||||
this.projectFactory = projectFactory;
|
||||
this.projectService = projectService;
|
||||
this.userService = userService;
|
||||
this.organizationService = organizationService;
|
||||
this.projectNoteService = projectNoteService;
|
||||
this.projectVisibilityService = projectVisibilityService;
|
||||
}
|
||||
|
||||
@GetMapping("/validateName")
|
||||
@ -156,4 +160,20 @@ public class ProjectController extends HangarController {
|
||||
public void addProjectNote(@PathVariable long projectId, @RequestBody @Valid StringContent content) {
|
||||
projectNoteService.addNote(projectId, content.getContent());
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(perms = NamedPermission.REVIEWER)
|
||||
@PostMapping(path = "/visibility/{projectId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void changeProjectVisibility(@PathVariable long projectId, @Valid @RequestBody VisibilityChangeForm visibilityChangeForm) {
|
||||
projectVisibilityService.changeVisibility(projectId, visibilityChangeForm.getVisibility(), visibilityChangeForm.getComment());
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_PAGE, args = "{#projectId}")
|
||||
@PostMapping("/visibility/{projectId}/sendforapproval")
|
||||
public void sendProjectForApproval(@PathVariable long projectId) {
|
||||
projectService.sendProjectForApproval(projectId);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,16 @@ package io.papermc.hangar.db.dao.internal.table;
|
||||
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVersionVisibilityChangeTable;
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVisibilityChangeTable;
|
||||
import org.jdbi.v3.sqlobject.config.KeyColumn;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindBean;
|
||||
import org.jdbi.v3.sqlobject.customizer.Timestamped;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlQuery;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Repository
|
||||
@RegisterConstructorMapper(ProjectVisibilityChangeTable.class)
|
||||
@RegisterConstructorMapper(ProjectVersionVisibilityChangeTable.class)
|
||||
@ -27,6 +31,14 @@ public interface VisibilityDAO {
|
||||
"WHERE pvc.project_id = sq.project_id")
|
||||
void updateLatestProjectChange(long userId, long projectId);
|
||||
|
||||
@KeyColumn("name")
|
||||
@SqlQuery("SELECT pvc.*, u.name " +
|
||||
"FROM project_visibility_changes pvc " +
|
||||
" LEFT JOIN users u ON pvc.created_by = u.id " +
|
||||
"WHERE pvc.project_id = :projectId " +
|
||||
"ORDER BY pvc.created_at DESC LIMIT 1")
|
||||
Entry<String, ProjectVisibilityChangeTable> getLatestProjectVisibilityChange(long projectId);
|
||||
|
||||
// Versions
|
||||
@Timestamped
|
||||
@SqlUpdate("INSERT INTO project_version_visibility_changes (created_at, created_by, version_id, comment, resolved_at, resolved_by, visibility) " +
|
||||
@ -40,4 +52,12 @@ public interface VisibilityDAO {
|
||||
" (SELECT version_id FROM project_version_visibility_changes WHERE version_id = :versionId AND resolved_at IS NULL AND resolved_by IS NULL ORDER BY created_at LIMIT 1) as subquery " +
|
||||
"WHERE project_version_visibility_changes.version_id = subquery.version_id")
|
||||
void updateLatestVersionChange(long userId, long versionId);
|
||||
|
||||
@KeyColumn("name")
|
||||
@SqlQuery("SELECT pvvc.*, u.name " +
|
||||
"FROM project_version_visibility_changes pvvc " +
|
||||
" LEFT JOIN users u ON pvvc.created_by = u.id " +
|
||||
"WHERE pvvc.version_id = :versionId " +
|
||||
"ORDER BY pvvc.created_at DESC LIMIT 1")
|
||||
Entry<String, ProjectVersionVisibilityChangeTable> getLatestVersionVisibilityChange(long versionId);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public interface ProjectsDAO {
|
||||
@GetGeneratedKeys
|
||||
@SqlUpdate("UPDATE projects SET name = :name, slug = :slug, category = :category, keywords = :keywords, issues = :issues, source = :source, " +
|
||||
"license_name = :licenseName, license_url = :licenseUrl, forum_sync = :forumSync, description = :description, visibility = :visibility, " +
|
||||
"notes = :notes, support = :support, homepage = :homepage WHERE id = :id")
|
||||
"support = :support, homepage = :homepage WHERE id = :id")
|
||||
ProjectTable update(@BindBean ProjectTable project);
|
||||
|
||||
@SqlUpdate("DELETE FROM projects WHERE id = :id")
|
||||
|
@ -16,6 +16,10 @@ public class HangarApiException extends ResponseStatusException {
|
||||
private final HttpHeaders httpHeaders;
|
||||
private final Object[] args;
|
||||
|
||||
public HangarApiException() {
|
||||
this(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
public HangarApiException(String reason) {
|
||||
this(HttpStatus.BAD_REQUEST, reason);
|
||||
}
|
||||
|
@ -4,24 +4,26 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
public enum Visibility {
|
||||
PUBLIC("public", false, ""),
|
||||
PUBLIC("public", false, "", "visibility.name.public"),
|
||||
|
||||
NEW("new", false, "project-new"),
|
||||
NEW("new", false, "project-new", "visibility.name.new"),
|
||||
|
||||
NEEDSCHANGES("needsChanges", true, "striped project-needsChanges"),
|
||||
NEEDSCHANGES("needsChanges", true, "striped project-needsChanges", "visibility.name.needsChanges"),
|
||||
|
||||
NEEDSAPPROVAL("needsApproval", false, "striped project-needsChanges"),
|
||||
NEEDSAPPROVAL("needsApproval", false, "striped project-needsChanges", "visibility.name.needsApproval"),
|
||||
|
||||
SOFTDELETE("softDelete", true, "striped project-hidden");
|
||||
SOFTDELETE("softDelete", true, "striped project-hidden", "visibility.name.softDelete");
|
||||
|
||||
private final String name;
|
||||
private final boolean showModal;
|
||||
private final String cssClass;
|
||||
private final String title;
|
||||
|
||||
Visibility(String name, boolean showModal, String cssClass) {
|
||||
Visibility(String name, boolean showModal, String cssClass, String title) {
|
||||
this.name = name;
|
||||
this.showModal = showModal;
|
||||
this.cssClass = cssClass;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -36,6 +38,10 @@ public enum Visibility {
|
||||
return cssClass;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonValue
|
||||
public String toString() {
|
||||
|
@ -0,0 +1,35 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class VisibilityChangeForm {
|
||||
|
||||
@NotNull
|
||||
private final Visibility visibility;
|
||||
private final String comment;
|
||||
|
||||
@JsonCreator
|
||||
public VisibilityChangeForm(Visibility visibility, String comment) {
|
||||
this.visibility = visibility;
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public Visibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VisibilityChangeForm{" +
|
||||
"visibility=" + visibility +
|
||||
", comment='" + comment + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package io.papermc.hangar.service;
|
||||
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
|
||||
import io.papermc.hangar.db.dao.HangarDao;
|
||||
import io.papermc.hangar.db.dao.internal.table.VisibilityDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.projects.ProjectsDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.versions.ProjectVersionsDAO;
|
||||
import io.papermc.hangar.model.ModelVisible;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import io.papermc.hangar.model.db.ProjectIdentified;
|
||||
import io.papermc.hangar.model.db.Table;
|
||||
import io.papermc.hangar.model.db.projects.ProjectTable;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVersionVisibilityChangeTable;
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVisibilityChangeTable;
|
||||
import io.papermc.hangar.model.db.visibility.VisibilityChangeTable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class VisibilityService<M extends Table & ModelVisible & ProjectIdentified, VT extends VisibilityChangeTable> extends HangarService {
|
||||
|
||||
@Autowired
|
||||
private PermissionService permissionService;
|
||||
|
||||
private final Consumer<VT> dbInsertion;
|
||||
private final BiConsumer<Long, Long> lastUpdater;
|
||||
private final VisibilityChangeTableConstructor<VT> changeTableConstructor;
|
||||
private final Function<M, M> modelUpdater;
|
||||
|
||||
protected VisibilityService(Consumer<VT> dbInsertion, BiConsumer<Long, Long> lastUpdater, VisibilityChangeTableConstructor<VT> changeTableConstructor, Function<M, M> modelUpdater) {
|
||||
this.dbInsertion = dbInsertion;
|
||||
this.lastUpdater = lastUpdater;
|
||||
this.changeTableConstructor = changeTableConstructor;
|
||||
this.modelUpdater = modelUpdater;
|
||||
}
|
||||
|
||||
public M changeVisibility(M model, Visibility newVisibility, String comment) {
|
||||
if (model.getVisibility() == newVisibility) return model;
|
||||
|
||||
lastUpdater.accept(getHangarPrincipal().getUserId(), model.getId());
|
||||
|
||||
dbInsertion.accept(changeTableConstructor.create(getHangarPrincipal().getUserId(), comment, newVisibility, model.getId()));
|
||||
|
||||
model.setVisibility(newVisibility);
|
||||
return modelUpdater.apply(model);
|
||||
}
|
||||
|
||||
public final M checkVisibility(@Nullable M model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
Permission perms = permissionService.getProjectPermissions(getHangarUserId(), model.getProjectId());
|
||||
if (!perms.has(Permission.SeeHidden) && !perms.has(Permission.IsProjectMember) && model.getVisibility() != Visibility.PUBLIC) {
|
||||
return null;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface VisibilityChangeTableConstructor<T extends VisibilityChangeTable> {
|
||||
|
||||
T create(long createdBy, String comment, Visibility visibility, long subjectId);
|
||||
}
|
||||
|
||||
@Service
|
||||
public static class ProjectVisibilityService extends VisibilityService<ProjectTable, ProjectVisibilityChangeTable> {
|
||||
|
||||
@Autowired
|
||||
public ProjectVisibilityService(HangarDao<VisibilityDAO> visibilityDAO, HangarDao<ProjectsDAO> projectsDAO) {
|
||||
super(visibilityDAO.get()::insert, visibilityDAO.get()::updateLatestProjectChange, ProjectVisibilityChangeTable::new, projectsDAO.get()::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectTable changeVisibility(ProjectTable model, Visibility newVisibility, String comment) {
|
||||
Visibility oldVis = model.getVisibility();
|
||||
// TODO add logging for visibility for versions and move this to the abstract class
|
||||
userActionLogService.project(LoggedActionType.PROJECT_VISIBILITY_CHANGE.with(ProjectContext.of(model.getId())), newVisibility.getName(), oldVis.getName());
|
||||
return super.changeVisibility(model, newVisibility, comment);
|
||||
}
|
||||
}
|
||||
|
||||
@Service
|
||||
public static class ProjectVersionVisibilityService extends VisibilityService<ProjectVersionTable, ProjectVersionVisibilityChangeTable> {
|
||||
|
||||
@Autowired
|
||||
public ProjectVersionVisibilityService(HangarDao<VisibilityDAO> visibilityDAO, HangarDao<ProjectVersionsDAO> projectVersionDAO) {
|
||||
super(visibilityDAO.get()::insert, visibilityDAO.get()::updateLatestVersionChange, ProjectVersionVisibilityChangeTable::new, projectVersionDAO.get()::update);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.exceptions.MultiHangarApiException;
|
||||
import io.papermc.hangar.model.api.project.Project;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import io.papermc.hangar.model.common.roles.ProjectRole;
|
||||
import io.papermc.hangar.model.db.OrganizationTable;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
@ -25,12 +26,12 @@ import io.papermc.hangar.model.internal.projects.HangarProject.HangarProjectInfo
|
||||
import io.papermc.hangar.model.internal.projects.HangarProjectPage;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.PermissionService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVisibilityService;
|
||||
import io.papermc.hangar.service.internal.organizations.OrganizationService;
|
||||
import io.papermc.hangar.service.internal.roles.MemberService.ProjectMemberService;
|
||||
import io.papermc.hangar.service.internal.roles.RoleService.ProjectRoleService;
|
||||
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
|
||||
import io.papermc.hangar.service.internal.users.NotificationService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVisibilityService;
|
||||
import io.papermc.hangar.util.FileUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -116,10 +117,18 @@ public class ProjectService extends HangarService {
|
||||
Pair<Long, Project> project = hangarProjectsDAO.getProject(author, slug, getHangarUserId());
|
||||
ProjectOwner projectOwner = getProjectOwner(author);
|
||||
var members = hangarProjectsDAO.getProjectMembers(project.getLeft(), getHangarUserId(), permissionService.getProjectPermissions(getHangarUserId(), project.getLeft()).has(Permission.EditProjectSettings));
|
||||
// TODO only include visibility change if not public (and if so, only include the user and comment)
|
||||
String lastVisibilityChangeComment = "";
|
||||
String lastVisibilityChangeUserName = "";
|
||||
if (project.getRight().getVisibility() == Visibility.NEEDSCHANGES || project.getRight().getVisibility() == Visibility.SOFTDELETE) {
|
||||
var projectVisibilityChangeTable = projectVisibilityService.getLastVisibilityChange(project.getLeft());
|
||||
lastVisibilityChangeComment = projectVisibilityChangeTable.getValue().getComment();
|
||||
if (project.getRight().getVisibility() == Visibility.SOFTDELETE) {
|
||||
lastVisibilityChangeUserName = projectVisibilityChangeTable.getKey();
|
||||
}
|
||||
}
|
||||
HangarProjectInfo info = hangarProjectsDAO.getHangarProjectInfo(project.getLeft());
|
||||
Map<Long, HangarProjectPage> pages = projectPageService.getProjectPages(project.getLeft());
|
||||
return new HangarProject(project.getRight(), project.getLeft(), projectOwner, members, "", "", info, pages.values());
|
||||
return new HangarProject(project.getRight(), project.getLeft(), projectOwner, members, lastVisibilityChangeComment, lastVisibilityChangeUserName, info, pages.values());
|
||||
}
|
||||
|
||||
public void saveSettings(String author, String slug, ProjectSettingsForm settingsForm) {
|
||||
@ -233,6 +242,17 @@ public class ProjectService extends HangarService {
|
||||
return projectsDAO.getProjectWatchers(projectId);
|
||||
}
|
||||
|
||||
public void sendProjectForApproval(long projectId) {
|
||||
ProjectTable projectTable = getProjectTable(projectId);
|
||||
if (projectTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (projectTable.getVisibility() != Visibility.NEEDSCHANGES) {
|
||||
throw new HangarApiException();
|
||||
}
|
||||
projectVisibilityService.changeVisibility(projectTable, Visibility.NEEDSAPPROVAL, "");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> ProjectTable getProjectTable(@Nullable T identifier, @NotNull Function<T, ProjectTable> projectTableFunction) {
|
||||
if (identifier == null) {
|
||||
|
@ -17,8 +17,8 @@ import io.papermc.hangar.model.db.versions.reviews.ProjectVersionReviewTable;
|
||||
import io.papermc.hangar.model.internal.api.requests.versions.ReviewMessage;
|
||||
import io.papermc.hangar.model.internal.versions.HangarReview;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVersionVisibilityService;
|
||||
import io.papermc.hangar.service.internal.users.NotificationService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVersionVisibilityService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -22,7 +22,6 @@ import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTagTable;
|
||||
import io.papermc.hangar.model.internal.versions.PendingVersion;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVisibilityService;
|
||||
import io.papermc.hangar.service.api.UsersApiService;
|
||||
import io.papermc.hangar.service.internal.projects.ChannelService;
|
||||
import io.papermc.hangar.service.internal.projects.PlatformService;
|
||||
@ -30,6 +29,7 @@ import io.papermc.hangar.service.internal.projects.ProjectService;
|
||||
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
|
||||
import io.papermc.hangar.service.internal.users.NotificationService;
|
||||
import io.papermc.hangar.service.internal.versions.plugindata.PluginFileWithData;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVisibilityService;
|
||||
import io.papermc.hangar.util.CryptoUtils;
|
||||
import io.papermc.hangar.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -9,7 +9,7 @@ import io.papermc.hangar.model.common.Platform;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.internal.versions.HangarVersion;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVersionVisibilityService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVersionVisibilityService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -0,0 +1,51 @@
|
||||
package io.papermc.hangar.service.internal.visibility;
|
||||
|
||||
import io.papermc.hangar.db.dao.HangarDao;
|
||||
import io.papermc.hangar.db.dao.internal.table.VisibilityDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.versions.ProjectVersionsDAO;
|
||||
import io.papermc.hangar.model.db.versions.ProjectVersionTable;
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVersionVisibilityChangeTable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Service
|
||||
public class ProjectVersionVisibilityService extends VisibilityService<ProjectVersionTable, ProjectVersionVisibilityChangeTable> {
|
||||
|
||||
private final ProjectVersionsDAO projectVersionsDAO;
|
||||
private final VisibilityDAO visibilityDAO;
|
||||
|
||||
@Autowired
|
||||
public ProjectVersionVisibilityService(HangarDao<VisibilityDAO> visibilityDAO, HangarDao<ProjectVersionsDAO> projectVersionDAO) {
|
||||
super(ProjectVersionVisibilityChangeTable::new);
|
||||
this.visibilityDAO = visibilityDAO.get();
|
||||
this.projectVersionsDAO = projectVersionDAO.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLastVisChange(long currentUserId, long modelId) {
|
||||
visibilityDAO.updateLatestVersionChange(currentUserId, modelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
ProjectVersionTable getModel(long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
ProjectVersionTable updateModel(ProjectVersionTable model) {
|
||||
return projectVersionsDAO.update(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
void insertNewVisibilityEntry(ProjectVersionVisibilityChangeTable visibilityTable) {
|
||||
visibilityDAO.insert(visibilityTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, ProjectVersionVisibilityChangeTable> getLastVisibilityChange(long principalId) {
|
||||
return visibilityDAO.getLatestVersionVisibilityChange(principalId);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
package io.papermc.hangar.service.internal.visibility;
|
||||
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
|
||||
import io.papermc.hangar.db.dao.HangarDao;
|
||||
import io.papermc.hangar.db.dao.internal.projects.HangarProjectsDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.VisibilityDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.projects.ProjectsDAO;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import io.papermc.hangar.model.db.projects.ProjectTable;
|
||||
import io.papermc.hangar.model.db.visibility.ProjectVisibilityChangeTable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Service
|
||||
public class ProjectVisibilityService extends VisibilityService<ProjectTable, ProjectVisibilityChangeTable> {
|
||||
|
||||
private final ProjectsDAO projectsDAO;
|
||||
private final VisibilityDAO visibilityDAO;
|
||||
private final HangarProjectsDAO hangarProjectsDAO;
|
||||
|
||||
@Autowired
|
||||
public ProjectVisibilityService(HangarDao<VisibilityDAO> visibilityDAO, HangarDao<ProjectsDAO> projectsDAO, HangarDao<HangarProjectsDAO> hangarProjectsDAO) {
|
||||
super(ProjectVisibilityChangeTable::new);
|
||||
this.projectsDAO = projectsDAO.get();
|
||||
this.visibilityDAO = visibilityDAO.get();
|
||||
this.hangarProjectsDAO = hangarProjectsDAO.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectTable changeVisibility(ProjectTable model, Visibility newVisibility, String comment) {
|
||||
if (model == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
Visibility oldVis = model.getVisibility();
|
||||
// TODO add logging for visibility for versions and move this to the abstract class
|
||||
userActionLogService.project(LoggedActionType.PROJECT_VISIBILITY_CHANGE.with(ProjectContext.of(model.getId())), newVisibility.getName(), oldVis.getName());
|
||||
return super.changeVisibility(model, newVisibility, comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLastVisChange(long currentUserId, long modelId) {
|
||||
visibilityDAO.updateLatestProjectChange(currentUserId, modelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
ProjectTable getModel(long id) {
|
||||
return projectsDAO.getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
ProjectTable updateModel(ProjectTable model) {
|
||||
return projectsDAO.update(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
void insertNewVisibilityEntry(ProjectVisibilityChangeTable visibilityTable) {
|
||||
visibilityDAO.insert(visibilityTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postUpdate() {
|
||||
hangarProjectsDAO.refreshHomeProjects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, ProjectVisibilityChangeTable> getLastVisibilityChange(long principalId) {
|
||||
return visibilityDAO.getLatestProjectVisibilityChange(principalId);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package io.papermc.hangar.service.internal.visibility;
|
||||
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.ModelVisible;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.projects.Visibility;
|
||||
import io.papermc.hangar.model.db.ProjectIdentified;
|
||||
import io.papermc.hangar.model.db.Table;
|
||||
import io.papermc.hangar.model.db.visibility.VisibilityChangeTable;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.PermissionService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public abstract class VisibilityService<M extends Table & ModelVisible & ProjectIdentified, VT extends VisibilityChangeTable> extends HangarService {
|
||||
|
||||
@Autowired
|
||||
private PermissionService permissionService;
|
||||
|
||||
private final VisibilityChangeTableConstructor<VT> changeTableConstructor;
|
||||
|
||||
protected VisibilityService(VisibilityChangeTableConstructor<VT> changeTableConstructor) {
|
||||
this.changeTableConstructor = changeTableConstructor;
|
||||
}
|
||||
|
||||
public M changeVisibility(long modelId, Visibility newVisibility, String comment) {
|
||||
return changeVisibility(getModel(modelId), newVisibility, comment);
|
||||
}
|
||||
|
||||
public M changeVisibility(@Nullable M model, Visibility newVisibility, String comment) {
|
||||
if (model == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (model.getVisibility() == newVisibility) return model;
|
||||
|
||||
updateLastVisChange(getHangarPrincipal().getUserId(), model.getId());
|
||||
|
||||
insertNewVisibilityEntry(changeTableConstructor.create(getHangarPrincipal().getUserId(), comment == null ? "" : comment, newVisibility, model.getId()));
|
||||
|
||||
model.setVisibility(newVisibility);
|
||||
model = updateModel(model);
|
||||
postUpdate();
|
||||
return model;
|
||||
}
|
||||
|
||||
public final M checkVisibility(@Nullable M model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
Permission perms = permissionService.getProjectPermissions(getHangarUserId(), model.getProjectId());
|
||||
if (!perms.has(Permission.SeeHidden) && !perms.has(Permission.IsProjectMember) && model.getVisibility() != Visibility.PUBLIC) {
|
||||
return null;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
abstract void updateLastVisChange(long currentUserId, long modelId);
|
||||
|
||||
abstract M getModel(long id);
|
||||
|
||||
abstract M updateModel(M model);
|
||||
|
||||
abstract void insertNewVisibilityEntry(VT visibilityTable);
|
||||
|
||||
protected void postUpdate() { }
|
||||
|
||||
abstract public Entry<String, VT> getLastVisibilityChange(long principalId);
|
||||
|
||||
@FunctionalInterface
|
||||
interface VisibilityChangeTableConstructor<T extends VisibilityChangeTable> {
|
||||
|
||||
T create(long createdBy, String comment, Visibility visibility, long subjectId);
|
||||
}
|
||||
|
||||
}
|
@ -16,8 +16,8 @@ import io.papermc.hangar.modelold.viewhelpers.ProjectData;
|
||||
import io.papermc.hangar.modelold.viewhelpers.ReviewQueueEntry;
|
||||
import io.papermc.hangar.modelold.viewhelpers.UserData;
|
||||
import io.papermc.hangar.modelold.viewhelpers.VersionData;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVersionVisibilityService;
|
||||
import io.papermc.hangar.service.internal.versions.VersionDependencyService;
|
||||
import io.papermc.hangar.service.internal.visibility.ProjectVersionVisibilityService;
|
||||
import io.papermc.hangar.serviceold.project.ChannelService;
|
||||
import io.papermc.hangar.serviceold.project.ProjectService;
|
||||
import io.papermc.hangar.util.RequestUtil;
|
||||
|
Loading…
x
Reference in New Issue
Block a user