Disable updating/deleting group posts and discussions for non-moderators
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
739516d2fd
commit
e754e1172a
|
@ -33,7 +33,7 @@
|
||||||
$options.filters.formatDateTimeString(new Date(post.insertedAt), false)
|
$options.filters.formatDateTimeString(new Date(post.insertedAt), false)
|
||||||
}}</small>
|
}}</small>
|
||||||
<small class="has-text-grey" v-if="isCurrentActorMember">{{
|
<small class="has-text-grey" v-if="isCurrentActorMember">{{
|
||||||
$t("Created by {username}", { username: usernameWithDomain(post.author) })
|
$t("Created by {username}", { username: `@${usernameWithDomain(post.author)}` })
|
||||||
}}</small>
|
}}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -158,6 +158,7 @@ export const DISCUSSION_COMMENT_CHANGED = gql`
|
||||||
actor {
|
actor {
|
||||||
id
|
id
|
||||||
preferredUsername
|
preferredUsername
|
||||||
|
name
|
||||||
domain
|
domain
|
||||||
avatar {
|
avatar {
|
||||||
url
|
url
|
||||||
|
|
|
@ -44,10 +44,19 @@ export default class GroupMixin extends Vue {
|
||||||
person!: IPerson;
|
person!: IPerson;
|
||||||
|
|
||||||
get isCurrentActorAGroupAdmin(): boolean {
|
get isCurrentActorAGroupAdmin(): boolean {
|
||||||
|
return this.hasCurrentActorThisRole(MemberRole.ADMINISTRATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCurrentActorAGroupModerator(): boolean {
|
||||||
|
return this.hasCurrentActorThisRole([MemberRole.MODERATOR, MemberRole.ADMINISTRATOR]);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
|
||||||
|
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
|
||||||
return (
|
return (
|
||||||
this.person &&
|
this.person &&
|
||||||
this.person.memberships.elements.some(
|
this.person.memberships.elements.some(
|
||||||
({ parent: { id }, role }) => id === this.group.id && role === MemberRole.ADMINISTRATOR
|
({ parent: { id }, role }) => id === this.group.id && roles.includes(role)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ $hero-body-padding-medium: 6rem 1.5rem;
|
||||||
|
|
||||||
main > .container {
|
main > .container {
|
||||||
background: $body-background-color;
|
background: $body-background-color;
|
||||||
|
min-height: 70vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
$title-color: #3c376e;
|
$title-color: #3c376e;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<h2 class="title" v-if="discussion.title && !editTitleMode">
|
<h2 class="title" v-if="discussion.title && !editTitleMode">
|
||||||
{{ discussion.title }}
|
{{ discussion.title }}
|
||||||
<span
|
<span
|
||||||
|
v-if="currentActor.id === discussion.creator.id || isCurrentActorAGroupModerator"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
newTitle = discussion.title;
|
newTitle = discussion.title;
|
||||||
|
@ -100,7 +101,8 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop } from "vue-property-decorator";
|
||||||
|
import { mixins } from "vue-class-component";
|
||||||
import {
|
import {
|
||||||
GET_DISCUSSION,
|
GET_DISCUSSION,
|
||||||
REPLY_TO_DISCUSSION,
|
REPLY_TO_DISCUSSION,
|
||||||
|
@ -113,6 +115,7 @@ import { usernameWithDomain } from "@/types/actor";
|
||||||
import DiscussionComment from "@/components/Discussion/DiscussionComment.vue";
|
import DiscussionComment from "@/components/Discussion/DiscussionComment.vue";
|
||||||
import { GraphQLError } from "graphql";
|
import { GraphQLError } from "graphql";
|
||||||
import { DELETE_COMMENT, UPDATE_COMMENT } from "@/graphql/comment";
|
import { DELETE_COMMENT, UPDATE_COMMENT } from "@/graphql/comment";
|
||||||
|
import GroupMixin from "@/mixins/group";
|
||||||
import RouteName from "../../router/name";
|
import RouteName from "../../router/name";
|
||||||
import { IComment } from "../../types/comment.model";
|
import { IComment } from "../../types/comment.model";
|
||||||
|
|
||||||
|
@ -170,7 +173,7 @@ import { IComment } from "../../types/comment.model";
|
||||||
},
|
},
|
||||||
metaInfo() {
|
metaInfo() {
|
||||||
return {
|
return {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
title: this.discussion.title,
|
title: this.discussion.title,
|
||||||
// all titles will be injected into this template
|
// all titles will be injected into this template
|
||||||
|
@ -178,7 +181,7 @@ import { IComment } from "../../types/comment.model";
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class discussion extends Vue {
|
export default class discussion extends mixins(GroupMixin) {
|
||||||
@Prop({ type: String, required: true }) slug!: string;
|
@Prop({ type: String, required: true }) slug!: string;
|
||||||
|
|
||||||
discussion: IDiscussion = new Discussion();
|
discussion: IDiscussion = new Discussion();
|
||||||
|
@ -199,7 +202,7 @@ export default class discussion extends Vue {
|
||||||
|
|
||||||
usernameWithDomain = usernameWithDomain;
|
usernameWithDomain = usernameWithDomain;
|
||||||
|
|
||||||
async reply() {
|
async reply(): Promise<void> {
|
||||||
if (this.newComment === "") return;
|
if (this.newComment === "") return;
|
||||||
|
|
||||||
await this.$apollo.mutate({
|
await this.$apollo.mutate({
|
||||||
|
@ -234,7 +237,7 @@ export default class discussion extends Vue {
|
||||||
this.newComment = "";
|
this.newComment = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateComment(comment: IComment) {
|
async updateComment(comment: IComment): Promise<void> {
|
||||||
const { data } = await this.$apollo.mutate<{ deleteComment: IComment }>({
|
const { data } = await this.$apollo.mutate<{ deleteComment: IComment }>({
|
||||||
mutation: UPDATE_COMMENT,
|
mutation: UPDATE_COMMENT,
|
||||||
variables: {
|
variables: {
|
||||||
|
@ -270,7 +273,7 @@ export default class discussion extends Vue {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteComment(comment: IComment) {
|
async deleteComment(comment: IComment): Promise<void> {
|
||||||
const { data } = await this.$apollo.mutate<{ deleteComment: IComment }>({
|
const { data } = await this.$apollo.mutate<{ deleteComment: IComment }>({
|
||||||
mutation: DELETE_COMMENT,
|
mutation: DELETE_COMMENT,
|
||||||
variables: {
|
variables: {
|
||||||
|
@ -308,7 +311,7 @@ export default class discussion extends Vue {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadMoreComments() {
|
async loadMoreComments(): Promise<void> {
|
||||||
if (!this.hasMoreComments) return;
|
if (!this.hasMoreComments) return;
|
||||||
this.page += 1;
|
this.page += 1;
|
||||||
try {
|
try {
|
||||||
|
@ -338,7 +341,7 @@ export default class discussion extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDiscussion() {
|
async updateDiscussion(): Promise<void> {
|
||||||
await this.$apollo.mutate({
|
await this.$apollo.mutate({
|
||||||
mutation: UPDATE_DISCUSSION,
|
mutation: UPDATE_DISCUSSION,
|
||||||
variables: {
|
variables: {
|
||||||
|
@ -368,7 +371,7 @@ export default class discussion extends Vue {
|
||||||
this.editTitleMode = false;
|
this.editTitleMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteConversation() {
|
async deleteConversation(): Promise<void> {
|
||||||
await this.$apollo.mutate({
|
await this.$apollo.mutate({
|
||||||
mutation: DELETE_DISCUSSION,
|
mutation: DELETE_DISCUSSION,
|
||||||
variables: {
|
variables: {
|
||||||
|
@ -376,28 +379,28 @@ export default class discussion extends Vue {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (this.discussion.actor) {
|
if (this.discussion.actor) {
|
||||||
return this.$router.push({
|
this.$router.push({
|
||||||
name: RouteName.DISCUSSION_LIST,
|
name: RouteName.DISCUSSION_LIST,
|
||||||
params: { preferredUsername: usernameWithDomain(this.discussion.actor) },
|
params: { preferredUsername: usernameWithDomain(this.discussion.actor) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleErrors(errors: GraphQLError[]) {
|
async handleErrors(errors: GraphQLError[]): Promise<void> {
|
||||||
if (errors[0].message.includes("No such discussion")) {
|
if (errors[0].message.includes("No such discussion")) {
|
||||||
await this.$router.push({ name: RouteName.PAGE_NOT_FOUND });
|
await this.$router.push({ name: RouteName.PAGE_NOT_FOUND });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted(): void {
|
||||||
window.addEventListener("scroll", this.handleScroll);
|
window.addEventListener("scroll", this.handleScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyed() {
|
destroyed(): void {
|
||||||
window.removeEventListener("scroll", this.handleScroll);
|
window.removeEventListener("scroll", this.handleScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScroll() {
|
handleScroll(): void {
|
||||||
const scrollTop =
|
const scrollTop =
|
||||||
(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
|
(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
|
||||||
const scrollHeight =
|
const scrollHeight =
|
||||||
|
|
|
@ -157,6 +157,10 @@ export default class PostList extends Vue {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.container.section {
|
||||||
|
background: $white;
|
||||||
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
div.intro,
|
div.intro,
|
||||||
.post-list {
|
.post-list {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<p class="buttons" v-if="isCurrentActorMember">
|
<p class="buttons" v-if="isCurrentActorMember">
|
||||||
<b-tag type="is-warning" size="is-medium" v-if="post.draft">{{ $t("Draft") }}</b-tag>
|
<b-tag type="is-warning" size="is-medium" v-if="post.draft">{{ $t("Draft") }}</b-tag>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="currentActor.id === post.author.id || isCurrentActorAGroupModerator"
|
||||||
:to="{ name: RouteName.POST_EDIT, params: { slug: post.slug } }"
|
:to="{ name: RouteName.POST_EDIT, params: { slug: post.slug } }"
|
||||||
tag="button"
|
tag="button"
|
||||||
class="button is-text"
|
class="button is-text"
|
||||||
|
@ -44,8 +45,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Prop } from "vue-property-decorator";
|
import { Component, Prop } from "vue-property-decorator";
|
||||||
|
import { mixins } from "vue-class-component";
|
||||||
import Editor from "@/components/Editor.vue";
|
import Editor from "@/components/Editor.vue";
|
||||||
|
import GroupMixin from "@/mixins/group";
|
||||||
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "../../graphql/actor";
|
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "../../graphql/actor";
|
||||||
import { FETCH_POST } from "../../graphql/post";
|
import { FETCH_POST } from "../../graphql/post";
|
||||||
|
|
||||||
|
@ -101,7 +104,7 @@ import Tag from "../../components/Tag.vue";
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class Post extends Vue {
|
export default class Post extends mixins(GroupMixin) {
|
||||||
@Prop({ required: true, type: String }) slug!: string;
|
@Prop({ required: true, type: String }) slug!: string;
|
||||||
|
|
||||||
post!: IPost;
|
post!: IPost;
|
||||||
|
|
Loading…
Reference in a new issue