Allow members-restricted posts to be viewable by instance moderators
But add a warning message on top of the post Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
c4bd26c120
commit
f3a05929d9
@ -1067,5 +1067,10 @@
|
|||||||
"View less": "View less",
|
"View less": "View less",
|
||||||
"View more": "View more",
|
"View more": "View more",
|
||||||
"Breadcrumbs": "Breadcrumbs",
|
"Breadcrumbs": "Breadcrumbs",
|
||||||
"Other actions": "Other actions"
|
"Other actions": "Other actions",
|
||||||
|
"Only group moderators can create, edit and delete events.": "Only group moderators can create, edit and delete events.",
|
||||||
|
"+ Create a post": "+ Create a post",
|
||||||
|
"Edited {relative_time} ago": "Edited {relative_time} ago",
|
||||||
|
"Members-only post": "Members-only post",
|
||||||
|
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator.": "This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator."
|
||||||
}
|
}
|
||||||
|
@ -1158,5 +1158,10 @@
|
|||||||
"View less": "Voir moins",
|
"View less": "Voir moins",
|
||||||
"View more": "Voir plus",
|
"View more": "Voir plus",
|
||||||
"Breadcrumbs": "Fil d'Ariane",
|
"Breadcrumbs": "Fil d'Ariane",
|
||||||
"Other actions": "Autres actions"
|
"Other actions": "Autres actions",
|
||||||
|
"Only group moderators can create, edit and delete events.": "Seule⋅s les modérateur⋅ices de groupe peuvent créer, éditer et supprimer des événements.",
|
||||||
|
"+ Create a post": "+ Créer un billet",
|
||||||
|
"Edited {relative_time} ago": "Édité il y a {relative_time}",
|
||||||
|
"Members-only post": "Billet reservé aux membres",
|
||||||
|
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator.": "Ce billet est accessible uniquement aux membres. Vous y avez accès à des fins de modération car vous êtes modérateur⋅ice de l'instance."
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,14 @@ export default class GroupMixin extends Vue {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCurrentActorAGroupMember(): boolean {
|
||||||
|
return this.hasCurrentActorThisRole([
|
||||||
|
MemberRole.MODERATOR,
|
||||||
|
MemberRole.ADMINISTRATOR,
|
||||||
|
MemberRole.MEMBER,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
|
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
|
||||||
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
|
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
|
||||||
return (
|
return (
|
||||||
|
@ -404,7 +404,7 @@
|
|||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
}"
|
}"
|
||||||
class="button is-primary"
|
class="button is-primary"
|
||||||
>{{ $t("+ Post a public message") }}</router-link
|
>{{ $t("+ Create a post") }}</router-link
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</group-section>
|
</group-section>
|
||||||
@ -800,31 +800,11 @@ export default class Group extends mixins(GroupMixin) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get groupMemberships(): (string | undefined)[] {
|
|
||||||
return this.person?.memberships?.elements
|
|
||||||
.filter(
|
|
||||||
(membership: IMember) =>
|
|
||||||
![
|
|
||||||
MemberRole.REJECTED,
|
|
||||||
MemberRole.NOT_APPROVED,
|
|
||||||
MemberRole.INVITED,
|
|
||||||
].includes(membership.role)
|
|
||||||
)
|
|
||||||
.map(({ parent: { id } }) => id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch("isCurrentActorAGroupMember")
|
@Watch("isCurrentActorAGroupMember")
|
||||||
refetchGroupData(): void {
|
refetchGroupData(): void {
|
||||||
this.$apollo.queries.group.refetch();
|
this.$apollo.queries.group.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
get isCurrentActorAGroupMember(): boolean {
|
|
||||||
return (
|
|
||||||
this.groupMemberships !== undefined &&
|
|
||||||
this.groupMemberships.includes(this.group.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isCurrentActorARejectedGroupMember(): boolean {
|
get isCurrentActorARejectedGroupMember(): boolean {
|
||||||
return (
|
return (
|
||||||
this.person &&
|
this.person &&
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
}"
|
}"
|
||||||
class="button is-primary"
|
class="button is-primary"
|
||||||
>{{ $t("+ Post a public message") }}</router-link
|
>{{ $t("+ Create a post") }}</router-link
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="post-list">
|
<div class="post-list">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<article class="container" v-if="post">
|
<article class="container post" v-if="post">
|
||||||
<header>
|
<header>
|
||||||
<div class="banner-container">
|
<div class="banner-container">
|
||||||
<lazy-image-wrapper :picture="post.picture" />
|
<lazy-image-wrapper :picture="post.picture" />
|
||||||
@ -78,6 +78,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<b-message
|
||||||
|
:title="$t('Members-only post')"
|
||||||
|
class="mx-4"
|
||||||
|
type="is-warning"
|
||||||
|
:closable="false"
|
||||||
|
v-if="
|
||||||
|
!$apollo.loading &&
|
||||||
|
isInstanceModerator &&
|
||||||
|
!isCurrentActorAGroupMember &&
|
||||||
|
post.visibility === PostVisibility.PRIVATE
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</b-message>
|
||||||
|
|
||||||
<section v-html="post.body" class="content" />
|
<section v-html="post.body" class="content" />
|
||||||
<section class="tags">
|
<section class="tags">
|
||||||
@ -96,7 +114,7 @@
|
|||||||
import { Component, Prop } from "vue-property-decorator";
|
import { Component, Prop } from "vue-property-decorator";
|
||||||
import { mixins } from "vue-class-component";
|
import { mixins } from "vue-class-component";
|
||||||
import GroupMixin from "@/mixins/group";
|
import GroupMixin from "@/mixins/group";
|
||||||
import { PostVisibility } from "@/types/enums";
|
import { ICurrentUserRole, PostVisibility } from "@/types/enums";
|
||||||
import { IMember } from "@/types/actor/member.model";
|
import { IMember } from "@/types/actor/member.model";
|
||||||
import {
|
import {
|
||||||
CURRENT_ACTOR_CLIENT,
|
CURRENT_ACTOR_CLIENT,
|
||||||
@ -111,9 +129,12 @@ import Tag from "../../components/Tag.vue";
|
|||||||
import LazyImageWrapper from "../../components/Image/LazyImageWrapper.vue";
|
import LazyImageWrapper from "../../components/Image/LazyImageWrapper.vue";
|
||||||
import ActorInline from "../../components/Account/ActorInline.vue";
|
import ActorInline from "../../components/Account/ActorInline.vue";
|
||||||
import { formatDistanceToNowStrict } from "date-fns";
|
import { formatDistanceToNowStrict } from "date-fns";
|
||||||
|
import { CURRENT_USER_CLIENT } from "@/graphql/user";
|
||||||
|
import { ICurrentUser } from "@/types/current-user.model";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
apollo: {
|
apollo: {
|
||||||
|
currentUser: CURRENT_USER_CLIENT,
|
||||||
currentActor: CURRENT_ACTOR_CLIENT,
|
currentActor: CURRENT_ACTOR_CLIENT,
|
||||||
memberships: {
|
memberships: {
|
||||||
query: PERSON_MEMBERSHIPS,
|
query: PERSON_MEMBERSHIPS,
|
||||||
@ -187,6 +208,8 @@ export default class Post extends mixins(GroupMixin) {
|
|||||||
|
|
||||||
RouteName = RouteName;
|
RouteName = RouteName;
|
||||||
|
|
||||||
|
currentUser!: ICurrentUser;
|
||||||
|
|
||||||
usernameWithDomain = usernameWithDomain;
|
usernameWithDomain = usernameWithDomain;
|
||||||
|
|
||||||
formatDistanceToNowStrict = formatDistanceToNowStrict;
|
formatDistanceToNowStrict = formatDistanceToNowStrict;
|
||||||
@ -205,10 +228,17 @@ export default class Post extends mixins(GroupMixin) {
|
|||||||
.map(({ parent: { id } }) => id)
|
.map(({ parent: { id } }) => id)
|
||||||
.includes(this.post.attributedTo.id);
|
.includes(this.post.attributedTo.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isInstanceModerator(): boolean {
|
||||||
|
return [
|
||||||
|
ICurrentUserRole.ADMINISTRATOR,
|
||||||
|
ICurrentUserRole.MODERATOR,
|
||||||
|
].includes(this.currentUser.role);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
article {
|
article.post {
|
||||||
background: $white !important;
|
background: $white !important;
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -16,7 +16,7 @@ const groupDomain = "remotedomain.net";
|
|||||||
const groupUsername = `${groupPreferredUsername}@${groupDomain}`;
|
const groupUsername = `${groupPreferredUsername}@${groupDomain}`;
|
||||||
|
|
||||||
const defaultSlotText = "A list of elements";
|
const defaultSlotText = "A list of elements";
|
||||||
const createSlotButtonText = "+ Post a public message";
|
const createSlotButtonText = "+ Create a post";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -8,7 +8,7 @@ exports[`GroupSection renders group section with basic informations 1`] = `
|
|||||||
<div class="main-slot">
|
<div class="main-slot">
|
||||||
<div>A list of elements</div>
|
<div>A list of elements</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="create-slot"><a href="/@my_group@remotedomain.net/p/new" class="button is-primary">+ Post a public message</a></div>
|
<div class="create-slot"><a href="/@my_group@remotedomain.net/p/new" class="button is-primary">+ Create a post</a></div>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -20,6 +20,6 @@ exports[`GroupSection renders public group section 1`] = `
|
|||||||
<div class="main-slot">
|
<div class="main-slot">
|
||||||
<div>A list of elements</div>
|
<div>A list of elements</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="create-slot"><a href="/@my_group@remotedomain.net/p/new" class="button is-primary">+ Post a public message</a></div>
|
<div class="create-slot"><a href="/@my_group@remotedomain.net/p/new" class="button is-primary">+ Create a post</a></div>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
|
@ -65,7 +65,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do
|
|||||||
%{slug: slug},
|
%{slug: slug},
|
||||||
%{
|
%{
|
||||||
context: %{
|
context: %{
|
||||||
current_user: %User{} = user
|
current_user: %User{role: user_role} = user
|
||||||
}
|
}
|
||||||
} = _resolution
|
} = _resolution
|
||||||
) do
|
) do
|
||||||
@ -73,7 +73,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do
|
|||||||
{:current_actor, Users.get_actor_for_user(user)},
|
{:current_actor, Users.get_actor_for_user(user)},
|
||||||
{:post, %Post{attributed_to: %Actor{}} = post} <-
|
{:post, %Post{attributed_to: %Actor{}} = post} <-
|
||||||
{:post, Posts.get_post_by_slug_with_preloads(slug)},
|
{:post, Posts.get_post_by_slug_with_preloads(slug)},
|
||||||
{:member, true} <- {:member, Permission.can_access_group_object?(current_profile, post)} do
|
{:member, true} <-
|
||||||
|
{:member,
|
||||||
|
Permission.can_access_group_object?(current_profile, post) or is_moderator(user_role)} do
|
||||||
{:ok, post}
|
{:ok, post}
|
||||||
else
|
else
|
||||||
{:member, false} -> get_post(parent, %{slug: slug}, nil)
|
{:member, false} -> get_post(parent, %{slug: slug}, nil)
|
||||||
|
Loading…
Reference in New Issue
Block a user