Merge branch 'fixes' into 'main'
Prevent AP collection page number being < 1 Closes #1184 et #1209 See merge request framasoft/mobilizon!1340
This commit is contained in:
commit
d03a249d55
@ -41,7 +41,7 @@ There's no lock-in, you can interact with the event without registration.
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We appreciate any contribution to Mobilizon. Check our [CONTRIBUTING](CONTRIBUTING.md) file for more information.
|
We appreciate any contribution to Mobilizon. Check [our contributing page](https://docs.joinmobilizon.org/contribute/) for more information.
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export const GROUP_FOLLOWERS = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const UPDATE_FOLLOWER = gql`
|
export const UPDATE_FOLLOWER = gql`
|
||||||
mutation UpdateFollower($id: ID!, $approved: Boolean) {
|
mutation UpdateFollower($id: ID!, $approved: Boolean!) {
|
||||||
updateFollower(id: $id, approved: $approved) {
|
updateFollower(id: $id, approved: $approved) {
|
||||||
id
|
id
|
||||||
approved
|
approved
|
||||||
|
@ -1446,5 +1446,7 @@
|
|||||||
"By car": "By car",
|
"By car": "By car",
|
||||||
"Select all resources": "Select all resources",
|
"Select all resources": "Select all resources",
|
||||||
"Select this resource": "Select this resource",
|
"Select this resource": "Select this resource",
|
||||||
"You can add resources by using the button above.": "You can add resources by using the button above."
|
"You can add resources by using the button above.": "You can add resources by using the button above.",
|
||||||
|
"{user}'s follow request was accepted": "{user}'s follow request was accepted",
|
||||||
|
"{user}'s follow request was rejected": "{user}'s follow request was rejected"
|
||||||
}
|
}
|
@ -1443,5 +1443,7 @@
|
|||||||
"By car": "En voiture",
|
"By car": "En voiture",
|
||||||
"Select all resources": "Sélectionner toutes les ressources",
|
"Select all resources": "Sélectionner toutes les ressources",
|
||||||
"Select this resource": "Sélectionner cette ressource",
|
"Select this resource": "Sélectionner cette ressource",
|
||||||
"You can add resources by using the button above.": "Vous pouvez ajouter des ressources en utilisant le bouton au dessus."
|
"You can add resources by using the button above.": "Vous pouvez ajouter des ressources en utilisant le bouton au dessus.",
|
||||||
|
"{user}'s follow request was accepted": "La demande de suivi de {user} a été acceptée",
|
||||||
|
"{user}'s follow request was rejected": "La demande de suivi de {user} a été rejetée"
|
||||||
}
|
}
|
||||||
|
@ -1297,9 +1297,11 @@ const maximumAttendeeCapacity = computed({
|
|||||||
return eventOptions.value.maximumAttendeeCapacity.toString();
|
return eventOptions.value.maximumAttendeeCapacity.toString();
|
||||||
},
|
},
|
||||||
set(newMaximumAttendeeCapacity: string) {
|
set(newMaximumAttendeeCapacity: string) {
|
||||||
eventOptions.value.maximumAttendeeCapacity = parseInt(newMaximumAttendeeCapacity);
|
eventOptions.value.maximumAttendeeCapacity = parseInt(
|
||||||
}
|
newMaximumAttendeeCapacity
|
||||||
})
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
const propsUUID = computed(() => props.uuid)
|
const propsUUID = computed(() => props.uuid);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
event,
|
event,
|
||||||
@ -308,8 +308,8 @@ const {
|
|||||||
} = useFetchEvent(props.uuid);
|
} = useFetchEvent(props.uuid);
|
||||||
|
|
||||||
watch(propsUUID, (newUUid) => {
|
watch(propsUUID, (newUUid) => {
|
||||||
refetchEvent({ uuid: newUUid })
|
refetchEvent({ uuid: newUUid });
|
||||||
})
|
});
|
||||||
|
|
||||||
const eventId = computed(() => event.value?.id);
|
const eventId = computed(() => event.value?.id);
|
||||||
const { currentActor } = useCurrentActorClient();
|
const { currentActor } = useCurrentActorClient();
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
{
|
{
|
||||||
name: RouteName.GROUP_SETTINGS,
|
name: RouteName.GROUP_SETTINGS,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
text: $t('Settings'),
|
text: t('Settings'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: RouteName.GROUP_FOLLOWERS_SETTINGS,
|
name: RouteName.GROUP_FOLLOWERS_SETTINGS,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
text: $t('Followers'),
|
text: t('Followers'),
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
@ -25,9 +25,9 @@
|
|||||||
class="container mx-auto section"
|
class="container mx-auto section"
|
||||||
v-if="group && isCurrentActorAGroupAdmin && followers"
|
v-if="group && isCurrentActorAGroupAdmin && followers"
|
||||||
>
|
>
|
||||||
<h1>{{ $t("Group Followers") }} ({{ followers.total }})</h1>
|
<h1>{{ t("Group Followers") }} ({{ followers.total }})</h1>
|
||||||
<o-field :label="$t('Status')" horizontal>
|
<o-field :label="t('Status')" horizontal>
|
||||||
<o-switch v-model="pending">{{ $t("Pending") }}</o-switch>
|
<o-switch v-model="pending">{{ t("Pending") }}</o-switch>
|
||||||
</o-field>
|
</o-field>
|
||||||
<o-table
|
<o-table
|
||||||
:data="followers.elements"
|
:data="followers.elements"
|
||||||
@ -37,10 +37,10 @@
|
|||||||
backend-pagination
|
backend-pagination
|
||||||
v-model:current-page="page"
|
v-model:current-page="page"
|
||||||
:pagination-simple="true"
|
:pagination-simple="true"
|
||||||
:aria-next-label="$t('Next page')"
|
:aria-next-label="t('Next page')"
|
||||||
:aria-previous-label="$t('Previous page')"
|
:aria-previous-label="t('Previous page')"
|
||||||
:aria-page-label="$t('Page')"
|
:aria-page-label="t('Page')"
|
||||||
:aria-current-label="$t('Current page')"
|
:aria-current-label="t('Current page')"
|
||||||
:total="followers.total"
|
:total="followers.total"
|
||||||
:per-page="FOLLOWERS_PER_PAGE"
|
:per-page="FOLLOWERS_PER_PAGE"
|
||||||
backend-sorting
|
backend-sorting
|
||||||
@ -51,7 +51,7 @@
|
|||||||
>
|
>
|
||||||
<o-table-column
|
<o-table-column
|
||||||
field="actor.preferredUsername"
|
field="actor.preferredUsername"
|
||||||
:label="$t('Follower')"
|
:label="t('Follower')"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
>
|
>
|
||||||
<article class="flex gap-1">
|
<article class="flex gap-1">
|
||||||
@ -76,39 +76,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</o-table-column>
|
</o-table-column>
|
||||||
<o-table-column field="insertedAt" :label="$t('Date')" v-slot="props">
|
<o-table-column field="insertedAt" :label="t('Date')" v-slot="props">
|
||||||
<span class="has-text-centered">
|
<span class="has-text-centered">
|
||||||
{{ formatDateString(props.row.insertedAt) }}<br />{{
|
{{ formatDateString(props.row.insertedAt) }}<br />{{
|
||||||
formatTimeString(props.row.insertedAt)
|
formatTimeString(props.row.insertedAt)
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</o-table-column>
|
</o-table-column>
|
||||||
<o-table-column field="actions" :label="$t('Actions')" v-slot="props">
|
<o-table-column field="actions" :label="t('Actions')" v-slot="props">
|
||||||
<div class="buttons">
|
<div class="flex gap-2">
|
||||||
<o-button
|
<o-button
|
||||||
v-if="!props.row.approved"
|
v-if="!props.row.approved"
|
||||||
@click="updateFollower(props.row, true)"
|
@click="updateFollower(props.row, true)"
|
||||||
icon-left="check"
|
icon-left="check"
|
||||||
variant="success"
|
variant="success"
|
||||||
>{{ $t("Accept") }}</o-button
|
>{{ t("Accept") }}</o-button
|
||||||
>
|
>
|
||||||
<o-button
|
<o-button
|
||||||
@click="updateFollower(props.row, false)"
|
@click="updateFollower(props.row, false)"
|
||||||
icon-left="close"
|
icon-left="close"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
>{{ $t("Reject") }}</o-button
|
>{{ t("Reject") }}</o-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</o-table-column>
|
</o-table-column>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<empty-content icon="account" inline>
|
<empty-content icon="account" inline>
|
||||||
{{ $t("No follower matches the filters") }}
|
{{ t("No follower matches the filters") }}
|
||||||
</empty-content>
|
</empty-content>
|
||||||
</template>
|
</template>
|
||||||
</o-table>
|
</o-table>
|
||||||
</section>
|
</section>
|
||||||
<o-notification v-else-if="!loading && group">
|
<o-notification v-else-if="!loading && group">
|
||||||
{{ $t("You are not an administrator for this group.") }}
|
{{ t("You are not an administrator for this group.") }}
|
||||||
</o-notification>
|
</o-notification>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -164,6 +164,7 @@ const { t } = useI18n({ useScope: "global" });
|
|||||||
useHead({ title: computed(() => t("Group Followers")) });
|
useHead({ title: computed(() => t("Group Followers")) });
|
||||||
|
|
||||||
const loadMoreFollowers = async (): Promise<void> => {
|
const loadMoreFollowers = async (): Promise<void> => {
|
||||||
|
console.debug("load more followers");
|
||||||
await fetchMore({
|
await fetchMore({
|
||||||
// New variables
|
// New variables
|
||||||
variables: {
|
variables: {
|
||||||
@ -183,6 +184,12 @@ const { onDone, onError, mutate } = useMutation<{ updateFollower: IFollower }>(
|
|||||||
refetchQueries: [
|
refetchQueries: [
|
||||||
{
|
{
|
||||||
query: GROUP_FOLLOWERS,
|
query: GROUP_FOLLOWERS,
|
||||||
|
variables: {
|
||||||
|
name: usernameWithDomain(group.value),
|
||||||
|
followersPage: page.value,
|
||||||
|
followersLimit: FOLLOWERS_PER_PAGE,
|
||||||
|
approved: !pending.value,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -192,11 +199,11 @@ onDone(({ data }) => {
|
|||||||
const follower = data?.updateFollower;
|
const follower = data?.updateFollower;
|
||||||
const message =
|
const message =
|
||||||
data?.updateFollower.approved === true
|
data?.updateFollower.approved === true
|
||||||
? t("@{username}'s follow request was accepted", {
|
? t("{user}'s follow request was accepted", {
|
||||||
username: follower?.actor.preferredUsername,
|
user: displayName(follower?.actor),
|
||||||
})
|
})
|
||||||
: t("@{username}'s follow request was rejected", {
|
: t("{user}'s follow request was rejected", {
|
||||||
username: follower?.actor.preferredUsername,
|
user: displayName(follower?.actor),
|
||||||
});
|
});
|
||||||
notifier?.success(message);
|
notifier?.success(message);
|
||||||
});
|
});
|
||||||
|
@ -191,6 +191,7 @@ defmodule Mobilizon.Web.ActivityPubController do
|
|||||||
|
|
||||||
defp actor_collection(conn, collection, %{"name" => name, "page" => page}) do
|
defp actor_collection(conn, collection, %{"name" => name, "page" => page}) do
|
||||||
with {page, ""} <- Integer.parse(page),
|
with {page, ""} <- Integer.parse(page),
|
||||||
|
page <- max(page, 1),
|
||||||
%Actor{} = actor <- Actors.get_local_actor_by_name_with_preload(name) do
|
%Actor{} = actor <- Actors.get_local_actor_by_name_with_preload(name) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|
@ -243,6 +243,34 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
|
|||||||
assert length(result["orderedItems"]) == 5
|
assert length(result["orderedItems"]) == 5
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it can't be called for a page < 1", %{conn: conn} do
|
||||||
|
actor = insert(:actor, visibility: :public)
|
||||||
|
|
||||||
|
Enum.each(1..15, fn _ ->
|
||||||
|
insert(:event, organizer_actor: actor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get(Actor.build_url(actor.preferred_username, :outbox))
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert length(result["first"]["orderedItems"]) == 10
|
||||||
|
assert result["totalItems"] == 15
|
||||||
|
|
||||||
|
page_0_result =
|
||||||
|
conn
|
||||||
|
|> get(Actor.build_url(actor.preferred_username, :outbox, page: 0))
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
page_1_result =
|
||||||
|
conn
|
||||||
|
|> get(Actor.build_url(actor.preferred_username, :outbox, page: 1))
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert page_0_result == page_1_result
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns an empty collection if the actor has private visibility", %{conn: conn} do
|
test "it returns an empty collection if the actor has private visibility", %{conn: conn} do
|
||||||
actor = insert(:actor, visibility: :private)
|
actor = insert(:actor, visibility: :private)
|
||||||
insert(:event, organizer_actor: actor)
|
insert(:event, organizer_actor: actor)
|
||||||
|
Loading…
Reference in New Issue
Block a user