forked from potsda.mn/mobilizon
Merge branch 'fix-csp' into 'main'
Add sha-256 hash for toggling dark theme code and remove inlined phoenix digest Closes #993 See merge request framasoft/mobilizon!1313
This commit is contained in:
commit
48da35ee41
|
@ -205,31 +205,34 @@ export const LOGGED_USER_DRAFTS = gql`
|
||||||
loggedUser {
|
loggedUser {
|
||||||
id
|
id
|
||||||
drafts(page: $page, limit: $limit) {
|
drafts(page: $page, limit: $limit) {
|
||||||
id
|
total
|
||||||
uuid
|
elements {
|
||||||
title
|
|
||||||
draft
|
|
||||||
picture {
|
|
||||||
id
|
id
|
||||||
url
|
uuid
|
||||||
alt
|
title
|
||||||
}
|
draft
|
||||||
beginsOn
|
picture {
|
||||||
status
|
id
|
||||||
visibility
|
url
|
||||||
attributedTo {
|
alt
|
||||||
...ActorFragment
|
}
|
||||||
}
|
beginsOn
|
||||||
organizerActor {
|
status
|
||||||
...ActorFragment
|
visibility
|
||||||
}
|
attributedTo {
|
||||||
participantStats {
|
...ActorFragment
|
||||||
going
|
}
|
||||||
notApproved
|
organizerActor {
|
||||||
}
|
...ActorFragment
|
||||||
options {
|
}
|
||||||
maximumAttendeeCapacity
|
participantStats {
|
||||||
remainingAttendeeCapacity
|
going
|
||||||
|
notApproved
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
maximumAttendeeCapacity
|
||||||
|
remainingAttendeeCapacity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export interface IUser extends ICurrentUser {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
participations: Paginate<IParticipant>;
|
participations: Paginate<IParticipant>;
|
||||||
mediaSize: number;
|
mediaSize: number;
|
||||||
drafts: IEvent[];
|
drafts: Paginate<IEvent>;
|
||||||
settings: IUserSettings;
|
settings: IUserSettings;
|
||||||
activitySettings: IActivitySetting[];
|
activitySettings: IActivitySetting[];
|
||||||
followedGroupEvents: Paginate<IFollowedGroupEvent>;
|
followedGroupEvents: Paginate<IFollowedGroupEvent>;
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- <o-loading v-model:active="$apollo.loading"></o-loading> -->
|
<!-- <o-loading v-model:active="$apollo.loading"></o-loading> -->
|
||||||
<div class="wrapper flex flex-wrap gap-4 items-start">
|
<div class="flex flex-wrap gap-4 items-start">
|
||||||
<div
|
<div
|
||||||
class="event-filter rounded p-3 flex-auto md:flex-none bg-zinc-300 dark:bg-zinc-700"
|
class="rounded p-3 flex-auto md:flex-none bg-zinc-300 dark:bg-zinc-700"
|
||||||
>
|
>
|
||||||
<o-field>
|
<o-field>
|
||||||
<o-switch v-model="showUpcoming">{{
|
<o-switch v-model="showUpcoming">{{
|
||||||
|
@ -73,15 +73,31 @@
|
||||||
/>
|
/>
|
||||||
</o-field>
|
</o-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-events flex-1">
|
<div class="flex-1 min-w-[300px]">
|
||||||
<section
|
<section
|
||||||
class="py-4"
|
class="py-4 first:pt-0"
|
||||||
v-if="showUpcoming && showDrafts && drafts.length > 0"
|
v-if="showUpcoming && showDrafts && drafts && drafts.total > 0"
|
||||||
>
|
>
|
||||||
<multi-event-minimalist-card :events="drafts" :showOrganizer="true" />
|
<h2 class="text-2xl mb-2">{{ t("Drafts") }}</h2>
|
||||||
|
<multi-event-minimalist-card
|
||||||
|
:events="drafts.elements"
|
||||||
|
:showOrganizer="true"
|
||||||
|
/>
|
||||||
|
<o-pagination
|
||||||
|
class="mt-4"
|
||||||
|
v-show="drafts.total > LOGGED_USER_DRAFTS_LIMIT"
|
||||||
|
:total="drafts.total"
|
||||||
|
v-model:current="draftsPage"
|
||||||
|
:per-page="LOGGED_USER_DRAFTS_LIMIT"
|
||||||
|
:aria-next-label="t('Next page')"
|
||||||
|
:aria-previous-label="t('Previous page')"
|
||||||
|
:aria-page-label="t('Page')"
|
||||||
|
:aria-current-label="t('Current page')"
|
||||||
|
>
|
||||||
|
</o-pagination>
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="py-4"
|
class="py-4 first:pt-0"
|
||||||
v-if="
|
v-if="
|
||||||
showUpcoming && monthlyFutureEvents && monthlyFutureEvents.size > 0
|
showUpcoming && monthlyFutureEvents && monthlyFutureEvents.size > 0
|
||||||
"
|
"
|
||||||
|
@ -92,7 +108,7 @@
|
||||||
v-for="month of monthlyFutureEvents"
|
v-for="month of monthlyFutureEvents"
|
||||||
:key="month[0]"
|
:key="month[0]"
|
||||||
>
|
>
|
||||||
<span class="upcoming-month">{{ month[0] }}</span>
|
<h2 class="text-2xl">{{ month[0] }}</h2>
|
||||||
<div v-for="element in month[1]" :key="element.id">
|
<div v-for="element in month[1]" :key="element.id">
|
||||||
<event-participation-card
|
<event-participation-card
|
||||||
v-if="'role' in element"
|
v-if="'role' in element"
|
||||||
|
@ -131,7 +147,7 @@
|
||||||
v-if="
|
v-if="
|
||||||
showUpcoming &&
|
showUpcoming &&
|
||||||
monthlyFutureEvents &&
|
monthlyFutureEvents &&
|
||||||
monthlyFutureEvents.length === 0 &&
|
monthlyFutureEvents.size === 0 &&
|
||||||
true // !$apollo.loading
|
true // !$apollo.loading
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
@ -209,10 +225,15 @@ import {
|
||||||
import { useQuery } from "@vue/apollo-composable";
|
import { useQuery } from "@vue/apollo-composable";
|
||||||
import { computed, inject, ref, defineAsyncComponent } from "vue";
|
import { computed, inject, ref, defineAsyncComponent } from "vue";
|
||||||
import { IUser } from "@/types/current-user.model";
|
import { IUser } from "@/types/current-user.model";
|
||||||
import { booleanTransformer, useRouteQuery } from "vue-use-route-query";
|
import {
|
||||||
|
booleanTransformer,
|
||||||
|
integerTransformer,
|
||||||
|
useRouteQuery,
|
||||||
|
} from "vue-use-route-query";
|
||||||
import { Locale } from "date-fns";
|
import { Locale } from "date-fns";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRestrictions } from "@/composition/apollo/config";
|
import { useRestrictions } from "@/composition/apollo/config";
|
||||||
|
import { useHead } from "@vueuse/head";
|
||||||
|
|
||||||
const EventParticipationCard = defineAsyncComponent(
|
const EventParticipationCard = defineAsyncComponent(
|
||||||
() => import("@/components/Event/EventParticipationCard.vue")
|
() => import("@/components/Event/EventParticipationCard.vue")
|
||||||
|
@ -253,8 +274,6 @@ const dateFilter = useRouteQuery("dateFilter", new Date(), {
|
||||||
const hasMoreFutureParticipations = ref(true);
|
const hasMoreFutureParticipations = ref(true);
|
||||||
const hasMorePastParticipations = ref(true);
|
const hasMorePastParticipations = ref(true);
|
||||||
|
|
||||||
// config: CONFIG
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
result: loggedUserUpcomingEventsResult,
|
result: loggedUserUpcomingEventsResult,
|
||||||
fetchMore: fetchMoreUpcomingEvents,
|
fetchMore: fetchMoreUpcomingEvents,
|
||||||
|
@ -277,10 +296,17 @@ const groupEvents = computed(
|
||||||
.elements ?? []
|
.elements ?? []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const LOGGED_USER_DRAFTS_LIMIT = 10;
|
||||||
|
const draftsPage = useRouteQuery("draftsPage", 1, integerTransformer);
|
||||||
|
|
||||||
const { result: draftsResult } = useQuery<{
|
const { result: draftsResult } = useQuery<{
|
||||||
loggedUser: Pick<IUser, "drafts">;
|
loggedUser: Pick<IUser, "drafts">;
|
||||||
}>(LOGGED_USER_DRAFTS, () => ({ page: 1, limit: 10 }));
|
}>(
|
||||||
const drafts = computed(() => draftsResult.value?.loggedUser.drafts ?? []);
|
LOGGED_USER_DRAFTS,
|
||||||
|
() => ({ page: draftsPage.value, limit: LOGGED_USER_DRAFTS_LIMIT }),
|
||||||
|
() => ({ fetchPolicy: "cache-and-network" })
|
||||||
|
);
|
||||||
|
const drafts = computed(() => draftsResult.value?.loggedUser.drafts);
|
||||||
|
|
||||||
const { result: participationsResult, fetchMore: fetchMoreParticipations } =
|
const { result: participationsResult, fetchMore: fetchMoreParticipations } =
|
||||||
useQuery<{
|
useQuery<{
|
||||||
|
@ -294,12 +320,6 @@ const pastParticipations = computed(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// metaInfo() {
|
|
||||||
// return {
|
|
||||||
// title: this.t("My events") as string,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
|
|
||||||
const monthlyEvents = (
|
const monthlyEvents = (
|
||||||
elements: Eventable[],
|
elements: Eventable[],
|
||||||
revertSort = false
|
revertSort = false
|
||||||
|
@ -413,86 +433,8 @@ const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
||||||
const firstDayOfWeek = computed((): number => {
|
const firstDayOfWeek = computed((): number => {
|
||||||
return dateFnsLocale?.options?.weekStartsOn ?? 0;
|
return dateFnsLocale?.options?.weekStartsOn ?? 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: computed(() => t("My events")),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.participation {
|
|
||||||
margin: 1rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
.upcoming-month,
|
|
||||||
.past-month {
|
|
||||||
text-transform: capitalize;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 100%;
|
|
||||||
content: "";
|
|
||||||
width: calc(100% + 30px);
|
|
||||||
height: 3px;
|
|
||||||
max-width: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-found {
|
|
||||||
margin-top: 2rem;
|
|
||||||
.img-container {
|
|
||||||
background-image: url("/img/pics/event_creation-480w.webp");
|
|
||||||
@media (min-resolution: 2dppx) {
|
|
||||||
& {
|
|
||||||
background-image: url("/img/pics/event_creation-1024w.webp");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
max-width: 450px;
|
|
||||||
height: 300px;
|
|
||||||
box-shadow: 0 0 8px 8px white inset;
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
box-shadow: 0 0 8px 8px #374151 inset;
|
|
||||||
}
|
|
||||||
background-size: cover;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: auto auto 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
// display: grid;
|
|
||||||
// grid-template-areas: "filter" "events";
|
|
||||||
// align-items: start;
|
|
||||||
|
|
||||||
// // @include desktop {
|
|
||||||
// gap: 2rem;
|
|
||||||
// grid-template-columns: 1fr 3fr;
|
|
||||||
// grid-template-areas: "filter events";
|
|
||||||
// // }
|
|
||||||
|
|
||||||
.event-filter {
|
|
||||||
grid-area: filter;
|
|
||||||
|
|
||||||
// @include desktop {
|
|
||||||
// padding: 2rem 1.25rem;
|
|
||||||
// :deep(.field.is-grouped) {
|
|
||||||
// display: block;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
:deep(.field > .field) {
|
|
||||||
margin: 0 auto 1.25rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-filter :deep(.field-body) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.my-events {
|
|
||||||
grid-area: events;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -96,7 +96,9 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
resolve(&User.user_memberships/3)
|
resolve(&User.user_memberships/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:drafts, list_of(:event), description: "The list of draft events this user has created") do
|
field(:drafts, :paginated_event_list,
|
||||||
|
description: "The list of draft events this user has created"
|
||||||
|
) do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated drafts events list"
|
description: "The page in the paginated drafts events list"
|
||||||
|
|
|
@ -465,13 +465,13 @@ defmodule Mobilizon.Events do
|
||||||
|> Page.build_page(page, limit)
|
|> Page.build_page(page, limit)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec list_drafts_for_user(integer, integer | nil, integer | nil) :: [Event.t()]
|
@spec list_drafts_for_user(integer, integer | nil, integer | nil) :: Page.t(Event.t())
|
||||||
def list_drafts_for_user(user_id, page \\ nil, limit \\ nil) do
|
def list_drafts_for_user(user_id, page \\ nil, limit \\ nil) do
|
||||||
Event
|
Event
|
||||||
|> user_events_query(user_id)
|
|> user_events_query(user_id)
|
||||||
|> filter_draft(true)
|
|> filter_draft(true)
|
||||||
|> Page.paginate(page, limit)
|
|> order_by(desc: :updated_at)
|
||||||
|> Repo.all()
|
|> Page.build_page(page, limit)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec is_user_moderator_for_event?(integer | String.t(), integer | String.t()) :: boolean
|
@spec is_user_moderator_for_event?(integer | String.t(), integer | String.t()) :: boolean
|
||||||
|
|
|
@ -85,7 +85,8 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlug do
|
||||||
else
|
else
|
||||||
[
|
[
|
||||||
@script_src,
|
@script_src,
|
||||||
"'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMBmQI=' "
|
"'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMBmQI=' ",
|
||||||
|
"'sha256-zJdRXhLWm9NGI6BFr+sNmHBBrjAdJdFr7MpUq0EwK58=' "
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<link rel="preload" href="/img/shape-3.svg" as="image" />
|
<link rel="preload" href="/img/shape-3.svg" as="image" />
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= tags(assigns) || assigns.tags %>
|
<%= tags(assigns) || assigns.tags %>
|
||||||
<%= Vite.inlined_phx_manifest() %>
|
|
||||||
<%= Vite.vite_client() %>
|
<%= Vite.vite_client() %>
|
||||||
<%= Vite.vite_snippet("src/main.ts") %>
|
<%= Vite.vite_snippet("src/main.ts") %>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -73,7 +73,7 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlugTest do
|
||||||
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
||||||
|
|
||||||
assert csp =~
|
assert csp =~
|
||||||
~r/script-src 'self' 'unsafe-eval' 'sha256-[\w+\/=]*' example.com matomo.example.com ;/
|
~r/script-src 'self' 'unsafe-eval' 'sha256-[\w+\/=]*' 'sha256-[\w+\/=]*' example.com matomo.example.com ;/
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue