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:
Thomas Citharel 2022-11-02 11:10:48 +00:00
commit 48da35ee41
8 changed files with 81 additions and 134 deletions

View file

@ -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
}
} }
} }
} }

View file

@ -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>;

View file

@ -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>

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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