mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-21 01:21:54 +08:00
project settings backend
This commit is contained in:
parent
9795d8b9a0
commit
fd370dbcd9
@ -162,6 +162,11 @@ const msgs: LocaleMessageObject = {
|
||||
nameExists: 'A project with this name already exists',
|
||||
slugExists: 'A project with this slug already exists',
|
||||
invalidName: 'This name contains invalid characters',
|
||||
tooLongName: 'Project name is too long',
|
||||
tooLongDesc: 'Project description is too long',
|
||||
tooManyKeywords: 'Project has too many keywords',
|
||||
noCategory: 'Project must have a category',
|
||||
noDescription: 'Project must have a description',
|
||||
},
|
||||
},
|
||||
sendForApproval: 'Send for approval',
|
||||
|
@ -4,7 +4,7 @@
|
||||
<v-card>
|
||||
<v-card-title class="sticky">
|
||||
{{ $t('project.settings.title') }}
|
||||
<v-btn class="flex-right" color="success" @click="save">
|
||||
<v-btn class="flex-right" color="success" :loading="loading.save" @click="save">
|
||||
<v-icon left>mdi-check</v-icon>
|
||||
{{ $t('project.settings.save') }}
|
||||
</v-btn>
|
||||
@ -30,7 +30,7 @@
|
||||
</h2>
|
||||
<p>{{ $t('project.settings.keywordsSub') }}</p>
|
||||
<v-combobox
|
||||
v-model="form.keywords"
|
||||
v-model="form.settings.keywords"
|
||||
small-chips
|
||||
deletable-chips
|
||||
multiple
|
||||
@ -47,7 +47,7 @@
|
||||
{{ $t('project.settings.homepage') }} <small>{{ $t('project.settings.optional') }}</small>
|
||||
</h2>
|
||||
<p>{{ $t('project.settings.homepageSub') }}</p>
|
||||
<v-text-field v-model.trim="form.links.homepage" dense hide-details filled prepend-inner-icon="mdi-home-search" />
|
||||
<v-text-field v-model.trim="form.settings.homepage" dense hide-details filled prepend-inner-icon="mdi-home-search" />
|
||||
</div>
|
||||
<v-divider />
|
||||
<div>
|
||||
@ -55,7 +55,7 @@
|
||||
{{ $t('project.settings.issues') }} <small>{{ $t('project.settings.optional') }}</small>
|
||||
</h2>
|
||||
<p>{{ $t('project.settings.issuesSub') }}</p>
|
||||
<v-text-field v-model.trim="form.links.issues" dense hide-details filled prepend-inner-icon="mdi-bug" />
|
||||
<v-text-field v-model.trim="form.settings.issues" dense hide-details filled prepend-inner-icon="mdi-bug" />
|
||||
</div>
|
||||
<v-divider />
|
||||
<div>
|
||||
@ -63,7 +63,7 @@
|
||||
{{ $t('project.settings.source') }} <small>{{ $t('project.settings.optional') }}</small>
|
||||
</h2>
|
||||
<p>{{ $t('project.settings.sourceSub') }}</p>
|
||||
<v-text-field v-model.trim="form.links.source" dense hide-details filled prepend-inner-icon="mdi-source-branch" />
|
||||
<v-text-field v-model.trim="form.settings.source" dense hide-details filled prepend-inner-icon="mdi-source-branch" />
|
||||
</div>
|
||||
<v-divider />
|
||||
<div>
|
||||
@ -71,10 +71,11 @@
|
||||
{{ $t('project.settings.support') }} <small>{{ $t('project.settings.optional') }}</small>
|
||||
</h2>
|
||||
<p>{{ $t('project.settings.supportSub') }}</p>
|
||||
<v-text-field v-model.trim="form.links.support" dense hide-details filled prepend-inner-icon="mdi-face-agent" />
|
||||
<v-text-field v-model.trim="form.settings.support" dense hide-details filled prepend-inner-icon="mdi-face-agent" />
|
||||
</div>
|
||||
<v-divider />
|
||||
<div>
|
||||
<!--TODO license stuff is outta whack. Different object schema on request and post-->
|
||||
<h2>
|
||||
{{ $t('project.settings.license') }} <small>{{ $t('project.settings.optional') }}</small>
|
||||
</h2>
|
||||
@ -82,7 +83,7 @@
|
||||
<v-row>
|
||||
<v-col cols="12" :md="isCustomLicense ? 4 : 6">
|
||||
<v-select
|
||||
v-model="form.license.type"
|
||||
v-model="form.settings.license.type"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -92,10 +93,16 @@
|
||||
/>
|
||||
</v-col>
|
||||
<v-col v-if="isCustomLicense" cols="12" md="8">
|
||||
<v-text-field v-model.trim="form.license.customName" dense hide-details filled :label="$t('project.settings.licenceCustom')" />
|
||||
<v-text-field
|
||||
v-model.trim="form.settings.license.customName"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
:label="$t('project.settings.licenceCustom')"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" :md="isCustomLicense ? 12 : 6">
|
||||
<v-text-field v-model.trim="form.license.url" dense hide-details filled :label="$t('project.settings.licenceUrl')" />
|
||||
<v-text-field v-model.trim="form.settings.license.url" dense hide-details filled :label="$t('project.settings.licenceUrl')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
@ -107,7 +114,7 @@
|
||||
<p>{{ $t('project.settings.forumSub') }}</p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-switch v-model="form.forumSync"></v-switch>
|
||||
<v-switch v-model="form.settings.forumSync"></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
@ -189,7 +196,7 @@
|
||||
<v-divider />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="success" @click="save">
|
||||
<v-btn color="success" :loading="loading.save" @click="save">
|
||||
<v-icon left>mdi-check</v-icon>
|
||||
{{ $t('project.settings.save') }}
|
||||
</v-btn>
|
||||
@ -211,6 +218,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component } from 'nuxt-property-decorator';
|
||||
import { ProjectSettingsForm } from 'hangar-internal';
|
||||
import { ProjectPermission } from '~/utils/perms';
|
||||
import { NamedPermission, ProjectCategory } from '~/types/enums';
|
||||
import { RootState } from '~/store';
|
||||
@ -224,33 +232,32 @@ import { HangarProjectMixin } from '~/components/mixins';
|
||||
export default class ProjectManagePage extends HangarProjectMixin {
|
||||
apiKey = '';
|
||||
newName = '';
|
||||
form = {
|
||||
keywords: [] as string[],
|
||||
links: {
|
||||
homepage: null as string | null,
|
||||
issues: null as string | null,
|
||||
source: null as string | null,
|
||||
support: null as string | null,
|
||||
form: ProjectSettingsForm = {
|
||||
settings: {
|
||||
homepage: null,
|
||||
issues: null,
|
||||
source: null,
|
||||
support: null,
|
||||
keywords: [],
|
||||
license: {
|
||||
type: '',
|
||||
url: '',
|
||||
customName: '',
|
||||
},
|
||||
forumSync: false,
|
||||
},
|
||||
forumSync: false,
|
||||
description: '',
|
||||
license: {
|
||||
type: '',
|
||||
url: '',
|
||||
customName: '',
|
||||
},
|
||||
category: ProjectCategory.UNDEFINED,
|
||||
};
|
||||
|
||||
loading = {
|
||||
save: false,
|
||||
};
|
||||
|
||||
created() {
|
||||
this.form.keywords = this.project.settings.keywords;
|
||||
this.form.links.homepage = this.project.settings.homepage;
|
||||
this.form.links.issues = this.project.settings.issues;
|
||||
this.form.links.source = this.project.settings.sources;
|
||||
this.form.links.support = this.project.settings.support;
|
||||
Object.assign(this.form.settings, this.project.settings);
|
||||
Object.assign(this.form.settings.license, this.project.settings.license);
|
||||
this.form.description = this.project.description;
|
||||
this.form.forumSync = this.project.settings.forumSync;
|
||||
Object.assign(this.form.license, this.project.settings.license);
|
||||
this.form.category = this.project.category;
|
||||
}
|
||||
|
||||
@ -259,7 +266,7 @@ export default class ProjectManagePage extends HangarProjectMixin {
|
||||
}
|
||||
|
||||
get isCustomLicense() {
|
||||
return this.form.license.type === '(custom)';
|
||||
return this.form.settings.license.type === '(custom)';
|
||||
}
|
||||
|
||||
// TODO do we want to get those from the server? Jake: I think so, it'd be nice to admins to be able to configure default licenses, but not needed for MVP
|
||||
@ -268,7 +275,20 @@ export default class ProjectManagePage extends HangarProjectMixin {
|
||||
}
|
||||
|
||||
// TODO implement
|
||||
save() {}
|
||||
save() {
|
||||
this.loading.save = true;
|
||||
this.$api
|
||||
.requestInternal(`projects/project/${this.$route.params.author}/${this.$route.params.slug}/settings`, true, 'post', {
|
||||
...this.form,
|
||||
})
|
||||
.then(() => {
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError)
|
||||
.finally(() => {
|
||||
this.loading.save = false;
|
||||
});
|
||||
}
|
||||
|
||||
rename() {}
|
||||
|
||||
|
@ -104,7 +104,7 @@
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model.trim="form.links.homepage"
|
||||
v-model.trim="form.settings.homepage"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -114,7 +114,7 @@
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model.trim="form.links.issues"
|
||||
v-model.trim="form.settings.issues"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -124,7 +124,7 @@
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model.trim="form.links.source"
|
||||
v-model.trim="form.settings.source"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -134,7 +134,7 @@
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model.trim="form.links.support"
|
||||
v-model.trim="form.settings.support"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -151,7 +151,7 @@
|
||||
<v-row>
|
||||
<v-col cols="12" :md="isCustomLicense ? 4 : 6">
|
||||
<v-select
|
||||
v-model="form.license.type"
|
||||
v-model="form.settings.license.type"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
@ -161,10 +161,16 @@
|
||||
/>
|
||||
</v-col>
|
||||
<v-col v-if="isCustomLicense" cols="12" md="8">
|
||||
<v-text-field v-model.trim="form.license.customName" dense hide-details filled :label="$t('project.new.step3.customName')" />
|
||||
<v-text-field
|
||||
v-model.trim="form.settings.license.customName"
|
||||
dense
|
||||
hide-details
|
||||
filled
|
||||
:label="$t('project.new.step3.customName')"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" :md="isCustomLicense ? 12 : 6">
|
||||
<v-text-field v-model.trim="form.license.url" dense hide-details filled :label="$t('project.new.step3.url')" />
|
||||
<v-text-field v-model.trim="form.settings.license.url" dense hide-details filled :label="$t('project.new.step3.url')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<div class="text-h6 pt-5">
|
||||
@ -175,7 +181,7 @@
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-combobox
|
||||
v-model="form.keywords"
|
||||
v-model="form.settings.keywords"
|
||||
small-chips
|
||||
deletable-chips
|
||||
multiple
|
||||
@ -230,31 +236,17 @@
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Watch } from 'nuxt-property-decorator';
|
||||
import { Context } from '@nuxt/types';
|
||||
import { ProjectOwner } from 'hangar-internal';
|
||||
import { ProjectOwner, ProjectSettingsForm } from 'hangar-internal';
|
||||
import { AxiosError } from 'axios';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
import StepperStepContent from '~/components/steppers/StepperStepContent.vue';
|
||||
import { RootState } from '~/store';
|
||||
import { ProjectCategory } from '~/types/enums';
|
||||
|
||||
interface NewProjectForm {
|
||||
interface NewProjectForm extends ProjectSettingsForm {
|
||||
ownerId: ProjectOwner['userId'];
|
||||
name: string;
|
||||
description: string;
|
||||
category: ProjectCategory;
|
||||
pageContent: string | null;
|
||||
links: {
|
||||
homepage: string | null;
|
||||
issues: string | null;
|
||||
source: string | null;
|
||||
support: string | null;
|
||||
};
|
||||
license: {
|
||||
type: string | null;
|
||||
url: string | null;
|
||||
customName: string | null;
|
||||
};
|
||||
keywords: string[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -272,12 +264,13 @@ export default class NewPage extends Vue {
|
||||
projectError = false;
|
||||
projectOwners!: ProjectOwner[];
|
||||
error = null as string | null;
|
||||
form = ({
|
||||
form: NewProjectForm = {
|
||||
category: ProjectCategory.ADMIN_TOOLS,
|
||||
links: {},
|
||||
license: {},
|
||||
keywords: [],
|
||||
} as unknown) as NewProjectForm;
|
||||
settings: ({
|
||||
license: {} as ProjectSettingsForm['settings']['license'],
|
||||
keywords: [],
|
||||
} as unknown) as ProjectSettingsForm['settings'],
|
||||
} as NewProjectForm;
|
||||
|
||||
nameErrors: TranslateResult[] = [];
|
||||
|
||||
@ -294,7 +287,7 @@ export default class NewPage extends Vue {
|
||||
}
|
||||
|
||||
get isCustomLicense() {
|
||||
return this.form.license.type === '(custom)';
|
||||
return this.form.settings.license.type === '(custom)';
|
||||
}
|
||||
|
||||
get noBasicSettingsError() {
|
||||
|
21
frontend/types/internal/projects.d.ts
vendored
21
frontend/types/internal/projects.d.ts
vendored
@ -1,6 +1,7 @@
|
||||
declare module 'hangar-internal' {
|
||||
import { Table, FlagReason } from 'hangar-internal';
|
||||
import { FlagReason, Table } from 'hangar-internal';
|
||||
import { Project, Role, User } from 'hangar-api';
|
||||
import { ProjectCategory } from '~/types/enums';
|
||||
|
||||
interface ProjectOwner {
|
||||
id: number;
|
||||
@ -64,4 +65,22 @@ declare module 'hangar-internal' {
|
||||
message: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
interface ProjectSettingsForm {
|
||||
settings: {
|
||||
homepage: string | null;
|
||||
issues: string | null;
|
||||
source: string | null;
|
||||
support: string | null;
|
||||
keywords: string[];
|
||||
license: {
|
||||
type: string;
|
||||
url: string;
|
||||
customName: string;
|
||||
};
|
||||
forumSync: false;
|
||||
};
|
||||
category: ProjectCategory;
|
||||
description: string;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
package io.papermc.hangar.controller.internal;
|
||||
|
||||
import io.papermc.hangar.controller.HangarController;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.common.PermissionType;
|
||||
import io.papermc.hangar.model.db.projects.ProjectTable;
|
||||
import io.papermc.hangar.model.internal.HangarProject;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProject;
|
||||
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.responses.PossibleProjectOwner;
|
||||
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;
|
||||
@ -64,7 +68,7 @@ public class ProjectController extends HangarController {
|
||||
|
||||
@Unlocked
|
||||
@PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> createProject(@RequestBody @Valid NewProject newProject) {
|
||||
public ResponseEntity<String> createProject(@RequestBody @Valid NewProjectForm newProject) {
|
||||
ProjectTable projectTable = projectFactory.createProject(newProject);
|
||||
return ResponseEntity.ok(projectTable.getUrl());
|
||||
}
|
||||
@ -76,6 +80,15 @@ public class ProjectController extends HangarController {
|
||||
return ResponseEntity.ok(projectService.getHangarProject(author, slug));
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_SUBJECT_SETTINGS, args = "{#author, #slug}")
|
||||
@PostMapping(path = "/project/{author}/{slug}/settings", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveProjectSettings(@PathVariable String author, @PathVariable String slug, @Valid @RequestBody ProjectSettingsForm settingsForm) {
|
||||
projectService.saveSettings(author, slug, settingsForm);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@VisibilityRequired(type = Type.PROJECT, args = "{#projectId}")
|
||||
@PostMapping("/project/{id}/star/{state}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@ -83,6 +96,7 @@ public class ProjectController extends HangarController {
|
||||
userService.toggleStarred(projectId, state);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@VisibilityRequired(type = Type.PROJECT, args = "{#projectId}")
|
||||
@PostMapping("/project/{id}/watch/{state}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
|
@ -1,15 +1,33 @@
|
||||
package io.papermc.hangar.model.api.project;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
|
||||
import io.papermc.hangar.util.StringUtils;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ProjectLicense {
|
||||
|
||||
private final String name;
|
||||
private final String url;
|
||||
|
||||
@JdbiConstructor
|
||||
public ProjectLicense(String name, String url) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@JsonCreator(mode = Mode.DELEGATING)
|
||||
public ProjectLicense(Map<String, String> map) {
|
||||
String licenseName = StringUtils.stringOrNull(map.get("customName"));
|
||||
if (licenseName == null) {
|
||||
licenseName = map.get("type");
|
||||
}
|
||||
this.name = licenseName;
|
||||
this.url = map.get("url");
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package io.papermc.hangar.model.api.project;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ProjectSettings {
|
||||
@ -12,9 +15,12 @@ public class ProjectSettings {
|
||||
private final String source;
|
||||
private final String support;
|
||||
private final ProjectLicense license;
|
||||
@NotNull
|
||||
@Validate(SpEL = "#root.size le @hangarConfig.projects.maxKeywords", message = "project.new.error.tooManyKeywords")
|
||||
private final Collection<String> keywords;
|
||||
private final boolean forumSync;
|
||||
|
||||
@JsonCreator
|
||||
public ProjectSettings(@Nullable String homepage, @Nullable String issues, @Nullable String source, @Nullable String support, @Nullable @Nested("license") ProjectLicense license, Collection<String> keywords, boolean forumSync) {
|
||||
this.homepage = homepage;
|
||||
this.issues = issues;
|
||||
|
@ -7,7 +7,7 @@ import io.papermc.hangar.model.common.projects.Category;
|
||||
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.internal.api.requests.projects.NewProject;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProjectForm;
|
||||
import io.papermc.hangar.util.StringUtils;
|
||||
import org.jdbi.v3.core.enums.EnumByOrdinal;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
@ -36,21 +36,21 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
private String licenseUrl;
|
||||
private boolean forumSync;
|
||||
|
||||
public ProjectTable(ProjectOwner projectOwner, NewProject newProject) {
|
||||
this.name = newProject.getName();
|
||||
public ProjectTable(ProjectOwner projectOwner, NewProjectForm form) {
|
||||
this.name = form.getName();
|
||||
this.slug = StringUtils.slugify(this.name);
|
||||
this.ownerName = projectOwner.getName();
|
||||
this.ownerId = projectOwner.getUserId();
|
||||
this.category = newProject.getCategory();
|
||||
this.description = newProject.getDescription();
|
||||
this.category = form.getCategory();
|
||||
this.description = form.getDescription();
|
||||
this.visibility = Visibility.NEW;
|
||||
this.homepage = newProject.getHomepageUrl();
|
||||
this.issues = newProject.getIssuesUrl();
|
||||
this.source = newProject.getSourceUrl();
|
||||
this.support = newProject.getSupportUrl();
|
||||
this.keywords = newProject.getKeywords();
|
||||
this.licenseName = newProject.getLicenseName();
|
||||
this.licenseUrl = newProject.getLicenseUrl();
|
||||
this.homepage = form.getSettings().getHomepage();
|
||||
this.issues = form.getSettings().getIssues();
|
||||
this.source = form.getSettings().getSource();
|
||||
this.support = form.getSettings().getSupport();
|
||||
this.keywords = form.getSettings().getKeywords();
|
||||
this.licenseName = form.getSettings().getLicense().getName();
|
||||
this.licenseUrl = form.getSettings().getLicense().getUrl();
|
||||
}
|
||||
|
||||
protected ProjectTable(ProjectTable other) {
|
||||
|
@ -1,121 +0,0 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
import io.papermc.hangar.util.StringUtils;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class NewProject {
|
||||
|
||||
private final long ownerId;
|
||||
@NotNull
|
||||
@Validate(SpEL = "#root.length le @hangarConfig.projects.maxNameLen", message = "Project name too long") // TODO i18n
|
||||
@Validate(SpEL = "#root matches @hangarConfig.projects.nameRegex", message = "Invalid project name") // TODO i18n
|
||||
private final String name;
|
||||
@NotNull
|
||||
@Validate(SpEL = "#root.length le @hangarConfig.projects.maxDescLen", message = "Project description too long") // TODO i18n
|
||||
private final String description;
|
||||
@NotNull
|
||||
private final Category category;
|
||||
private final String pageContent;
|
||||
private final String homepageUrl;
|
||||
private final String issuesUrl;
|
||||
private final String sourceUrl;
|
||||
private final String supportUrl;
|
||||
private final String licenseName;
|
||||
private final String licenseUrl;
|
||||
@NotNull
|
||||
@Validate(SpEL = "#root.size le @hangarConfig.projects.maxKeywords", message = "Too many keywords") // TODO i18n
|
||||
private final Set<String> keywords;
|
||||
|
||||
@JsonCreator
|
||||
public NewProject(long ownerId, @NotNull String name, @NotNull String description, @NotNull Category category, String pageContent, @JsonProperty("links") Map<String, String> linkMap, @JsonProperty("license") Map<String, String> licenseMap, @NotNull Set<String> keywords) {
|
||||
this.ownerId = ownerId;
|
||||
this.name = StringUtils.compact(name);
|
||||
this.description = description;
|
||||
this.category = category;
|
||||
this.pageContent = pageContent;
|
||||
this.homepageUrl = linkMap.get("homepage");
|
||||
this.issuesUrl = linkMap.get("issues");
|
||||
this.sourceUrl = linkMap.get("source");
|
||||
this.supportUrl = linkMap.get("support");
|
||||
String licenseName = StringUtils.stringOrNull(licenseMap.get("customName"));
|
||||
if (licenseName == null) {
|
||||
licenseName = licenseMap.get("type");
|
||||
}
|
||||
this.licenseName = licenseName;
|
||||
this.licenseUrl = licenseMap.get("url");
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
public long getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getPageContent() {
|
||||
return pageContent;
|
||||
}
|
||||
|
||||
public String getHomepageUrl() {
|
||||
return homepageUrl;
|
||||
}
|
||||
|
||||
public String getIssuesUrl() {
|
||||
return issuesUrl;
|
||||
}
|
||||
|
||||
public String getSourceUrl() {
|
||||
return sourceUrl;
|
||||
}
|
||||
|
||||
public String getSupportUrl() {
|
||||
return supportUrl;
|
||||
}
|
||||
|
||||
public String getLicenseName() {
|
||||
return licenseName;
|
||||
}
|
||||
|
||||
public String getLicenseUrl() {
|
||||
return licenseUrl;
|
||||
}
|
||||
|
||||
public Set<String> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NewProject{" +
|
||||
"ownerId=" + ownerId +
|
||||
", name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", category=" + category +
|
||||
", pageContent='" + pageContent + '\'' +
|
||||
", homepageUrl='" + homepageUrl + '\'' +
|
||||
", issuesUrl='" + issuesUrl + '\'' +
|
||||
", sourceUrl='" + sourceUrl + '\'' +
|
||||
", supportUrl='" + supportUrl + '\'' +
|
||||
", licenseName='" + licenseName + '\'' +
|
||||
", licenseUrl='" + licenseUrl + '\'' +
|
||||
", keywords=" + keywords +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.api.project.ProjectSettings;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class NewProjectForm extends ProjectSettingsForm {
|
||||
|
||||
private final long ownerId;
|
||||
@NotNull(message = "project.new.error.invalidName")
|
||||
@Validate(SpEL = "#root.length le @hangarConfig.projects.maxNameLen", message = "project.new.error.tooLongName")
|
||||
@Validate(SpEL = "#root matches @hangarConfig.projects.nameRegex", message = "project.new.error.invalidName")
|
||||
private final String name;
|
||||
private final String pageContent;
|
||||
|
||||
public NewProjectForm(ProjectSettings settings, Category category, String description, long ownerId, String name, String pageContent) {
|
||||
super(settings, category, description);
|
||||
this.ownerId = ownerId;
|
||||
this.name = name;
|
||||
this.pageContent = pageContent;
|
||||
}
|
||||
|
||||
public long getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPageContent() {
|
||||
return pageContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NewProjectForm{" +
|
||||
"ownerId=" + ownerId +
|
||||
", name='" + name + '\'' +
|
||||
", pageContent='" + pageContent + '\'' +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import io.papermc.hangar.controller.validations.Validate;
|
||||
import io.papermc.hangar.model.api.project.ProjectSettings;
|
||||
import io.papermc.hangar.model.common.projects.Category;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class ProjectSettingsForm {
|
||||
|
||||
@Valid
|
||||
private final ProjectSettings settings;
|
||||
@NotNull(message = "project.new.error.noCategory")
|
||||
private final Category category;
|
||||
@NotNull(message = "project.new.error.noDescription")
|
||||
@Validate(SpEL = "#root.length le @hangarConfig.projects.maxDescLen", message = "project.new.error.tooLongDesc")
|
||||
private final String description;
|
||||
|
||||
@JsonCreator
|
||||
public ProjectSettingsForm(ProjectSettings settings, Category category, String description) {
|
||||
this.settings = settings;
|
||||
this.category = category;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public ProjectSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectSettingsForm{" +
|
||||
"settings=" + settings +
|
||||
", category=" + category +
|
||||
", description='" + description + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import io.papermc.hangar.model.common.roles.ProjectRole;
|
||||
import io.papermc.hangar.model.db.members.ProjectMemberTable;
|
||||
import io.papermc.hangar.model.db.projects.ProjectOwner;
|
||||
import io.papermc.hangar.model.db.projects.ProjectTable;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProject;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.NewProjectForm;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.api.UsersApiService;
|
||||
import io.papermc.hangar.service.internal.roles.MemberService;
|
||||
@ -37,7 +37,7 @@ public class ProjectFactory extends HangarService {
|
||||
this.usersApiService = usersApiService;
|
||||
}
|
||||
|
||||
public ProjectTable createProject(NewProject newProject) {
|
||||
public ProjectTable createProject(NewProjectForm newProject) {
|
||||
ProjectOwner projectOwner = projectService.getProjectOwner(newProject.getOwnerId());
|
||||
if (projectOwner == null) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "error.project.ownerNotFound");
|
||||
|
@ -1,9 +1,12 @@
|
||||
package io.papermc.hangar.service.internal.projects;
|
||||
|
||||
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.HangarProjectsDAO;
|
||||
import io.papermc.hangar.db.dao.internal.HangarUsersDAO;
|
||||
import io.papermc.hangar.db.dao.internal.table.projects.ProjectsDAO;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.api.project.Project;
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.OrganizationTable;
|
||||
@ -15,6 +18,7 @@ import io.papermc.hangar.model.internal.HangarProject;
|
||||
import io.papermc.hangar.model.internal.HangarProject.HangarProjectInfo;
|
||||
import io.papermc.hangar.model.internal.HangarProjectFlag;
|
||||
import io.papermc.hangar.model.internal.HangarProjectPage;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.ProjectSettingsForm;
|
||||
import io.papermc.hangar.model.internal.user.JoinableMember;
|
||||
import io.papermc.hangar.service.HangarService;
|
||||
import io.papermc.hangar.service.VisibilityService.ProjectVisibilityService;
|
||||
@ -23,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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;
|
||||
@ -89,6 +94,28 @@ public class ProjectService extends HangarService {
|
||||
return new HangarProject(project.getRight(), project.getLeft(), projectOwner, members, "", "", info, pages.values());
|
||||
}
|
||||
|
||||
public void saveSettings(String author, String slug, ProjectSettingsForm settingsForm) {
|
||||
ProjectTable projectTable = getProjectTable(author, slug);
|
||||
if (projectTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
projectTable.setCategory(settingsForm.getCategory());
|
||||
projectTable.setKeywords(settingsForm.getSettings().getKeywords());
|
||||
projectTable.setHomepage(settingsForm.getSettings().getHomepage());
|
||||
projectTable.setIssues(settingsForm.getSettings().getIssues());
|
||||
projectTable.setSource(settingsForm.getSettings().getSource());
|
||||
projectTable.setSupport(settingsForm.getSettings().getSupport());
|
||||
projectTable.setLicenseName(settingsForm.getSettings().getLicense().getName());
|
||||
projectTable.setLicenseUrl(settingsForm.getSettings().getLicense().getUrl());
|
||||
projectTable.setForumSync(settingsForm.getSettings().isForumSync());
|
||||
projectTable.setDescription(settingsForm.getDescription());
|
||||
projectsDAO.update(projectTable);
|
||||
// TODO is icon change?
|
||||
// TODO role updates
|
||||
refreshHomeProjects();
|
||||
userActionLogService.project(LoggedActionType.PROJECT_SETTINGS_CHANGED.with(ProjectContext.of(projectTable.getId())), "", "");
|
||||
}
|
||||
|
||||
// TODO implement flag view
|
||||
public List<HangarProjectFlag> getHangarProjectFlags(String author, String slug) {
|
||||
return hangarProjectsDAO.getHangarProjectFlags(author, slug);
|
||||
|
Loading…
Reference in New Issue
Block a user