Merge branch 'fixes' into 'main'

Do not list drafts in upcoming / old events event if moderator

Closes #466 et #1086

See merge request framasoft/mobilizon!1314
This commit is contained in:
Thomas Citharel 2022-11-02 17:47:45 +00:00
commit ce3fa41505
8 changed files with 125 additions and 63 deletions

View File

@ -266,7 +266,7 @@ button.menubar__button {
@apply px-3 dark:text-black; @apply px-3 dark:text-black;
} }
.pagination-link-current { .pagination-link-current {
@apply bg-primary cursor-not-allowed pointer-events-none border-primary text-white; @apply bg-primary cursor-not-allowed pointer-events-none border-primary text-white dark:text-zinc-900;
} }
.pagination-ellipsis { .pagination-ellipsis {
@apply text-center m-1 text-gray-300; @apply text-center m-1 text-gray-300;

View File

@ -2,7 +2,7 @@
<share-modal <share-modal
:title="t('Share this post')" :title="t('Share this post')"
:text="post.title" :text="post.title"
:url="postURL" :url="post.url ?? ''"
:input-label="t('Post URL')" :input-label="t('Post URL')"
> >
<o-notification <o-notification
@ -11,7 +11,7 @@
:closable="false" :closable="false"
> >
{{ {{
$t( t(
"This post is accessible only through it's link. Be careful where you post this link." "This post is accessible only through it's link. Be careful where you post this link."
) )
}} }}
@ -22,29 +22,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { PostVisibility } from "@/types/enums"; import { PostVisibility } from "@/types/enums";
import { IPost } from "../../types/post.model"; import { IPost } from "../../types/post.model";
import RouteName from "@/router/name";
import { computed } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import ShareModal from "@/components/Share/ShareModal.vue"; import ShareModal from "@/components/Share/ShareModal.vue";
const props = defineProps<{ defineProps<{
post: IPost; post: IPost;
}>(); }>();
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const router = useRouter();
const postURL = computed((): string => {
if (props.post.id) {
return router.resolve({
name: RouteName.POST,
params: { id: props.post.id },
}).href;
}
return props.post.url ?? "";
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.diaspora, .diaspora,

View File

@ -44,8 +44,18 @@
<button <button
v-if="!isBasicMode" v-if="!isBasicMode"
class="menubar__button" class="menubar__button"
:class="{ 'is-active': editor?.isActive('heading', { level: 3 }) }" :class="{
@click="editor?.chain().focus().toggleHeading({ level: 3 }).run()" 'is-active': editor?.isActive('heading', {
level: props.headingLevel[0],
}),
}"
@click="
editor
?.chain()
.focus()
.toggleHeading({ level: props.headingLevel[0] })
.run()
"
type="button" type="button"
:title="t('Heading Level 1')" :title="t('Heading Level 1')"
> >
@ -55,8 +65,18 @@
<button <button
v-if="!isBasicMode" v-if="!isBasicMode"
class="menubar__button" class="menubar__button"
:class="{ 'is-active': editor?.isActive('heading', { level: 4 }) }" :class="{
@click="editor?.chain().focus().toggleHeading({ level: 4 }).run()" 'is-active': editor?.isActive('heading', {
level: props.headingLevel[1],
}),
}"
@click="
editor
?.chain()
.focus()
.toggleHeading({ level: props.headingLevel[1] })
.run()
"
type="button" type="button"
:title="t('Heading Level 2')" :title="t('Heading Level 2')"
> >
@ -66,8 +86,18 @@
<button <button
v-if="!isBasicMode" v-if="!isBasicMode"
class="menubar__button" class="menubar__button"
:class="{ 'is-active': editor?.isActive('heading', { level: 5 }) }" :class="{
@click="editor?.chain().focus().toggleHeading({ level: 5 }).run()" 'is-active': editor?.isActive('heading', {
level: props.headingLevel[2],
}),
}"
@click="
editor
?.chain()
.focus()
.toggleHeading({ level: props.headingLevel[2] })
.run()
"
type="button" type="button"
:title="t('Heading Level 3')" :title="t('Heading Level 3')"
> >
@ -196,7 +226,7 @@
import { useEditor, EditorContent, BubbleMenu } from "@tiptap/vue-3"; import { useEditor, EditorContent, BubbleMenu } from "@tiptap/vue-3";
import Blockquote from "@tiptap/extension-blockquote"; import Blockquote from "@tiptap/extension-blockquote";
import BulletList from "@tiptap/extension-bullet-list"; import BulletList from "@tiptap/extension-bullet-list";
import Heading from "@tiptap/extension-heading"; import Heading, { Level } from "@tiptap/extension-heading";
import Document from "@tiptap/extension-document"; import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph"; import Paragraph from "@tiptap/extension-paragraph";
import Bold from "@tiptap/extension-bold"; import Bold from "@tiptap/extension-bold";
@ -248,10 +278,12 @@ const props = withDefaults(
ariaLabel?: string; ariaLabel?: string;
currentActor: IPerson; currentActor: IPerson;
placeholder?: string; placeholder?: string;
headingLevel?: Level[];
}>(), }>(),
{ {
mode: "description", mode: "description",
maxSize: 100_000_000, maxSize: 100_000_000,
headingLevel: () => [3, 4, 5],
} }
); );
@ -310,7 +342,7 @@ const editor = useEditor({
Blockquote, Blockquote,
BulletList, BulletList,
Heading.configure({ Heading.configure({
levels: [3, 4, 5], levels: props.headingLevel,
}), }),
Document, Document,
Paragraph, Paragraph,
@ -611,3 +643,12 @@ onBeforeUnmount(() => {
height: 0; height: 0;
} }
</style> </style>
<style>
.menubar__button {
@apply mx-0.5;
}
.menubar__button.is-active {
@apply bg-zinc-300 dark:bg-zinc-500;
}
</style>

View File

@ -55,7 +55,10 @@ export function useGroup(
name: unref(name), name: unref(name),
...options, ...options,
}), }),
() => ({ enabled: unref(name) !== undefined && unref(name) !== "" }) () => ({
enabled: unref(name) !== undefined && unref(name) !== "",
fetchPolicy: "cache-and-network",
})
); );
const group = computed(() => result.value?.group); const group = computed(() => result.value?.group);
return { group, error, loading, onResult, onError, refetch }; return { group, error, loading, onResult, onError, refetch };

View File

@ -43,6 +43,7 @@
:aria-label="t('Post body')" :aria-label="t('Post body')"
:current-actor="currentActor" :current-actor="currentActor"
:placeholder="t('Write your post')" :placeholder="t('Write your post')"
:headingLevel="[2, 3, 4]"
/> />
</o-field> </o-field>
<h2 class="mt-2">{{ t("Who can view this post") }}</h2> <h2 class="mt-2">{{ t("Who can view this post") }}</h2>

View File

@ -1,22 +1,38 @@
<template> <template>
<article class="container mx-auto post" v-if="post"> <article class="container mx-auto post" v-if="post">
<breadcrumbs-nav
v-if="post.attributedTo"
:links="[
{ name: RouteName.MY_GROUPS, text: t('My groups') },
{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(post.attributedTo) },
text: displayName(post.attributedTo),
},
{
name: RouteName.POST,
params: { slug: post.slug },
text: post.title,
},
]"
/>
<header> <header>
<div class="flex justify-center"> <div class="flex justify-center">
<lazy-image-wrapper :picture="post.picture" /> <lazy-image-wrapper :picture="post.picture" />
</div> </div>
<div class="relative flex flex-col"> <div class="relative flex flex-col">
<div <div
class="px-2 py-3 flex flex-wrap justify-center items-center" class="px-2 py-3 flex flex-wrap gap-4 justify-center items-center"
dir="auto" dir="auto"
> >
<div class="flex-1"> <div class="flex-auto min-w-[300px] max-w-screen-lg">
<div class="inline"> <div class="inline">
<tag <tag
class="mr-2" class="mr-2"
variant="warning" variant="warning"
size="medium" size="medium"
v-if="post.draft" v-if="post.draft"
>{{ $t("Draft") }}</tag >{{ t("Draft") }}</tag
> >
<h1 class="inline" :lang="post.language"> <h1 class="inline" :lang="post.language">
{{ post.title }} {{ post.title }}
@ -52,7 +68,7 @@
> >
<Clock :size="16" /> <Clock :size="16" />
{{ {{
$t("Edited {relative_time} ago", { t("Edited {relative_time} ago", {
relative_time: formatDistanceToNowStrict( relative_time: formatDistanceToNowStrict(
new Date(post.updatedAt), new Date(post.updatedAt),
{ {
@ -64,7 +80,7 @@
</span> </span>
<span v-if="post.visibility === PostVisibility.UNLISTED" class=""> <span v-if="post.visibility === PostVisibility.UNLISTED" class="">
<o-icon icon="link" size="small" /> <o-icon icon="link" size="small" />
{{ $t("Accessible only by link") }} {{ t("Accessible only by link") }}
</span> </span>
<span <span
v-else-if="post.visibility === PostVisibility.PRIVATE" v-else-if="post.visibility === PostVisibility.PRIVATE"
@ -72,7 +88,7 @@
> >
<Lock :size="16" /> <Lock :size="16" />
{{ {{
$t("Accessible only to members", { t("Accessible only to members", {
group: post.attributedTo?.name, group: post.attributedTo?.name,
}) })
}} }}
@ -82,24 +98,28 @@
<o-dropdown position="bottom-left" aria-role="list"> <o-dropdown position="bottom-left" aria-role="list">
<template #trigger> <template #trigger>
<o-button role="button" icon-right="dots-horizontal"> <o-button role="button" icon-right="dots-horizontal">
{{ $t("Actions") }} {{ t("Actions") }}
</o-button> </o-button>
</template> </template>
<o-dropdown-item <o-dropdown-item
aria-role="listitem" aria-role="listitem"
has-link has-link
tabIndex="-1"
v-if=" v-if="
currentActor?.id === post?.author?.id || currentActor?.id === post?.author?.id ||
isCurrentActorAGroupModerator isCurrentActorAGroupModerator
" "
> >
<router-link <router-link
class="flex gap-1 whitespace-nowrap flex-1"
:to="{ :to="{
name: RouteName.POST_EDIT, name: RouteName.POST_EDIT,
params: { slug: post.slug }, params: { slug: post.slug },
}" }"
>{{ $t("Edit") }} <o-icon icon="pencil" >
/></router-link> <Pencil />
{{ t("Edit") }}
</router-link>
</o-dropdown-item> </o-dropdown-item>
<o-dropdown-item <o-dropdown-item
aria-role="listitem" aria-role="listitem"
@ -107,11 +127,15 @@
currentActor?.id === post?.author?.id || currentActor?.id === post?.author?.id ||
isCurrentActorAGroupModerator isCurrentActorAGroupModerator
" "
@click="openDeletePostModal" tabIndex="-1"
@keyup.enter="openDeletePostModal"
> >
{{ $t("Delete") }} <button
<o-icon icon="delete" /> @click="openDeletePostModal"
class="flex gap-1 whitespace-nowrap"
>
<Delete />
{{ t("Delete") }}
</button>
</o-dropdown-item> </o-dropdown-item>
<hr <hr
@ -126,32 +150,36 @@
<o-dropdown-item <o-dropdown-item
aria-role="listitem" aria-role="listitem"
v-if="!post.draft" v-if="!post.draft"
@click="triggerShare()" tabIndex="-1"
@keyup.enter="triggerShare()"
> >
<span> <button
{{ $t("Share this event") }} @click="triggerShare()"
<o-icon icon="share" /> class="flex gap-1 whitespace-nowrap"
</span> >
<Share />
{{ t("Share this event") }}
</button>
</o-dropdown-item> </o-dropdown-item>
<o-dropdown-item <o-dropdown-item
aria-role="listitem" aria-role="listitem"
v-if="ableToReport" v-if="ableToReport"
@click="isReportModalActive = true" tabIndex="-1"
@keyup.enter="isReportModalActive = true"
> >
<span> <button
{{ $t("Report") }} @click="isReportModalActive = true"
<o-icon icon="flag" /> class="flex gap-1 whitespace-nowrap"
</span> >
<Flag />
{{ t("Report") }}
</button>
</o-dropdown-item> </o-dropdown-item>
</o-dropdown> </o-dropdown>
</div> </div>
</div> </div>
</header> </header>
<o-notification <o-notification
:title="$t('Members-only post')" :title="t('Members-only post')"
class="mx-4" class="mx-4"
variant="warning" variant="warning"
:closable="false" :closable="false"
@ -164,7 +192,7 @@
" "
> >
{{ {{
$t( t(
"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."
) )
}} }}
@ -173,10 +201,10 @@
<section <section
v-html="post.body" v-html="post.body"
dir="auto" dir="auto"
class="px-1 prose lg:prose-xl prose-p:mt-6 dark:prose-invert" class="px-2 md:px-4 py-4 prose lg:prose-xl prose-p:mt-6 dark:prose-invert bg-white dark:bg-zinc-700 mx-auto"
:lang="post.language" :lang="post.language"
/> />
<section class="flex gap-2 my-6" dir="auto"> <section class="flex gap-2 my-6 justify-center" dir="auto">
<router-link <router-link
v-for="tag in post.tags" v-for="tag in post.tags"
:key="tag.title" :key="tag.title"
@ -186,14 +214,14 @@
</router-link> </router-link>
</section> </section>
<o-modal <o-modal
:close-button-aria-label="$t('Close')" :close-button-aria-label="t('Close')"
v-model:active="isReportModalActive" v-model:active="isReportModalActive"
has-modal-card has-modal-card
ref="reportModal" ref="reportModal"
> >
<ReportModal <ReportModal
:on-confirm="reportPost" :on-confirm="reportPost"
:title="$t('Report this post')" :title="t('Report this post')"
:outside-domain="groupDomain" :outside-domain="groupDomain"
@close="isReportModalActive = false" @close="isReportModalActive = false"
/> />
@ -202,7 +230,7 @@
v-model:active="isShareModalActive" v-model:active="isShareModalActive"
has-modal-card has-modal-card
ref="shareModal" ref="shareModal"
:close-button-aria-label="$t('Close')" :close-button-aria-label="t('Close')"
> >
<share-post-modal :post="post" /> <share-post-modal :post="post" />
</o-modal> </o-modal>
@ -241,6 +269,10 @@ import { useRouter } from "vue-router";
import { useCreateReport } from "@/composition/apollo/report"; import { useCreateReport } from "@/composition/apollo/report";
import Clock from "vue-material-design-icons/Clock.vue"; import Clock from "vue-material-design-icons/Clock.vue";
import Lock from "vue-material-design-icons/Lock.vue"; import Lock from "vue-material-design-icons/Lock.vue";
import Pencil from "vue-material-design-icons/Pencil.vue";
import Delete from "vue-material-design-icons/Delete.vue";
import Share from "vue-material-design-icons/Share.vue";
import Flag from "vue-material-design-icons/Flag.vue";
import { Dialog } from "@/plugins/dialog"; import { Dialog } from "@/plugins/dialog";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";

View File

@ -446,8 +446,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
} }
} }
) do ) do
if Actors.is_member?(actor_id, group_id) or is_moderator(user_role) do if Actors.is_member?(actor_id, group_id) do
# TODO : Handle public / restricted to group members events
{:ok, {:ok,
Events.list_organized_events_for_group( Events.list_organized_events_for_group(
group, group,

View File

@ -93,7 +93,8 @@ defmodule Mix.Tasks.Mobilizon.Actors.Delete do
end end
end end
defp check_actor(%Actor{type: :Person, domain: nil} = profile, assume_yes?) do defp check_actor(%Actor{type: :Person, domain: nil, user_id: user_id} = profile, assume_yes?)
when not is_nil(user_id) do
%User{actors: actors, email: email} = Users.get_user_with_actors!(profile.user_id) %User{actors: actors, email: email} = Users.get_user_with_actors!(profile.user_id)
if length(actors) == 1 do if length(actors) == 1 do