Merge branch 'main' of https://framagit.org/framasoft/mobilizon into fomo-3.1.0

This commit is contained in:
johndoe4 2023-06-13 01:05:41 +02:00
commit f12e39c907
23 changed files with 153 additions and 73 deletions

View file

@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 3.1.1 (2023-06-02)
### Features
* **anti-spam:** allow to only scan for spam in profiles or events ([c971287](https://framagit.org/framasoft/mobilizon/-/commit/c971287624913c8555fe288af0df1175701e6209))
### Bug Fixes
* **front:** fix group settings getting unresponsive because of reactive bug ([f1e119c](https://framagit.org/framasoft/mobilizon/-/commit/f1e119cb7ad580dfab73de3083f20a7303822888)), closes [#1298](https://framagit.org/framasoft/mobilizon/-/issues/1298)
* **search:** fix global search sorting ([39e24c3](https://framagit.org/framasoft/mobilizon/-/commit/39e24c328a21f7058e4d2526e13eae85e39bae86)), closes [#1297](https://framagit.org/framasoft/mobilizon/-/issues/1297)
## 3.1.0 (2023-05-31) ## 3.1.0 (2023-05-31)
### Features ### Features
@ -84,11 +95,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* **i18n:** fix Swedish translations error that prevented Participate button from showing up ([643a5b5](https://framagit.org/framasoft/mobilizon/-/commit/643a5b5921f91fed6a9f674c0ab3a36bf2d05835)), closes [#1281](https://framagit.org/framasoft/mobilizon/-/issues/1281) * **i18n:** fix Swedish translations error that prevented Participate button from showing up ([643a5b5](https://framagit.org/framasoft/mobilizon/-/commit/643a5b5921f91fed6a9f674c0ab3a36bf2d05835)), closes [#1281](https://framagit.org/framasoft/mobilizon/-/issues/1281)
* **rich media:** fix error handling when resource preview URL leads to empty parsed data ([850b4e2](https://framagit.org/framasoft/mobilizon/-/commit/850b4e2a735e335c4737caa8b60e190613e778ef)), closes [#1279](https://framagit.org/framasoft/mobilizon/-/issues/1279) * **rich media:** fix error handling when resource preview URL leads to empty parsed data ([850b4e2](https://framagit.org/framasoft/mobilizon/-/commit/850b4e2a735e335c4737caa8b60e190613e778ef)), closes [#1279](https://framagit.org/framasoft/mobilizon/-/issues/1279)
* **sharepostmodal:** only show the share warning message if the post is accessible by link ([8e626dc](https://framagit.org/framasoft/mobilizon/-/commit/8e626dce7807640a89770e50ca2621d34d6a5d97)) * **sharepostmodal:** only show the share warning message if the post is accessible by link ([8e626dc](https://framagit.org/framasoft/mobilizon/-/commit/8e626dce7807640a89770e50ca2621d34d6a5d97))
* **apps:** fix device flow authorization process ([9a457fb](https://framagit.org/framasoft/mobilizon/commits/9a457fb011b77b27dc465f1bc7327a08f554ccfb)) * **apps:** fix device flow authorization process ([9a457fb](https://framagit.org/framasoft/mobilizon/-/commit/9a457fb011b77b27dc465f1bc7327a08f554ccfb))
* **apps:** fix typo in redirect_uri parameter ([5664625](https://framagit.org/framasoft/mobilizon/commits/5664625c1c57ccba947400475414c1301d4bf955)) * **apps:** fix typo in redirect_uri parameter ([5664625](https://framagit.org/framasoft/mobilizon/-/commit/5664625c1c57ccba947400475414c1301d4bf955))
* **apps:** show scope from device activation in authorize device view ([c9d2074](https://framagit.org/framasoft/mobilizon/commits/c9d20748a4dd3e0687515f4776335d0ec9bdfcdc)) * **apps:** show scope from device activation in authorize device view ([c9d2074](https://framagit.org/framasoft/mobilizon/-/commit/c9d20748a4dd3e0687515f4776335d0ec9bdfcdc))
* **front:** fix homepage event and groups cards snapping ([8809db5](https://framagit.org/framasoft/mobilizon/commits/8809db582ccf45fcd477f46dcf70e106720626a8)) * **front:** fix homepage event and groups cards snapping ([8809db5](https://framagit.org/framasoft/mobilizon/-/commit/8809db582ccf45fcd477f46dcf70e106720626a8))
* **front:** fix selecting addresses in autocomplete ([e0488dd](https://framagit.org/framasoft/mobilizon/commits/e0488dd87ffc0184162a2ff67a13717e6263d56d)) * **front:** fix selecting addresses in autocomplete ([e0488dd](https://framagit.org/framasoft/mobilizon/-/commit/e0488dd87ffc0184162a2ff67a13717e6263d56d))
* include user role in moderator role ([c4d6019](https://framagit.org/framasoft/mobilizon/-/commit/c4d60194a6900a3f9430355c5fbb346d910e4df6)) * include user role in moderator role ([c4d6019](https://framagit.org/framasoft/mobilizon/-/commit/c4d60194a6900a3f9430355c5fbb346d910e4df6))
@ -96,11 +107,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Bug Fixes ### Bug Fixes
* **apps:** fix device flow authorization process ([9a457fb](https://framagit.org/framasoft/mobilizon/commits/9a457fb011b77b27dc465f1bc7327a08f554ccfb)) * **apps:** fix device flow authorization process ([9a457fb](https://framagit.org/framasoft/mobilizon/-/commit/9a457fb011b77b27dc465f1bc7327a08f554ccfb))
* **apps:** fix typo in redirect_uri parameter ([5664625](https://framagit.org/framasoft/mobilizon/commits/5664625c1c57ccba947400475414c1301d4bf955)) * **apps:** fix typo in redirect_uri parameter ([5664625](https://framagit.org/framasoft/mobilizon/-/commit/5664625c1c57ccba947400475414c1301d4bf955))
* **apps:** show scope from device activation in authorize device view ([c9d2074](https://framagit.org/framasoft/mobilizon/commits/c9d20748a4dd3e0687515f4776335d0ec9bdfcdc)) * **apps:** show scope from device activation in authorize device view ([c9d2074](https://framagit.org/framasoft/mobilizon/-/commit/c9d20748a4dd3e0687515f4776335d0ec9bdfcdc))
* **front:** fix homepage event and groups cards snapping ([8809db5](https://framagit.org/framasoft/mobilizon/commits/8809db582ccf45fcd477f46dcf70e106720626a8)) * **front:** fix homepage event and groups cards snapping ([8809db5](https://framagit.org/framasoft/mobilizon/-/commit/8809db582ccf45fcd477f46dcf70e106720626a8))
* **front:** fix selecting addresses in autocomplete ([e0488dd](https://framagit.org/framasoft/mobilizon/commits/e0488dd87ffc0184162a2ff67a13717e6263d56d)) * **front:** fix selecting addresses in autocomplete ([e0488dd](https://framagit.org/framasoft/mobilizon/-/commit/e0488dd87ffc0184162a2ff67a13717e6263d56d))
## 3.1.0-rc.1 (2023-05-30) ## 3.1.0-rc.1 (2023-05-30)

View file

@ -306,7 +306,7 @@ config :mobilizon, Oban,
crontab: [ crontab: [
{"@hourly", Mobilizon.Service.Workers.BuildSiteMap, queue: :background}, {"@hourly", Mobilizon.Service.Workers.BuildSiteMap, queue: :background},
{"17 4 * * *", Mobilizon.Service.Workers.RefreshGroups, queue: :background}, {"17 4 * * *", Mobilizon.Service.Workers.RefreshGroups, queue: :background},
{"36 * * * *", Mobilizon.Service.Workers.RefreshInstances, queue: :background}, {"36 3 * * *", Mobilizon.Service.Workers.RefreshInstances, queue: :background},
{"@hourly", Mobilizon.Service.Workers.CleanOrphanMediaWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.CleanOrphanMediaWorker, queue: :background},
{"@hourly", Mobilizon.Service.Workers.CleanUnconfirmedUsersWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.CleanUnconfirmedUsersWorker, queue: :background},
{"@hourly", Mobilizon.Service.Workers.ExportCleanerWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.ExportCleanerWorker, queue: :background},

View file

@ -1,6 +1,6 @@
{ {
"name": "mobilizon", "name": "mobilizon",
"version": "3.1.0", "version": "3.1.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View file

@ -295,7 +295,7 @@ export const JOIN_EVENT = gql`
$email: String $email: String
$message: String $message: String
$locale: String $locale: String
$timezone: String $timezone: Timezone
) { ) {
joinEvent( joinEvent(
eventId: $eventId eventId: $eventId

View file

@ -151,7 +151,7 @@ export const LOGGED_USER_TIMEZONE = gql`
export const SET_USER_SETTINGS = gql` export const SET_USER_SETTINGS = gql`
mutation SetUserSettings( mutation SetUserSettings(
$timezone: String $timezone: Timezone
$notificationOnDay: Boolean $notificationOnDay: Boolean
$notificationEachWeek: Boolean $notificationEachWeek: Boolean
$notificationBeforeEvent: Boolean $notificationBeforeEvent: Boolean

View file

@ -16,7 +16,7 @@
"A cookie is a small file containing information that is sent to your computer when you visit a website. When you visit the site again, the cookie allows that site to recognize your browser. Cookies may store user preferences and other information. You can configure your browser to refuse all cookies. However, this may result in some website features or services partially working. Local storage works the same way but allows you to store more data.": "Kolačić je mala datoteka koja sadrži sve informacije poslane vašem računalu kada posjetite stranicu. Kada ju opet posjetite, kolačić omogućuje stranici da prepozna vaš preglednik. Kolačić može sadržavati vaše postavke ili druge informacije. Možete postaviti svoj preglednik da blokira sve kolačiće, ali to može dovesti do djelomičnog kvara nekih funkcija stranice. Lokalno spremište radi na isti način ali vam dopušta da spremite više podataka.", "A cookie is a small file containing information that is sent to your computer when you visit a website. When you visit the site again, the cookie allows that site to recognize your browser. Cookies may store user preferences and other information. You can configure your browser to refuse all cookies. However, this may result in some website features or services partially working. Local storage works the same way but allows you to store more data.": "Kolačić je mala datoteka koja sadrži sve informacije poslane vašem računalu kada posjetite stranicu. Kada ju opet posjetite, kolačić omogućuje stranici da prepozna vaš preglednik. Kolačić može sadržavati vaše postavke ili druge informacije. Možete postaviti svoj preglednik da blokira sve kolačiće, ali to može dovesti do djelomičnog kvara nekih funkcija stranice. Lokalno spremište radi na isti način ali vam dopušta da spremite više podataka.",
"A discussion has been created or updated": "Jedna diskusija je stvorena ili aktualizirana", "A discussion has been created or updated": "Jedna diskusija je stvorena ili aktualizirana",
"A federated software": "Federaliziran softver", "A federated software": "Federaliziran softver",
"A fediverse account URL to follow for event updates": "", "A fediverse account URL to follow for event updates": "URL Fediverse računa za praćenje aktualiziranja događaja",
"A few lines about your group": "Par redaka o tvojoj grupi", "A few lines about your group": "Par redaka o tvojoj grupi",
"A link to a page presenting the event schedule": "Poveznica na stranicu s rasporedom događaja", "A link to a page presenting the event schedule": "Poveznica na stranicu s rasporedom događaja",
"A link to a page presenting the price options": "Poveznica na stranicu sa cijenama", "A link to a page presenting the price options": "Poveznica na stranicu sa cijenama",
@ -32,7 +32,7 @@
"A practical tool": "Praktični alat", "A practical tool": "Praktični alat",
"A resource has been created or updated": "Jedan resurs je stvoren ili aktualiziran", "A resource has been created or updated": "Jedan resurs je stvoren ili aktualiziran",
"A short tagline for your instance homepage. Defaults to \"Gather ⋅ Organize ⋅ Mobilize\"": "Kartki slogan za naslovnu stranicu instance. Zadan je na \"Skupi ⋅ Organiziraj ⋅ Mobiliziraj\"", "A short tagline for your instance homepage. Defaults to \"Gather ⋅ Organize ⋅ Mobilize\"": "Kartki slogan za naslovnu stranicu instance. Zadan je na \"Skupi ⋅ Organiziraj ⋅ Mobiliziraj\"",
"A twitter account handle to follow for event updates": "", "A twitter account handle to follow for event updates": "Twitter račun za praćenje aktualiziranja događaja",
"A user-friendly, emancipatory and ethical tool for gathering, organising, and mobilising.": "Emancipacijski i etični alat za okupljanje, organiziranje, i mobiliziranje, sa prijateljskim korisničkim sučeljem.", "A user-friendly, emancipatory and ethical tool for gathering, organising, and mobilising.": "Emancipacijski i etični alat za okupljanje, organiziranje, i mobiliziranje, sa prijateljskim korisničkim sučeljem.",
"A validation email was sent to {email}": "E-mail za ovjeru je poslan na {email}", "A validation email was sent to {email}": "E-mail za ovjeru je poslan na {email}",
"API": "API", "API": "API",
@ -86,7 +86,7 @@
"All the places have already been taken": "Sva mjesta su već zauzeta", "All the places have already been taken": "Sva mjesta su već zauzeta",
"Allow all comments from users with accounts": "Dozvoli sve komentare prijavljenih korisnika", "Allow all comments from users with accounts": "Dozvoli sve komentare prijavljenih korisnika",
"Allow registrations": "Dozvoli registracije", "Allow registrations": "Dozvoli registracije",
"An URL to an external ticketing platform": "", "An URL to an external ticketing platform": "URL-adresa eksterne platforme za prodaju karata",
"An error has occured while refreshing the page.": "Dogodila se greška prilikom aktualiziranja stranice.", "An error has occured while refreshing the page.": "Dogodila se greška prilikom aktualiziranja stranice.",
"An error has occured. Sorry about that. You may try to reload the page.": "Desila se greška! Isprike. Možete probati ponovno učitati stranicu.", "An error has occured. Sorry about that. You may try to reload the page.": "Desila se greška! Isprike. Možete probati ponovno učitati stranicu.",
"An ethical alternative": "Etična alternativa", "An ethical alternative": "Etična alternativa",
@ -98,6 +98,7 @@
"An event from one of my groups has been published": "Događaj jedne od mojih grupa je objavljen", "An event from one of my groups has been published": "Događaj jedne od mojih grupa je objavljen",
"An event from one of my groups has been updated or deleted": "Događaj jedne od mojih grupa je aktualiziran ili izbrisan", "An event from one of my groups has been updated or deleted": "Događaj jedne od mojih grupa je aktualiziran ili izbrisan",
"An instance is an installed version of the Mobilizon software running on a server. An instance can be run by anyone using the {mobilizon_software} or other federated apps, aka the “fediverse”. This instance's name is {instance_name}. Mobilizon is a federated network of multiple instances (just like email servers), users registered on different instances may communicate even though they didn't register on the same instance.": "Instanca je instalirana verzija Mobilizon softvera koja radi na serveru. Instancu može postaviti bilo tko, tko koristi {mobilizon_software} ili druge federalizirane aplikacije, poznato i kao „fediverse”. Ime ove instance je {instance_name}. Mobilizon je federalizirana mreža kaj se sastoji od više instanca (kao e-mail serveri) gdje korisnici, koji su registrirani na različitim instancama mogu komunicirati iako se ne nalaze na istoj instanci.", "An instance is an installed version of the Mobilizon software running on a server. An instance can be run by anyone using the {mobilizon_software} or other federated apps, aka the “fediverse”. This instance's name is {instance_name}. Mobilizon is a federated network of multiple instances (just like email servers), users registered on different instances may communicate even though they didn't register on the same instance.": "Instanca je instalirana verzija Mobilizon softvera koja radi na serveru. Instancu može postaviti bilo tko, tko koristi {mobilizon_software} ili druge federalizirane aplikacije, poznato i kao „fediverse”. Ime ove instance je {instance_name}. Mobilizon je federalizirana mreža kaj se sastoji od više instanca (kao e-mail serveri) gdje korisnici, koji su registrirani na različitim instancama mogu komunicirati iako se ne nalaze na istoj instanci.",
"An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events, automatically and remotely.": "„Sučelje za programiranje aplikacija” ili „API” je komunikacijski protokol koji omogućuje komponentama softvera da međusobno komuniciraju. Mobilizon API, na primjer, može dozvoliti softverskim alatima trećih strana da komuniciraju s Mobilizon instancama za izvođenje određenih radnji, kao što je objavljivanje događaja, automatski i na daljinski način.",
"And {number} comments": "I {number} komentara", "And {number} comments": "I {number} komentara",
"Announcements and mentions notifications are always sent straight away.": "Najave i obavijesti o spominjanjima uvijek se šalju odmah.", "Announcements and mentions notifications are always sent straight away.": "Najave i obavijesti o spominjanjima uvijek se šalju odmah.",
"Anonymous participant": "Anonimni sudionik", "Anonymous participant": "Anonimni sudionik",
@ -158,7 +159,7 @@
"Cancel": "Otkaži", "Cancel": "Otkaži",
"Cancel anonymous participation": "Otkaži anonimno sudjelovanje", "Cancel anonymous participation": "Otkaži anonimno sudjelovanje",
"Cancel creation": "Otkaži stvaranje", "Cancel creation": "Otkaži stvaranje",
"Cancel discussion title edition": "", "Cancel discussion title edition": "Prekini mijenjanje naslova diskusije",
"Cancel edition": "Otkaži uređivanje", "Cancel edition": "Otkaži uređivanje",
"Cancel follow request": "", "Cancel follow request": "",
"Cancel membership request": "", "Cancel membership request": "",
@ -297,7 +298,7 @@
"Do you really want to suspend this account? All of the user's profiles will be deleted.": "Stvarno želiš suspendirati ovaj račun? Svi korisnički profili će se izbrisati.", "Do you really want to suspend this account? All of the user's profiles will be deleted.": "Stvarno želiš suspendirati ovaj račun? Svi korisnički profili će se izbrisati.",
"Do you wish to {create_event} or {explore_events}?": "Želite li {create_event} ili {explore_event}?", "Do you wish to {create_event} or {explore_events}?": "Želite li {create_event} ili {explore_event}?",
"Do you wish to {create_group} or {explore_groups}?": "Želite li {create_group} ili {explore_groups}?", "Do you wish to {create_group} or {explore_groups}?": "Želite li {create_group} ili {explore_groups}?",
"Does the event needs to be confirmed later or is it cancelled?": "", "Does the event needs to be confirmed later or is it cancelled?": "Treba li događaj kasnije potvrditi ili je otkazan?",
"Domain": "Domena", "Domain": "Domena",
"Draft": "Skica", "Draft": "Skica",
"Drafts": "Skice", "Drafts": "Skice",
@ -684,7 +685,7 @@
"On {instance} and other federated instances": "Na {instance} i drugim federaliziranim instancama", "On {instance} and other federated instances": "Na {instance} i drugim federaliziranim instancama",
"Online": "Online", "Online": "Online",
"Online events": "Online događaji", "Online events": "Online događaji",
"Online ticketing": "", "Online ticketing": "Online prodaja karata",
"Only accessible through link": "Dostupno samo preko poveznice", "Only accessible through link": "Dostupno samo preko poveznice",
"Only accessible through link (private)": "Dostupno samo kroz poveznicu (privatno)", "Only accessible through link (private)": "Dostupno samo kroz poveznicu (privatno)",
"Only accessible to members of the group": "Dostupno samo članovima grupe", "Only accessible to members of the group": "Dostupno samo članovima grupe",
@ -1075,7 +1076,7 @@
"Upcoming events from your groups": "", "Upcoming events from your groups": "",
"Update": "Ažuriraj", "Update": "Ažuriraj",
"Update app": "Aktualiziraj program", "Update app": "Aktualiziraj program",
"Update discussion title": "", "Update discussion title": "Aktualiziraj naslov diskusije",
"Update event {name}": "Ažuriraj događaj {name}", "Update event {name}": "Ažuriraj događaj {name}",
"Update group": "Ažuriraj grupu", "Update group": "Ažuriraj grupu",
"Update my event": "Ažuriraj moj događaj", "Update my event": "Ažuriraj moj događaj",
@ -1136,7 +1137,7 @@
"Whether the event is accessible with a wheelchair": "Je li postoji pristup za osobe u invalidskim kolicima", "Whether the event is accessible with a wheelchair": "Je li postoji pristup za osobe u invalidskim kolicima",
"Whether the event is interpreted in sign language": "Je li se događaj prevodi na znakovni jezik", "Whether the event is interpreted in sign language": "Je li se događaj prevodi na znakovni jezik",
"Whether the event live video is subtitled": "Je li video događaja uživo titlovan", "Whether the event live video is subtitled": "Je li video događaja uživo titlovan",
"Who can post a comment?": "", "Who can post a comment?": "Tko smije komentirati?",
"Who can view this event and participate": "Tko može vidjeti ovaj događaj i sudjelovati", "Who can view this event and participate": "Tko može vidjeti ovaj događaj i sudjelovati",
"Who can view this post": "Tko može vidjeti ovu objavu", "Who can view this post": "Tko može vidjeti ovu objavu",
"Who published {number} events": "Koji su objavili {number} događaja", "Who published {number} events": "Koji su objavili {number} događaja",

View file

@ -426,7 +426,9 @@
> >
<option <option
v-for="timezone in groupTimezones" v-for="timezone in groupTimezones"
:value="`${group}/${timezone}`" :value="
group === t('Other') ? timezone : `${group}/${timezone}`
"
:key="timezone" :key="timezone"
> >
{{ sanitizeTimezone(timezone) }} {{ sanitizeTimezone(timezone) }}

View file

@ -344,11 +344,11 @@ const canShowCopyButton = computed((): boolean => {
}); });
const currentAddress = computed({ const currentAddress = computed({
get(): IAddress { get(): IAddress | null {
return new Address(editableGroup.value?.physicalAddress); return editableGroup.value?.physicalAddress ?? null;
}, },
set(address: IAddress) { set(address: IAddress | null) {
if (editableGroup.value) { if (editableGroup.value && address) {
editableGroup.value = { editableGroup.value = {
...editableGroup.value, ...editableGroup.value,
physicalAddress: address, physicalAddress: address,

View file

@ -73,7 +73,7 @@
<tr v-for="subType in notificationType.subtypes" :key="subType.id"> <tr v-for="subType in notificationType.subtypes" :key="subType.id">
<td v-for="(method, key) in notificationMethods" :key="key"> <td v-for="(method, key) in notificationMethods" :key="key">
<o-checkbox <o-checkbox
:value="notificationValues[subType.id][key].enabled" :modelValue="notificationValues[subType.id][key].enabled"
@update:modelValue=" @update:modelValue="
(e: boolean) => (e: boolean) =>
updateNotificationValue({ updateNotificationValue({
@ -645,7 +645,7 @@ const dialog = inject<Dialog>("dialog");
const openRegenerateFeedTokensConfirmation = () => { const openRegenerateFeedTokensConfirmation = () => {
dialog?.confirm({ dialog?.confirm({
type: "warning", variant: "warning",
title: t("Regenerate new links") as string, title: t("Regenerate new links") as string,
message: t( message: t(
"You'll need to change the URLs where there were previously entered." "You'll need to change the URLs where there were previously entered."

View file

@ -22,7 +22,9 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
| {:error, | {:error,
:invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json} :invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json}
def fetch(url, options \\ []) do def fetch(url, options \\ []) do
on_behalf_of = Keyword.get(options, :on_behalf_of, Relay.get_actor()) Logger.debug("Fetching #{url} with AP Fetcher")
on_behalf_of = Keyword.get(options, :on_behalf_of, actor_relay())
Logger.debug("Fetching on behalf of #{inspect(on_behalf_of.url)}")
date = Signature.generate_date_header() date = Signature.generate_date_header()
headers = headers =
@ -32,29 +34,8 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
client = ActivityPubClient.client(headers: headers) client = ActivityPubClient.client(headers: headers)
if address_valid?(url) do if address_valid?(url) and url != on_behalf_of.url do
case ActivityPubClient.get(client, url) do do_fetch(url, client)
{:ok, %Tesla.Env{body: data, status: code}} when code in 200..299 and is_map(data) ->
{:ok, data}
{:ok, %Tesla.Env{status: 410}} ->
Logger.debug("Resource at #{url} is 410 Gone")
{:error, :http_gone}
{:ok, %Tesla.Env{status: 404}} ->
Logger.debug("Resource at #{url} is 404 Gone")
{:error, :http_not_found}
{:ok, %Tesla.Env{body: data}} when is_binary(data) ->
{:error, :content_not_json}
{:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code #{inspect(res)}")
{:error, :http_error}
{:error, err} ->
{:error, err}
end
else else
{:error, :invalid_url} {:error, :invalid_url}
end end
@ -169,4 +150,38 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
%URI{host: host, scheme: scheme} = URI.parse(address) %URI{host: host, scheme: scheme} = URI.parse(address)
is_valid_string(host) and is_valid_string(scheme) is_valid_string(host) and is_valid_string(scheme)
end end
defp actor_relay do
Relay.get_actor()
end
@spec do_fetch(String.t(), Tesla.Client.t()) ::
{:ok, map()}
| {:error,
:invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json}
defp do_fetch(url, client) do
case ActivityPubClient.get(client, url) do
{:ok, %Tesla.Env{body: data, status: code}} when code in 200..299 and is_map(data) ->
Logger.debug("Found the following from ActivityPubClient fetch: #{inspect(data)}")
{:ok, data}
{:ok, %Tesla.Env{status: 410}} ->
Logger.debug("Resource at #{url} is 410 Gone")
{:error, :http_gone}
{:ok, %Tesla.Env{status: 404}} ->
Logger.debug("Resource at #{url} is 404 Gone")
{:error, :http_not_found}
{:ok, %Tesla.Env{body: data}} when is_binary(data) ->
{:error, :content_not_json}
{:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code #{inspect(res)}")
{:error, :http_error}
{:error, err} ->
{:error, err}
end
end
end end

View file

@ -55,7 +55,7 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do
@type fetch_actor_errors :: ActivityPubActor.make_actor_errors() | fetch_collection_errors() @type fetch_actor_errors :: ActivityPubActor.make_actor_errors() | fetch_collection_errors()
@spec fetch_group(String.t(), Actor.t()) :: :ok | {:error, fetch_actor_errors} @spec fetch_group(String.t(), Actor.t()) :: :ok | {:error, fetch_actor_errors}
def fetch_group(group_url, %Actor{} = on_behalf_of) do def fetch_group(group_url, %Actor{} = on_behalf_of) when is_binary(group_url) do
Logger.debug("Fetching group #{group_url}") Logger.debug("Fetching group #{group_url}")
case ActivityPubActor.make_actor_from_url(group_url, on_behalf_of: on_behalf_of) do case ActivityPubActor.make_actor_from_url(group_url, on_behalf_of: on_behalf_of) do

View file

@ -23,7 +23,13 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
def init do def init do
# Wait for everything to settle. # Wait for everything to settle.
Process.sleep(1000 * 5) Process.sleep(1000 * 5)
get_actor() relay = get_actor()
unless Regex.match?(~r/BEGIN RSA PRIVATE KEY/, relay.keys) do
{:ok, relay} = Actors.actor_key_rotation(relay)
end
relay
end end
@spec get_actor() :: Actor.t() | no_return @spec get_actor() :: Actor.t() | no_return

View file

@ -12,6 +12,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.Relay
require Logger require Logger
@ -94,13 +95,19 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
%{"keyId" => kid} = HTTPSignatures.signature_for_conn(conn) %{"keyId" => kid} = HTTPSignatures.signature_for_conn(conn)
actor_url = key_id_to_actor_url(kid) actor_url = key_id_to_actor_url(kid)
Logger.debug("Refetching public key for #{actor_url}") Logger.debug("Refetching public key for #{actor_url}")
relay = Relay.get_actor()
# In this specific case we don't sign object fetches because if actor_url == relay.url do
# this would cause infinite recursion when servers both need # Special case if ever it's our own actor fetching ourselves
# to fetch each other's keys get_actor_public_key(relay)
with {:ok, %Actor{} = actor} <- else
ActivityPubActor.make_actor_from_url(actor_url, ignore_sign_object_fetches: true) do # In this specific case we don't sign object fetches because
get_actor_public_key(actor) # this would cause infinite recursion when servers both need
# to fetch each other's keys
with {:ok, %Actor{} = actor} <-
ActivityPubActor.make_actor_from_url(actor_url, ignore_sign_object_fetches: true) do
get_actor_public_key(actor)
end
end end
end end

View file

@ -31,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema do
import_types(Absinthe.Plug.Types) import_types(Absinthe.Plug.Types)
import_types(Custom.UUID) import_types(Custom.UUID)
import_types(Custom.Point) import_types(Custom.Point)
import_types(Custom.Timezone)
import_types(Schema.ActivityType) import_types(Schema.ActivityType)
import_types(Schema.UserType) import_types(Schema.UserType)

View file

@ -22,7 +22,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
field(:url, :string, description: "The address's URL") field(:url, :string, description: "The address's URL")
field(:id, :id, description: "The address's ID") field(:id, :id, description: "The address's ID")
field(:origin_id, :string, description: "The address's original ID from the provider") field(:origin_id, :string, description: "The address's original ID from the provider")
field(:timezone, :string, description: "The (estimated) timezone of the location") field(:timezone, :timezone, description: "The (estimated) timezone of the location")
field(:picture_info, :picture_info, description: "A picture associated with the address") field(:picture_info, :picture_info, description: "A picture associated with the address")
end end
@ -75,7 +75,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
field(:url, :string, description: "The address's URL") field(:url, :string, description: "The address's URL")
field(:id, :id, description: "The address's ID") field(:id, :id, description: "The address's ID")
field(:origin_id, :string, description: "The address's original ID from the provider") field(:origin_id, :string, description: "The address's original ID from the provider")
field(:timezone, :string, description: "The (estimated) timezone of the location") field(:timezone, :timezone, description: "The (estimated) timezone of the location")
end end
@desc """ @desc """

View file

@ -43,7 +43,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
field(:upload_limits, :upload_limits, description: "The configuration for upload limits") field(:upload_limits, :upload_limits, description: "The configuration for upload limits")
field(:timezones, list_of(:string), description: "The instance's available timezones") field(:timezones, list_of(:timezone), description: "The instance's available timezones")
field(:features, :features, description: "The instance's features") field(:features, :features, description: "The instance's features")
field(:restrictions, :restrictions, description: "The instance's restrictions") field(:restrictions, :restrictions, description: "The instance's restrictions")
field(:version, :string, description: "The instance's version") field(:version, :string, description: "The instance's version")

View file

@ -0,0 +1,35 @@
defmodule Mobilizon.GraphQL.Schema.Custom.Timezone do
@moduledoc """
The timezone scalar type allows timezone ID strings to be passed in and out.
"""
use Absinthe.Schema.Notation
import Mobilizon.Web.Gettext, only: [dgettext: 3]
scalar :timezone, name: "Timezone" do
description("""
The `Timezone` scalar type represents a timezone identifier,
as registered in the IANA Time Zone Database.
""")
serialize(&encode/1)
parse(&decode/1)
end
@spec decode(Absinthe.Blueprint.Input.String.t()) :: {:ok, term} | :error
@spec decode(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp decode(%Absinthe.Blueprint.Input.String{value: value}) do
if Tzdata.zone_exists?(value),
do: {:ok, value},
else: {:error, dgettext("errors", "Timezone ID %{timezone} is invalid", timezone: value)}
end
defp decode(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp decode(_) do
:error
end
defp encode(value), do: value
end

View file

@ -250,7 +250,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
field(:show_start_time, :boolean, description: "Show event start time") field(:show_start_time, :boolean, description: "Show event start time")
field(:show_end_time, :boolean, description: "Show event end time") field(:show_end_time, :boolean, description: "Show event end time")
field(:timezone, :string, description: "The event's timezone") field(:timezone, :timezone, description: "The event's timezone")
field(:hide_organizer_when_group_event, :boolean, field(:hide_organizer_when_group_event, :boolean,
description: description:
@ -303,7 +303,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
field(:show_start_time, :boolean, description: "Show event start time") field(:show_start_time, :boolean, description: "Show event start time")
field(:show_end_time, :boolean, description: "Show event end time") field(:show_end_time, :boolean, description: "Show event end time")
field(:timezone, :string, description: "The event's timezone") field(:timezone, :timezone, description: "The event's timezone")
field(:hide_organizer_when_group_event, :boolean, field(:hide_organizer_when_group_event, :boolean,
description: description:

View file

@ -96,7 +96,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
arg(:email, :string, description: "The anonymous participant's email") arg(:email, :string, description: "The anonymous participant's email")
arg(:message, :string, description: "The anonymous participant's message") arg(:message, :string, description: "The anonymous participant's message")
arg(:locale, :string, description: "The anonymous participant's locale") arg(:locale, :string, description: "The anonymous participant's locale")
arg(:timezone, :string, description: "The anonymous participant's timezone") arg(:timezone, :timezone, description: "The anonymous participant's timezone")
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation") middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
resolve(&Participant.actor_join_event/3) resolve(&Participant.actor_join_event/3)
end end

View file

@ -220,7 +220,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
""" """
object :user_settings do object :user_settings do
meta(:authorize, :user) meta(:authorize, :user)
field(:timezone, :string, description: "The timezone for this user") field(:timezone, :timezone, description: "The timezone for this user")
field(:notification_on_day, :boolean, field(:notification_on_day, :boolean,
description: "Whether this user will receive an email at the start of the day of an event." description: "Whether this user will receive an email at the start of the day of an event."
@ -450,7 +450,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
@desc "Set user settings" @desc "Set user settings"
field :set_user_settings, :user_settings do field :set_user_settings, :user_settings do
arg(:timezone, :string, description: "The timezone for this user") arg(:timezone, :timezone, description: "The timezone for this user")
arg(:notification_on_day, :boolean, arg(:notification_on_day, :boolean,
description: description:

View file

@ -4,6 +4,7 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
""" """
use Absinthe.Schema.Notation use Absinthe.Schema.Notation
alias Mobilizon.GraphQL.Resolvers.Users.ActivitySettings alias Mobilizon.GraphQL.Resolvers.Users.ActivitySettings
alias Mobilizon.Users.ActivitySetting
object :activity_setting do object :activity_setting do
meta(:authorize, :user) meta(:authorize, :user)
@ -21,8 +22,9 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
middleware(Rajska.QueryAuthorization, middleware(Rajska.QueryAuthorization,
permit: :user, permit: :user,
scope: false, scope: ActivitySetting,
rule: :"write:user:setting:activity" rule: :"write:user:setting:activity",
args: %{key: :key}
) )
resolve(&ActivitySettings.upsert_user_activity_setting/3) resolve(&ActivitySettings.upsert_user_activity_setting/3)

View file

@ -426,7 +426,7 @@ defmodule Mobilizon.Applications do
@spec prune_old_application_device_activations(pos_integer()) :: {non_neg_integer(), nil} @spec prune_old_application_device_activations(pos_integer()) :: {non_neg_integer(), nil}
def prune_old_application_device_activations(lifetime) do def prune_old_application_device_activations(lifetime) do
exp = DateTime.add(DateTime.utc_now(), -lifetime) exp = DateTime.utc_now() |> DateTime.add(-lifetime) |> DateTime.to_unix()
ApplicationDeviceActivation ApplicationDeviceActivation
|> where([at], at.expires_in < ^exp) |> where([at], at.expires_in < ^exp)

View file

@ -1,7 +1,7 @@
defmodule Mobilizon.Mixfile do defmodule Mobilizon.Mixfile do
use Mix.Project use Mix.Project
@version "3.1.0" @version "3.1.1"
def project do def project do
[ [