Improve group dashboard for members without moderator rights
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
e754e1172a
commit
2ce5f8e66c
|
@ -37,6 +37,7 @@ section {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
border: 2px solid $violet;
|
border: 2px solid $violet;
|
||||||
|
min-height: 30vh;
|
||||||
|
|
||||||
.create-slot {
|
.create-slot {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -48,6 +49,7 @@ section {
|
||||||
.main-slot {
|
.main-slot {
|
||||||
min-height: 5rem;
|
min-height: 5rem;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -731,7 +731,6 @@
|
||||||
"Create to-do lists for all the tasks you need to do, assign them and set due dates.": "Create to-do lists for all the tasks you need to do, assign them and set due dates.",
|
"Create to-do lists for all the tasks you need to do, assign them and set due dates.": "Create to-do lists for all the tasks you need to do, assign them and set due dates.",
|
||||||
"A place to store links to documents or resources of any type.": "A place to store links to documents or resources of any type.",
|
"A place to store links to documents or resources of any type.": "A place to store links to documents or resources of any type.",
|
||||||
"{group}'s events": "{group}'s events",
|
"{group}'s events": "{group}'s events",
|
||||||
"When someone from the group creates an event and attributes it to the group, it will show up here.": "When someone from the group creates an event and attributes it to the group, it will show up here.",
|
|
||||||
"View all": "View all",
|
"View all": "View all",
|
||||||
"+ Start a discussion": "+ Start a discussion",
|
"+ Start a discussion": "+ Start a discussion",
|
||||||
"+ Add a resource": "+ Add a resource",
|
"+ Add a resource": "+ Add a resource",
|
||||||
|
@ -815,5 +814,7 @@
|
||||||
"An ethical alternative": "An ethical alternative",
|
"An ethical alternative": "An ethical alternative",
|
||||||
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.",
|
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.",
|
||||||
"A federated software": "A federated software",
|
"A federated software": "A federated software",
|
||||||
"Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>.": "Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>."
|
"Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>.": "Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>.",
|
||||||
|
"When a moderator 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.",
|
||||||
|
"Only group moderators can create, edit and delete posts.": "Only group moderators can create, edit and delete posts."
|
||||||
}
|
}
|
||||||
|
|
|
@ -867,5 +867,7 @@
|
||||||
"An ethical alternative": "Une alternative éthique",
|
"An ethical alternative": "Une alternative éthique",
|
||||||
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Alternative éthique aux événements, groupes et pages Facebook, Mobilizon est <b>un outil conçu pour vous servir</b>. Point.",
|
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Alternative éthique aux événements, groupes et pages Facebook, Mobilizon est <b>un outil conçu pour vous servir</b>. Point.",
|
||||||
"A federated software": "Un logiciel fédéré",
|
"A federated software": "Un logiciel fédéré",
|
||||||
"Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>.": "Mobilizon n’est pas une plateforme géante, mais une <b>multitude de sites web Mobilizon interconnectés</b>."
|
"Mobilizon is not a giant platform, but a <b>multitude of interconnected Mobilizon websites</b>.": "Mobilizon n’est pas une plateforme géante, mais une <b>multitude de sites web Mobilizon interconnectés</b>.",
|
||||||
|
"When a moderator from the group creates an event and attributes it to the group, it will show up here.": "Lorsqu'un·e modérateur·ice du groupe crée un événement et l'attribue au groupe, il s'affichera ici.",
|
||||||
|
"Only group moderators can create, edit and delete posts.": "Seul·es les modérateur·ices du groupe peuvent créer, éditer et supprimer des billets."
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,18 @@
|
||||||
<p v-if="isCurrentActorMember">
|
<p v-if="isCurrentActorMember">
|
||||||
{{
|
{{
|
||||||
$t(
|
$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>
|
</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>
|
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||||
<section v-if="group">
|
<section v-if="group">
|
||||||
<subtitle>
|
<subtitle>
|
||||||
|
@ -58,12 +66,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<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 { FETCH_GROUP } from "@/graphql/group";
|
||||||
import RouteName from "@/router/name";
|
import RouteName from "@/router/name";
|
||||||
import Subtitle from "@/components/Utils/Subtitle.vue";
|
import Subtitle from "@/components/Utils/Subtitle.vue";
|
||||||
import EventListViewCard from "@/components/Event/EventListViewCard.vue";
|
import EventListViewCard from "@/components/Event/EventListViewCard.vue";
|
||||||
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
|
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
|
||||||
|
import GroupMixin from "@/mixins/group";
|
||||||
import { IGroup, IMember, IPerson, usernameWithDomain } from "../../types/actor";
|
import { IGroup, IMember, IPerson, usernameWithDomain } from "../../types/actor";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -98,7 +108,7 @@ import { IGroup, IMember, IPerson, usernameWithDomain } from "../../types/actor"
|
||||||
EventListViewCard,
|
EventListViewCard,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class GroupEvents extends Vue {
|
export default class GroupEvents extends mixins(GroupMixin) {
|
||||||
group!: IGroup;
|
group!: IGroup;
|
||||||
|
|
||||||
memberships!: IMember[];
|
memberships!: IMember[];
|
||||||
|
@ -117,3 +127,8 @@ export default class GroupEvents extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container.section {
|
||||||
|
background: $white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -231,6 +231,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:create>
|
<template v-slot:create>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="isCurrentActorAGroupModerator"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.CREATE_EVENT,
|
name: RouteName.CREATE_EVENT,
|
||||||
}"
|
}"
|
||||||
|
@ -259,6 +260,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:create>
|
<template v-slot:create>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="isCurrentActorAGroupModerator"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.POST_CREATE,
|
name: RouteName.POST_CREATE,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
|
|
|
@ -1,96 +1,110 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="publish(false)">
|
<div>
|
||||||
<div class="container section">
|
<form @submit.prevent="publish(false)" v-if="isCurrentActorAGroupModerator">
|
||||||
<h1 class="title" v-if="isUpdate === true">
|
<div class="container section">
|
||||||
{{ $t("Edit post") }}
|
<h1 class="title" v-if="isUpdate === true">
|
||||||
</h1>
|
{{ $t("Edit post") }}
|
||||||
<h1 class="title" v-else>
|
</h1>
|
||||||
{{ $t("Add a new post") }}
|
<h1 class="title" v-else>
|
||||||
</h1>
|
{{ $t("Add a new post") }}
|
||||||
<subtitle>{{ $t("General information") }}</subtitle>
|
</h1>
|
||||||
<picture-upload
|
<subtitle>{{ $t("General information") }}</subtitle>
|
||||||
v-model="pictureFile"
|
<picture-upload
|
||||||
:textFallback="$t('Headline picture')"
|
v-model="pictureFile"
|
||||||
:defaultImageSrc="post.picture ? post.picture.url : null"
|
:textFallback="$t('Headline picture')"
|
||||||
/>
|
:defaultImageSrc="post.picture ? post.picture.url : null"
|
||||||
|
/>
|
||||||
|
|
||||||
<b-field
|
<b-field
|
||||||
:label="$t('Title')"
|
:label="$t('Title')"
|
||||||
:type="errors.title ? 'is-danger' : null"
|
:type="errors.title ? 'is-danger' : null"
|
||||||
:message="errors.title"
|
: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
|
|
||||||
>
|
>
|
||||||
</div>
|
<b-input size="is-large" aria-required="true" required v-model="post.title" />
|
||||||
<div class="field">
|
</b-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>
|
|
||||||
|
|
||||||
<span v-else>{{ $t("Update post") }}</span>
|
<tag-input v-model="post.tags" :data="tags" path="title" />
|
||||||
</b-button>
|
|
||||||
</span>
|
<div class="field">
|
||||||
</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
<nav class="navbar">
|
||||||
</form>
|
<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>
|
</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 { FETCH_GROUP } from "@/graphql/group";
|
import { FETCH_GROUP } from "@/graphql/group";
|
||||||
import { buildFileFromIPicture, readFileAsync } from "@/utils/image";
|
import { buildFileFromIPicture, readFileAsync } from "@/utils/image";
|
||||||
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
|
import GroupMixin from "@/mixins/group";
|
||||||
import { TAGS } from "../../graphql/tags";
|
import { TAGS } from "../../graphql/tags";
|
||||||
import { CONFIG } from "../../graphql/config";
|
import { CONFIG } from "../../graphql/config";
|
||||||
import { FETCH_POST, CREATE_POST, UPDATE_POST, DELETE_POST } from "../../graphql/post";
|
import { FETCH_POST, CREATE_POST, UPDATE_POST, DELETE_POST } from "../../graphql/post";
|
||||||
|
@ -105,7 +119,6 @@ import PictureUpload from "../../components/PictureUpload.vue";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
apollo: {
|
apollo: {
|
||||||
currentActor: CURRENT_ACTOR_CLIENT,
|
|
||||||
tags: TAGS,
|
tags: TAGS,
|
||||||
config: CONFIG,
|
config: CONFIG,
|
||||||
post: {
|
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 }) slug: undefined | string;
|
||||||
|
|
||||||
@Prop({ required: false, type: String }) preferredUsername!: string;
|
@Prop({ required: false, type: String }) preferredUsername!: string;
|
||||||
|
@ -278,14 +291,27 @@ export default class EditPost extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
get actualGroup(): IActor {
|
get actualGroup(): IActor {
|
||||||
if (!this.group) {
|
if (!this.group.id) {
|
||||||
return this.post.attributedTo as IActor;
|
return this.post.attributedTo as IActor;
|
||||||
}
|
}
|
||||||
return this.group;
|
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>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.container.section {
|
||||||
|
background: $white;
|
||||||
|
}
|
||||||
form {
|
form {
|
||||||
nav.navbar {
|
nav.navbar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
|
@ -35,8 +35,11 @@
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
|
<p v-if="isCurrentActorMember">
|
||||||
|
{{ $t("Only group moderators can create, edit and delete posts.") }}
|
||||||
|
</p>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="isCurrentActorMember"
|
v-if="isCurrentActorAGroupModerator"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.POST_CREATE,
|
name: RouteName.POST_CREATE,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
|
@ -75,8 +78,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 { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
|
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 { FETCH_GROUP_POSTS } from "../../graphql/post";
|
||||||
import { Paginate } from "../../types/paginate";
|
import { Paginate } from "../../types/paginate";
|
||||||
import { IPost } from "../../types/post.model";
|
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;
|
@Prop({ required: true, type: String }) preferredUsername!: string;
|
||||||
|
|
||||||
group!: IGroup;
|
group!: IGroup;
|
||||||
|
|
Loading…
Reference in a new issue