mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-01-18 14:14:50 +08:00
channel editing
This commit is contained in:
parent
1522489435
commit
1bbe90ff99
@ -70,7 +70,6 @@ export default class ChannelModal extends HangarFormModal {
|
||||
name: '',
|
||||
color: '',
|
||||
nonReviewed: false,
|
||||
temp: true,
|
||||
versionCount: 0,
|
||||
};
|
||||
|
||||
|
@ -352,6 +352,7 @@ const msgs: LocaleMessageObject = {
|
||||
maxChannels: 'This project already has the maximum number of channels: {0}',
|
||||
duplicateColor: 'This project already has a channel with this color',
|
||||
duplicateName: 'This project already has a channel with this name',
|
||||
tooLongName: 'Channel name is too long',
|
||||
},
|
||||
},
|
||||
manage: {
|
||||
|
@ -25,7 +25,9 @@
|
||||
<td>
|
||||
<ChannelModal :edit="true" :channel="channel" @create="editChannel">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn small color="warning" v-bind="attrs" v-on="on">{{ $t('channel.manage.editButton') }}</v-btn>
|
||||
<v-btn small color="warning" v-bind="attrs" v-on="on">
|
||||
{{ $t('channel.manage.editButton') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
</ChannelModal>
|
||||
</td>
|
||||
@ -40,7 +42,14 @@
|
||||
<v-card-actions>
|
||||
<ChannelModal @create="addChannel">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn v-if="channels.length < validations.project.maxChannelCount" color="primary" v-bind="attrs" v-on="on">
|
||||
<v-btn
|
||||
v-if="channels.length < validations.project.maxChannelCount"
|
||||
:loading="loading.add"
|
||||
:disabled="channels.length >= validations.project.maxChannelCount"
|
||||
color="primary"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon left>mdi-plus</v-icon>
|
||||
{{ $t('channel.manage.add') }}
|
||||
</v-btn>
|
||||
@ -65,10 +74,24 @@ import { RootState } from '~/store';
|
||||
})
|
||||
export default class ProjectChannelsPage extends HangarProjectMixin {
|
||||
channels!: ProjectChannel[];
|
||||
loading = {
|
||||
add: false,
|
||||
};
|
||||
|
||||
// TODO editChannel
|
||||
editChannel(name: String) {
|
||||
console.log('edit channel ', name);
|
||||
editChannel(channel: ProjectChannel) {
|
||||
if (!channel.id) return;
|
||||
const id = channel.id;
|
||||
this.$api
|
||||
.requestInternal(`channels/${this.project.id}/edit`, true, 'post', {
|
||||
id,
|
||||
name: channel.name,
|
||||
color: channel.color,
|
||||
nonReviewed: channel.nonReviewed,
|
||||
})
|
||||
.then(() => {
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError);
|
||||
}
|
||||
|
||||
// TODO deleteChannel
|
||||
@ -76,9 +99,21 @@ export default class ProjectChannelsPage extends HangarProjectMixin {
|
||||
console.log('delete channel ', name);
|
||||
}
|
||||
|
||||
// TODO addChannel
|
||||
addChannel(channel: ProjectChannel) {
|
||||
this.channels.push(Object.assign({}, channel));
|
||||
this.loading.add = true;
|
||||
this.$api
|
||||
.requestInternal(`channels/${this.project.id}/create`, true, 'post', {
|
||||
name: channel.name,
|
||||
color: channel.color,
|
||||
nonReviewed: channel.nonReviewed,
|
||||
})
|
||||
.then(() => {
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError)
|
||||
.finally(() => {
|
||||
this.loading.add = false;
|
||||
});
|
||||
}
|
||||
|
||||
async asyncData({ $api, $util, params }: Context) {
|
||||
|
@ -40,6 +40,7 @@ import { Component, Vue } from 'nuxt-property-decorator';
|
||||
import { Flag } from 'hangar-internal';
|
||||
import UserAvatar from '~/components/users/UserAvatar.vue';
|
||||
import { Visibility } from '~/types/enums';
|
||||
|
||||
@Component({
|
||||
components: { UserAvatar },
|
||||
})
|
||||
@ -59,7 +60,7 @@ export default class AdminFlagsPage extends Vue {
|
||||
];
|
||||
|
||||
get visibilities(): Visibility[] {
|
||||
return Object.keys(Visibility);
|
||||
return Object.keys(Visibility) as Visibility[];
|
||||
}
|
||||
|
||||
// todo send to server
|
||||
|
3
frontend/types/internal/versions.d.ts
vendored
3
frontend/types/internal/versions.d.ts
vendored
@ -1,5 +1,6 @@
|
||||
declare module 'hangar-internal' {
|
||||
import { DependencyVersion, FileInfo, Named, ProjectNamespace, Version } from 'hangar-api';
|
||||
import { Table } from 'hangar-internal';
|
||||
import { Platform } from '~/types/enums';
|
||||
|
||||
interface PlatformDependency {
|
||||
@ -24,7 +25,7 @@ declare module 'hangar-internal' {
|
||||
isFile: boolean;
|
||||
}
|
||||
|
||||
interface ProjectChannel extends Named {
|
||||
interface ProjectChannel extends Named, Partial<Table> {
|
||||
color: string;
|
||||
nonReviewed: boolean;
|
||||
temp?: boolean;
|
||||
|
@ -1,10 +1,15 @@
|
||||
package io.papermc.hangar.controller.internal;
|
||||
|
||||
import io.papermc.hangar.controller.HangarController;
|
||||
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.projects.ProjectTable;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.ChannelForm;
|
||||
import io.papermc.hangar.model.internal.api.requests.projects.EditChannelForm;
|
||||
import io.papermc.hangar.model.internal.projects.HangarChannel;
|
||||
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.VisibilityRequired;
|
||||
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
|
||||
import io.papermc.hangar.service.internal.projects.ChannelService;
|
||||
@ -16,11 +21,14 @@ import org.springframework.http.ResponseEntity;
|
||||
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;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Anyone
|
||||
@Controller
|
||||
@RequestMapping(value = "/api/internal/channels", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class ChannelController extends HangarController {
|
||||
@ -34,13 +42,27 @@ public class ChannelController extends HangarController {
|
||||
this.projectService = projectService;
|
||||
}
|
||||
|
||||
@Anyone
|
||||
@VisibilityRequired(type = Type.PROJECT, args = "{#author, #slug}")
|
||||
@GetMapping(path = "/{author}/{slug}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<List<HangarChannel>> getProjectChannels(@PathVariable String author, @PathVariable String slug) {
|
||||
public ResponseEntity<List<HangarChannel>> getChannels(@PathVariable String author, @PathVariable String slug) {
|
||||
ProjectTable projectTable = projectService.getProjectTable(author, slug);
|
||||
if (projectTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return ResponseEntity.ok(channelService.getProjectChannels(projectTable.getId()));
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_TAGS, args = "{#projectId}")
|
||||
@PostMapping(path = "/{projectId}/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void createChannel(@PathVariable long projectId, @Valid @RequestBody ChannelForm channelForm) {
|
||||
channelService.createProjectChannel(channelForm.getName(), channelForm.getColor(), projectId, channelForm.isNonReviewed());
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_TAGS, args = "{#projectId}")
|
||||
@PostMapping(path = "/{projectId}/edit", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void editChannel(@PathVariable long projectId, @Valid @RequestBody EditChannelForm channelForm) {
|
||||
channelService.editProjectChannel(channelForm.getId(), channelForm.getName(), channelForm.getColor(), projectId, channelForm.isNonReviewed());
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,8 @@ public interface HangarProjectsDAO {
|
||||
@SqlQuery("SELECT pc.*," +
|
||||
" (SELECT count(*) FROM project_versions pv WHERE pv.channel_id = pc.id) as version_count" +
|
||||
" FROM project_channels pc" +
|
||||
" WHERE pc.project_id = :projectId")
|
||||
" WHERE pc.project_id = :projectId" +
|
||||
" ORDER BY pc.created_at")
|
||||
List<HangarChannel> getHangarChannels(long projectId);
|
||||
|
||||
@SqlUpdate("REFRESH MATERIALIZED VIEW home_projects")
|
||||
|
@ -22,12 +22,18 @@ public interface ProjectChannelsDAO {
|
||||
@SqlUpdate("INSERT INTO project_channels (created_at, name, color, project_id, non_reviewed) VALUES (:now, :name, :color, :projectId, :nonReviewed)")
|
||||
ProjectChannelTable insert(@BindBean ProjectChannelTable projectChannelTable);
|
||||
|
||||
@SqlUpdate("UPDATE project_channels SET name = :name, color = :color, non_reviewed = :nonReviewed WHERE id = :id")
|
||||
void update(@BindBean ProjectChannelTable projectChannelTable);
|
||||
|
||||
@SqlQuery("SELECT * FROM project_channels WHERE project_id = :projectId")
|
||||
List<ProjectChannelTable> getProjectChannels(long projectId);
|
||||
|
||||
@SqlQuery("SELECT * FROM project_channels WHERE project_id = :projectId AND name = :name AND color = :color")
|
||||
ProjectChannelTable getProjectChannel(long projectId, String name, @EnumByOrdinal Color color);
|
||||
|
||||
@SqlQuery("SELECT * FROM project_channels WHERE id = :channelId")
|
||||
ProjectChannelTable getProjectChannel(long channelId);
|
||||
|
||||
@SqlQuery("SELECT pc.* FROM project_channels pc JOIN project_versions pv ON pc.id = pv.channel_id WHERE pv.id = :versionId")
|
||||
ProjectChannelTable getProjectChannelForVersion(long versionId);
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
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.common.Color;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class ChannelForm {
|
||||
|
||||
@NotBlank
|
||||
@Validate(SpEL = "@validations.regex(#root, @hangarConfig.channels.nameRegex)", message = "channel.modal.error.invalidName")
|
||||
@Validate(SpEL = "@validations.max(#root, @hangarConfig.channels.maxNameLen)", message = "channel.modal.error.tooLongName")
|
||||
private final String name;
|
||||
@NotNull
|
||||
private final Color color;
|
||||
private final boolean nonReviewed;
|
||||
|
||||
@JsonCreator
|
||||
public ChannelForm(String name, Color color, boolean nonReviewed) {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
this.nonReviewed = nonReviewed;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean isNonReviewed() {
|
||||
return nonReviewed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ChannelForm{" +
|
||||
"name='" + name + '\'' +
|
||||
", color=" + color +
|
||||
", nonReviewed=" + nonReviewed +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.papermc.hangar.model.internal.api.requests.projects;
|
||||
|
||||
import io.papermc.hangar.model.common.Color;
|
||||
|
||||
public class EditChannelForm extends ChannelForm {
|
||||
|
||||
private final long id;
|
||||
|
||||
public EditChannelForm(String name, Color color, boolean nonReviewed, long id) {
|
||||
super(name, color, nonReviewed);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EditChannelForm{" +
|
||||
"id=" + id +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated(forRemoval = true)
|
||||
public @interface ProjectPermission {
|
||||
NamedPermission[] value() default {};
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated(forRemoval = true)
|
||||
public @interface UserLock {
|
||||
@AliasFor("route")
|
||||
Routes value() default Routes.SHOW_HOME;
|
||||
|
@ -12,6 +12,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ChannelService extends HangarService {
|
||||
@ -24,27 +25,41 @@ public class ChannelService extends HangarService {
|
||||
this.hangarProjectsDAO = hangarProjectsDAO.get();
|
||||
}
|
||||
|
||||
public ProjectChannelTable createProjectChannel(String name, Color color, long projectId, boolean nonReviewed) {
|
||||
private void validateChannel(String name, Color color, long projectId, boolean nonReviewed, List<ProjectChannelTable> existingChannels) {
|
||||
if (!config.channels.isValidChannelName(name)) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.invalidName");
|
||||
}
|
||||
|
||||
List<ProjectChannelTable> existingTables = projectChannelsDAO.getProjectChannels(projectId);
|
||||
if (existingTables.size() >= config.projects.getMaxChannels()) {
|
||||
if (existingChannels.size() >= config.projects.getMaxChannels()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.maxChannels", config.projects.getMaxChannels());
|
||||
}
|
||||
|
||||
if (existingTables.stream().anyMatch(ch -> ch.getColor() == color)) {
|
||||
if (existingChannels.stream().anyMatch(ch -> ch.getColor() == color)) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.duplicateColor");
|
||||
}
|
||||
|
||||
if (existingTables.stream().anyMatch(ch -> ch.getName().equalsIgnoreCase(name))) {
|
||||
if (existingChannels.stream().anyMatch(ch -> ch.getName().equalsIgnoreCase(name))) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.duplicateName");
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectChannelTable createProjectChannel(String name, Color color, long projectId, boolean nonReviewed) {
|
||||
validateChannel(name, color, projectId, nonReviewed, projectChannelsDAO.getProjectChannels(projectId));
|
||||
return projectChannelsDAO.insert(new ProjectChannelTable(name, color, projectId, nonReviewed));
|
||||
}
|
||||
|
||||
public void editProjectChannel(long channelId, String name, Color color, long projectId, boolean nonReviewed) {
|
||||
ProjectChannelTable projectChannelTable = getProjectChannel(channelId);
|
||||
if (projectChannelTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
validateChannel(name, color, projectId, nonReviewed, projectChannelsDAO.getProjectChannels(projectId).stream().filter(ch -> ch.getId() != channelId).collect(Collectors.toList()));
|
||||
projectChannelTable.setName(name);
|
||||
projectChannelTable.setColor(color);
|
||||
projectChannelTable.setNonReviewed(nonReviewed);
|
||||
projectChannelsDAO.update(projectChannelTable);
|
||||
}
|
||||
|
||||
public List<HangarChannel> getProjectChannels(long projectId) {
|
||||
return hangarProjectsDAO.getHangarChannels(projectId);
|
||||
}
|
||||
@ -53,6 +68,10 @@ public class ChannelService extends HangarService {
|
||||
return projectChannelsDAO.getProjectChannel(projectId, name, color);
|
||||
}
|
||||
|
||||
public ProjectChannelTable getProjectChannel(long channelId) {
|
||||
return projectChannelsDAO.getProjectChannel(channelId);
|
||||
}
|
||||
|
||||
public ProjectChannelTable getProjectChannelForVersion(long versionId) {
|
||||
return projectChannelsDAO.getProjectChannelForVersion(versionId);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user