Remove recommended version setting

Missing migrations, possibly broke controller?
This commit is contained in:
Nassim Jahnke 2022-06-26 20:50:04 +02:00
parent 2b671c1482
commit 65bd042caa
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
28 changed files with 182 additions and 384 deletions

View File

@ -12,7 +12,7 @@ import { useVuelidate } from "@vuelidate/core";
import InputCheckbox from "~/components/ui/InputCheckbox.vue";
import { ChannelFlag } from "~/types/enums";
const possibleFlags = [ChannelFlag.UNSTABLE, ChannelFlag.SKIP_REVIEW_QUEUE, ChannelFlag.PINNED]; // TODO maybe load from backend? unsure if needed
const possibleFlags = [ChannelFlag.UNSTABLE, ChannelFlag.PINNED]; // TODO maybe load from backend? unsure if needed
const props = defineProps<{
projectId: number;

View File

@ -20,15 +20,13 @@ interface DownloadableVersion {
const props = withDefaults(
defineProps<{
project: HangarProject;
recommended?: boolean;
small?: boolean;
// Define either version and platform or pinnedVersion
// Define either version and platform or pinnedVersion, or neither to use main channel versions
platform?: Platform;
version?: DownloadableVersion;
pinnedVersion?: PinnedVersion;
}>(),
{
recommended: false,
small: false,
}
);
@ -38,7 +36,7 @@ function downloadLink(platform: Platform, version: DownloadableVersion) {
return version.externalUrl;
}
const versionString = props.recommended ? "recommended" : version.name;
const versionString = version.name;
const path = `/api/v1/projects/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${versionString}/${platform.toLowerCase()}/download`;
return import.meta.env.SSR ? path : `${window.location.protocol}//${window.location.host}${path}`;
}
@ -48,27 +46,7 @@ const external = computed(() => false);
<template>
<div class="flex items-center">
<DropdownButton v-if="recommended" :button-size="small ? 'medium' : 'large'">
<template #button-label>
<span class="items-center inline-flex">
<IconMdiDownloadOutline />
<span v-if="!small" class="ml-1">{{ external ? i18n.t("version.page.downloadExternal") : i18n.t("version.page.download") }}</span>
</span>
</template>
<DropdownItem
v-for="(url, pl, idx) in project.recommendedVersions"
:key="`${pl}-${idx}`"
class="flex items-center"
:href="url || downloadLink(pl, null)"
target="_blank"
rel="noopener noreferrer"
>
<PlatformLogo :platform="pl" :size="24" class="mr-1" />
{{ backendData.platforms.get(pl).name }}
</DropdownItem>
</DropdownButton>
<DropdownButton v-else-if="pinnedVersion" :button-size="small ? 'medium' : 'large'">
<DropdownButton v-if="pinnedVersion" :button-size="small ? 'medium' : 'large'">
<template #button-label>
<span class="items-center inline-flex">
<IconMdiDownloadOutline />
@ -88,11 +66,31 @@ const external = computed(() => false);
</DropdownItem>
</DropdownButton>
<a v-else :href="downloadLink(platform, version)" target="_blank" rel="noopener noreferrer">
<a v-else-if="platform && version" :href="downloadLink(platform, version)" target="_blank" rel="noopener noreferrer">
<Button :size="small ? 'medium' : 'large'">
<IconMdiDownloadOutline />
<span v-if="!small" class="ml-1">{{ external ? i18n.t("version.page.downloadExternal") : i18n.t("version.page.download") }}</span>
</Button>
</a>
<DropdownButton v-else :button-size="small ? 'medium' : 'large'">
<template #button-label>
<span class="items-center inline-flex">
<IconMdiDownloadOutline />
<span v-if="!small" class="ml-1">{{ i18n.t("version.page.download") }}</span>
</span>
</template>
<DropdownItem
v-for="(v, p) in project.mainChannelVersions"
:key="p"
class="flex items-center"
:href="downloadLink(p, v)"
target="_blank"
rel="noopener noreferrer"
>
<PlatformLogo :platform="p" :size="24" class="mr-1" />
{{ backendData.platforms?.get(p).name }}
</DropdownItem>
</DropdownButton>
</div>
</template>

View File

@ -118,7 +118,7 @@ async function sendForApproval() {
<p>{{ project.description }}</p>
</div>
<div class="flex flex-col justify-around <sm:items-center space-y-2 items-end justify-between flex-shrink-0">
<DownloadButton v-if="project.recommendedVersions && Object.keys(project.recommendedVersions).length > 0" :project="project" recommended />
<DownloadButton v-if="Object.keys(project.mainChannelVersions).length !== 0" :project="project" />
<div class="flex">
<Tooltip>
<template #content>

View File

@ -397,7 +397,6 @@
"channel": "Channel",
"addChannel": "Add Channel",
"unstable": "Unstable",
"recommended": "Recommended",
"forumPost": "Forum Post",
"release": {
"bulletin": "Release Bulletin",
@ -475,10 +474,8 @@
"adminMsg": "Approved by {0} {1}",
"reviewLogs": "Review logs",
"reviewStart": "Start review",
"setRecommended": "Set as Recommended",
"manage": "Management",
"visibility": "Visibility: {0}",
"setRecommendedTooltip": "Set this version as recommended for {0} platform",
"pinned": {
"tooltip": {
"none": "Pin this version to the main project page",
@ -501,7 +498,6 @@
"download": "Download",
"downloadExternal": "Download External",
"adminActions": "Admin actions",
"recommended": "Recommended version",
"partiallyApproved": "Partially approved",
"approved": "Approved",
"userAdminLogs": "User Admin Logs",
@ -526,8 +522,7 @@
"success": {
"softDelete": "You have deleted this version",
"hardDelete": "You have fully deleted this version",
"restore": "You have restored this version",
"recommended": "You have marked this version as recommended for {0} platform"
"restore": "You have restored this version"
}
},
"channel": {
@ -537,7 +532,6 @@
"name": "Channel Name",
"color": "Channel Color",
"flags": {
"skip_review_queue": "Exclude from moderation review queue?",
"unstable": "Versions are to be considered unstable",
"pinned": "Latest version is highlighted on main project page"
},
@ -557,6 +551,8 @@
}
},
"manage": {
"mainTitle": "Main stable channel",
"mainSubtitle": "The most recent builds on the main stable channel will be provided in the download button in the project's header.",
"title": "Release channels",
"reviewInfo": "Channel names may automatically be flagged for review and will only be published after",
"subtitle": "Release channels represent the state of a plugin release. A project may have up to five release channels.",

View File

@ -20,6 +20,8 @@ import { projectIconUrl } from "~/composables/useUrlHelper";
import { useRoute } from "vue-router";
import Tooltip from "~/components/design/Tooltip.vue";
import { useNotificationStore } from "~/store/notification";
import InputRadio from "~/components/ui/InputRadio.vue";
import { ref } from "vue";
const props = defineProps<{
user: User;
@ -31,6 +33,7 @@ const route = useRoute();
const channels = await useProjectChannels(props.project.namespace.owner, props.project.namespace.slug).catch((e) => handleRequestError(e, ctx, i18n));
const validations = useBackendDataStore().validations;
const notifications = useNotificationStore();
//const mainChannel = ref(null); //TODO set
useHead(
useSeo("Channels | " + props.project.name, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug))
@ -78,6 +81,16 @@ async function editChannel(channel: ProjectChannel) {
</script>
<template>
<!--<Card class="mb-4">
<template #header>{{ i18n.t("channel.manage.mainTitle") }}</template>
<p class="mb-2">{{ i18n.t("channel.manage.mainSubtitle") }}</p>
<ul>
<li v-for="channel in channels" :key="channel.name" class="inline-flex w-full">
<InputRadio v-model="mainChannel" :value="channel.name" />
<Tag :name="channel.name" :color="{ background: channel.color }"></Tag>
</li>
</ul>
</Card>-->
<Card>
<template #header>{{ i18n.t("channel.manage.title") }}</template>
<p class="mb-2">{{ i18n.t("channel.manage.subtitle") }}</p>
@ -87,12 +100,6 @@ async function editChannel(channel: ProjectChannel) {
<tr>
<th><IconMdiTag />{{ i18n.t("channel.manage.channelName") }}</th>
<th><IconMdiFormatListNumbered />{{ i18n.t("channel.manage.versionCount") }}</th>
<Tooltip>
<template #content>
{{ i18n.t("channel.manage.reviewInfo") }}
</template>
<th><IconMdiFileFind />{{ i18n.t("channel.manage.reviewed") }}</th>
</Tooltip>
<th><IconMdiPencil />{{ i18n.t("channel.manage.edit") }}</th>
<th v-if="channels.length !== 1"><IconMdiDelete />{{ i18n.t("channel.manage.trash") }}</th>
</tr>
@ -101,10 +108,6 @@ async function editChannel(channel: ProjectChannel) {
<tr v-for="channel in channels" :key="channel.name">
<td><Tag :name="channel.name" :color="{ background: channel.color }" /></td>
<td>{{ channel.versionCount }}</td>
<td>
<IconMdiCheckboxBlankCircleOutline v-if="channel.flags.indexOf(ChannelFlag.SKIP_REVIEW_QUEUE) > -1" />
<IconMdiCheckCircle v-else />
</td>
<td>
<ChannelModal :project-id="props.project.id" edit :channel="channel" @create="editChannel">
<template #activator="{ on, attrs }">

View File

@ -96,16 +96,6 @@ async function savePage(content: string) {
}
}
async function setRecommended() {
try {
await useInternalApi(`versions/version/${props.project.id}/${projectVersion.value?.id}/${platform.value?.enumName}/recommend`, true, "post");
notification.success(i18n.t("version.success.recommended", [platform.value?.name]));
router.go(0);
} catch (e) {
handleRequestError(e as AxiosError, ctx, i18n);
}
}
async function setPinned(value: boolean) {
try {
await useInternalApi(`versions/version/${props.project.id}/${projectVersion.value?.id}/pinned?value=${value}`, true, "post");
@ -164,9 +154,6 @@ async function restoreVersion() {
<h1 class="text-3xl sm:inline-flex items-center">
<TagComponent class="mr-1" :name="projectVersion.channel.name" :color="{ background: projectVersion.channel.color }" :short-form="true" />
{{ projectVersion.name }}
<Tooltip v-if="projectVersion.recommended.includes(platform?.enumName)" :content="i18n.t('version.page.recommended')" class="text-base">
<IconMdiDiamondStone :title="i18n.t('version.page.recommended')" class="text-2xl ml-1" />
</Tooltip>
<Tooltip v-if="isReviewStateChecked" :content="approvalTooltip" class="text-base">
<IconMdiCheckCircleOutline class="text-2xl ml-1" />
</Tooltip>
@ -218,21 +205,6 @@ async function restoreVersion() {
</span>
<div class="flex gap-2 flex-wrap mt-2">
<Tooltip
v-if="
hasPerms(NamedPermission.EDIT_VERSION) &&
projectVersion.visibility !== Visibility.SOFT_DELETE &&
!projectVersion.recommended.includes(platform?.enumName)
"
>
<template #content>
<span>{{ i18n.t("version.page.setRecommendedTooltip", [platform?.name]) }}</span>
</template>
<Button size="small" @click="setRecommended">
<IconMdiDiamond class="mr-1" />
{{ i18n.t("version.page.setRecommended") }}
</Button>
</Tooltip>
<Tooltip>
<template #content>
<span v-if="projectVersion.pinnedStatus === PinnedStatus.CHANNEL">{{ i18n.t("version.page.pinned.tooltip.channel") }}</span>

View File

@ -246,14 +246,6 @@ useHead(
</div>-->
</div>
<!-- todo: remove -->
<h2 class="mt-5 text-xl">{{ t("version.new.form.tags") }}</h2>
<div class="flex flex-wrap">
<div class="basis-4/12 mt-2">
<InputCheckbox v-model="pendingVersion.recommended" :label="t('version.new.form.recommended')" />
</div>
</div>
<h2 class="mt-5 mb-2 text-xl">{{ t("version.new.form.platforms") }}</h2>
<div v-for="platform in platforms" :key="platform.name" class="ml-2">
<InputCheckbox

View File

@ -44,7 +44,6 @@ declare module "hangar-api" {
channel: ProjectChannel;
pinned: boolean;
pinnedStatus: PinnedStatus;
recommended: Platform[];
}
interface Version extends VersionCompact, DependencyVersion {

View File

@ -109,7 +109,7 @@ export enum Prompt {
export enum ChannelFlag {
FROZEN = "FROZEN",
UNSTABLE = "UNSTABLE",
SKIP_REVIEW_QUEUE = "SKIP_REVIEW_QUEUE",
SKIP_REVIEW_QUEUE = "SKIP_REVIEW_QUEUE", //TODO remove
PINNED = "PINNED",
}

View File

@ -42,7 +42,7 @@ declare module "hangar-internal" {
info: HangarProjectInfo;
pages: HangarProjectPage[];
pinnedVersions: PinnedVersion[];
recommendedVersions: Record<Platform, string | null>;
mainChannelVersions: Record<Platform, HangarVersion>;
}
interface ProjectPage extends Table {

View File

@ -19,8 +19,6 @@ import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
import io.papermc.hangar.service.api.VersionsApiService;
import io.papermc.hangar.service.internal.versions.DownloadService;
import io.papermc.hangar.service.internal.versions.RecommendedVersionService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
@ -39,13 +37,11 @@ public class VersionsController implements IVersionsController {
private final DownloadService downloadService;
private final VersionsApiService versionsApiService;
private final RecommendedVersionService recommendedVersionService;
@Autowired
public VersionsController(DownloadService downloadService, VersionsApiService versionsApiService, RecommendedVersionService recommendedVersionService) {
public VersionsController(DownloadService downloadService, VersionsApiService versionsApiService) {
this.downloadService = downloadService;
this.versionsApiService = versionsApiService;
this.recommendedVersionService = recommendedVersionService;
}
@Override
@ -70,14 +66,12 @@ public class VersionsController implements IVersionsController {
@Override
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.IS_SUBJECT_MEMBER, args = "{#author, #slug}")
public Map<String, VersionStats> getVersionStats(String author, String slug, String versionString, Platform platform, @NotNull OffsetDateTime fromDate, @NotNull OffsetDateTime toDate) {
versionString = recommendedVersionService.fixVersionString(author, slug, versionString, platform); // TODO remove recommended special casing
return versionsApiService.getVersionStats(author, slug, versionString, platform, fromDate, toDate);
}
@Override
@VisibilityRequired(type = Type.VERSION, args = "{#author, #slug, #versionString, #platform}")
public FileSystemResource downloadVersion(String author, String slug, String versionString, Platform platform) {
versionString = recommendedVersionService.fixVersionString(author, slug, versionString, platform); // TODO remove recommended special casing
return downloadService.getVersionFile(author, slug, versionString, platform, false, null);
}
}

View File

@ -111,7 +111,7 @@ public interface IVersionsController {
@GetMapping("/projects/{author}/{slug}/versions/{name}/{platform}/stats")
Map<String, VersionStats> getVersionStats(@ApiParam("The author of the version to return the stats for") @PathVariable String author,
@ApiParam("The slug of the project to return stats for") @PathVariable String slug,
@ApiParam("The version to return the stats for. Can be 'recommended'.") @PathVariable("name") String versionString,
@ApiParam("The version to return the stats for.") @PathVariable("name") String versionString,
@ApiParam("The platform of the version to return") @PathVariable Platform platform,
@ApiParam(value = "The first date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime fromDate,
@ApiParam(value = "The last date to include in the result", required = true) @RequestParam @NotNull OffsetDateTime toDate);
@ -133,6 +133,6 @@ public interface IVersionsController {
@GetMapping(value = "/projects/{author}/{slug}/versions/{name}/{platform}/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
FileSystemResource downloadVersion(@ApiParam("The author of the project to return the version for") @PathVariable String author,
@ApiParam("The slug of the project to return") @PathVariable String slug,
@ApiParam("The name of the version to return. Can be 'recommended'.") @PathVariable("name") String versionString,
@ApiParam("The name of the version to return.") @PathVariable("name") String versionString,
@ApiParam("The platform of the version to return") @PathVariable Platform platform);
}

View File

@ -13,8 +13,8 @@ import io.papermc.hangar.model.internal.api.requests.versions.UpdatePluginDepend
import io.papermc.hangar.model.internal.logs.LogAction;
import io.papermc.hangar.model.internal.logs.contexts.VersionContext;
import io.papermc.hangar.model.internal.versions.HangarVersion;
import io.papermc.hangar.model.internal.versions.PendingVersion;
import io.papermc.hangar.model.internal.versions.LastDependencies;
import io.papermc.hangar.model.internal.versions.PendingVersion;
import io.papermc.hangar.security.annotations.permission.PermissionRequired;
import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
@ -22,7 +22,6 @@ import io.papermc.hangar.security.annotations.visibility.VisibilityRequired;
import io.papermc.hangar.security.annotations.visibility.VisibilityRequired.Type;
import io.papermc.hangar.service.internal.versions.DownloadService;
import io.papermc.hangar.service.internal.versions.PinnedVersionService;
import io.papermc.hangar.service.internal.versions.RecommendedVersionService;
import io.papermc.hangar.service.internal.versions.VersionDependencyService;
import io.papermc.hangar.service.internal.versions.VersionFactory;
import io.papermc.hangar.service.internal.versions.VersionService;
@ -42,7 +41,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.util.List;
@ -55,16 +53,14 @@ public class VersionController extends HangarComponent {
private final VersionFactory versionFactory;
private final VersionService versionService;
private final VersionDependencyService versionDependencyService;
private final RecommendedVersionService recommendedVersionService;
private final DownloadService downloadService;
private final PinnedVersionService pinnedVersionService;
@Autowired
public VersionController(VersionFactory versionFactory, VersionService versionService, VersionDependencyService versionDependencyService, RecommendedVersionService recommendedVersionService, DownloadService downloadService, final PinnedVersionService pinnedVersionService) {
public VersionController(VersionFactory versionFactory, VersionService versionService, VersionDependencyService versionDependencyService, DownloadService downloadService, final PinnedVersionService pinnedVersionService) {
this.versionFactory = versionFactory;
this.versionService = versionService;
this.versionDependencyService = versionDependencyService;
this.recommendedVersionService = recommendedVersionService;
this.downloadService = downloadService;
this.pinnedVersionService = pinnedVersionService;
}
@ -144,14 +140,6 @@ public class VersionController extends HangarComponent {
versionDependencyService.updateVersionPluginDependencies(projectId, versionId, updatePluginDependencies);
}
@Unlocked
@ResponseStatus(HttpStatus.OK)
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_VERSION, args = "{#projectId}")
@PostMapping("/version/{projectId}/{versionId}/{platform}/recommend")
public void setRecommended(@PathVariable long projectId, @PathVariable long versionId, @PathVariable Platform platform) {
recommendedVersionService.setRecommendedVersion(projectId, versionId, platform);
}
@Unlocked
@ResponseStatus(HttpStatus.OK)
@PermissionRequired(type = PermissionType.PROJECT, perms = NamedPermission.EDIT_SUBJECT_SETTINGS, args = "{#projectId}")
@ -193,14 +181,12 @@ public class VersionController extends HangarComponent {
@VisibilityRequired(type = Type.VERSION, args = "{#author, #slug, #versionString, #platform}")
@GetMapping(path = "/version/{author}/{slug}/versions/{versionString}/{platform}/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public FileSystemResource download(@PathVariable String author, @PathVariable String slug, @PathVariable String versionString, @PathVariable Platform platform, @RequestParam(required = false) String token) {
versionString = recommendedVersionService.fixVersionString(author, slug, versionString, platform); // TODO remove recommended special casing
return downloadService.getVersionFile(author, slug, versionString, platform, true, token);
}
@VisibilityRequired(type = Type.VERSION, args = "{#author, #slug, #versionString, #platform}")
@GetMapping(path = "/version/{author}/{slug}/versions/{versionString}/{platform}/downloadCheck")
public ResponseEntity<String> downloadCheck(@PathVariable String author, @PathVariable String slug, @PathVariable String versionString, @PathVariable Platform platform) {
versionString = recommendedVersionService.fixVersionString(author, slug, versionString, platform); // TODO remove recommended special casing
boolean requiresConfirmation = downloadService.requiresConfirmation(author, slug, versionString, platform);
if (requiresConfirmation) {
String token = downloadService.createConfirmationToken(author, slug, versionString, platform);

View File

@ -1,40 +0,0 @@
package io.papermc.hangar.db.dao.internal.table.versions;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.db.versions.RecommendedProjectVersionTable;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.sqlobject.config.KeyColumn;
import org.jdbi.v3.sqlobject.config.ValueColumn;
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;
@Repository
public interface RecommendedProjectVersionsDAO {
@Timestamped
@SqlUpdate("INSERT INTO recommended_project_versions (created_at, version_id, project_id, platform) VALUES (:now, :versionId, :projectId, :platform)")
void insert(@BindBean RecommendedProjectVersionTable recommendedProjectVersionTable);
@SqlUpdate("DELETE FROM recommended_project_versions WHERE project_id = :projectId AND platform = :platform")
void delete(long projectId, @EnumByOrdinal Platform platform);
@KeyColumn("platform")
@ValueColumn("external_url")
@SqlQuery("SELECT rpv.platform, pv.external_url FROM recommended_project_versions rpv JOIN project_versions pv ON rpv.version_id = pv.id WHERE rpv.project_id = :projectId ORDER BY platform")
Map<Platform, String> getRecommendedVersions(long projectId);
@KeyColumn("platform")
@ValueColumn("version_string")
@SqlQuery("SELECT platform, version_string " +
"FROM recommended_project_versions rpv " +
" JOIN projects p on p.id = rpv.project_id " +
" JOIN project_versions pv on rpv.version_id = pv.id " +
"WHERE p.owner_name = :owner AND p.slug = :slug " +
"ORDER BY platform")
Map<Platform, String> getRecommendedVersions(String owner, String slug);
}

View File

@ -40,7 +40,6 @@ public interface HangarVersionsDAO {
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'" +
" ELSE 'NONE'" +
" END AS pinnedStatus," +
" array(SELECT DISTINCT rpv.platform FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id ORDER BY rpv.platform) as recommended," +
" ru.name approved_by" +
" FROM project_versions pv" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +
@ -82,7 +81,6 @@ public interface HangarVersionsDAO {
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'" +
" ELSE 'NONE'" +
" END AS pinnedStatus," +
" array(SELECT DISTINCT rpv.platform FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id ORDER BY rpv.platform) as recommended," +
" ru.name approved_by" +
" FROM project_versions pv" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +

View File

@ -51,8 +51,7 @@ public interface VersionsApiDAO {
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'channel') THEN 'CHANNEL'" +
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'" +
" ELSE 'NONE'" +
" END AS pinnedStatus," +
" array(SELECT DISTINCT rpv.platform FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id ORDER BY rpv.platform) as recommended" +
" END AS pinnedStatus" +
" FROM project_versions pv" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +
" JOIN projects p ON pv.project_id = p.id" +
@ -91,8 +90,7 @@ public interface VersionsApiDAO {
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'channel') THEN 'CHANNEL'" +
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'" +
" ELSE 'NONE'" +
" END AS pinnedStatus," +
" array(SELECT DISTINCT rpv.platform FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id ORDER BY rpv.platform) as recommended" +
" END AS pinnedStatus" +
" FROM project_versions pv" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +
" JOIN projects p ON pv.project_id = p.id" +
@ -134,8 +132,7 @@ public interface VersionsApiDAO {
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'channel') THEN 'CHANNEL'" +
" WHEN exists(SELECT * FROM pinned_versions piv WHERE piv.version_id = pv.id AND lower(type) = 'version') THEN 'VERSION'" +
" ELSE 'NONE'" +
" END AS pinnedStatus," +
" array(SELECT DISTINCT rpv.platform FROM recommended_project_versions rpv WHERE rpv.version_id = pv.id ORDER BY rpv.platform) as recommended" +
" END AS pinnedStatus" +
" FROM project_versions pv" +
" JOIN projects p ON pv.project_id = p.id" +
" JOIN project_channels pc ON pv.channel_id = pc.id" +

View File

@ -4,11 +4,12 @@ import io.papermc.hangar.model.api.project.ProjectChannel;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.ReviewState;
import io.papermc.hangar.model.common.projects.Visibility;
import java.time.OffsetDateTime;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.Nested;
import org.jdbi.v3.core.mapper.reflect.ColumnName;
@ -18,8 +19,8 @@ public class Version extends VersionCompact {
private final Map<Platform, Set<PluginDependency>> pluginDependencies;
private final Map<Platform, Set<String>> platformDependencies;
public Version(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final List<Platform> recommended, final Long postId) {
super(createdAt, name, visibility, description, stats, fileInfo, externalUrl, author, reviewState, channel, pinnedStatus, recommended, postId);
public Version(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final Long postId) {
super(createdAt, name, visibility, description, stats, fileInfo, externalUrl, author, reviewState, channel, pinnedStatus, postId);
this.pluginDependencies = new EnumMap<>(Platform.class);
this.platformDependencies = new EnumMap<>(Platform.class);
}

View File

@ -5,11 +5,11 @@ import io.papermc.hangar.model.Model;
import io.papermc.hangar.model.Named;
import io.papermc.hangar.model.Visible;
import io.papermc.hangar.model.api.project.ProjectChannel;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.ReviewState;
import io.papermc.hangar.model.common.projects.Visibility;
import java.time.OffsetDateTime;
import java.util.List;
import org.jdbi.v3.core.enums.EnumByName;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.Nested;
@ -27,10 +27,9 @@ public class VersionCompact extends Model implements Named, Visible {
private final ReviewState reviewState;
private final ProjectChannel channel;
private final PinnedStatus pinnedStatus;
private final List<Platform> recommended;
private final Long postId;
protected VersionCompact(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final List<Platform> recommended, final Long postId) {
protected VersionCompact(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final Long postId) {
super(createdAt);
this.name = name;
this.visibility = visibility;
@ -42,7 +41,6 @@ public class VersionCompact extends Model implements Named, Visible {
this.reviewState = reviewState;
this.channel = channel;
this.pinnedStatus = pinnedStatus;
this.recommended = recommended;
this.postId = postId;
}
@ -88,10 +86,6 @@ public class VersionCompact extends Model implements Named, Visible {
return this.pinnedStatus;
}
public List<Platform> getRecommended() {
return this.recommended;
}
public Long getPostId() {
return this.postId;
}

View File

@ -5,11 +5,11 @@ import org.jdbi.v3.core.enums.EnumByOrdinal;
@EnumByOrdinal
@JsonFormat(shape = JsonFormat.Shape.STRING)
// remember never to delete or re-order these, just deprecate it
public enum ChannelFlag {
FROZEN(false),
UNSTABLE(true),
SKIP_REVIEW_QUEUE(true),
@Deprecated(forRemoval = true)
SKIP_REVIEW_QUEUE(true), //TODO remove and add id migration
PINNED(true),
;

View File

@ -1,51 +0,0 @@
package io.papermc.hangar.model.db.versions;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.db.Table;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
import java.time.OffsetDateTime;
public class RecommendedProjectVersionTable extends Table {
private final long versionId;
private final long projectId;
private final Platform platform;
@JdbiConstructor
public RecommendedProjectVersionTable(OffsetDateTime createdAt, long id, long versionId, long projectId, @EnumByOrdinal Platform platform) {
super(createdAt, id);
this.versionId = versionId;
this.projectId = projectId;
this.platform = platform;
}
public RecommendedProjectVersionTable(long versionId, long projectId, Platform platform) {
this.versionId = versionId;
this.projectId = projectId;
this.platform = platform;
}
public long getVersionId() {
return versionId;
}
public long getProjectId() {
return projectId;
}
@EnumByOrdinal
public Platform getPlatform() {
return platform;
}
@Override
public String toString() {
return "RecommendedProjectVersionTable{" +
"versionId=" + versionId +
", projectId=" + projectId +
", platform=" + platform +
"} " + super.toString();
}
}

View File

@ -13,13 +13,15 @@ import io.papermc.hangar.model.db.roles.ProjectRoleTable;
import io.papermc.hangar.model.identified.ProjectIdentified;
import io.papermc.hangar.model.internal.Joinable;
import io.papermc.hangar.model.internal.user.JoinableMember;
import io.papermc.hangar.model.internal.versions.HangarVersion;
import org.jdbi.v3.core.enums.EnumByName;
import org.jdbi.v3.core.mapper.Nested;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jdbi.v3.core.enums.EnumByName;
import org.jdbi.v3.core.mapper.Nested;
import org.jetbrains.annotations.Nullable;
public class HangarProject extends Project implements Joinable<ProjectRoleTable>, ProjectIdentified {
@ -31,9 +33,9 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
private final HangarProjectInfo info;
private final Collection<HangarProjectPage> pages;
private final List<PinnedVersion> pinnedVersions;
private final Map<Platform, String> recommendedVersions;
private final Map<Platform, HangarVersion> mainChannelVersions;
public HangarProject(final Project project, final long id, final ProjectOwner owner, final List<JoinableMember<ProjectRoleTable>> members, final String lastVisibilityChangeComment, final String lastVisibilityChangeUserName, final HangarProjectInfo info, final Collection<HangarProjectPage> pages, final List<PinnedVersion> pinnedVersions, final Map<Platform, String> recommendedVersions) {
public HangarProject(final Project project, final long id, final ProjectOwner owner, final List<JoinableMember<ProjectRoleTable>> members, final String lastVisibilityChangeComment, final String lastVisibilityChangeUserName, final HangarProjectInfo info, final Collection<HangarProjectPage> pages, final List<PinnedVersion> pinnedVersions, final Map<Platform, HangarVersion> mainChannelVersions) {
super(project);
this.id = id;
this.owner = owner;
@ -43,7 +45,7 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
this.info = info;
this.pages = pages;
this.pinnedVersions = pinnedVersions;
this.recommendedVersions = recommendedVersions;
this.mainChannelVersions = mainChannelVersions;
}
public long getId() {
@ -90,8 +92,8 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
return this.pinnedVersions;
}
public Map<Platform, String> getRecommendedVersions() {
return recommendedVersions;
public Map<Platform, HangarVersion> getMainChannelVersions() {
return mainChannelVersions;
}
@Override
@ -105,7 +107,6 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
", info=" + this.info +
", pages=" + this.pages +
", pinnedVersions=" + this.pinnedVersions +
", recommendedVersions=" + this.recommendedVersions +
"} " + super.toString();
}
@ -150,17 +151,18 @@ public class HangarProject extends Project implements Joinable<ProjectRoleTable>
@Override
public String toString() {
return "HangarProjectInfo{" +
"publicVersions=" + publicVersions +
", flagCount=" + flagCount +
", noteCount=" + noteCount +
", starCount=" + starCount +
", watcherCount=" + watcherCount +
'}';
"publicVersions=" + publicVersions +
", flagCount=" + flagCount +
", noteCount=" + noteCount +
", starCount=" + starCount +
", watcherCount=" + watcherCount +
'}';
}
}
public record PinnedVersion(Type type, String name, Set<Platform> platforms, @Nested("pc") ProjectChannel channel, @Nested("fi") @Nullable FileInfo fileInfo, @Nullable String externalUrl) {
public record PinnedVersion(Type type, String name, Set<Platform> platforms, @Nested("pc") ProjectChannel channel,
@Nested("fi") @Nullable FileInfo fileInfo, @Nullable String externalUrl) {
@EnumByName
@JsonFormat(shape = JsonFormat.Shape.STRING)

View File

@ -7,22 +7,21 @@ import io.papermc.hangar.model.api.project.version.FileInfo;
import io.papermc.hangar.model.api.project.version.Version;
import io.papermc.hangar.model.api.project.version.VersionStats;
import io.papermc.hangar.model.common.NamedPermission;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.ReviewState;
import io.papermc.hangar.model.common.projects.Visibility;
import java.time.OffsetDateTime;
import java.util.List;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.Nested;
import org.jdbi.v3.core.mapper.reflect.ColumnName;
import java.time.OffsetDateTime;
public class HangarVersion extends Version implements Identified {
private final long id;
private final String approvedBy;
public HangarVersion(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final List<Platform> recommended, final long id, final String approvedBy, final long postId) {
super(createdAt, name, visibility, description, stats, fileInfo, externalUrl, author, reviewState, channel, pinnedStatus, recommended, postId);
public HangarVersion(final OffsetDateTime createdAt, @ColumnName("version_string") final String name, final Visibility visibility, final String description, @Nested("vs") final VersionStats stats, @Nested("fi") final FileInfo fileInfo, final String externalUrl, final String author, @EnumByOrdinal final ReviewState reviewState, @Nested("pc") final ProjectChannel channel, final PinnedStatus pinnedStatus, final long id, final String approvedBy, final long postId) {
super(createdAt, name, visibility, description, stats, fileInfo, externalUrl, author, reviewState, channel, pinnedStatus, postId);
this.id = id;
this.approvedBy = approvedBy;
}

View File

@ -44,11 +44,10 @@ public class PendingVersion {
private final Color channelColor;
private final Set<ChannelFlag> channelFlags;
private final boolean forumSync;
private final boolean recommended;
private final boolean isFile;
@JsonCreator(mode = Mode.PROPERTIES)
public PendingVersion(String versionString, Map<Platform, Set<PluginDependency>> pluginDependencies, EnumMap<Platform, SortedSet<String>> platformDependencies, String description, FileInfo fileInfo, String externalUrl, String channelName, Color channelColor, Set<ChannelFlag> channelFlags, boolean forumSync, boolean recommended, boolean isFile) {
public PendingVersion(String versionString, Map<Platform, Set<PluginDependency>> pluginDependencies, EnumMap<Platform, SortedSet<String>> platformDependencies, String description, FileInfo fileInfo, String externalUrl, String channelName, Color channelColor, Set<ChannelFlag> channelFlags, boolean forumSync, boolean isFile) {
this.versionString = versionString;
this.pluginDependencies = pluginDependencies;
this.platformDependencies = platformDependencies;
@ -59,7 +58,6 @@ public class PendingVersion {
this.channelColor = channelColor;
this.channelFlags = channelFlags;
this.forumSync = forumSync;
this.recommended = recommended;
this.isFile = isFile;
}
@ -74,7 +72,6 @@ public class PendingVersion {
this.channelName = projectChannelTable.getName();
this.channelColor = projectChannelTable.getColor();
this.channelFlags = projectChannelTable.getFlags();
this.recommended = false;
this.isFile = true;
}
@ -93,7 +90,6 @@ public class PendingVersion {
this.channelName = projectChannelTable.getName();
this.channelColor = projectChannelTable.getColor();
this.channelFlags = projectChannelTable.getFlags();
this.recommended = false;
this.isFile = false;
}
@ -138,10 +134,6 @@ public class PendingVersion {
return forumSync;
}
public boolean isRecommended() {
return recommended;
}
@JsonProperty("isFile")
public boolean isFile() {
return isFile;
@ -160,7 +152,6 @@ public class PendingVersion {
", channelColor=" + channelColor +
", channelFlags=" + channelFlags +
", forumSync=" + forumSync +
", recommended=" + recommended +
", isFile=" + isFile +
'}';
}

View File

@ -5,7 +5,6 @@ import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.security.annotations.HangarDecisionVoter;
import io.papermc.hangar.security.annotations.visibility.VisibilityRequiredMetadataExtractor.VisibilityRequiredAttribute;
import io.papermc.hangar.service.internal.projects.ProjectService;
import io.papermc.hangar.service.internal.versions.RecommendedVersionService;
import io.papermc.hangar.service.internal.versions.VersionService;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
@ -20,14 +19,12 @@ public class VisibilityRequiredVoter extends HangarDecisionVoter<VisibilityRequi
private final ProjectService projectService;
private final VersionService versionService;
private final RecommendedVersionService recommendedVersionService;
@Autowired
public VisibilityRequiredVoter(ProjectService projectService, VersionService versionService, RecommendedVersionService recommendedVersionService) {
public VisibilityRequiredVoter(ProjectService projectService, VersionService versionService) {
super(VisibilityRequiredAttribute.class);
this.projectService = projectService;
this.versionService = versionService;
this.recommendedVersionService = recommendedVersionService;
}
@Override
@ -50,8 +47,7 @@ public class VisibilityRequiredVoter extends HangarDecisionVoter<VisibilityRequi
if (arguments.length == 1 && versionService.getProjectVersionTable((long) arguments[0]) != null) {
return ACCESS_GRANTED;
} else {
String versionId = recommendedVersionService.fixVersionString((String) arguments[0], (String) arguments[1], (String) arguments[2], (Platform) arguments[3]); // TODO remove recommended special casing
if (versionService.getProjectVersionTable((String) arguments[0], (String) arguments[1], versionId, (Platform) arguments[3]) != null) {
if (versionService.getProjectVersionTable((String) arguments[0], (String) arguments[1], (String) arguments[2], (Platform) arguments[3]) != null) {
return ACCESS_GRANTED;
} else {
return ACCESS_DENIED;

View File

@ -1,32 +1,16 @@
package io.papermc.hangar.service.internal.projects;
import io.papermc.hangar.service.internal.versions.PinnedVersionService;
import org.apache.commons.lang3.StringUtils;
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.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.controller.extras.pagination.filters.versions.VersionChannelFilter;
import io.papermc.hangar.controller.extras.pagination.filters.versions.VersionPlatformFilter;
import io.papermc.hangar.db.dao.internal.HangarUsersDAO;
import io.papermc.hangar.db.dao.internal.projects.HangarProjectsDAO;
import io.papermc.hangar.db.dao.internal.table.projects.ProjectsDAO;
import io.papermc.hangar.db.dao.internal.versions.HangarVersionsDAO;
import io.papermc.hangar.db.dao.v1.VersionsApiDAO;
import io.papermc.hangar.exceptions.HangarApiException;
import io.papermc.hangar.model.api.project.Project;
import io.papermc.hangar.model.api.requests.RequestPagination;
import io.papermc.hangar.model.common.Permission;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.Visibility;
@ -43,12 +27,32 @@ import io.papermc.hangar.model.internal.projects.HangarProject;
import io.papermc.hangar.model.internal.projects.HangarProject.HangarProjectInfo;
import io.papermc.hangar.model.internal.projects.HangarProjectPage;
import io.papermc.hangar.model.internal.user.JoinableMember;
import io.papermc.hangar.model.internal.versions.HangarVersion;
import io.papermc.hangar.service.PermissionService;
import io.papermc.hangar.service.internal.organizations.OrganizationService;
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
import io.papermc.hangar.service.internal.versions.RecommendedVersionService;
import io.papermc.hangar.service.internal.versions.PinnedVersionService;
import io.papermc.hangar.service.internal.visibility.ProjectVisibilityService;
import io.papermc.hangar.util.FileUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
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.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class ProjectService extends HangarComponent {
@ -62,10 +66,11 @@ public class ProjectService extends HangarComponent {
private final ProjectFiles projectFiles;
private final PermissionService permissionService;
private final PinnedVersionService pinnedVersionService;
private final RecommendedVersionService recommendedVersionService;
private final VersionsApiDAO versionsApiDAO;
private final HangarVersionsDAO hangarVersionsDAO;
@Autowired
public ProjectService(ProjectsDAO projectDAO, HangarUsersDAO hangarUsersDAO, HangarProjectsDAO hangarProjectsDAO, ProjectVisibilityService projectVisibilityService, OrganizationService organizationService, ProjectPageService projectPageService, ProjectFiles projectFiles, PermissionService permissionService, final PinnedVersionService pinnedVersionService, RecommendedVersionService recommendedVersionService) {
public ProjectService(ProjectsDAO projectDAO, HangarUsersDAO hangarUsersDAO, HangarProjectsDAO hangarProjectsDAO, ProjectVisibilityService projectVisibilityService, OrganizationService organizationService, ProjectPageService projectPageService, ProjectFiles projectFiles, PermissionService permissionService, final PinnedVersionService pinnedVersionService, final VersionsApiDAO versionsApiDAO, final HangarVersionsDAO hangarVersionsDAO) {
this.projectsDAO = projectDAO;
this.hangarUsersDAO = hangarUsersDAO;
this.hangarProjectsDAO = hangarProjectsDAO;
@ -75,7 +80,8 @@ public class ProjectService extends HangarComponent {
this.projectFiles = projectFiles;
this.permissionService = permissionService;
this.pinnedVersionService = pinnedVersionService;
this.recommendedVersionService = recommendedVersionService;
this.versionsApiDAO = versionsApiDAO;
this.hangarVersionsDAO = hangarVersionsDAO;
}
@Nullable
@ -119,8 +125,33 @@ public class ProjectService extends HangarComponent {
HangarProjectInfo info = hangarProjectsDAO.getHangarProjectInfo(project.getLeft());
Map<Long, HangarProjectPage> pages = projectPageService.getProjectPages(project.getLeft());
final List<HangarProject.PinnedVersion> pinnedVersions = this.pinnedVersionService.getPinnedVersions(project.getLeft());
Map<Platform, String> recommendedVersions = recommendedVersionService.getRecommendedVersions(project.getLeft());
return new HangarProject(project.getRight(), project.getLeft(), projectOwner, members, lastVisibilityChangeComment, lastVisibilityChangeUserName, info, pages.values(), pinnedVersions, recommendedVersions);
final Map<Platform, HangarVersion> mainChannelVersions = new EnumMap<>(Platform.class);
for (final Platform platform : Platform.getValues()) {
final HangarVersion version = getLastVersion(author, slug, platform, config.channels.getNameDefault());
if (version != null) {
mainChannelVersions.put(platform, version);
}
}
return new HangarProject(project.getRight(), project.getLeft(), projectOwner, members, lastVisibilityChangeComment, lastVisibilityChangeUserName, info, pages.values(), pinnedVersions, mainChannelVersions);
}
public @Nullable HangarVersion getLastVersion(String author, String slug, Platform platform, @Nullable String channel) {
RequestPagination pagination = new RequestPagination(1L, 0L);
pagination.getFilters().add(new VersionPlatformFilter.VersionPlatformFilterInstance(new Platform[]{platform}));
if (channel != null) {
// Find the last version with the specified channel
pagination.getFilters().add(new VersionChannelFilter.VersionChannelFilterInstance(new String[]{channel}));
}
Long versionId = versionsApiDAO.getVersions(author, slug, false, getHangarUserId(), pagination).entrySet().stream().map(Map.Entry::getKey).findAny().orElse(null);
if (versionId != null) {
return hangarVersionsDAO.getVersion(versionId, getGlobalPermissions().has(Permission.SeeHidden), getHangarUserId());
}
// Try again with any channel, else empty
return channel != null ? getLastVersion(author, slug, platform, null) : null;
}
public void saveSettings(String author, String slug, ProjectSettingsForm settingsForm) {

View File

@ -1,49 +0,0 @@
package io.papermc.hangar.service.internal.versions;
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.db.dao.internal.table.versions.RecommendedProjectVersionsDAO;
import io.papermc.hangar.exceptions.HangarApiException;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.db.versions.RecommendedProjectVersionTable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class RecommendedVersionService extends HangarComponent {
private final RecommendedProjectVersionsDAO recommendedProjectVersionsDAO;
@Autowired
public RecommendedVersionService(RecommendedProjectVersionsDAO recommendedProjectVersionsDAO) {
this.recommendedProjectVersionsDAO = recommendedProjectVersionsDAO;
}
public void setRecommendedVersion(long projectId, long versionId, Platform platform) {
recommendedProjectVersionsDAO.delete(projectId, platform);
recommendedProjectVersionsDAO.insert(new RecommendedProjectVersionTable(versionId, projectId, platform));
}
public Map<Platform, String> getRecommendedVersions(long projectId) {
return recommendedProjectVersionsDAO.getRecommendedVersions(projectId);
}
public Map<Platform, String> getRecommendedVersions(String owner, String slug) {
return recommendedProjectVersionsDAO.getRecommendedVersions(owner, slug);
}
// TODO we shouldn't have a recommended endpoint, the url should direct on the frontend
public String fixVersionString(String author, String slug, String versionString, Platform platform) {
if (!"recommended".equals(versionString)) {
return versionString;
}
Map<Platform, String> recommendedVersions = getRecommendedVersions(author, slug);
String recommendedVersion = recommendedVersions.get(platform);
if (recommendedVersion != null) {
return recommendedVersion;
}
throw new HangarApiException(HttpStatus.NOT_FOUND, "No recommended version found");
}
}

View File

@ -1,27 +1,5 @@
package io.papermc.hangar.service.internal.versions;
import io.papermc.hangar.model.common.ChannelFlag;
import java.util.stream.Collectors;
import org.spongepowered.configurate.ConfigurateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import io.papermc.hangar.HangarComponent;
import io.papermc.hangar.db.dao.internal.table.PlatformVersionDAO;
import io.papermc.hangar.db.dao.internal.table.versions.ProjectVersionsDAO;
@ -31,6 +9,7 @@ import io.papermc.hangar.db.dao.v1.VersionsApiDAO;
import io.papermc.hangar.exceptions.HangarApiException;
import io.papermc.hangar.model.api.project.version.FileInfo;
import io.papermc.hangar.model.api.project.version.PluginDependency;
import io.papermc.hangar.model.common.ChannelFlag;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.Visibility;
import io.papermc.hangar.model.db.PlatformVersionTable;
@ -57,6 +36,25 @@ 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.spongepowered.configurate.ConfigurateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.stream.Collectors;
@Service
public class VersionFactory extends HangarComponent {
@ -70,7 +68,6 @@ public class VersionFactory extends HangarComponent {
private final PluginDataService pluginDataService;
private final ChannelService channelService;
private final ProjectVisibilityService projectVisibilityService;
private final RecommendedVersionService recommendedVersionService;
private final ProjectService projectService;
private final NotificationService notificationService;
private final PlatformService platformService;
@ -79,7 +76,7 @@ public class VersionFactory extends HangarComponent {
private final ValidationService validationService;
@Autowired
public VersionFactory(ProjectVersionPlatformDependenciesDAO projectVersionPlatformDependencyDAO, ProjectVersionDependenciesDAO projectVersionDependencyDAO, PlatformVersionDAO platformVersionDAO, ProjectVersionsDAO projectVersionDAO, VersionsApiDAO versionsApiDAO, ProjectFiles projectFiles, PluginDataService pluginDataService, ChannelService channelService, ProjectVisibilityService projectVisibilityService, RecommendedVersionService recommendedVersionService, ProjectService projectService, NotificationService notificationService, PlatformService platformService, UsersApiService usersApiService, JobService jobService, ValidationService validationService) {
public VersionFactory(ProjectVersionPlatformDependenciesDAO projectVersionPlatformDependencyDAO, ProjectVersionDependenciesDAO projectVersionDependencyDAO, PlatformVersionDAO platformVersionDAO, ProjectVersionsDAO projectVersionDAO, VersionsApiDAO versionsApiDAO, ProjectFiles projectFiles, PluginDataService pluginDataService, ChannelService channelService, ProjectVisibilityService projectVisibilityService, ProjectService projectService, NotificationService notificationService, PlatformService platformService, UsersApiService usersApiService, JobService jobService, ValidationService validationService) {
this.projectVersionPlatformDependenciesDAO = projectVersionPlatformDependencyDAO;
this.projectVersionDependenciesDAO = projectVersionDependencyDAO;
this.platformVersionDAO = platformVersionDAO;
@ -89,7 +86,6 @@ public class VersionFactory extends HangarComponent {
this.pluginDataService = pluginDataService;
this.channelService = channelService;
this.projectVisibilityService = projectVisibilityService;
this.recommendedVersionService = recommendedVersionService;
this.projectService = projectService;
this.notificationService = notificationService;
this.platformService = platformService;
@ -135,13 +131,13 @@ public class VersionFactory extends HangarComponent {
ProjectChannelTable projectChannelTable = channelService.getFirstChannel(projectId);
PendingVersion pendingVersion = new PendingVersion(
StringUtils.slugify(pluginDataFile.getData().getVersion()),
pluginDataFile.getData().getDependencies(),
pluginDataFile.getData().getPlatformDependencies(),
pluginDataFile.getData().getDescription(),
new FileInfo(pluginDataFile.getPath().getFileName().toString(), pluginDataFile.getPath().toFile().length(), pluginDataFile.getMd5()),
projectChannelTable,
projectTable.isForumSync()
StringUtils.slugify(pluginDataFile.getData().getVersion()),
pluginDataFile.getData().getDependencies(),
pluginDataFile.getData().getPlatformDependencies(),
pluginDataFile.getData().getDescription(),
new FileInfo(pluginDataFile.getPath().getFileName().toString(), pluginDataFile.getPath().toFile().length(), pluginDataFile.getMd5()),
projectChannelTable,
projectTable.isForumSync()
);
if (!validationService.isValidVersionName(pendingVersion.getVersionString())) {
@ -221,16 +217,16 @@ public class VersionFactory extends HangarComponent {
}
//TODO automatic checks for malicious code or files => set visibility to NEEDSAPPROVAL
projectVersionTable = projectVersionsDAO.insert(new ProjectVersionTable(
pendingVersion.getVersionString(),
pendingVersion.getDescription(),
projectId,
projectChannelTable.getId(),
fileSize,
fileHash,
fileName,
getHangarPrincipal().getUserId(),
pendingVersion.isForumSync(),
pendingVersion.getExternalUrl()
pendingVersion.getVersionString(),
pendingVersion.getDescription(),
projectId,
projectChannelTable.getId(),
fileSize,
fileHash,
fileName,
getHangarPrincipal().getUserId(),
pendingVersion.isForumSync(),
pendingVersion.getExternalUrl()
));
List<ProjectVersionPlatformDependencyTable> platformDependencyTables = new ArrayList<>();
@ -282,12 +278,6 @@ public class VersionFactory extends HangarComponent {
jobService.save(new UpdateDiscourseProjectTopicJob(projectId));
}
if (pendingVersion.isRecommended()) {
for (Platform platform : pendingVersion.getPlatformDependencies().keySet()) {
recommendedVersionService.setRecommendedVersion(projectId, projectVersionTable.getId(), platform);
}
}
actionLogger.version(LogAction.VERSION_CREATED.create(VersionContext.of(projectId, projectVersionTable.getId()), "published", ""));
if (pendingVersion.isForumSync()) {

View File

@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;