diff --git a/frontend/locales/en.ts b/frontend/locales/en.ts index 77b600116..c9fbb3555 100644 --- a/frontend/locales/en.ts +++ b/frontend/locales/en.ts @@ -353,6 +353,7 @@ const msgs: LocaleMessageObject = { 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', + cannotDelete: 'You cannot delete this channel', }, }, manage: { diff --git a/frontend/pages/_author/_slug/channels.vue b/frontend/pages/_author/_slug/channels.vue index 28dccc520..67376705d 100644 --- a/frontend/pages/_author/_slug/channels.vue +++ b/frontend/pages/_author/_slug/channels.vue @@ -11,7 +11,7 @@ mdi-format-list-numbered{{ $t('channel.manage.versionCount') }} mdi-file-find{{ $t('channel.manage.reviewed') }} mdi-pencil{{ $t('channel.manage.edit') }} - mdi-delete{{ $t('channel.manage.trash') }} + mdi-delete{{ $t('channel.manage.trash') }} @@ -31,9 +31,10 @@ - - - {{ $t('channel.manage.deleteButton') }} + + + {{ $t('channel.manage.deleteButton') }} + @@ -80,10 +81,9 @@ export default class ProjectChannelsPage extends HangarProjectMixin { editChannel(channel: ProjectChannel) { if (!channel.id) return; - const id = channel.id; this.$api .requestInternal(`channels/${this.project.id}/edit`, true, 'post', { - id, + id: channel.id, name: channel.name, color: channel.color, nonReviewed: channel.nonReviewed, @@ -94,9 +94,14 @@ export default class ProjectChannelsPage extends HangarProjectMixin { .catch(this.$util.handleRequestError); } - // TODO deleteChannel - deleteChannel(name: String) { - console.log('delete channel ', name); + deleteChannel(channel: ProjectChannel) { + if (!channel.id) return; + this.$api + .requestInternal(`channels/${this.project.id}/delete/${channel.id}`, true, 'post') + .then(() => { + this.$nuxt.refresh(); + }) + .catch(this.$util.handleRequestError); } addChannel(channel: ProjectChannel) { diff --git a/src/main/java/io/papermc/hangar/controller/internal/ChannelController.java b/src/main/java/io/papermc/hangar/controller/internal/ChannelController.java index 2351f61d4..20ce5da24 100644 --- a/src/main/java/io/papermc/hangar/controller/internal/ChannelController.java +++ b/src/main/java/io/papermc/hangar/controller/internal/ChannelController.java @@ -51,7 +51,7 @@ public class ChannelController extends HangarController { } @Unlocked - @ResponseStatus(HttpStatus.OK) + @ResponseStatus(HttpStatus.CREATED) @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) { @@ -65,4 +65,12 @@ public class ChannelController extends HangarController { public void editChannel(@PathVariable long projectId, @Valid @RequestBody EditChannelForm channelForm) { channelService.editProjectChannel(channelForm.getId(), channelForm.getName(), channelForm.getColor(), projectId, channelForm.isNonReviewed()); } + + @Unlocked + @ResponseStatus(HttpStatus.OK) + @PermissionRequired(type = PermissionType.PROJECT,perms = NamedPermission.EDIT_TAGS, args = "{#projectId}") + @PostMapping("/{projectId}/delete/{channelId}") + public void deleteChannel(@PathVariable long projectId, @PathVariable long channelId) { + channelService.deleteProjectChannel(projectId, channelId); + } } diff --git a/src/main/java/io/papermc/hangar/db/dao/internal/HangarProjectsDAO.java b/src/main/java/io/papermc/hangar/db/dao/internal/HangarProjectsDAO.java index a2971aa0e..a9de22d45 100644 --- a/src/main/java/io/papermc/hangar/db/dao/internal/HangarProjectsDAO.java +++ b/src/main/java/io/papermc/hangar/db/dao/internal/HangarProjectsDAO.java @@ -100,6 +100,13 @@ public interface HangarProjectsDAO { " WHERE lower(p.owner_name) = lower(:author) AND lower(p.slug) = lower(:slug)") List getHangarProjectFlags(String author, String slug); + @RegisterConstructorMapper(HangarChannel.class) + @SqlQuery("SELECT pc.*," + + " (SELECT count(*) FROM project_versions pv WHERE pv.channel_id = pc.id) as version_count" + + " FROM project_channels pc" + + " WHERE pc.id = :channelId") + HangarChannel getHangarChannel(long channelId); + @RegisterConstructorMapper(HangarChannel.class) @SqlQuery("SELECT pc.*," + " (SELECT count(*) FROM project_versions pv WHERE pv.channel_id = pc.id) as version_count" + diff --git a/src/main/java/io/papermc/hangar/db/dao/internal/table/projects/ProjectChannelsDAO.java b/src/main/java/io/papermc/hangar/db/dao/internal/table/projects/ProjectChannelsDAO.java index fd1c456cd..67053ab89 100644 --- a/src/main/java/io/papermc/hangar/db/dao/internal/table/projects/ProjectChannelsDAO.java +++ b/src/main/java/io/papermc/hangar/db/dao/internal/table/projects/ProjectChannelsDAO.java @@ -25,6 +25,9 @@ public interface ProjectChannelsDAO { @SqlUpdate("UPDATE project_channels SET name = :name, color = :color, non_reviewed = :nonReviewed WHERE id = :id") void update(@BindBean ProjectChannelTable projectChannelTable); + @SqlUpdate("DELETE FROM project_channels WHERE id = :id") + void delete(@BindBean ProjectChannelTable projectChannelTable); + @SqlQuery("SELECT * FROM project_channels WHERE project_id = :projectId") List getProjectChannels(long projectId); diff --git a/src/main/java/io/papermc/hangar/service/internal/projects/ChannelService.java b/src/main/java/io/papermc/hangar/service/internal/projects/ChannelService.java index 6b122b3e0..db8eedf5c 100644 --- a/src/main/java/io/papermc/hangar/service/internal/projects/ChannelService.java +++ b/src/main/java/io/papermc/hangar/service/internal/projects/ChannelService.java @@ -35,16 +35,17 @@ public class ChannelService extends HangarService { } if (existingChannels.stream().anyMatch(ch -> ch.getColor() == color)) { - throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.duplicateColor"); + throw new HangarApiException(HttpStatus.CONFLICT, "channel.modal.error.duplicateColor"); } if (existingChannels.stream().anyMatch(ch -> ch.getName().equalsIgnoreCase(name))) { - throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.duplicateName"); + throw new HangarApiException(HttpStatus.CONFLICT, "channel.modal.error.duplicateName"); } } public ProjectChannelTable createProjectChannel(String name, Color color, long projectId, boolean nonReviewed) { validateChannel(name, color, projectId, nonReviewed, projectChannelsDAO.getProjectChannels(projectId)); + // TODO user action logging return projectChannelsDAO.insert(new ProjectChannelTable(name, color, projectId, nonReviewed)); } @@ -58,6 +59,20 @@ public class ChannelService extends HangarService { projectChannelTable.setColor(color); projectChannelTable.setNonReviewed(nonReviewed); projectChannelsDAO.update(projectChannelTable); + // TODO user action logging + } + + public void deleteProjectChannel(long projectId, long channelId) { + HangarChannel hangarChannel = hangarProjectsDAO.getHangarChannel(channelId); + if (hangarChannel == null) { + throw new HangarApiException(HttpStatus.NOT_FOUND); + } + if (hangarChannel.getVersionCount() != 0 || getProjectChannels(projectId).size() == 1) { + // Cannot delete channels with versions or if its the last channel + throw new HangarApiException(HttpStatus.BAD_REQUEST, "channel.modal.error.cannotDelete"); + } + projectChannelsDAO.delete(hangarChannel); + // TODO user action logging } public List getProjectChannels(long projectId) {