Improve group dashboard for members without moderator rights

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-10-22 10:48:49 +02:00
parent e754e1172a
commit 2ce5f8e66c
7 changed files with 148 additions and 95 deletions

View File

@@ -29,10 +29,18 @@
<p v-if="isCurrentActorMember">
{{
$t(
"When someone from the group creates an event and attributes it to the group, it will show up here."
"When a moderator from the group creates an event and attributes it to the group, it will show up here."
)
}}
</p>
<router-link
v-if="isCurrentActorAGroupModerator"
:to="{
name: RouteName.CREATE_EVENT,
}"
class="button is-primary"
>{{ $t("+ Create an event") }}</router-link
>
<b-loading :active.sync="$apollo.loading"></b-loading>
<section v-if="group">
<subtitle>
@@ -58,12 +66,14 @@
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { Component } from "vue-property-decorator";
import { mixins } from "vue-class-component";
import { FETCH_GROUP } from "@/graphql/group";
import RouteName from "@/router/name";
import Subtitle from "@/components/Utils/Subtitle.vue";
import EventListViewCard from "@/components/Event/EventListViewCard.vue";
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
import GroupMixin from "@/mixins/group";
import { IGroup, IMember, IPerson, usernameWithDomain } from "../../types/actor";
@Component({
@@ -98,7 +108,7 @@ import { IGroup, IMember, IPerson, usernameWithDomain } from "../../types/actor"
EventListViewCard,
},
})
export default class GroupEvents extends Vue {
export default class GroupEvents extends mixins(GroupMixin) {
group!: IGroup;
memberships!: IMember[];
@@ -117,3 +127,8 @@ export default class GroupEvents extends Vue {
}
}
</script>
<style lang="scss" scoped>
.container.section {
background: $white;
}
</style>

View File

@@ -231,6 +231,7 @@
</template>
<template v-slot:create>
<router-link
v-if="isCurrentActorAGroupModerator"
:to="{
name: RouteName.CREATE_EVENT,
}"
@@ -259,6 +260,7 @@
</template>
<template v-slot:create>
<router-link
v-if="isCurrentActorAGroupModerator"
:to="{
name: RouteName.POST_CREATE,
params: { preferredUsername: usernameWithDomain(group) },

View File

@@ -1,96 +1,110 @@
<template>
<form @submit.prevent="publish(false)">
<div class="container section">
<h1 class="title" v-if="isUpdate === true">
{{ $t("Edit post") }}
</h1>
<h1 class="title" v-else>
{{ $t("Add a new post") }}
</h1>
<subtitle>{{ $t("General information") }}</subtitle>
<picture-upload
v-model="pictureFile"
:textFallback="$t('Headline picture')"
:defaultImageSrc="post.picture ? post.picture.url : null"
/>
<div>
<form @submit.prevent="publish(false)" v-if="isCurrentActorAGroupModerator">
<div class="container section">
<h1 class="title" v-if="isUpdate === true">
{{ $t("Edit post") }}
</h1>
<h1 class="title" v-else>
{{ $t("Add a new post") }}
</h1>
<subtitle>{{ $t("General information") }}</subtitle>
<picture-upload
v-model="pictureFile"
:textFallback="$t('Headline picture')"
:defaultImageSrc="post.picture ? post.picture.url : null"
/>
<b-field
:label="$t('Title')"
:type="errors.title ? 'is-danger' : null"
:message="errors.title"
>
<b-input size="is-large" aria-required="true" required v-model="post.title" />
</b-field>
<tag-input v-model="post.tags" :data="tags" path="title" />
<div class="field">
<label class="label">{{ $t("Post") }}</label>
<p v-if="errors.body" class="help is-danger">{{ errors.body }}</p>
<editor v-model="post.body" />
</div>
<subtitle>{{ $t("Who can view this post") }}</subtitle>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.PUBLIC"
>{{ $t("Visible everywhere on the web") }}</b-radio
<b-field
:label="$t('Title')"
:type="errors.title ? 'is-danger' : null"
:message="errors.title"
>
</div>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.UNLISTED"
>{{ $t("Only accessible through link") }}</b-radio
>
</div>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.PRIVATE"
>{{ $t("Only accessible to members of the group") }}</b-radio
>
</div>
</div>
<nav class="navbar">
<div class="container">
<div class="navbar-menu">
<div class="navbar-end">
<span class="navbar-item">
<b-button type="is-text" @click="$router.go(-1)">{{ $t("Cancel") }}</b-button>
</span>
<span class="navbar-item" v-if="this.isUpdate">
<b-button type="is-danger is-outlined" @click="deletePost">{{
$t("Delete post")
}}</b-button>
</span>
<!-- If an post has been published we can't make it draft anymore -->
<span class="navbar-item" v-if="post.draft === true">
<b-button type="is-primary" outlined @click="publish(true)">{{
$t("Save draft")
}}</b-button>
</span>
<span class="navbar-item">
<b-button type="is-primary" native-type="submit">
<span v-if="isUpdate === false || post.draft === true">{{ $t("Publish") }}</span>
<b-input size="is-large" aria-required="true" required v-model="post.title" />
</b-field>
<span v-else>{{ $t("Update post") }}</span>
</b-button>
</span>
</div>
<tag-input v-model="post.tags" :data="tags" path="title" />
<div class="field">
<label class="label">{{ $t("Post") }}</label>
<p v-if="errors.body" class="help is-danger">{{ errors.body }}</p>
<editor v-model="post.body" />
</div>
<subtitle>{{ $t("Who can view this post") }}</subtitle>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.PUBLIC"
>{{ $t("Visible everywhere on the web") }}</b-radio
>
</div>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.UNLISTED"
>{{ $t("Only accessible through link") }}</b-radio
>
</div>
<div class="field">
<b-radio
v-model="post.visibility"
name="postVisibility"
:native-value="PostVisibility.PRIVATE"
>{{ $t("Only accessible to members of the group") }}</b-radio
>
</div>
</div>
</nav>
</form>
<nav class="navbar">
<div class="container">
<div class="navbar-menu">
<div class="navbar-end">
<span class="navbar-item">
<b-button type="is-text" @click="$router.go(-1)">{{ $t("Cancel") }}</b-button>
</span>
<span class="navbar-item" v-if="this.isUpdate">
<b-button type="is-danger is-outlined" @click="deletePost">{{
$t("Delete post")
}}</b-button>
</span>
<!-- If an post has been published we can't make it draft anymore -->
<span class="navbar-item" v-if="post.draft === true">
<b-button type="is-primary" outlined @click="publish(true)">{{
$t("Save draft")
}}</b-button>
</span>
<span class="navbar-item">
<b-button type="is-primary" native-type="submit">
<span v-if="isUpdate === false || post.draft === true">{{ $t("Publish") }}</span>
<span v-else>{{ $t("Update post") }}</span>
</b-button>
</span>
</div>
</div>
</div>
</nav>
</form>
<b-loading
v-else-if="$apollo.loading"
:is-full-page="false"
:active.sync="$apollo.loading"
:can-cancel="false"
></b-loading>
<div class="container section" v-else>
<b-message type="is-danger">
{{ $t("Only group moderators can create, edit and delete posts.") }}
</b-message>
</div>
</div>
</template>
<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 { FETCH_GROUP } from "@/graphql/group";
import { buildFileFromIPicture, readFileAsync } from "@/utils/image";
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
import GroupMixin from "@/mixins/group";
import { TAGS } from "../../graphql/tags";
import { CONFIG } from "../../graphql/config";
import { FETCH_POST, CREATE_POST, UPDATE_POST, DELETE_POST } from "../../graphql/post";
@@ -105,7 +119,6 @@ import PictureUpload from "../../components/PictureUpload.vue";
@Component({
apollo: {
currentActor: CURRENT_ACTOR_CLIENT,
tags: TAGS,
config: CONFIG,
post: {
@@ -150,7 +163,7 @@ import PictureUpload from "../../components/PictureUpload.vue";
};
},
})
export default class EditPost extends Vue {
export default class EditPost extends mixins(GroupMixin) {
@Prop({ required: false, type: String }) slug: undefined | string;
@Prop({ required: false, type: String }) preferredUsername!: string;
@@ -278,14 +291,27 @@ export default class EditPost extends Vue {
}
get actualGroup(): IActor {
if (!this.group) {
if (!this.group.id) {
return this.post.attributedTo as IActor;
}
return this.group;
}
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
return (
this.person &&
this.person.memberships.elements.some(
({ parent: { id }, role }) => id === this.actualGroup.id && roles.includes(role)
)
);
}
}
</script>
<style lang="scss" scoped>
.container.section {
background: $white;
}
form {
nav.navbar {
position: sticky;

View File

@@ -35,8 +35,11 @@
)
}}
</p>
<p v-if="isCurrentActorMember">
{{ $t("Only group moderators can create, edit and delete posts.") }}
</p>
<router-link
v-if="isCurrentActorMember"
v-if="isCurrentActorAGroupModerator"
:to="{
name: RouteName.POST_CREATE,
params: { preferredUsername: usernameWithDomain(group) },
@@ -75,8 +78,10 @@
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import { Component, Prop } from "vue-property-decorator";
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
import { mixins } from "vue-class-component";
import GroupMixin from "@/mixins/group";
import { FETCH_GROUP_POSTS } from "../../graphql/post";
import { Paginate } from "../../types/paginate";
import { IPost } from "../../types/post.model";
@@ -131,7 +136,7 @@ const POSTS_PAGE_LIMIT = 10;
};
},
})
export default class PostList extends Vue {
export default class PostList extends mixins(GroupMixin) {
@Prop({ required: true, type: String }) preferredUsername!: string;
group!: IGroup;