mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-21 01:21:54 +08:00
user tagline editing
This commit is contained in:
parent
1fbfd4cbed
commit
631ffeb43e
@ -85,10 +85,17 @@ export class HangarModal extends Vue {
|
||||
activatorClass!: string;
|
||||
|
||||
@Watch('dialog')
|
||||
onToggleView() {
|
||||
if (typeof this.$refs.modalForm !== 'undefined') {
|
||||
// @ts-ignore
|
||||
this.$refs.modalForm.reset();
|
||||
onToggleView(val: boolean) {
|
||||
if (!val) {
|
||||
this.$nextTick(() => {
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
if (typeof this.$refs.modalForm !== 'undefined') {
|
||||
// @ts-ignore
|
||||
this.$refs.modalForm.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
frontend/components/modals/HangarModal.vue
Normal file
71
frontend/components/modals/HangarModal.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<v-dialog v-model="dialog" :max-width="maxWidth" @input="$emit('open')">
|
||||
<template #activator="props">
|
||||
<slot name="activator" v-bind="props" />
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title>{{ title }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="modalForm" v-model="validForm">
|
||||
<slot />
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn text color="warning" @click.stop="close">{{ $t('general.close') }}</v-btn>
|
||||
<slot name="other-btns" />
|
||||
<v-btn color="success" :disabled="!validForm || submitDisabled" :loading="loading" @click.stop="submit0">
|
||||
{{ submitLabel }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop } from 'nuxt-property-decorator';
|
||||
import { PropType } from 'vue';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
import { HangarFormModal } from '~/components/mixins';
|
||||
|
||||
@Component
|
||||
export default class HangarModal extends HangarFormModal {
|
||||
@Prop({ type: String, default: '500' })
|
||||
maxWidth!: string;
|
||||
|
||||
@Prop({ type: String as PropType<TranslateResult>, required: true })
|
||||
title!: TranslateResult;
|
||||
|
||||
@Prop({ type: String as PropType<TranslateResult>, required: true })
|
||||
submitLabel!: TranslateResult;
|
||||
|
||||
@Prop({ type: Function as PropType<() => Promise<void>>, required: true })
|
||||
submit!: () => Promise<void>;
|
||||
|
||||
@Prop({ type: Boolean, default: false })
|
||||
submitDisabled!: boolean;
|
||||
|
||||
$refs!: {
|
||||
modalForm: any;
|
||||
};
|
||||
|
||||
close() {
|
||||
this.$refs.modalForm.reset();
|
||||
this.dialog = false;
|
||||
this.$emit('close');
|
||||
}
|
||||
|
||||
submit0() {
|
||||
this.loading = true;
|
||||
this.submit()
|
||||
.then(() => {
|
||||
this.dialog = false;
|
||||
this.$refs.modalForm.reset();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -6,7 +6,7 @@
|
||||
<v-card>
|
||||
<v-card-title>{{ title }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="messageForm" v-model="validForm">
|
||||
<v-form ref="modalForm" v-model="validForm">
|
||||
<v-textarea
|
||||
v-model.trim="message"
|
||||
autofocus
|
||||
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Watch } from 'nuxt-property-decorator';
|
||||
import { Component, Prop } from 'nuxt-property-decorator';
|
||||
import { PropType } from 'vue';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
import { HangarFormModal } from '~/components/mixins';
|
||||
@ -57,19 +57,6 @@ export default class TextareaModal extends HangarFormModal {
|
||||
this.dialog = false;
|
||||
});
|
||||
}
|
||||
|
||||
@Watch('dialog')
|
||||
onToggle(val: boolean) {
|
||||
if (val) {
|
||||
this.loading = false;
|
||||
this.$nextTick(() => {
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
this.$refs.messageForm.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -6,6 +6,7 @@ const msgs: LocaleMessageObject = {
|
||||
submit: 'Submit',
|
||||
save: 'Save',
|
||||
comment: 'Comment',
|
||||
change: 'Change',
|
||||
donate: 'Donate',
|
||||
continue: 'Continue',
|
||||
create: 'Create',
|
||||
@ -25,8 +26,8 @@ const msgs: LocaleMessageObject = {
|
||||
projectSearch: {
|
||||
query: 'Search in {0} projects, proudly made by the community...',
|
||||
relevanceSort: 'Sort with relevance',
|
||||
noProjects: 'There are no projects. :/',
|
||||
noProjectsFound: 'Found 0 projects. :/',
|
||||
noProjects: 'There are no projects. 😢',
|
||||
noProjectsFound: 'Found 0 projects. 😢',
|
||||
},
|
||||
subtitle: 'A Minecraft package repository',
|
||||
sponsoredBy: 'Sponsored by',
|
||||
@ -67,9 +68,9 @@ const msgs: LocaleMessageObject = {
|
||||
},
|
||||
project: {
|
||||
stargazers: 'Stargazers',
|
||||
noStargazers: 'There are no stargazers on this project yet :/',
|
||||
noStargazers: 'There are no stargazers on this project yet 😢',
|
||||
watchers: 'Watchers',
|
||||
noWatchers: 'There are no watchers on this project yet :/',
|
||||
noWatchers: 'There are no watchers on this project yet 😢',
|
||||
members: 'Members',
|
||||
category: {
|
||||
info: 'Category: {0}',
|
||||
@ -394,6 +395,8 @@ const msgs: LocaleMessageObject = {
|
||||
stars: 'Stars',
|
||||
orgs: 'Organizations',
|
||||
viewOnForums: 'View on forums ',
|
||||
taglineLabel: 'User Tagline',
|
||||
editTagline: 'Edit Tagline',
|
||||
memberSince: 'A member since {0}',
|
||||
numProjects: 'No projects | {0} project | {0} projects',
|
||||
addTagline: 'Add a tagline',
|
||||
@ -408,6 +411,9 @@ const msgs: LocaleMessageObject = {
|
||||
activity: 'User Activity',
|
||||
admin: 'User Admin',
|
||||
},
|
||||
error: {
|
||||
invalidTagline: 'Invalid tagline',
|
||||
},
|
||||
},
|
||||
linkout: {
|
||||
title: 'External Link Warning',
|
||||
@ -511,7 +517,10 @@ const msgs: LocaleMessageObject = {
|
||||
addVersion: 'Add Version',
|
||||
saveChanges: 'Save Changes',
|
||||
},
|
||||
message: 'Good morning!',
|
||||
validation: {
|
||||
required: '{0} is required',
|
||||
maxLength: 'Max length is {0}',
|
||||
},
|
||||
};
|
||||
|
||||
export default msgs;
|
||||
|
@ -28,12 +28,34 @@
|
||||
</v-list>
|
||||
<div>
|
||||
<v-subheader>
|
||||
<template v-if="user.tagline">{{ user.tagline }}</template>
|
||||
<!-- TODO tagline edit -->
|
||||
<!--<template v-else-if="u.isCurrent() || canEditOrgSettings(u, o, so)">{{ $t('author.addTagline') }}</template>-->
|
||||
<v-btn icon>
|
||||
<v-icon>mdi-pencil</v-icon>
|
||||
</v-btn>
|
||||
<span v-if="user.tagline">{{ user.tagline }}</span>
|
||||
<span v-else-if="canEditCurrent">{{ $t('author.addTagline') }}</span>
|
||||
<HangarModal
|
||||
v-if="canEditCurrent"
|
||||
ref="taglineModal"
|
||||
:title="$t('author.editTagline')"
|
||||
:submit-label="$t('general.change')"
|
||||
:submit-disabled="taglineForm === user.tagline"
|
||||
:submit="changeTagline"
|
||||
@open="taglineForm = user.tagline"
|
||||
>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-btn icon small color="warning" v-bind="attrs" v-on="on">
|
||||
<v-icon small>mdi-pencil</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-text-field
|
||||
v-model.trim="taglineForm"
|
||||
counter="100"
|
||||
:label="$t('author.taglineLabel')"
|
||||
:rules="[$util.$vc.require($t('author.taglineLabel')), $util.$vc.maxLength(100)]"
|
||||
/>
|
||||
<template #other-btns>
|
||||
<v-btn color="info" text :loading="loading.resetTagline" :disabled="!user.tagline" @click.stop="resetTagline">{{
|
||||
$t('general.reset')
|
||||
}}</v-btn>
|
||||
</template>
|
||||
</HangarModal>
|
||||
</v-subheader>
|
||||
</div>
|
||||
</v-col>
|
||||
@ -41,7 +63,7 @@
|
||||
<v-col cols="2">
|
||||
<v-subheader>{{ $tc('author.numProjects', user.projectCount, [user.projectCount]) }}</v-subheader>
|
||||
<v-subheader>{{ $t('author.memberSince', [$util.prettyDate(user.joinDate)]) }}</v-subheader>
|
||||
<a :href="$util.forumUrl(user.name)">{{ $t('author.viewOnForums') }}<v-icon>mdi-open-in-new</v-icon></a>
|
||||
<a :href="$util.forumUrl(user.name)">{{ $t('author.viewOnForums') }}<v-icon small>mdi-open-in-new</v-icon></a>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-divider />
|
||||
@ -54,6 +76,7 @@ import { Component, Vue } from 'nuxt-property-decorator';
|
||||
import { HangarUser } from 'hangar-internal';
|
||||
import { Context } from '@nuxt/types';
|
||||
import UserAvatar from '../components/UserAvatar.vue';
|
||||
import HangarModal from '~/components/modals/HangarModal.vue';
|
||||
|
||||
interface Button {
|
||||
icon: string;
|
||||
@ -64,10 +87,14 @@ interface Button {
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { UserAvatar },
|
||||
components: { HangarModal, UserAvatar },
|
||||
})
|
||||
export default class UserParentPage extends Vue {
|
||||
user!: HangarUser;
|
||||
taglineForm: string | null = null;
|
||||
loading = {
|
||||
resetTagline: false,
|
||||
};
|
||||
|
||||
get buttons(): Button[] {
|
||||
const buttons = [] as Button[];
|
||||
@ -80,11 +107,44 @@ export default class UserParentPage extends Vue {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
get canEditCurrent() {
|
||||
return this.user.id === this.$store.state.auth.user.id /* || org perms */;
|
||||
}
|
||||
|
||||
get avatarClazz(): String {
|
||||
return 'user-avatar-md';
|
||||
// todo check org an add 'organization-avatar'
|
||||
}
|
||||
|
||||
changeTagline() {
|
||||
return this.$api
|
||||
.requestInternal(`users/${this.user.id}/settings/tagline`, true, 'post', {
|
||||
content: this.taglineForm,
|
||||
})
|
||||
.then(() => {
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError);
|
||||
}
|
||||
|
||||
$refs!: {
|
||||
taglineModal: HangarModal;
|
||||
};
|
||||
|
||||
resetTagline() {
|
||||
this.loading.resetTagline = true;
|
||||
this.$api
|
||||
.requestInternal(`users/${this.user.id}/settings/resetTagline`, true, 'post')
|
||||
.then(() => {
|
||||
this.$refs.taglineModal.close();
|
||||
this.$nuxt.refresh();
|
||||
})
|
||||
.catch(this.$util.handleRequestError)
|
||||
.finally(() => {
|
||||
this.loading.resetTagline = false;
|
||||
});
|
||||
}
|
||||
|
||||
async asyncData({ $api, $util, params }: Context) {
|
||||
const user = await $api.requestInternal<HangarUser>(`users/${params.user}`, false).catch<any>($util.handlePageRequestError);
|
||||
if (typeof user === 'undefined') return;
|
||||
|
@ -14,7 +14,6 @@ import { NotifPayload } from '~/store/snackbar';
|
||||
import { AuthState } from '~/store/auth';
|
||||
|
||||
type Validation = (v: any) => boolean | string;
|
||||
type ValidationArgument = (any: any) => Validation;
|
||||
|
||||
function handleRequestError(err: AxiosError, error: Context['error'], i18n: Context['app']['i18n']) {
|
||||
if (!err.isAxiosError) {
|
||||
@ -220,9 +219,10 @@ const createUtil = ({ store, error, app: { i18n } }: Context) => {
|
||||
}
|
||||
}
|
||||
|
||||
$vc: Record<string, ValidationArgument> = {
|
||||
require: ((name: string | TranslateResult = 'Field') => (v: string) => !!v || `${name} is required`) as ValidationArgument,
|
||||
requireNonEmptyArray: ((name: string | TranslateResult = 'Field') => (v: any[]) => v.length > 0 || `${name} is required`) as ValidationArgument,
|
||||
$vc = {
|
||||
require: (name: TranslateResult = 'Field') => (v: string) => !!v || i18n.t('validation.required', [name]),
|
||||
maxLength: (maxLength: number) => (v: string) => (!!v && v.length <= maxLength) || i18n.t('validation.maxLength', [maxLength]),
|
||||
requireNonEmptyArray: (name: TranslateResult = 'Field') => (v: any[]) => v.length > 0 || i18n.t('validation.required', [name]),
|
||||
};
|
||||
|
||||
$v: Record<string, Validation> = {};
|
||||
|
@ -3,6 +3,7 @@ package io.papermc.hangar.controller.extras;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class ApiScope {
|
||||
|
||||
private final ApiScopeType type;
|
||||
@ -65,6 +66,7 @@ public class ApiScope {
|
||||
return new ApiScope(ApiScopeType.ORGANIZATION, organizationName);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public enum ApiScopeType {
|
||||
GLOBAL,
|
||||
PROJECT,
|
||||
|
@ -1,34 +0,0 @@
|
||||
package io.papermc.hangar.controller.extras;
|
||||
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
import io.papermc.hangar.model.db.auth.ApiKeyTable;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class HangarApiRequest extends HangarRequest {
|
||||
|
||||
private final ApiKeyTable apiKeyTable;
|
||||
private final String session;
|
||||
private final OffsetDateTime expires;
|
||||
|
||||
public HangarApiRequest(@Nested("u") UserTable userTable, @Nested("ak") ApiKeyTable apiKeyTable, String session, OffsetDateTime expires, Permission globalPermissions) {
|
||||
super(userTable, globalPermissions);
|
||||
this.apiKeyTable = apiKeyTable;
|
||||
this.session = session;
|
||||
this.expires = expires;
|
||||
}
|
||||
|
||||
public ApiKeyTable getApiKeyTable() {
|
||||
return apiKeyTable;
|
||||
}
|
||||
|
||||
public String getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public OffsetDateTime getExpires() {
|
||||
return expires;
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package io.papermc.hangar.controller.extras;
|
||||
|
||||
import io.papermc.hangar.model.common.Permission;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
|
||||
public class HangarRequest {
|
||||
|
||||
private final UserTable userTable;
|
||||
private final Permission globalPermissions;
|
||||
|
||||
public HangarRequest(UserTable userTable, Permission globalPermissions) {
|
||||
this.userTable = userTable;
|
||||
this.globalPermissions = globalPermissions;
|
||||
}
|
||||
|
||||
public boolean hasUser() {
|
||||
return userTable != null;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userTable == null ? null : userTable.getId();
|
||||
}
|
||||
|
||||
public UserTable getUserTable() {
|
||||
return userTable;
|
||||
}
|
||||
|
||||
public Permission getGlobalPermissions() {
|
||||
return globalPermissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HangarRequest{" +
|
||||
"userTable=" + userTable +
|
||||
", globalPermissions=" + globalPermissions +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -3,19 +3,29 @@ package io.papermc.hangar.controller.internal;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.papermc.hangar.controller.HangarController;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType;
|
||||
import io.papermc.hangar.db.customtypes.LoggedActionType.UserContext;
|
||||
import io.papermc.hangar.exceptions.HangarApiException;
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.roles.Role;
|
||||
import io.papermc.hangar.model.db.UserTable;
|
||||
import io.papermc.hangar.model.db.roles.ExtendedRoleTable;
|
||||
import io.papermc.hangar.model.internal.api.requests.StringContent;
|
||||
import io.papermc.hangar.model.internal.user.HangarUser;
|
||||
import io.papermc.hangar.model.internal.user.notifications.HangarNotification;
|
||||
import io.papermc.hangar.security.HangarAuthenticationToken;
|
||||
import io.papermc.hangar.security.annotations.permission.PermissionRequired;
|
||||
import io.papermc.hangar.security.annotations.unlocked.Unlocked;
|
||||
import io.papermc.hangar.service.api.UsersApiService;
|
||||
import io.papermc.hangar.service.internal.roles.MemberService;
|
||||
import io.papermc.hangar.service.internal.roles.MemberService.OrganizationMemberService;
|
||||
import io.papermc.hangar.service.internal.roles.MemberService.ProjectMemberService;
|
||||
import io.papermc.hangar.service.internal.roles.OrganizationRoleService;
|
||||
import io.papermc.hangar.service.internal.roles.ProjectRoleService;
|
||||
import io.papermc.hangar.service.internal.roles.RoleService;
|
||||
import io.papermc.hangar.service.internal.users.InviteService;
|
||||
import io.papermc.hangar.service.internal.users.NotificationService;
|
||||
import io.papermc.hangar.service.internal.users.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -25,10 +35,12 @@ 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.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@ -38,6 +50,7 @@ public class HangarUserController extends HangarController {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
private final UsersApiService usersApiService;
|
||||
private final UserService userService;
|
||||
private final NotificationService notificationService;
|
||||
private final InviteService inviteService;
|
||||
private final ProjectRoleService projectRoleService;
|
||||
@ -46,9 +59,10 @@ public class HangarUserController extends HangarController {
|
||||
private final MemberService.OrganizationMemberService organizationMemberService;
|
||||
|
||||
@Autowired
|
||||
public HangarUserController(ObjectMapper mapper, UsersApiService usersApiService, NotificationService notificationService, InviteService inviteService, ProjectRoleService projectRoleService, OrganizationRoleService organizationRoleService, MemberService.ProjectMemberService projectMemberService, MemberService.OrganizationMemberService organizationMemberService) {
|
||||
public HangarUserController(ObjectMapper mapper, UsersApiService usersApiService, UserService userService, NotificationService notificationService, InviteService inviteService, ProjectRoleService projectRoleService, OrganizationRoleService organizationRoleService, ProjectMemberService projectMemberService, OrganizationMemberService organizationMemberService) {
|
||||
this.mapper = mapper;
|
||||
this.usersApiService = usersApiService;
|
||||
this.userService = userService;
|
||||
this.notificationService = notificationService;
|
||||
this.inviteService = inviteService;
|
||||
this.projectRoleService = projectRoleService;
|
||||
@ -67,6 +81,42 @@ public class HangarUserController extends HangarController {
|
||||
return ResponseEntity.ok(usersApiService.getUser(userName, HangarUser.class));
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_OWN_USER_SETTINGS)
|
||||
@PostMapping(path = "/users/{userId}/settings/tagline", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public void saveTagline(@PathVariable long userId, @Valid @RequestBody StringContent content) {
|
||||
UserTable userTable = userService.getUserTable(userId);
|
||||
if (userTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (content.getContent().length() > hangarConfig.user.getMaxTaglineLen()) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST, "author.error.invalidTagline");
|
||||
}
|
||||
String oldTagline = userTable.getTagline() == null ? "" : userTable.getTagline();
|
||||
userTable.setTagline(content.getContent());
|
||||
userService.updateUser(userTable);
|
||||
userActionLogService.user(LoggedActionType.USER_TAGLINE_CHANGED.with(UserContext.of(userId)), userTable.getTagline(), oldTagline);
|
||||
}
|
||||
|
||||
@Unlocked
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@PermissionRequired(perms = NamedPermission.EDIT_OWN_USER_SETTINGS)
|
||||
@PostMapping("/users/{userId}/settings/resetTagline")
|
||||
public void resetTagline(@PathVariable long userId) {
|
||||
UserTable userTable = userService.getUserTable(userId);
|
||||
if (userTable == null) {
|
||||
throw new HangarApiException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
String oldTagline = userTable.getTagline();
|
||||
if (oldTagline == null) {
|
||||
throw new HangarApiException(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
userTable.setTagline(null);
|
||||
userService.updateUser(userTable);
|
||||
userActionLogService.user(LoggedActionType.USER_TAGLINE_CHANGED.with(UserContext.of(userId)), "", oldTagline);
|
||||
}
|
||||
|
||||
@GetMapping("/notifications")
|
||||
public ResponseEntity<List<HangarNotification>> getUserNotifications() {
|
||||
return ResponseEntity.ok(notificationService.getUsersNotifications());
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.papermc.hangar.controller.internal;
|
||||
|
||||
import io.papermc.hangar.model.common.NamedPermission;
|
||||
import io.papermc.hangar.model.common.PermissionType;
|
||||
import io.papermc.hangar.model.common.ReviewAction;
|
||||
import io.papermc.hangar.model.common.projects.ReviewState;
|
||||
import io.papermc.hangar.model.internal.api.requests.versions.ReviewMessage;
|
||||
@ -28,7 +27,7 @@ import java.util.List;
|
||||
@Controller
|
||||
@Secured("ROLE_USER")
|
||||
@RequestMapping(path = "/api/internal/reviews")
|
||||
@PermissionRequired(type = PermissionType.GLOBAL, perms = NamedPermission.REVIEWER)
|
||||
@PermissionRequired(perms = NamedPermission.REVIEWER)
|
||||
public class ReviewController {
|
||||
|
||||
private final ReviewService reviewService;
|
||||
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.papermc.hangar.model.db.projects.ProjectOwner;
|
||||
import org.jdbi.v3.core.mapper.PropagateNull;
|
||||
import org.jdbi.v3.core.mapper.reflect.JdbiConstructor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
@ -69,6 +70,7 @@ public class UserTable extends Table implements ProjectOwner {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTagline() {
|
||||
return tagline;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface PermissionRequired {
|
||||
PermissionType type();
|
||||
PermissionType type() default PermissionType.GLOBAL;
|
||||
NamedPermission[] perms();
|
||||
String args() default "{}";
|
||||
}
|
||||
|
@ -60,6 +60,11 @@ public class UserService extends HangarService {
|
||||
hangarUsersDAO.setNotStarred(projectId, getHangarPrincipal().getUserId());
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUser(UserTable userTable) {
|
||||
userDAO.update(userTable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> UserTable getUserTable(@Nullable T identifier, @NotNull Function<T, UserTable> userTableFunction) {
|
||||
if (identifier == null) {
|
||||
|
Loading…
Reference in New Issue
Block a user