Compare commits

...

664 commits

Author SHA1 Message Date
778a69cd 4cdbf78037 Merge remote-tracking branch 'potsdamn/feature/calendar' 2024-02-01 00:40:38 +01:00
778a69cd 59c3c87281 flake.lock: Update
Flake lock file updates:

• Updated input 'napalm':
    'github:nix-community/napalm/a8215ccf1c80070f51a92771f3bc637dd9b9f7ee' (2023-09-06)
  → 'github:nix-community/napalm/edcb26c266ca37c9521f6a97f33234633cbec186' (2023-12-20)
• Updated input 'napalm/flake-utils':
    'github:numtide/flake-utils/c0e246b9b83f637f4681389ecabcb2681b4f3af0' (2022-08-07)
  → 'github:numtide/flake-utils/4022d587cbbfd70fe950c1e2083a02621806a725' (2023-12-04)
• Added input 'napalm/flake-utils/systems':
    'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09)
• Updated input 'nix-filter':
    'github:numtide/nix-filter/c843418ecfd0344ecb85844b082ff5675e02c443' (2023-12-04)
  → 'github:numtide/nix-filter/3449dc925982ad46246cfc36469baf66e1b64f17' (2024-01-15)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/a9bf124c46ef298113270b1f84a164865987a91c' (2023-12-11)
  → 'github:NixOS/nixpkgs/97b17f32362e475016f942bbdfda4a4a72a8a652' (2024-01-29)
2024-01-31 17:09:21 +01:00
778a69cd 81ae56d850 Merge remote-tracking branch 'origin/main' 2024-01-31 17:03:11 +01:00
Thomas Citharel 5770d6f0f9 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1525
2024-01-29 18:19:59 +00:00
summersamara c2fc80cade Fix z-index of FullAddressAutoComplete
potsda.mn/mobilizon#48
potsda.mn/mobilizon#46
2024-01-27 17:53:02 +01:00
summersamara 87a7738842 Fix FullAddressAutoComplete in Event form
potsda.mn/mobilizon#43
2024-01-23 22:07:56 +01:00
drkfrd 08bf4a90da Translated using Weblate (Swedish)
Currently translated at 98.7% (1623 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 17:27:55 +01:00
drkfrd fe09e43be2 Translated using Weblate (Swedish)
Currently translated at 70.1% (1153 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 14:53:32 +01:00
drkfrd 5d409d8029 Translated using Weblate (Swedish)
Currently translated at 69.8% (1149 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 14:43:34 +01:00
drkfrd ee49f57ee7 Translated using Weblate (Swedish)
Currently translated at 61.0% (1003 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 14:24:53 +01:00
drkfrd 51c7dfc593 Translated using Weblate (Swedish)
Currently translated at 53.1% (874 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 14:00:10 +01:00
drkfrd eb8d65d1c8 Translated using Weblate (Swedish)
Currently translated at 51.5% (848 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/sv/
2024-01-19 11:46:26 +01:00
drkfrd 25bbab3827 Translated using Weblate (Swedish)
Currently translated at 94.5% (86 of 91 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/sv/
2024-01-19 10:47:43 +01:00
Thomas Citharel baf75dd890 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1524
2024-01-18 12:59:01 +00:00
Balázs Meskó 602798da0f Translated using Weblate (Hungarian)
Currently translated at 92.3% (1518 of 1644 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hu/
2024-01-18 13:16:43 +01:00
778a69cd a1d7adc538 add german translation for "Calendar" 2024-01-05 15:36:59 +01:00
Thomas Citharel 6178f5a76e Merge branch 'docker-compose' into 'main'
build(docker): change docker-compose to docker compose in Makefile

See merge request framasoft/mobilizon!1522
2024-01-04 14:20:57 +00:00
Thomas Citharel 08f80341c2
build(docker): change docker-compose to docker compose in Makefile
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 15:20:17 +01:00
summersamara a48b315f16 Implement graphql query for events calendar and agenda components
- remove ICSCalendar and ICSAgenda components
- fix highlight selected date

potsda.mn/mobilizon#40
potsda.mn/mobilizon#41
2024-01-04 15:03:40 +01:00
Thomas Citharel 5997e9e14c Merge branch 'fix-group-report' into 'main'
fix(front): fix reporting group

See merge request framasoft/mobilizon!1521
2024-01-04 13:25:15 +00:00
Thomas Citharel 57d0372ce8
fix(front): fix reporting group
Item was hidden unless anonymous reporting was enabled

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 14:02:13 +01:00
Thomas Citharel b317fe6163 Merge branch 'fixes' into 'main'
Fix various instance view stuff and legacy cleaning

Closes #1393

See merge request framasoft/mobilizon!1520
2024-01-04 13:00:30 +00:00
Thomas Citharel fb173414c9
style(lint): add Elixir lint on pre-commit
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:41:21 +01:00
Thomas Citharel d0835232d6
refactor(backend): change naming of function names to avoid the is_ prefix
Following Credo.Check.Readability.PredicateFunctionNames check

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:35:08 +01:00
Thomas Citharel fe0cf93604
fix(front): fix debouncing instances filtering
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:02:17 +01:00
Thomas Citharel b3ba45e8a7
fix(back): fix instances filtering
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:02:16 +01:00
Thomas Citharel 428537df1f
refactor: remove some outdated todos and legacy fallbacks
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:02:16 +01:00
Thomas Citharel f7585cfc75
fix(backend): validate length of instance actor details and set description column to text
Closes #1393

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:02:05 +01:00
Thomas Citharel eb43b7c79c
style(credo): disable credo Credo.Check.Warning.MissedMetadataKeyInLoggerConfig check
It's buggy https://github.com/rrrene/credo/issues/1101

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 13:01:22 +01:00
Thomas Citharel c3aa145148
refactor(backend): extract convert_ecto_errors in the Mobilizon.Storage.Ecto module
And use it to log refreshing instance errors

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-04 12:59:15 +01:00
778a69cd 9028332b0d Merge remote-tracking branch 'origin/main' 2024-01-03 23:38:23 +01:00
Thomas Citharel 5cdcc2e985 Merge branch 'update-deps' into 'main'
chore(deps): update deps

See merge request framasoft/mobilizon!1519
2024-01-03 18:18:41 +00:00
Thomas Citharel 11cb0a4a81 Merge branch 'fix-event-view-window-title' into 'main'
Fix: Window title in Event View doesn't show the Event title

See merge request framasoft/mobilizon!1518
2024-01-03 18:08:12 +00:00
Thomas Citharel e8d7663f2a
chore(deps): update deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:54:53 +01:00
Thomas Citharel 606f3df866 Merge branch 'federation-fixes' into 'main'
fix(activitypub): various federation follow & nodeinfo fixes

See merge request framasoft/mobilizon!1516
2024-01-03 17:53:58 +00:00
Thomas Citharel 0bd7b670ae
refactor(front): move IdentityPicker and Wrapper to components instead of views
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:48:06 +01:00
Thomas Citharel 9308c5399d
fix(activitypub): handle any type of error when fetching Application actor from NodeInfo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:16 +01:00
Thomas Citharel 0948cce83e
fix(front): uI fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:15 +01:00
Thomas Citharel 7c51ef79b9
refactor(activitypub): simplify request to get instances list and status
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:15 +01:00
Thomas Citharel 2f4b8feeba
fix(activitypub): refresh NodeInfo metadata straight away when adding a new instance to follow
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:14 +01:00
Thomas Citharel 6d2f08f3c1
refactor(activitypub): log instance actor details before it's created
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:13 +01:00
Thomas Citharel da3b074619
fix(nodeinfo): make sure we only process JSON content
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2024-01-03 18:33:13 +01:00
778a69cd 953033b58b Merge remote-tracking branch 'origin/main' 2023-12-31 14:21:45 +01:00
Thomas Citharel 7d3b46d905 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1517
2023-12-31 09:31:39 +00:00
summersamara 81ca3e052f Fix: Window title in Event View doesn't show the Event title
- Prevent IdentityPicker from setting the window title
2023-12-24 10:11:22 +01:00
summersamara dec26525c0 Merge remote-tracking branch 'origin/main' into feature/calendar 2023-12-24 07:51:52 +01:00
Ferdi Scholten 99867d6dda Translated using Weblate (Dutch)
Currently translated at 73.9% (1215 of 1643 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/nl/
2023-12-23 08:15:42 +01:00
Thomas Citharel 881695ca19
refactor(http): extract HTTP header tests to a new module
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-22 16:00:33 +01:00
Thomas Citharel 7351468842
fix(activitypub): handle actors following with manually_approves_followers not set
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-22 15:57:29 +01:00
778a69cd 8013eb95f7 Merge remote-tracking branch 'origin/main' 2023-12-22 15:44:50 +01:00
Thomas Citharel b89f4f47fa Merge branch 'upgrade-deps' into 'main'
Upgrade deps

See merge request framasoft/mobilizon!1515
2023-12-22 13:29:18 +00:00
Thomas Citharel a6a80cabc6
Merge branch 'oruga-0.8' into upgrade-deps 2023-12-22 12:17:17 +01:00
Thomas Citharel 112019d21c
refactor: fix date-fns import in GroupView.vue
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-22 12:16:01 +01:00
Thomas Citharel 72c0b18aa9
chore(deps): upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-22 12:15:37 +01:00
Thomas Citharel b7cb2b9bd1 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1514
2023-12-22 11:08:51 +00:00
Milo Ivir 4ddbc20e2b Translated using Weblate (Croatian)
Currently translated at 81.5% (292 of 358 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/hr/
2023-12-21 21:11:11 +01:00
778a69cd 6fa7e23655 Merge remote-tracking branch 'origin/main' 2023-12-21 11:28:21 +01:00
Thomas Citharel 58e4239aae Merge branch 'extract-nodeinfo-metadata' into 'main'
feat(nodeinfo): extract and save NodeInfo information from instances to...

Closes #1392

See merge request framasoft/mobilizon!1513
2023-12-21 10:05:26 +00:00
Thomas Citharel 99b2339424
feat(nodeinfo): extract and save NodeInfo information from instances to display it on instances list
We also try to detect the application actor if it's not given by NodeInfo metadata (FEP-2677)
(guessing for Mobilizon, PeerTube & Mastodon).

Closes #1392

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-21 10:45:56 +01:00
778a69cd 05381e47e8 Merge remote-tracking branch 'origin/main' 2023-12-20 14:19:10 +01:00
Thomas Citharel b09f9053b2 Merge branch 'fix-ics-feed-events-sorting' into 'main'
Fix ICS feed missing events: order by "begins_on" DESC

See merge request framasoft/mobilizon!1512
2023-12-20 08:45:18 +00:00
Thomas Citharel ff0440c634
fix(feeds): increase feed item limit from 500 to 5000
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-20 09:26:58 +01:00
Thomas Citharel 3c75856149
fix(feeds): make sure posts for feeds are ordered by publication date desc
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-20 09:26:28 +01:00
Thomas Citharel e73fd9b370
fix(activitypub): make relay outbox events ordered by desc publication date
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-20 09:24:59 +01:00
summersamara 31411bfc03
Fix ICS feed missing events: order by "begins_on" DESC so that the latest events are always included
potsda.mn/mobilizon#37
2023-12-20 09:07:47 +01:00
778a69cd 15c9518877 typo in docs 2023-12-20 00:33:44 +01:00
summersamara 1d430f5707 Supply sort and direction as arguments to list_public_local_events instead of hardcoding
potsda.mn/mobilizon#37
2023-12-20 00:25:38 +01:00
summersamara 5fb5897c34 Fix: order the fetched events for the ICS feed DESC by "beings_on"
potsda.mn/mobilizon#37
2023-12-19 23:55:34 +01:00
summersamara f3051d5f11 Remember the calendar state in ICSAgenda component when navigating back 2023-12-19 22:12:42 +01:00
778a69cd 00c408a502 Merge remote-tracking branch 'origin/main' 2023-12-19 13:16:16 +01:00
Thomas Citharel 2fba6379f1 Merge branch 'docker-remove-openssl-11-compat' into 'main'
fix(docker): remove openssl1.1-compat

Closes #1390

See merge request framasoft/mobilizon!1511
2023-12-19 11:24:01 +00:00
Thomas Citharel 889cb91f26
fix(docker): add --break-system-packages to pip install to add weasyprint and pyexcel-ods3
That's the only use of Python so we should be fine

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 12:05:22 +01:00
Thomas Citharel 75d7816a6c
fix(docker): remove openssl1.1-compat
Closes #1390

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 12:04:22 +01:00
Thomas Citharel f4e51c2a90 Merge branch 'address-text-only' into 'main'
feat(activitypub): allow simple text for address field

Closes #1387

See merge request framasoft/mobilizon!1510
2023-12-19 10:13:12 +00:00
Thomas Citharel 001a0ed1a5
fix(activitypub): do not try to calculate timezone from missing geo-coordinates
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 10:53:12 +01:00
Thomas Citharel 64237cfc26
feat(activitypub): allow simple text for address field
Closes #1387

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 10:52:31 +01:00
Thomas Citharel 8544278c9a Merge branch 'nodeinfo-fixes' into 'main'
fix(nodeinfo): fix getting application actor information from NodeInfo response

See merge request framasoft/mobilizon!1509
2023-12-19 09:40:41 +00:00
Thomas Citharel 78d3e76e46
test(nodeinfo): add tests for Mobilizon.Federation.NodeInfo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 10:01:20 +01:00
Thomas Citharel 227c176628 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1508
2023-12-19 08:54:26 +00:00
Thomas Citharel 00250ff33a
refactor(activitypub): cleanup unused imports and variables
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 09:53:05 +01:00
Thomas Citharel dd775b6ae2
fix(nodeinfo): fix getting application actor information from NodeInfo response
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-19 09:52:32 +01:00
Phongpanot d3b0647b11 Added translation using Weblate (Thai) 2023-12-19 09:20:39 +01:00
summersamara 1e0db0d8c9 Fix sizing issue + use better type declarations 2023-12-19 03:03:14 +01:00
summersamara 4ceee2efc7 Fix Calendar View style and add features
- create ICSCalendar component
- create ICSAgenda component (for mobile view)
- temporary add rotes.potsda.mn ics feed for testing
2023-12-19 01:23:41 +01:00
778a69cd c90ad81879 Merge remote-tracking branch 'origin/main' 2023-12-18 18:46:56 +01:00
summersamara 69717f26f3 try to fix events calendar routing 2023-12-18 18:29:44 +01:00
summersamara 9c0c5b6e83 Fix fullcalendar CSP errors
- whitelist the 'data:' protocol for fonts
- Add CSP hash directive to allow fullcalendar inline style
2023-12-18 18:05:25 +01:00
778a69cd 81948b45ca fix: actually route /events/calendar 2023-12-18 18:05:25 +01:00
summersamara 5095749157 create Events Calendar View
- install full-calendar npm packages
- create FullCalendar component
- create Events Calendar route
- Fix: remove unused imports in NavBar.vue
2023-12-18 18:05:25 +01:00
Thomas Citharel 707add36dd Merge branch 'nodeinfo-relay' into 'main'
feat(activitypub): implement FEP-2677 to identify the application actor used for federation

Closes #1367

See merge request framasoft/mobilizon!1507
2023-12-18 15:44:15 +00:00
Thomas Citharel d5eb235661 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1504
2023-12-18 15:43:49 +00:00
josé m 8b2e885159 Translated using Weblate (Galician)
Currently translated at 77.3% (1268 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/gl/
2023-12-18 06:37:14 +01:00
josé m 48e1b7a5ec Translated using Weblate (Galician)
Currently translated at 76.8% (1261 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/gl/
2023-12-18 06:04:50 +01:00
josé m 91fdaf5abc Translated using Weblate (Galician)
Currently translated at 76.2% (1250 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/gl/
2023-12-17 04:53:38 +01:00
778a69cd 863a1dbe3f reintroduce json_library config, apparently removal is not necessary anymore 2023-12-15 12:39:08 +01:00
Thomas Citharel f10977a99a
feat(activitypub): implement FEP-2677 to identify the application actor used for federation
Instead of always assuming it will be @relay@host.tld

Closes #1367

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 16:31:58 +01:00
778a69cd 1eaad40c66 flake.lock: Update
Flake lock file updates:

• Updated input 'nix-filter':
    'github:numtide/nix-filter/41fd48e00c22b4ced525af521ead8792402de0ea' (2023-09-16)
  → 'github:numtide/nix-filter/c843418ecfd0344ecb85844b082ff5675e02c443' (2023-12-04)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/e92039b55bcd58469325ded85d4f58dd5a4eaf58' (2023-11-29)
  → 'github:NixOS/nixpkgs/a9bf124c46ef298113270b1f84a164865987a91c' (2023-12-11)
2023-12-14 16:04:21 +01:00
778a69cd 7d7abd0dda Merge remote-tracking branch 'origin/main' 2023-12-14 15:06:06 +01:00
josé m 708bb6b353 Translated using Weblate (Galician)
Currently translated at 73.3% (1203 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/gl/
2023-12-14 11:57:02 +01:00
Thomas Citharel 049ffd61b7 Merge branch 'front-fixes' into 'main'
Various front-end fixes

See merge request framasoft/mobilizon!1505
2023-12-14 10:56:57 +00:00
Thomas Citharel a9676d6481
feat(front): upgrade to Oruga 0.8.x
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:30:54 +01:00
Thomas Citharel 76668e0beb
fix(front): fix focus when creating a new resource
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:24 +01:00
Thomas Citharel cecbea6db5
fix(front): show correct label when adding a new calc or videoconference resource in resources
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:23 +01:00
Thomas Citharel 1d39eb5488
fix(front): improve display of SendPasswordReset view
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:23 +01:00
Thomas Citharel a408b476cf
fix(front): add condition on DraggableList in ResourceFolder.vue
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:22 +01:00
Thomas Citharel 45f8757d72
fix(front): create head without old options
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:22 +01:00
Thomas Citharel 89641c502e
fix(front): fix dialog from EventParticipationCard.vue without input
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:21 +01:00
Thomas Citharel 442d072857
refactor(front): use useCurrentActorClient() instead of making query in EventParticipationCard
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:21 +01:00
Thomas Citharel f4ee116112
fix(front): escape event.title when it's passed to dialog component HTML message
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:29:13 +01:00
Thomas Citharel 83eb5c6a69
fix(front): add announcements link on EventParticipationCard as well as EventView
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:15:08 +01:00
Thomas Citharel ee6381463d
fix(front): remove broken identity check in EventMinimalistCard
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:15:07 +01:00
Thomas Citharel 00d8bc733d
fix(front): fix ErrorComponent.vue sentry integration
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 11:15:07 +01:00
Thomas Citharel 51d43aa2d1
fix(front): various little CSS fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-14 10:47:41 +01:00
Thomas Citharel 68c40e6bf5
chore(deps): update deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-12 11:48:54 +01:00
Thomas Citharel a47f4f6444
fix(graphql): add missing operation name for RegisterPerson
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-12 11:40:56 +01:00
Thomas Citharel d4489f691b
fix(front-end): fix issues with expired accessToken refreshment queue
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-12 11:40:33 +01:00
Thomas Citharel ae466b879c
fix(front-end): fix current actor not being set on first access when relogging
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-12 11:39:27 +01:00
Thomas Citharel 820d4adb69 Merge branch 'docker-webpush' into 'main'
refactor(docker): allow webPush configuration to be configured using env variables in Docker

Closes #1383

See merge request framasoft/mobilizon!1503
2023-12-11 15:51:01 +00:00
Thomas Citharel 459f486a90
refactor(docker): allow webPush configuration to be configured using env variables in Docker
Closes #1383

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-11 16:31:24 +01:00
778a69cd 744008273d Merge remote-tracking branch 'origin/main' 2023-12-11 12:17:38 +01:00
Thomas Citharel f6ff99987f Merge branch 'html_to_text-nil' into 'main'
fix: allow html_to_text/1 to receive nil, e.g. for empty event descriptions

See merge request framasoft/mobilizon!1502
2023-12-11 09:44:06 +00:00
778a69cd 5030b755a0
fix: allow html_to_text to receive nil, e.g. for empty event descriptions
Fixes potsda.mn/mobilizon#34

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-11 09:42:06 +01:00
778a69cd 63129b0769 fix: allow html_to_text to receive nil, e.g. for empty event descriptions
Fixes potsda.mn/mobilizon#34
2023-12-09 21:48:23 +01:00
summersamara 9c60c6d1ad make group's address field optional again
- reverts some changes in e4607567ce
- issue potsda.mn/mobilizon#27
2023-12-08 19:14:57 +01:00
778a69cd 0e0272fb5a set version to 4.0.2 2023-12-08 11:26:44 +01:00
summersamara 6b26aaa1ef Merge remote-tracking branch 'upstream/main' 2023-12-07 15:08:12 +01:00
Thomas Citharel 987c5b59d3
chore(release): 4.0.2
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 14:55:16 +01:00
Thomas Citharel 0427e42f8a Merge branch 'xss-fixes' into 'main'
fix(front-end): add more security fixes for formatted lists and notifier

See merge request framasoft/mobilizon!1501
2023-12-07 13:52:34 +00:00
potsda.mn-Kollektiv 1af8e37e9b
fix(front-end): add more security fixes for formatted lists and notifier
- introduce html escape function
- escape message content in notifier plugin
- escape user name in ConversationListItem
- escape user name in the Event EditView contacts section
- display user summary as plain text in ActorCard

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 14:29:20 +01:00
778a69cd b91b5f8133 Merge remote-tracking branch 'origin/main' 2023-12-07 13:43:16 +01:00
Thomas Citharel 5e3d8a861f
chore(release): 4.0.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 12:36:11 +01:00
Thomas Citharel 0caaf2bf2e
build(packages): remove alpine packages as there's no demand for it
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 12:30:49 +01:00
Thomas Citharel 795ef2463f
build(packages): add back Debian Buster as it seems people are still using it
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 12:30:27 +01:00
Thomas Citharel 38f2443d7b Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1499
2023-12-07 11:19:20 +00:00
josé m 97c53bb8d3 Translated using Weblate (Galician)
Currently translated at 100.0% (91 of 91 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/gl/
2023-12-07 12:14:44 +01:00
josé m e08b057e06 Translated using Weblate (Galician)
Currently translated at 71.2% (1169 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/gl/
2023-12-07 12:14:43 +01:00
josé m ec5e4366e5 Translated using Weblate (Galician)
Currently translated at 6.5% (6 of 91 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/gl/
2023-12-07 12:14:43 +01:00
Thomas Citharel 211d07b68e Merge branch 'xss-fixes' into 'main'
Security and docker fixes

See merge request framasoft/mobilizon!1500
2023-12-07 11:14:36 +00:00
Thomas Citharel db385501aa
fix(emails): use tls_certificate_check to add tls config for mailer
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 11:42:19 +01:00
Thomas Citharel 7210f86889
feat(cli): add command to test emails send correctly
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 09:17:33 +01:00
Thomas Citharel 4855af8f87
feat(docker): allow to configure loglevel at runtime through env variable
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-07 09:17:33 +01:00
Thomas Citharel 9907f887c9
fix(docker): use separate env for tzdata dir path
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 16:07:42 +01:00
Thomas Citharel 126727bf58
fix(docker): fix getting default value for MOBILIZON_SMTP_SSL env
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 16:07:03 +01:00
Thomas Citharel 7d725bd942
fix(config): fix setting path for Mobilizon.Service.SiteMap
System.get_env/2 makes no sense at compile time

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 16:06:20 +01:00
Thomas Citharel 28063bd1d9
fix(docker): fix getting configuration value from env MOBILIZON_SMTP_TLS
Closes #1381

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 16:05:33 +01:00
Thomas Citharel 09f41328ab
fix(graphql): set default value for resource type parameter
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 14:43:51 +01:00
778a69cd 3dc3e7e972 Merge remote-tracking branch 'origin/main' 2023-12-06 13:29:23 +01:00
Thomas Citharel 94bf2e53bf
fix(front): put correct value for CONVERSATION_LIST enum value
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:51:45 +01:00
Thomas Citharel dc6647f5dc
fix: sanitize descriptions from resources
Currently resources descriptions are not used anywhere but they are
prefilled from source URL preview. Still, doesn't hurt to sanitize
these.

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:47:28 +01:00
Thomas Citharel 58e50e3c9f
test: add new tests for XSS in actors summary
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:19:19 +01:00
Thomas Citharel 41227d994c
style: linting front-end
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:18:36 +01:00
Thomas Citharel 2c12fbfd09
fix(front): anonymous participant text is plain text, avoid using v-html
It was using v-html when opening to "view more"

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:18:05 +01:00
Thomas Citharel 77518deb54 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1498
2023-12-06 10:07:48 +00:00
Thomas Citharel ffff379d47
fix: always consider report content as text
Report content was used as HTML in front-end and e-mails but wasn't sanitized as such.

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 11:05:56 +01:00
FoW 1a1ad5295b Translated using Weblate (Korean)
Currently translated at 0.5% (2 of 355 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/ko/
2023-12-06 10:49:28 +01:00
FoW 7b4c31d66a Translated using Weblate (Korean)
Currently translated at 1.1% (19 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/ko/
2023-12-06 10:49:27 +01:00
Thomas Citharel ded59bec27
fix(front): fix XSS because of bad operations when setting the group's summary
Group summary (HTML) is properly sanitized by the backend, but for groups we did a special operation
before setting the HTML in the Vue app. This is now removed

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 08:47:44 +01:00
Thomas Citharel 935799f123
fix(front): fix editing group
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 08:37:48 +01:00
Thomas Citharel 5b337f952a
refactor(activitypub): handle failure finding public key in actor keys
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-06 08:25:02 +01:00
Thomas Citharel 114e850682 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1497
2023-12-06 06:38:24 +00:00
Milo Ivir 9c88faeafb Translated using Weblate (Croatian)
Currently translated at 88.9% (1458 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-05 22:34:12 +01:00
778a69cd daf33b747c Merge remote-tracking branch 'origin/main' 2023-12-05 17:41:28 +01:00
Milo Ivir 623f4ee556 Translated using Weblate (Croatian)
Currently translated at 87.1% (1430 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-05 17:34:13 +01:00
Milo Ivir 1162dd0f7d Translated using Weblate (Croatian)
Currently translated at 81.6% (290 of 355 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/hr/
2023-12-05 17:34:13 +01:00
Thomas Citharel 3a55baeffd
chore(release): release 4.0.0
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 09:07:11 +01:00
Thomas Citharel f93457131a Merge branch 'fixes' into 'main'
Little fixes

See merge request framasoft/mobilizon!1496
2023-12-05 08:06:10 +00:00
Thomas Citharel 147096cc3d
refactor: to lower cyclomatic complexity
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 08:40:01 +01:00
Thomas Citharel f81472e081
fix(front): fix tag loading
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 08:28:11 +01:00
Thomas Citharel c4d2ec69ad
fix(front): only show participants & announcements menu items to organizers
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 08:15:11 +01:00
Thomas Citharel 89d1ee42f4
fix(backend): handle ecto errors when fetching and create entities
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 08:06:16 +01:00
Thomas Citharel f1084c101f
fix(activitypub): fix receiving comments
Should fix race conditions and actors deleted of received comments

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-05 08:02:09 +01:00
Thomas Citharel 749e90b6c9 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1495
2023-12-05 06:51:42 +00:00
Milo Ivir a26ff98b13 Translated using Weblate (Croatian)
Currently translated at 86.2% (1414 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-04 19:30:01 +01:00
Milo Ivir 1683f01662 Translated using Weblate (Croatian)
Currently translated at 53.7% (143 of 266 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/hr/
2023-12-04 19:30:01 +01:00
Milo Ivir aa7f870a79 Translated using Weblate (Croatian)
Currently translated at 86.2% (1414 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-04 19:18:45 +01:00
Milo Ivir 1ce34eaffb Translated using Weblate (Croatian)
Currently translated at 86.0% (1412 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-04 19:13:15 +01:00
Milo Ivir 5e7edc0784 Translated using Weblate (Croatian)
Currently translated at 85.9% (1410 of 1640 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-12-04 19:10:07 +01:00
Milo Ivir d777d8874c Translated using Weblate (Croatian)
Currently translated at 80.0% (284 of 355 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/hr/
2023-12-04 19:10:06 +01:00
Milo Ivir 0118d974e9 Translated using Weblate (Croatian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/hr/
2023-12-04 19:10:06 +01:00
Thomas Citharel 5677f8170f Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1494
2023-12-04 18:01:07 +00:00
Milo Ivir 805e931e53 Translated using Weblate (Croatian)
Currently translated at 97.8% (89 of 91 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/hr/
2023-12-04 18:47:51 +01:00
778a69cd d8ba0bc12c Merge remote-tracking branch 'origin/main' 2023-12-04 16:08:22 +01:00
Thomas Citharel c8f85df3db Merge branch 'fixes' into 'main'
make recipient field placeholder translatable and an AP fix

Closes #1378

See merge request framasoft/mobilizon!1493
2023-12-04 13:28:23 +00:00
Thomas Citharel 5e8f9afb62
fix(activitypub): compact ical:status in activitystream data
Was kept for no other reason than disambiguation with https://schema.org/status, but no need if we
specify the correct alias in the context.

Keeps a fallback for older versions compatibility

Closes #1378

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-04 14:05:27 +01:00
Thomas Citharel 10ce812660
fix(front): make recipient field placeholder translatable
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-04 13:49:42 +01:00
Thomas Citharel 24d92f60f7
chore(release): 4.0.0-rc.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-04 09:33:02 +01:00
Thomas Citharel 3851392225 Merge branch 'suspension-fixes' into 'main'
fix(backend): only send suspension notification emails when actor's suspended and not just deleted

See merge request framasoft/mobilizon!1492
2023-12-04 08:30:06 +00:00
Thomas Citharel 9e41bc1ad6
fix(backend): only send suspension notification emails when actor's suspended and not just deleted
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-04 09:02:38 +01:00
Thomas Citharel 51e1ed642c Merge branch 'nginx' into 'main'
docs(nginx): improve nginx configuration

See merge request framasoft/mobilizon!1490
2023-12-04 07:41:59 +00:00
Thomas Citharel 6c992cade5
docs(nginx): improve nginx configuration
Remove etag off and improve Cache-Control

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-04 08:18:45 +01:00
778a69cd 2d69dc2076 Merge remote-tracking branch 'origin/main' 2023-12-03 23:40:50 +01:00
Thomas Citharel 3a2c6afc0d Merge branch 'fixes' into 'main'
Two last-minute fixes

Closes #1376 et #1357

See merge request framasoft/mobilizon!1489
2023-12-03 17:34:33 +00:00
Thomas Citharel 32caebb1d0
fix: prevent sending group physical address if it's empty and allow empty text for timezone
As old addresses don't hold TZ information

Closes #1357

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-03 18:14:00 +01:00
Thomas Citharel 8795576865
fix(activitypub): add missing externalParticipationUrl context
Closes #1376

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-03 17:51:05 +01:00
778a69cd 9a10666aa8 reduce nix output path size by 60MB 2023-12-02 04:26:04 +01:00
778a69cd bedada2efc more stable override for ex_cldr 2023-12-02 03:54:04 +01:00
778a69cd 059814f751 remove unneded reference to node-packages.nix 2023-12-02 02:17:49 +01:00
778a69cd 87fd975182 bump version used in nix to 4.0.0 2023-12-01 21:21:02 +01:00
778a69cd 0ff5418978 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/e4ad989506ec7d71f7302cc3067abd82730a4beb' (2023-11-19)
  → 'github:NixOS/nixpkgs/e92039b55bcd58469325ded85d4f58dd5a4eaf58' (2023-11-29)
2023-12-01 21:10:12 +01:00
778a69cd 69fe7b44c5 nixpkgs-fmt 2023-12-01 20:49:47 +01:00
778a69cd 4bd97ebfcb Merge remote-tracking branch 'origin/main' 2023-12-01 20:27:18 +01:00
Thomas Citharel 0d31872737
Release 4.0.0-beta.2
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 17:00:50 +01:00
Thomas Citharel 55287b016f Merge branch 'fix-tests' into 'main'
test: fix tests using verified routes

See merge request framasoft/mobilizon!1488
2023-12-01 15:59:04 +00:00
Thomas Citharel 5fcf3d5267
test: fix tests using verified routes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 16:34:48 +01:00
Thomas Citharel 6000c41364 Merge branch 'fixes' into 'main'
refactor: use Phoenix verified routes

See merge request framasoft/mobilizon!1487
2023-12-01 10:30:11 +00:00
Thomas Citharel d831dff9fc
feat(front): add dedicated page and route for event announcements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:56 +01:00
Thomas Citharel 11e42d6601
feat(background): add a job to refresh participant stats
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:56 +01:00
Thomas Citharel 6df16ef114
chore(i18n): update backend translations
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:55 +01:00
Thomas Citharel 3f2a88fcfa
fix: fix creating participant stats
Map.update/4 does not pass initial value to callback function, so increasing for a new role would
set 0 instead of 1

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:54 +01:00
Thomas Citharel 9e6b232a78
feat: add links to cancel anonymous participations in emails
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:53 +01:00
Thomas Citharel b315e1d7ff
refactor: use Phoenix verified routes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-12-01 11:29:53 +01:00
778a69cd 696f0e4901 Merge remote-tracking branch 'origin/main' 2023-12-01 01:34:37 +01:00
Thomas Citharel 4f15535fa9
ci(docker): fix docker build for pre-releases
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-30 10:03:48 +01:00
Thomas Citharel 2e4b70cf93
Release 4.0.0-beta.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-30 09:55:23 +01:00
Thomas Citharel 1a4c4b41e3
Merge branch 'mobilizon-fix-full-address-autocomplete-not-loading-result' 2023-11-30 09:46:23 +01:00
Thomas Citharel 49b070d939
fix(api): allow localhost as a valid uri host for applications
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-30 09:45:25 +01:00
summersamara e4607567ce fix error when creating a group without a valid address
- make address input required in the group create- and group settings-view

potsda.mn/mobilizon#27
2023-11-28 10:48:19 +01:00
Thomas Citharel f81804d57f resolve result promise in a shorter way 2023-11-27 16:43:12 +00:00
summersamara 83da88ca28 fix fullAddressAutocomplete component not loading results
- introduce refetch method to fetch the result when query variables change
2023-11-27 16:52:23 +01:00
summersamara c0d530be92 fix full address autocomplete component
potsda.mn/mobilizon#28
2023-11-27 16:13:57 +01:00
778a69cd 1eb3afca56 Merge remote-tracking branch 'origin/main' 2023-11-24 12:43:48 +01:00
778a69cd bd89ca3355 rerun .#update, forgot to do that after last merge
Also remove npm2nix stage, as the lock file should be good to go now
2023-11-24 12:22:56 +01:00
Thomas Citharel ad597db271 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1485
2023-11-24 07:05:32 +00:00
Renne Rocha 9f78c73c68 Translated using Weblate (Portuguese (Brazil))
Currently translated at 32.5% (115 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pt_BR/
2023-11-24 05:22:14 +01:00
Fábio Tramasoli 802ab78968 Translated using Weblate (Portuguese (Brazil))
Currently translated at 47.4% (775 of 1632 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pt_BR/
2023-11-24 05:22:14 +01:00
778a69cd 0530612b78 even stricter source directory filtering for better caching 2023-11-23 20:00:26 +01:00
summersamara d31a5b68ec Merge remote-tracking branch 'upstream/main' 2023-11-23 16:39:53 +01:00
Thomas Citharel 0ed48d5b0a Merge branch 'fix-docker-development' into 'main'
Fix docker development:

See merge request framasoft/mobilizon!1483
2023-11-23 14:53:53 +00:00
summersamara bfb7e3ca40 introduce VITE_HOST env var and pass it to the node watcher vite --host 2023-11-23 15:33:31 +01:00
Thomas Citharel 42ddf3f653 Merge branch 'ci-build' into 'main'
ci: Release on multiple distributions & fix Docker multiple-step build

Closes #1012

See merge request framasoft/mobilizon!1484
2023-11-23 13:35:45 +00:00
Thomas Citharel f34099d384
build(docker): optimize image size
By executing the curl call directly as the nobody user

Closes #1012

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-23 14:16:47 +01:00
Thomas Citharel b2bacbf6eb
build: downgrade Sentry since it doesn't want to compile
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-23 11:20:38 +01:00
Thomas Citharel cd53062c01
fix: add a final fallback if we have default_language: nil in instance config
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-23 11:20:38 +01:00
Thomas Citharel 262d1fcd4d
ci: Release on multiple distributions & fix Docker multiple-step build
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-23 11:20:37 +01:00
summersamara 970597876b Fix docker development:
- Specify elixir version (1.15) in Dockerfile base image

- Add npm package to Dockerfile

- Fix vite server unreachable from outside the docker container:
  issue https://framagit.org/framasoft/mobilizon/-/issues/1225
2023-11-22 20:57:50 +01:00
summersamara d668067fee fix docker development environment: Add npm package to Docker image + Add vite server config 2023-11-22 20:14:08 +01:00
Thomas Citharel 797eb2334b Merge branch 'passed-events-related' into 'main'
fix: don't show passed/finished events in related events section

See merge request framasoft/mobilizon!1482
2023-11-22 16:19:13 +00:00
summersamara 8a1b122711 remove unnecessary function 2023-11-22 16:45:43 +01:00
778a69cd 55dfb554a4 make flake output truly independent of platform 2023-11-22 12:38:33 +01:00
778a69cd 115a094b88 add nixos test for better testing, fix runtime issue 2023-11-22 12:38:17 +01:00
778a69cd 1ab82cdfb8 Merge remote-tracking branch 'origin/main' 2023-11-21 23:30:30 +01:00
summersamara 69e4a5c532 fix: don't show passed/finished events in related events section 2023-11-21 22:55:35 +01:00
778a69cd 599c4ce0f5 remove unnecessary logging 2023-11-21 22:42:27 +01:00
778a69cd 257da414bf flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/e44462d6021bfe23dfb24b775cc7c390844f773d' (2023-11-12)
  → 'github:NixOS/nixpkgs/e4ad989506ec7d71f7302cc3067abd82730a4beb' (2023-11-19)
2023-11-21 22:31:42 +01:00
summersamara 7d7f925907 Fix: don't show passed/finished events in related events section
potsda.mn/mobilizon#19
2023-11-21 22:28:04 +01:00
778a69cd 0a300ea5e5 Merge remote-tracking branch 'origin/main' 2023-11-21 22:09:20 +01:00
Thomas Citharel 1334bad8a8 Merge branch 'fixes' into 'main'
various fixes

Closes #888

See merge request framasoft/mobilizon!1481
2023-11-21 16:52:40 +00:00
Thomas Citharel c731f0f084
test: fix tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 17:29:58 +01:00
Thomas Citharel f47889b5e0
fix: set correct watcher config for E2E tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 16:58:30 +01:00
Thomas Citharel 5e86ef1e8c
fix(front): hide all categories card if we don't have even one
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 16:40:34 +01:00
Thomas Citharel 16cd377357
fix: fix Dockerfile copying assets path
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 16:40:33 +01:00
Thomas Citharel 5602164c62
build: replace @vueuse/head with @unhead/vue
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 16:40:32 +01:00
Thomas Citharel 99c80c6490
chore: upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 16:40:32 +01:00
Thomas Citharel d7daafc4ea
fix(export): fix iCalendar export description HTML conversion
Paragraphs and line breaks are now properly handled when converting from HTML to text

Closes #888

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 15:40:34 +01:00
Thomas Citharel b97f1c997f
refactor: use dedicated email for event announcements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 15:28:56 +01:00
Thomas Citharel 18314956ca
fix(api): fix allowing posting event private announcement
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 12:00:04 +01:00
Thomas Citharel 6ecfa48511
chore(i18n): add missing translation key
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-21 11:22:27 +01:00
Thomas Citharel 1f3f3e0d81 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1480
2023-11-20 17:58:39 +00:00
Weblate 9aa9cd2056 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/
2023-11-20 18:57:15 +01:00
Thomas Citharel 1228ec122f
ci: fix handling pages deploy with existing public folder
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-20 18:57:07 +01:00
Thomas Citharel 70e9ce0a26
chore(i18n): update translation templates
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-20 18:55:14 +01:00
FoW a11fab6e6d
Added translation using Weblate (Korean) 2023-11-20 18:46:49 +01:00
TA d065193546
Translated using Weblate (Indonesian)
Currently translated at 45.2% (730 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/id/
2023-11-20 18:46:31 +01:00
Jiri Podhorecky d702ca203c
Translated using Weblate (Czech)
Currently translated at 98.2% (1583 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/cs/
2023-11-20 18:46:31 +01:00
Jiri Podhorecky 9224f89d9d
Translated using Weblate (Czech)
Currently translated at 98.0% (1581 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/cs/
2023-11-20 18:46:31 +01:00
Jiri Podhorecky c14dffb234
Translated using Weblate (Czech)
Currently translated at 97.7% (1576 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/cs/
2023-11-20 18:46:30 +01:00
Thomas Citharel dcfcf066f9 Merge branch 'feat-private-messages' into 'main'
Private messages

Closes #496

See merge request framasoft/mobilizon!1477
2023-11-20 16:58:20 +00:00
Thomas Citharel 7ef85fe19b
fix(announcements): load group announcements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-20 17:35:37 +01:00
778a69cd 5b2fb5dde7 use gender-neutral language for german translation 2023-11-20 16:05:47 +01:00
Thomas Citharel b635937091
fix: various fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-20 09:35:21 +01:00
778a69cd b441fa3761 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/85f1ba3e51676fa8cc604a3d863d729026a6b8eb' (2023-11-04)
  → 'github:NixOS/nixpkgs/e44462d6021bfe23dfb24b775cc7c390844f773d' (2023-11-12)
2023-11-16 09:33:35 +01:00
778a69cd a755fd39b7 Merge remote-tracking branch 'origin/main' 2023-11-16 09:23:00 +01:00
Thomas Citharel 074099a36e Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1478
2023-11-15 18:18:28 +00:00
Jiri Podhorecky a7d70d5b44 Translated using Weblate (Czech)
Currently translated at 97.1% (1566 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/cs/
2023-11-15 19:16:11 +01:00
Thomas Citharel 3c288c5858
fix: build pictures at correct location and fix Plug.Static
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 18:15:21 +01:00
Thomas Citharel f24866012b
test: fix ActivityPub headers test
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 17:30:49 +01:00
Thomas Citharel 8d11073965
build: only run ecto create & migrate & tz_world update on prepare_test task, not main test one
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 16:37:22 +01:00
Thomas Citharel e051df1ab3
test: fix unit backend tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 16:35:40 +01:00
Thomas Citharel 5d65981dcd
ci: install python3 instead of python
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 14:53:30 +01:00
Thomas Citharel c255ceacbb
chore: fix prettier configuration and run it
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 14:46:09 +01:00
Thomas Citharel 105d3b5814
test: fix front-end tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-15 12:38:36 +01:00
Thomas Citharel bfbc299f37
test: fix histoire configuration
Nearly

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 18:59:28 +01:00
Thomas Citharel 2e72f6faf4
build: switch from yarn to npm to manage js dependencies and move js contents to root
yarn v1 is being deprecated and starts to have some issues

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 17:24:42 +01:00
Thomas Citharel 32055122c3
ci: bump node version in CI
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 15:35:37 +01:00
Thomas Citharel 3d9beaa1ca
chore(deps): upgrade dependencies
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 14:24:19 +01:00
Thomas Citharel 435bd9dccc
build: replace @pluralsh/socket with @framasoft/socket
For https://github.com/pluralsh/absinthe-socket/issues/13

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 14:24:18 +01:00
Thomas Citharel 1d0398df42
chore: update Sobelow security ignores
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 14:24:18 +01:00
Thomas Citharel ec397aa489
build(deps): replace absinthe socket library with fork
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 14:24:17 +01:00
Thomas Citharel b5672cee7e
WIP
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 14:24:17 +01:00
Thomas Citharel 0613f7f736
fix(histoire): fix URL to Framapiaf avatars
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 12:46:14 +01:00
778a69cd 4259f47ebd Merge remote-tracking branch 'origin/main' 2023-11-07 10:49:30 +01:00
Thomas Citharel 3574256025 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1476
2023-11-07 07:52:38 +00:00
FoW c529a83ff4 Added translation using Weblate (Korean) 2023-11-07 08:50:22 +01:00
Thomas Citharel 7ca54d2b91 Merge branch 'elixir-1.15' into 'main'
update geo_postgis to 3.5.0 for Elixir 1.15 compat

See merge request framasoft/mobilizon!1475
2023-11-07 07:50:14 +00:00
778a69cd 3936eb4cc5 chore(deps): update geo_postgis to 3.5.0 for Elixir 1.15 compat 2023-11-06 18:44:53 +01:00
778a69cd e7b441493d regenerate mix.nix with newer mix2nix, use elixir 1.15 2023-11-06 18:22:35 +01:00
778a69cd 036e2924b4 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/0cbe9f69c234a7700596e943bfae7ef27a31b735' (2023-10-29)
  → 'github:NixOS/nixpkgs/85f1ba3e51676fa8cc604a3d863d729026a6b8eb' (2023-11-04)
2023-11-06 18:17:25 +01:00
778a69cd 970420760b chore(deps): update geo_postgis to 3.5.0 for Elixir 1.15 compat 2023-11-06 18:14:20 +01:00
778a69cd 4bc52f527d use default beamPackages for nix build 2023-11-06 18:09:27 +01:00
778a69cd a0b001576b make flake generic over platform 2023-11-06 17:56:12 +01:00
778a69cd 4fb5be561f provide passthru.elixirPackage as derivation passthru 2023-11-06 00:24:08 +01:00
778a69cd e56858ccc8 use instance slogan in Home for non-logged-in users instead of description 2023-11-04 18:49:09 +01:00
778a69cd ef95e543f1 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/7c9cc5a6e5d38010801741ac830a3f8fd667a7a0' (2023-10-19)
  → 'github:NixOS/nixpkgs/0cbe9f69c234a7700596e943bfae7ef27a31b735' (2023-10-29)
2023-11-02 17:02:04 +01:00
778a69cd 2527ea7764 rework mobilizonLogosOverride, patch logos at build time now 2023-11-02 16:53:29 +01:00
778a69cd e31bb5d615 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/6500b4580c2a1f3d0f980d32d285739d8e156d92' (2023-09-25)
  → 'github:NixOS/nixpkgs/7c9cc5a6e5d38010801741ac830a3f8fd667a7a0' (2023-10-19)
2023-10-23 14:38:14 +02:00
778a69cd cea9b15361 mobilizonPatchLogos: fix path for logo.svg 2023-10-23 14:26:23 +02:00
778a69cd f7892cfc41 Merge remote-tracking branch 'origin/main' 2023-10-23 14:16:51 +02:00
Thomas Citharel 63a167b167 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1474
2023-10-23 06:27:30 +00:00
Jakub Urbanowicz d62c31e769 Translated using Weblate (Polish)
Currently translated at 100.0% (264 of 264 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/pl/
2023-10-23 01:11:38 +02:00
Jaime Marquínez Ferrándiz ee5ee8d2f1 Translated using Weblate (Spanish)
Currently translated at 100.0% (79 of 79 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/es/
2023-10-22 20:27:29 +02:00
Jaime Marquínez Ferrándiz 66c49e4d26 Translated using Weblate (Spanish)
Currently translated at 99.7% (352 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/es/
2023-10-22 20:27:29 +02:00
Jakub Urbanowicz a8ea217c0b Translated using Weblate (Polish)
Currently translated at 99.2% (262 of 264 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/pl/
2023-10-22 10:00:45 +02:00
Jakub Urbanowicz 42537afc6e Translated using Weblate (Polish)
Currently translated at 100.0% (79 of 79 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/pl/
2023-10-22 06:28:38 +02:00
Jakub Urbanowicz fb0a74e5ee Translated using Weblate (Polish)
Currently translated at 97.7% (258 of 264 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/pl/
2023-10-22 06:28:37 +02:00
Jakub Urbanowicz 2458076650 Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pl/
2023-10-22 06:28:37 +02:00
778a69cd c9b6653bf2 mobilizonPatchLogos: patch logo.svg 2023-10-20 20:43:18 +02:00
778a69cd 4401f32ca8 put back the regular Mobilizon logo as a default 2023-10-20 20:15:34 +02:00
778a69cd afe1cb57e3 provide mobilizonPatchLogos as a nix flake library function 2023-10-20 11:50:11 +02:00
778a69cd dad52ae432 add flake checks 2023-10-20 11:48:09 +02:00
778a69cd dad96e81bd Merge remote-tracking branch 'origin/main' 2023-10-18 14:27:10 +02:00
Thomas Citharel f8f9b4d231 Merge branch 'docs/readme-for-devs' into 'main'
docs(dev.md): keep some info about structure

See merge request framasoft/mobilizon!1473
2023-10-16 16:01:58 +00:00
Jérémy Dufraisse d130b156eb docs(dev.md): keep some info about structure 2023-10-16 14:58:18 +02:00
Thomas Citharel f451eab140 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1472
2023-10-16 10:12:50 +00:00
coronabond b5e9f62b26 Translated using Weblate (Italian)
Currently translated at 99.4% (355 of 357 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/it/
2023-10-16 10:36:01 +02:00
Thomas Citharel abeceba397 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1471
2023-10-16 07:54:23 +00:00
coronabond e8e1a6291b Translated using Weblate (Italian)
Currently translated at 99.4% (355 of 357 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/it/
2023-10-15 16:56:20 +02:00
coronabond 84fc175875 Translated using Weblate (Italian)
Currently translated at 82.1% (217 of 264 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/it/
2023-10-15 11:22:25 +02:00
coronabond 5b6438860f Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-15 11:22:24 +02:00
Vri 7732f87e0b Translated using Weblate (German)
Currently translated at 100.0% (79 of 79 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/de/
2023-10-14 02:00:48 +02:00
778a69cd 224b5607b6 Merge remote-tracking branch 'origin/main'
nix run .#update
2023-10-13 14:07:21 +02:00
Thomas Citharel 2fe7028482 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1468
2023-10-13 11:05:05 +00:00
coronabond 5e3dedb2dd Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-13 12:40:06 +02:00
coronabond afe4dd2ff8 Translated using Weblate (Italian)
Currently translated at 99.7% (352 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/it/
2023-10-13 12:40:06 +02:00
coronabond fa0ae83a79 Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-13 12:40:06 +02:00
coronabond 181a5a7955 Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-13 12:40:06 +02:00
coronabond 827caa34e9 Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-13 12:40:06 +02:00
coronabond d08d350596 Translated using Weblate (Italian)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/it/
2023-10-13 12:40:06 +02:00
coronabond e9d38c20c2 Translated using Weblate (Italian)
Currently translated at 89.8% (1448 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/it/
2023-10-13 12:40:06 +02:00
coronabond a4578f376e Translated using Weblate (Italian)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/it/
2023-10-13 12:40:06 +02:00
Thomas Citharel 0f103df25d Merge branch 'l10n-fixes' into 'main'
fix(i18n): fix typos in translation sources

See merge request framasoft/mobilizon!1470
2023-10-13 10:39:54 +00:00
Thomas Citharel d7ad934a74
chore(i18n): update gettext dependency and regenerate translation files
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-10-13 11:48:37 +02:00
Thomas Citharel 2ecd55df96
fix(i18n): fix typos in translation sources
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-10-13 10:46:09 +02:00
778a69cd 7cd4b34cb8 Merge remote-tracking branch 'origin/main' 2023-10-11 17:42:31 +02:00
Thomas Citharel 5681e6e8b8 Merge branch 'fix-ics-status' into 'main'
feat(export): add event status in iCalendar exports

See merge request framasoft/mobilizon!1469
2023-10-11 15:36:30 +00:00
Thomas Citharel 7a1bfcac49
feat(export): add event status in iCalendar exports
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-10-11 17:08:31 +02:00
Thomas Citharel da7a08d130
Merge branch 'mobilizon-derek' 2023-10-11 16:04:06 +02:00
778a69cd 0b7642b161 Merge remote-tracking branch 'origin/main' 2023-10-10 11:43:09 +02:00
Thomas Citharel 7e4310b293 Merge branch 'fix-docker-smtp-tls' into 'main'
fix(docker): allow to configure SMTP TLS

Closes #1363

See merge request framasoft/mobilizon!1467
2023-10-09 17:01:58 +00:00
Thomas Citharel 2ecdf05bf9
fix(docker): allow to configure SMTP TLS
The :tls configuration was not configurable with ENV variables, so it couldn't be changed to
:always, :never or :if_available

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-10-09 17:33:02 +02:00
Thomas Citharel 2cf4554345 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1466
2023-10-09 13:57:36 +00:00
Thomas Citharel c7ba0039f5 Translated using Weblate (French)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/fr/
2023-10-09 15:35:50 +02:00
778a69cd 6aa5c06462 Merge remote-tracking branch 'origin/main' 2023-10-07 18:59:40 +02:00
summersamara bd46f1fea1 Fix issue#7: resize start-time badge in Event card
Closes #7
2023-10-07 18:25:55 +02:00
summersamara 81c85df847 Fix issue#4: Event picture fills the card.
Closes #4
2023-10-07 18:14:56 +02:00
summersamara b624d6e4ba Remove Upcoming Online events section from homepage 2023-10-07 17:55:48 +02:00
summersamara 7abfcf2b1c Fix issue#6: compressed group's pfp.
Closes #6
2023-10-07 17:52:45 +02:00
778a69cd 240dfb4e40 remove colorful blobs from startpage
Closes #10
2023-10-07 17:29:19 +02:00
Thomas Citharel c0b3217e9a Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1465
2023-10-07 11:29:09 +00:00
Jakub Urbanowicz 46ffc8ceb6 Translated using Weblate (Polish)
Currently translated at 100.0% (1612 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-10-06 19:43:35 +02:00
Thomas Citharel 8a6d38c051 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1464
2023-10-06 13:01:27 +00:00
Jakub Urbanowicz f0d78079e1 Translated using Weblate (Polish)
Currently translated at 98.8% (1593 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-10-06 13:07:48 +02:00
Derek Zhou 02af9a437d add simplified Chinese mapping 2023-10-04 01:04:16 +00:00
778a69cd b578195553 Merge remote-tracking branch 'origin/main' 2023-10-03 20:41:18 +02:00
summersamara 70c4488f1e issue#8: Customized instance logo
- Install vite-svg-loader package
- Load svg logo from assets/logo.svg
2023-10-03 20:05:24 +02:00
summersamara a883f2467a issue#9: Increase the limit of the displayed events on home page to the max (99) 2023-10-03 18:03:41 +02:00
summersamara 49a9d27020 Fix issue#3: Display Events cards in full-width on mobile 2023-10-03 16:35:26 +02:00
summersamara 6bfaf97284 issue#2 move the search input to the nav bar 2023-10-03 15:47:33 +02:00
summersamara 7f77694d20 Fix issue#5: Bug in updating Group Settings.
Change the default value of address.timezone property.
2023-10-03 12:25:32 +02:00
summersamara 82e9493f44 Fix :public_key.cacerts_load error 2023-10-03 11:35:08 +02:00
Thomas Citharel 04315f91ea Merge branch 'sandro-main-patch-56891' into 'main'
Fix typo in ctl help text

See merge request framasoft/mobilizon!1461
2023-10-02 05:44:22 +00:00
Sandro 495d163a44 Fix typo in ctl help text 2023-10-02 02:09:56 +00:00
778a69cd 8b1598f8cc flake.lock: Update
Flake lock file updates:

• Updated input 'nix-filter':
    'github:numtide/nix-filter/d90c75e8319d0dd9be67d933d8eb9d0894ec9174' (2023-06-19)
  → 'github:numtide/nix-filter/41fd48e00c22b4ced525af521ead8792402de0ea' (2023-09-16)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b' (2023-09-08)
  → 'github:NixOS/nixpkgs/6500b4580c2a1f3d0f980d32d285739d8e156d92' (2023-09-25)
2023-09-28 12:30:18 +02:00
778a69cd 61d98308f4 Merge remote-tracking branch 'origin' 2023-09-28 12:27:34 +02:00
Thomas Citharel a5c260fb52 Merge branch 'typos' into 'main'
Fix typos

See merge request framasoft/mobilizon!1460
2023-09-25 06:11:45 +00:00
Sandro Jäckel 66e89b9ee2
Fix typos 2023-09-25 01:26:02 +02:00
Thomas Citharel 017bffc155 Merge branch 'es/update-texts' into 'main'
fix(i18n): update Spanish translations

See merge request framasoft/mobilizon!1459
2023-09-22 08:20:17 +00:00
Gonzalo Arreche cfebc355de
fix(i18n): update spanish translations
This fixes some issues with spanish translations, and tries to improve
consistency within texts.
2023-09-22 01:06:58 -03:00
Thomas Citharel e09910852a Merge branch 'refresh-remote-instance-actor-after-relay-follow' into 'main'
feat(federation): expose public activities as announcements in relay outbox and refresh profile after follow

See merge request framasoft/mobilizon!1458
2023-09-20 05:46:53 +00:00
Thomas Citharel 85e4715412
feat(federation): expose public activities as announcements in relay outbx & rfrsh profile aftr fllw
Also change ActorView to send proper HTTP error codes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-19 18:07:18 +02:00
Thomas Citharel daa68533d5 Merge branch 'fix/docker-mail-tls' into 'main'
fix(docker): convert smtp tls sni to char list

Closes #1352

See merge request framasoft/mobilizon!1456
2023-09-19 11:31:36 +00:00
Hugo Renard b3be7c6a20
fix(docker): convert smtp tls sni to char list
Signed-off-by: Hugo Renard <hugo.renard@protonmail.com>
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-19 11:45:23 +02:00
Thomas Citharel 6797ceea4b Merge branch 'fix/normalize-suggested-username' into 'main'
fix: normalize suggested username

See merge request framasoft/mobilizon!1457
2023-09-19 06:59:37 +00:00
Thomas Citharel 9a563efcff Merge branch 'fix/docker-sitemap' into 'main'
fix(docker): add sitemap folder

See merge request framasoft/mobilizon!1454
2023-09-18 15:05:45 +00:00
Hugo Renard bd38449eda
fix(docker): add sitemap folder
Signed-off-by: Hugo Renard <hugo.renard@protonmail.com>
2023-09-18 14:57:50 +02:00
Hugo Renard 4960387174
fix: normalize suggested username
oauth providers can be less strict

Signed-off-by: Hugo Renard <hugo.renard@protonmail.com>
2023-09-18 14:56:21 +02:00
778a69cd 2b01de96e6 Merge remote-tracking branch 'origin/main' 2023-09-14 11:35:16 +02:00
Thomas Citharel 8324a7b4a7 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1453
2023-09-13 08:36:22 +00:00
Timur Seber ba5f8f8a12 Translated using Weblate (Tatar)
Currently translated at 0.1% (1 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/tt/
2023-09-13 07:24:18 +02:00
Timur Seber cefdaf84f1 Added translation using Weblate (Tatar) 2023-09-13 03:12:57 +02:00
Milo Ivir e510e09e4a Translated using Weblate (Croatian)
Currently translated at 86.9% (1401 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-09-12 23:22:26 +02:00
778a69cd 7522bb6134 add disclaimer about this fork to the readme 2023-09-12 18:06:19 +02:00
778a69cd 6e456a80f3 use nixos-unstable for nixpkgs 2023-09-10 10:52:50 +02:00
778a69cd c8398b07a9 use nixpkgs master for now 2023-09-08 13:45:53 +02:00
778a69cd 5b910788b1 use upsream ex_cldr, remove some outdated overrides 2023-09-08 13:32:47 +02:00
778a69cd 45b63be9f3 remove outdated compileFlags from nix expression 2023-09-08 12:17:17 +02:00
778a69cd a74c99a731 bump version in nix expression to 3.2.0 2023-09-08 12:08:02 +02:00
778a69cd 54e09ed919 Merge remote-tracking branch 'origin/main' 2023-09-08 11:55:18 +02:00
Thomas Citharel 49e4174f85
chore(release): release 3.2.0
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 13:17:47 +02:00
Thomas Citharel 384864cc82 Merge branch 'fixes' into 'main'
fix(auth): small front fixes in 3rd-party auth provider callback

See merge request framasoft/mobilizon!1451
2023-09-07 11:12:20 +00:00
778a69cd f273c69e75 Merge remote-tracking branch 'origin/main' 2023-09-07 12:48:52 +02:00
Thomas Citharel 3d63c12e88
fix(config): rollback Mailer tls setting to :never by default
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 12:41:42 +02:00
Thomas Citharel 7e4934513a
feat(auth): pre-initialize registration fields with information from 3rd-party provider
When using a 3rd-party auth provider, we now use the given username & display name information from
the provider to fill fields from the profile RegistrationView.

Partly addresses #1105

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 12:18:46 +02:00
Thomas Citharel 7e13e2baa7
fix(front): fixes in EditIdentity view
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 11:57:09 +02:00
Thomas Citharel bde7206a1c
fix(auth): small front fixes in 3rd-party auth provider callback
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 11:56:49 +02:00
Thomas Citharel 28c4ae2e48 Merge branch 'docker-fix-port' into 'main'
fix(docker): fix entrypoint PostgreSQL extensions creations not using MOBILIZON_DATABASE_PORT

See merge request framasoft/mobilizon!1450
2023-09-07 07:27:48 +00:00
Thomas Citharel 9b4991844e
fix(docker): fix entrypoint PostgreSQL extensions creations not using MOBILIZON_DATABASE_PORT
Final step to complete #1321

Closes #1321

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-07 09:03:53 +02:00
Thomas Citharel cea66094d0 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1449
2023-09-06 12:38:22 +00:00
Thomas Citharel b755908e00 Translated using Weblate (Spanish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/es/
2023-09-06 14:15:14 +02:00
778a69cd 73aa8bd0f1 Merge remote-tracking branch 'origin/main' 2023-09-06 14:13:41 +02:00
Thomas Citharel 97439535f2
chore(release): release 3.2.0-beta.5
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-06 11:29:04 +02:00
Thomas Citharel 1c1afcb069 Merge branch 'fixes' into 'main'
Various fixes

Closes #1321

See merge request framasoft/mobilizon!1448
2023-09-06 08:24:39 +00:00
Thomas Citharel f28109ad50
fix(sitemap): save generated sitemaps in configurable directory
Previously in priv/static, now by default in production in /var/lib/mobilizon/sitemap and
configurable through the $MOBILIZON_SITEMAP_PATH ENV variable

Closes #1321

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 20:04:26 +02:00
Thomas Citharel 13099e0f11
fix(docker): make Docker entrypoint port configurable via $MOBILIZON_DATABASE_PORT
See https://framagit.org/framasoft/mobilizon/-/issues/1321

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 19:47:08 +02:00
Thomas Citharel 8c8daae006
chore(docker): bump Elixir version for Docker images
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 19:45:19 +02:00
Thomas Citharel 895378a96b
fix(front): fix fetching and rendering profile mentions and fetching tags
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 19:28:36 +02:00
778a69cd 71d2a50356 Merge remote-tracking branch 'origin/main' 2023-09-05 17:41:49 +02:00
Thomas Citharel a8ddc90ea2
chore(release): fix a typo in CHANGELOG.md
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 16:37:01 +02:00
Thomas Citharel 2881717f6d
chore(release): release 3.2.0-beta.4
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 16:36:22 +02:00
Thomas Citharel f3ea98822b Merge branch 'fixes' into 'main'
Various fixes

See merge request framasoft/mobilizon!1447
2023-09-05 14:34:23 +00:00
Thomas Citharel 23adeaee47
ci(release): bump release buildpack to 1.15.5-erlang-26.0.2-debian-buster
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 16:33:41 +02:00
Thomas Citharel 92b222b091
fix(back): allow any other type of actor to be suspended
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 16:03:45 +02:00
Thomas Citharel 8c14ba441c
fix(front): don't return promise if result is not finished loading for tags
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 15:55:13 +02:00
Thomas Citharel 31b2d065a9
fix(front): fix getting result from interactable object in InteractView
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 15:52:27 +02:00
Thomas Citharel cfc984345e
fix(back): only try to insert activities for groups
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-05 15:35:04 +02:00
778a69cd 311c989167 Merge remote-tracking branch 'origin/main'
nix run .#update
2023-09-05 11:36:53 +02:00
Thomas Citharel b86928b6b3
chore(release): release 3.2.0-beta.3
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-04 14:09:00 +02:00
Thomas Citharel 814927a213
chore(deps): upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-04 14:08:37 +02:00
Thomas Citharel ed5232bb84 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1446
2023-09-04 07:20:55 +00:00
Vri 626cdc01b8 Translated using Weblate (German)
Currently translated at 92.9% (1498 of 1612 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/de/
2023-09-03 20:40:09 +02:00
778a69cd 25836b5971 Merge remote-tracking branch 'origin/main' 2023-09-02 14:48:50 +02:00
Thomas Citharel 19f595c2d8 Merge branch 'external-events' into 'main'
External events

See merge request framasoft/mobilizon!1229
2023-09-01 16:42:29 +00:00
Thomas Citharel f6611e8eb5
feat(back): add admin setting to disable external event feature
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 18:16:40 +02:00
Thomas Citharel af670f3947
fix(i18n): add missing translations
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 18:16:39 +02:00
Luca Eichler 2de6937407
feat: Add option to link an external registration provider for events
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 18:16:35 +02:00
Thomas Citharel 4fb1282e76 Merge branch 'allow-group-creation-with-HTML-description-and-location' into 'main'
improve group creation view

See merge request framasoft/mobilizon!1248
2023-09-01 14:06:44 +00:00
setop 3f60174877 improve group creation view 2023-09-01 14:06:44 +00:00
Thomas Citharel 4ce79f5136
chore(release): release 3.2.0-beta.2
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 14:08:04 +02:00
Thomas Citharel 7c019e59c6 Merge branch 'fixes' into 'main'
ci: fix Gitlab CI exunit run by separating mix compile and tz_world.update

See merge request framasoft/mobilizon!1445
2023-09-01 10:51:35 +00:00
778a69cd 239ca025bb Merge remote-tracking branch 'origin/main' 2023-09-01 12:43:33 +02:00
Thomas Citharel 84f62cd043
fix(front): fix behavior of local toggle for profiles & groups view depending on domain value
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 12:17:27 +02:00
Thomas Citharel 5999252e02
ci: fix Gitlab CI exunit run by separating mix compile and tz_world.update
Reference https://github.com/kimlai/tz_world/issues/33

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 12:14:36 +02:00
Thomas Citharel 89b57a5ec1
chore(release): release 3.2.0-beta.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-09-01 11:50:10 +02:00
Thomas Citharel bd4f61cc1a Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1444
2023-09-01 06:45:51 +00:00
ewm fdda088b3d Translated using Weblate (Polish)
Currently translated at 99.0% (1591 of 1607 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-09-01 01:21:04 +02:00
Weblate 3d4999e7e2 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/
2023-08-31 23:16:58 +02:00
Thomas Citharel e3d9a76074 Merge branch 'report-multiple-events' into 'main'
feat(reports): allow reports to hold multiple events

See merge request framasoft/mobilizon!1407
2023-08-31 21:16:40 +00:00
Thomas Citharel afd2ffe722
fix(reports): make front-end handle nullified reported_id and reported_id
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 19:06:40 +02:00
Thomas Citharel b9a165a7fc
feat(reports): show suspended status next to reported profile
And only show the suspend button if the profile isn't already suspended (or the user disabled)

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 19:04:12 +02:00
Thomas Citharel 4f530cabcf
fix(reports): remove on delete cascade for reports
Deleting an actor should not remove the reports

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 19:04:12 +02:00
Thomas Citharel 69588dbf4c
feat(reports): allow to suspend a profile or a user account directly from the report view
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 17:08:55 +02:00
Thomas Citharel b105c508c0
feat(reports): improve reportview and allow removing content + resolve report automatically
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 14:37:54 +02:00
Thomas Citharel f2ac3e2e5d
feat(reports): allow reports to hold multiple events
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-31 10:43:36 +02:00
Thomas Citharel 538139eefa Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1443
2023-08-30 07:32:13 +00:00
Milo Ivir 482369d5fe Translated using Weblate (Croatian)
Currently translated at 87.9% (1393 of 1584 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-08-30 00:56:15 +02:00
778a69cd 0aada9ae98 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:erictapen/nixpkgs/6f43a2f938963cfebc376da771edfa0953585b4e' (2023-08-29)
  → 'github:erictapen/nixpkgs/afd2449b859efa913603181b8f21844a2db37cee' (2023-08-29)
2023-08-29 17:32:20 +02:00
778a69cd 5e4a36b4e4 use workaround for mkYarnPackage patch
flake.lock: Update

Flake lock file updates:

• Updated input 'nixpkgs':
    'github:erictapen/nixpkgs/1b8435e4c7386212d892e057620c8c167ae17adc' (2023-08-03)
  → 'github:erictapen/nixpkgs/6f43a2f938963cfebc376da771edfa0953585b4e' (2023-08-29)
2023-08-29 16:31:55 +02:00
778a69cd f7fb0283cb Merge remote-tracking branch 'origin/main' 2023-08-29 15:01:32 +02:00
Thomas Citharel 4fc1cfc3a2 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1442
2023-08-28 17:32:35 +00:00
Jakub Urbanowicz cd1549752b Translated using Weblate (Polish)
Currently translated at 100.0% (254 of 254 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/pl/
2023-08-28 19:06:14 +02:00
Jakub Urbanowicz 5e49b5457b Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pl/
2023-08-28 19:06:13 +02:00
Jakub Urbanowicz ccc3d228e4 Translated using Weblate (Polish)
Currently translated at 100.0% (1584 of 1584 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-28 19:06:13 +02:00
778a69cd 779563d767 remove js/yarn.nix, as it can be generated at buildtime in the Nix derivation 2023-08-28 15:11:58 +02:00
778a69cd d02d62bd0d Merge remote-tracking branch 'origin/main' 2023-08-25 13:51:29 +02:00
Thomas Citharel b1aa9ea92d Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1441
2023-08-25 07:20:00 +00:00
Jakub Urbanowicz 2bf45348cc Translated using Weblate (Polish)
Currently translated at 100.0% (1584 of 1584 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-24 22:52:53 +02:00
Thomas Citharel a8ac41bfb8 Merge branch 'fix-push-subscriptions-registration' into 'main'
Various fixes

Closes #1343 and #1344

See merge request framasoft/mobilizon!1440
2023-08-24 16:36:59 +00:00
Thomas Citharel da532c7059
feat(notifications): add missing notifications when an user registers to an event
Closes #1344

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 17:36:58 +02:00
Thomas Citharel f99267c611
fix(front): fix confirm anonymous participation
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 17:36:58 +02:00
Thomas Citharel beef3ff16d
fix(front): fix selecting all participants in participant view
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 17:36:58 +02:00
Thomas Citharel 010a5e426d
fix(front): fix changing language not being saved to the user's settings
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 17:36:58 +02:00
Thomas Citharel fef60ed0f9
feat(export): add date of participant creation in participant exports
Closes #1343

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 17:36:58 +02:00
Thomas Citharel 467f1ec60f Merge branch 'JocelynD-main-patch-99496' into 'main'
Fix typo in french translation

See merge request framasoft/mobilizon!1439
2023-08-24 10:20:00 +00:00
Thomas Citharel 8617382af2
test: add tests for push notification registration GraphQL resolver
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 11:49:19 +02:00
Thomas Citharel fdf87ea991
fix(push): fix push subscriptions registration
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-24 11:49:19 +02:00
Jocelyn fa94d145d3 Fix typo in french translation 2023-08-23 16:47:00 +00:00
778a69cd 340aba5624 Merge remote-tracking branch 'origin/main' 2023-08-23 13:55:14 +02:00
Thomas Citharel 7cc4abd47b Merge branch 'fix-user-clitests' into 'main'
test: fix user deletion CLI tests and bring back the -k option

See merge request framasoft/mobilizon!1438
2023-08-23 06:26:13 +00:00
Thomas Citharel d2490f9304
test: fix user deletion CLI tests and bring back the -k option
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-23 08:24:28 +02:00
Thomas Citharel f61001f81e Merge branch 'correctly-skip-tests' into 'main'
test: correctly skip invalid tests

See merge request framasoft/mobilizon!1436
2023-08-22 11:57:47 +00:00
Thomas Citharel 860b4eb9a3 Merge branch 'multi-user-deletion' into 'main'
feat(cli): allow the mobilizon.users.delete command to delete multiple users by email domain or ip

See merge request framasoft/mobilizon!1437
2023-08-22 08:33:13 +00:00
Thomas Citharel bc50ab66f3
feat(cli): allow the mobilizon.users.delete command to delete multiple users by email domain or ip
Also allow to delete groups the user's actors are admins of

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-22 10:28:38 +02:00
Thomas Citharel 3d491fc034
test: correctly skip invalid tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-21 16:14:34 +02:00
778a69cd 95a69ab1f4 Merge remote-tracking branch 'origin/main' 2023-08-21 13:57:45 +02:00
Thomas Citharel d29f1e1ee2 Merge branch 'fix-arm-build' into 'main'
Fix Docker build on arm64

Closes #1249 et #1241

See merge request framasoft/mobilizon!1435
2023-08-21 11:52:01 +00:00
Thomas Citharel 8e3f90f713
fix(docker): fix Qemu segfaulting on arm64
Closes #1241 #1249

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-21 12:09:34 +02:00
778a69cd 8d9ddbdfe8 Merge remote-tracking branch 'origin/main'
nix run .#update

added cacerts_load check to build inside Nix sandbox
2023-08-19 18:12:41 +02:00
Thomas Citharel 98470f3d8d
build: remove traces of cypress
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 17:46:49 +02:00
Thomas Citharel c987d7b2e7
test: disable tests that fail unexpicably for now
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 16:20:05 +02:00
Thomas Citharel 64c28ec271 Merge branch 'main' into 'main'
add static location for img

See merge request framasoft/mobilizon!1346
2023-08-18 13:54:21 +00:00
Thomas Citharel 5160c3e526 Merge branch 'mobilizon-fork-ui-margins' into 'main'
UI: Minor tweaks and fixes

See merge request framasoft/mobilizon!1434
2023-08-18 13:51:12 +00:00
Thomas Citharel f3c218f841
fix(front): reduce horizontal padding on main element
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 15:17:33 +02:00
Thomas Citharel 8c313b5397
fix(front): fix some alignment of some UI elements in mobile event view
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 15:17:33 +02:00
JonFreer 7cbbf84217
ui: dialog width and account @ alignment 2023-08-18 15:17:33 +02:00
JonFreer de30b46a87
ui: participate button align right 2023-08-18 15:17:32 +02:00
JonFreer 7688bf88ee
ui: event buttons allignment 2023-08-18 15:17:32 +02:00
JonFreer 95f3be2da9
ui: button height to match inputs 2023-08-18 15:17:32 +02:00
JonFreer f04907c2a6
ui: margins on main component 2023-08-18 15:17:32 +02:00
JonFreer 15cf103dfa
ui: rounded inputs 2023-08-18 15:17:31 +02:00
JonFreer abd609ecc8
ui: create events page margin 2023-08-18 15:17:31 +02:00
Thomas Citharel 75dddeb792 Merge branch 'otp26-mails' into 'main'
Fix sending mails on OTP26

Closes #1341

See merge request framasoft/mobilizon!1432
2023-08-18 13:02:38 +00:00
778a69cd f52ddfffa2 Merge remote-tracking branch 'origin/main'
nix run .#update
2023-08-18 15:02:28 +02:00
Thomas Citharel 4820307dad
build(deps): rollback tz_world to 1.3.0 to fix module :ssl_cipher is not available
Reference https://github.com/kimlai/tz_world/issues/33

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 14:43:03 +02:00
Thomas Citharel f54fff56fc
fix(mail): fix sending mail on OTP26
For TLS connections specifying cacerts & server_name_indication is now required

Closes #1341

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 13:41:46 +02:00
Thomas Citharel 3de4d84329
test: refactor resend confirmation email tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 13:41:46 +02:00
Thomas Citharel b2492a3870
fix(backend): handle email not being sent when resending registration instructions
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 13:41:46 +02:00
Thomas Citharel 52b3e5b151 Merge branch 'fix-config-cache' into 'main'
fix(backend): fix config cache not being used everytime

See merge request framasoft/mobilizon!1433
2023-08-18 11:41:06 +00:00
Thomas Citharel af46bea7f7
fix: add inets and ssl to extra_applications in test env
Ref https://github.com/voltone/x509/issues/63 and https://github.com/voltone/x509/pull/64/files
Issue: https://github.com/phoenixframework/phoenix/issues/5502

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-18 12:29:10 +02:00
Thomas Citharel 7cc9a37c78
test: fix depreciated warn log levels in tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 17:44:13 +02:00
Thomas Citharel 1b0a7499f8
test(export): fix exporting participants CSV
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 16:35:26 +02:00
Thomas Citharel c49d816253
chore(deps): upgrade minimum Elixir version to 1.13
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 15:40:57 +02:00
Thomas Citharel e84da72ab9
build(deps): upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 15:40:29 +02:00
Thomas Citharel 7bbdae9101
build: update tests Dockerfile
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 14:50:44 +02:00
Thomas Citharel 107bab33c9
style: run mix format
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 14:50:44 +02:00
Thomas Citharel 675ac38289
style: remove leftover comment
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 14:20:28 +02:00
Thomas Citharel ed3cd5858c
fix(backend): fix config cache not being used everytime
When loading a page, we inject various meta tags in the HTML. These made database calls everytime

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-17 14:20:28 +02:00
778a69cd 922ce515f9 Merge remote-tracking branch 'origin/main' 2023-08-16 13:29:05 +02:00
Thomas Citharel a6721ec4ae Merge branch 'mobilizon-fork-ui-image-upload' into 'main'
ui: redesign image upload section

See merge request framasoft/mobilizon!1430
2023-08-16 09:33:45 +00:00
Thomas Citharel 21149cee65
refactor(front): remove unused CSS code
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-16 11:21:33 +02:00
Jon Freer 1c7383235d
Add horizontal tag to form item in profile settings 2023-08-16 11:21:33 +02:00
JonFreer b1f2d4e22d
ui: redesign image upload section 2023-08-16 11:21:33 +02:00
Thomas Citharel da40a63737 Merge branch 'setting-theme-jitter' into 'main'
Fix jitter when toggling "adapt to system theme"

Closes #1339

See merge request framasoft/mobilizon!1428
2023-08-16 09:01:36 +00:00
Sosthène Guédon d037642f55 Fix jitter when toggling "adapt to system theme"
This patch makes the border around the "dark" and "light" radio button to always be present.
Previously it was not present when the button were disabled because using the system theme,
which caused it to make the elements larger and made them move when toggling "adapt to system theme"

To keep the same meaning as previously, the border is only colored when the button is activated

Fix #1339
2023-08-13 16:13:07 +02:00
778a69cd 223eca1fa3 Merge remote-tracking branch 'origin/main' 2023-08-13 00:10:13 +02:00
Thomas Citharel 22e3e30f8f Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1427
2023-08-12 05:10:38 +00:00
Jakub Urbanowicz 5d32370a6f Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pl/
2023-08-12 03:06:10 +02:00
Jakub Urbanowicz 9072403eb5 Translated using Weblate (Polish)
Currently translated at 100.0% (1581 of 1581 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-12 03:06:10 +02:00
778a69cd 21e22e9514 Merge remote-tracking branch 'origin/main' 2023-08-11 17:44:33 +02:00
Thomas Citharel 6e7701b3c8 Merge branch 'mobilizon-fork-ui-input-border-box' into 'main'
fix: create event time/date allignment

Closes #1336

See merge request framasoft/mobilizon!1426
2023-08-11 13:52:52 +00:00
Thomas Citharel 50695fcfd5
fix(front): fix alignment of some input elements on event edition form
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-11 15:38:24 +02:00
JonFreer 3de90a3c73
fix: create event time/date allignment 2023-08-11 15:23:04 +02:00
778a69cd 33094bb160 Merge remote-tracking branch 'origin/main' 2023-08-11 13:39:52 +02:00
778a69cd a6b29f1745 Revert "reintroduce webmanifest for PWA compatability"
This reverts commit c1299bddf6.
2023-08-11 13:39:48 +02:00
778a69cd d1d49d827e Revert "webmanifest: add theme_color"
This reverts commit 3f747459d1.
2023-08-11 13:39:37 +02:00
Thomas Citharel 027013eba3 Merge branch 'mobilizon-webmanifest' into 'main'
reintroduce webmanifest for PWA compatability

See merge request framasoft/mobilizon!1424
2023-08-11 10:59:54 +00:00
Thomas Citharel e351d3cb2f
fix: fix some typescript issues with pwa
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-11 12:23:16 +02:00
Thomas Citharel 04c5ac1163
fix(pwa): improvements to the PWA configuration
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-11 12:23:16 +02:00
778a69cd 2e08aa2573
reintroduce webmanifest for PWA compatability 2023-08-11 12:23:16 +02:00
Thomas Citharel 45a1a3a2dd Merge branch 'respect-endtime' into 'main'
Display events on startpage until their end time, unless they have no end time

See merge request framasoft/mobilizon!1423
2023-08-11 10:01:25 +00:00
Thomas Citharel 1316a9e350 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1420
2023-08-11 09:27:45 +00:00
778a69cd 951ff96b2a show events until their end time, unless they have no end time 2023-08-11 00:15:56 +02:00
778a69cd 3f747459d1 webmanifest: add theme_color 2023-08-11 00:06:34 +02:00
778a69cd 733d58a49b Merge remote-tracking branch 'origin/main'
nix run .#update
2023-08-10 23:49:12 +02:00
778a69cd 17f59c866e add some notes about this fork to the readme 2023-08-10 23:48:20 +02:00
778a69cd c1299bddf6 reintroduce webmanifest for PWA compatability 2023-08-10 23:48:12 +02:00
778a69cd 3cc51c015f remove search field for location 2023-08-10 21:11:41 +02:00
778a69cd 634438da38 remove Mobilizon advertisement from below the recent events on the start page 2023-08-10 21:11:14 +02:00
778a69cd 1f9ebc8f14 better src separation in flake, for fewer rebuilds 2023-08-10 20:43:08 +02:00
778a69cd e6c6e0c0e2 remove advertisement for Mobilizon above the search bar 2023-08-10 20:30:13 +02:00
Jakub Urbanowicz 90dca68a46 Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz d9b34e4ebc Translated using Weblate (Polish)
Currently translated at 100.0% (1569 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz d8ef2e0690 Translated using Weblate (Polish)
Currently translated at 100.0% (77 of 77 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz bac255136b Translated using Weblate (Polish)
Currently translated at 100.0% (254 of 254 strings)

Translation: Mobilizon/Backend errors
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend-errors/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz 1901bb64ae Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)

Translation: Mobilizon/Backend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/backend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz b98156854e Translated using Weblate (Polish)
Currently translated at 100.0% (1569 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz add5ce1efc Translated using Weblate (Polish)
Currently translated at 83.4% (1310 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz 1f7503f6dd Translated using Weblate (Polish)
Currently translated at 80.1% (1258 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz d8a6b551ea Translated using Weblate (Polish)
Currently translated at 60.1% (944 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz 56d6395a5d Translated using Weblate (Polish)
Currently translated at 100.0% (77 of 77 strings)

Translation: Mobilizon/Activity
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/activity/pl/
2023-08-10 18:30:30 +02:00
Jakub Urbanowicz 8faf9a5c79 Translated using Weblate (Polish)
Currently translated at 56.0% (880 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/pl/
2023-08-10 18:30:30 +02:00
Vodoyo Kamal 0a0f18ae9c Translated using Weblate (Bengali)
Currently translated at 2.1% (33 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/bn/
2023-08-10 18:30:30 +02:00
Thomas Citharel a3d885e6b7 Merge branch 'fix-api' into 'main'
Fix apps authorization

Closes #1314

See merge request framasoft/mobilizon!1421
2023-08-10 16:30:20 +00:00
Thomas Citharel 7e98097c71
fix(apps): add missing app scopes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel 1a6095d27a
fix(apps): make sure we can set status for an application token
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel 086d208ee5
fix(front): properly handle error when approving app
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel 2c8c332ad0
chore(deps): replace use of custom erlport with upstream one
Now that upstream has the fix

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel 12cbff154a
fix(front): fix showing error message when app to approve doesn't exist
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel c76dba3dbf
fix(front): fix missing type causing eslint error
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel c6b83c42d6
fix(front): fix display of participants list
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:01 +02:00
Thomas Citharel cc8f02d0a6
fix(front): fix comment not showing up when replying in a discussion
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:00 +02:00
Thomas Citharel fee0e388af
fix(front): fix discussion edition panel always showing up
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:00 +02:00
Thomas Citharel 8f84ba1d08
fix(front): fix map
Closes #1314

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:00 +02:00
Thomas Citharel 60aceb442a
fix(lint): fix lint after upgrades
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-10 18:02:00 +02:00
johndoe4 77274ed29d Fix Eventview Rows in 3.1.0
(cherry picked from commit 113ea75ed6)
2023-08-10 16:46:43 +02:00
johndoe4 bfcdae945f Footer: Remove Picture & change bg-color
(cherry picked from commit 9154c24332)
2023-08-10 16:45:47 +02:00
johndoe4 a722d71dae Uncomment unnecessary stuff from HomeView
(cherry picked from commit e9680509bf)
2023-08-10 16:45:25 +02:00
778a69cd 3bf7db8caa less redundancy in flake.nix 2023-08-10 14:29:06 +02:00
778a69cd d4bee06d24 enable tests for js flake build 2023-08-10 14:09:50 +02:00
778a69cd 7a1390825f make flake compile without setting --no-validate-compile-env 2023-08-10 14:09:05 +02:00
778a69cd 51ee15e659 add nix update script 2023-08-09 16:03:20 +02:00
778a69cd 8038f967cc filter *.nix files as source arguments for lesser rebuilds 2023-08-09 15:48:22 +02:00
778a69cd 6deb3f4226 nixpkgs-fmt 2023-08-09 15:28:38 +02:00
778a69cd 2c5b4ca3bc ignore nix out-links 2023-08-09 15:28:06 +02:00
778a69cd 3a8c1074c0 build latest main with nix 2023-08-09 15:22:49 +02:00
778a69cd d27bd79178 build js as own flake output 2023-08-03 18:08:15 +02:00
778a69cd e046a6ef16 move nix package definition directly into fork 2023-08-03 17:06:58 +02:00
778a69cd 11c1590cac flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/963006aab35e3e8ebbf6052b6bf4ea712fdd3c28' (2023-05-16)
  → 'github:NixOS/nixpkgs/66aedfd010204949cb225cf749be08cb13ce1813' (2023-08-02)
2023-08-03 16:52:29 +02:00
778a69cd 496ba488b6 Merge remote-tracking branch 'origin/main' 2023-08-03 16:43:36 +02:00
Thomas Citharel 7916261c5c
fix(front): avoid crashing if we don't have configuration data in time when in guard
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-02 10:06:06 +02:00
Thomas Citharel da70427e32
fix: fix Elixir 1.15 depreciations
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-02 09:59:09 +02:00
Thomas Citharel 5373f1378f
chore(deps): upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-08-02 09:58:45 +02:00
Thomas Citharel 5ac1d9048a Merge branch 'fix-profile-pics-gruppe' into 'main'
fix(federation): fix getting pictures from Gruppe actors

See merge request framasoft/mobilizon!1417
2023-06-29 18:56:38 +00:00
Thomas Citharel 25a53b44dc Merge branch 'fix-post-not-found' into 'main'
fix(front): properly handle post not found

See merge request framasoft/mobilizon!1418
2023-06-29 18:29:07 +00:00
Thomas Citharel 8db31c99df
fix(front): properly handle post not found
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-29 19:45:54 +02:00
Thomas Citharel 7c5f8b2431
fix(federation): fix getting pictures from Gruppe actors
They directly put urls for icon/image properties

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-29 19:30:13 +02:00
Thomas Citharel 1858580714 Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1416
2023-06-29 16:58:38 +00:00
Milo Ivir edb0445137 Translated using Weblate (Croatian)
Currently translated at 87.2% (1369 of 1569 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/hr/
2023-06-27 18:07:28 +02:00
Thomas Citharel 8f4333b528
chore(release): release 3.1.3
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-21 18:31:51 +02:00
Thomas Citharel 4b8f54a31b Merge branch 'fix' into 'main'
fix(groups): fix unauthenticated access to groups because of missing read:group:members permission

Closes #1311

See merge request framasoft/mobilizon!1415
2023-06-21 16:31:18 +00:00
Thomas Citharel 3714925896
fix(groups): fix unauthenticated access to groups because of missing read:group:members permission
The permission in question is now removed

Closes #1311

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-21 18:09:45 +02:00
Thomas Citharel 93f175da2c
chore(release): release 3.1.2
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-21 07:49:56 +02:00
Thomas Citharel 0f0fca0b9f Merge branch 'weblate-mobilizon-frontend' into 'main'
Translations update from Framasoft Weblate

See merge request framasoft/mobilizon!1412
2023-06-20 20:44:56 +00:00
Jiri Podhorecky e2d58906e3 Translated using Weblate (Czech)
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Mobilizon/Frontend
Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/cs/
2023-06-20 18:09:32 +02:00
Thomas Citharel e46a38ca2a Merge branch 'fixes' into 'main'
Various fixes

Closes #1303

See merge request framasoft/mobilizon!1414
2023-06-20 16:09:24 +00:00
Thomas Citharel a1f5d1dacc
test(federation): fix APControllerTest failing because of datetime different when we reach 2nd page
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 17:40:50 +02:00
Thomas Citharel f749518bf7
fix(group): fix getting group members count
Closes #1303

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel f04d2b9225
fix(backend): filter out nil tags before starting looking for existing ones
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel d5021647d7
fix(federation): allow federated usernames with capitals
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel 56f341e960
fix(federation): ignore mentions from everything that's not a AP Person
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel 2729d5ed7a
fix(federation): handle string values for tags when constructing mentions
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel 552ab4c80b
fix(federation): handle fetch_actor with a map
For some reason

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel 5cc5c9943c
fix(participant): handle re-confirming participation
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel 02098bbb3d
test(export): add basic test for ExportController
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:37 +02:00
Thomas Citharel a76b1ca66d
fix(exports): properly handle export format not being handled
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 16:55:36 +02:00
Thomas Citharel 96b4ef08c6
style: unused variable
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 14:59:57 +02:00
Thomas Citharel ca06ec397f
fix(discussions): handle changeset errors when updating discussion
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 14:49:18 +02:00
Thomas Citharel f2ffcfec5b Merge branch 'fixes' into 'main'
Participant fixes

See merge request framasoft/mobilizon!1413
2023-06-20 10:40:30 +00:00
Thomas Citharel 49b04c9b19
fix(participant exports): fix participants by returning the export type as well as the file path
We previously used the Apollo context but that's really unreliable.

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 12:11:52 +02:00
Thomas Citharel a5a86a5e1b
fix(front): add missing title to Participants View page
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 12:09:47 +02:00
Thomas Citharel d63999c081
fix(deps): fix compatibility with elixir-plug/mime 2.0.5
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 12:06:38 +02:00
Thomas Citharel 45ea9cc774
chore(deps): upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-06-20 11:36:24 +02:00
778a69cd b507b2ddd3 Revert "read logo from separate svg file for easier customization"
This reverts commit 67765a645e.
2023-05-29 15:58:35 +02:00
778a69cd 4e757b0f9e Merge remote-tracking branch 'origin/main' 2023-05-29 14:19:06 +02:00
johndoe4 1041cf7cd7 Add Venue-Name to InlineAddress(for Eventcard) 2023-05-26 22:53:15 +02:00
778a69cd 88e11925df Merge remote-tracking branch 'origin/main' 2023-05-26 14:57:41 +02:00
778a69cd 0d804cba1f include nodejs in devShell 2023-05-24 17:16:01 +02:00
778a69cd 4284e8e635 Merge remote-tracking branch 'origin/main' 2023-05-24 13:00:37 +02:00
Thomas Citharel 7defbe6bc9 chore(changelog): fix changelog version and move an element in the correct section
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-05-23 14:37:13 +02:00
Thomas Citharel 26e22edfc0 build(docker-tests): Update test container
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-05-23 14:37:13 +02:00
Thomas Citharel ba3a52cd4b build(docker): Fix production Dockerfile
Make sure every data folder properly exists

See https://forge.tedomum.net/tedomum/mobilizon/-/issues/14

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-05-23 14:37:13 +02:00
johndoe4 08764fff72 Adding Starttime to Eventcards and Header 2023-05-23 01:30:53 +02:00
johndoe4 a50b9128fe Adding Weekday to .datetime-container-header 2023-05-22 23:12:55 +02:00
johndoe4 be8a206b0d Things commented out which in my opinion are no longer needed 6bcff6d1ae 2023-05-22 00:52:05 +02:00
johndoe4 04e7a2e02d revert 96871853d1
revert Removed some unnecessary stuff
2023-05-22 00:28:49 +02:00
johndoe4 96871853d1 Removed some unnecessary stuff 2023-05-22 00:22:45 +02:00
johndoe4 6bcff6d1ae Use Columns and Rows instead of scrolling 2023-05-22 00:21:09 +02:00
778a69cd 7127b0da28 Merge remote-tracking branch 'origin/main' 2023-05-17 17:51:20 +02:00
778a69cd cba53e2e9d remove phoenix.json_library config 2023-05-17 17:35:11 +02:00
778a69cd 1fac112942 mix deps.update ex_cldr ex_cldr_numbers 2023-05-17 17:19:32 +02:00
johndoe4 c5ecaf5d7c Fix for Commit 2072402e98
- forgot to change the variable name
2023-05-17 16:55:14 +02:00
johndoe4 974c73e071 Temp. Fix: mbz-cards didnt get a fixed (max) width on small screens
- Add a max-width and width for mbz-card in App.vue
2023-05-17 00:59:36 +02:00
johndoe4 2072402e98 Adding UpcomingEvents.vue & include it to HomeView.vue
- Will show all upcoming Events sorted by Date on the Startpage
2023-05-17 00:52:22 +02:00
summersamara 67765a645e read logo from separate svg file for easier customization 2023-05-16 21:19:49 +02:00
summersamara f887455d66 make vite server reachable from outside the container
[fix #1225] https://framagit.org/framasoft/mobilizon/-/issues/1225
2023-05-16 21:19:49 +02:00
778a69cd d52dc72f9a flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/0470f36b02ef01d4f43c641bbf07020bcab71bf1' (2023-05-14)
  → 'github:NixOS/nixpkgs/963006aab35e3e8ebbf6052b6bf4ea712fdd3c28' (2023-05-16)
2023-05-16 20:47:34 +02:00
778a69cd 0b73264392 add yarn2nix to flake.nix 2023-05-16 20:45:49 +02:00
778a69cd cc89e9f98e Merge branch 'potsda.mn-3.0.4' 2023-05-16 20:41:30 +02:00
778a69cd 609512a9ed use latest nixos-unstable in devShell, add mix2nix 2023-05-16 17:32:48 +02:00
summersamara 773ddd22a7 add elixir version 1.13.4 to Dockerfile for dev 2023-05-16 17:32:48 +02:00
778a69cd 8e00c78a11 use yarn with an older nodejs version in devShell 2023-05-16 17:32:48 +02:00
778a69cd d830597206 use nixpkgs from september 2022 for nix devShell 2023-05-16 17:32:47 +02:00
778a69cd 438f1dde3e init nix flake 2023-05-16 17:32:47 +02:00
778a69cd d2af20dd59 show events until their end time, unless they have no end time 2023-05-16 17:32:47 +02:00
778a69cd aa043d8793 fix wrong link in instance follow request email
this is upstreamable
2023-05-16 17:32:47 +02:00
778a69cd 23378a378a add Mobilizon version to instance config 2023-05-16 17:32:47 +02:00
778a69cd 0824694c56 Revert "default anonymous participation in events to true"
This reverts commit 3a1b7a4660b4fd9488e7d06e677027ae41e0c10c.
2023-05-16 17:32:47 +02:00
778a69cd dcd097bf6d default anonymous participation in events to true 2023-05-16 17:32:47 +02:00
778a69cd d53f7b93e9 Use our own categories for event creation 2023-05-16 17:32:47 +02:00
778a69cd 1b9aafa855 remove pictures for error and 404 site 2023-05-15 19:36:58 +02:00
Thomas Citharel 0837090e30
Bump version to 3.0.4
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-03-20 18:26:59 +01:00
Thomas Citharel 5bb09927a3 Merge branch 'v3.0.4' into 'stable-3.0'
Fix LDAP and participant export

See merge request framasoft/mobilizon!1362
2023-03-20 16:14:38 +00:00
Thomas Citharel 20fc9d1f6b
Fetch config for export event participant formats
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-03-20 16:39:27 +01:00
Thomas Citharel cef536d5b9
Fix LDAP authentificator tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-03-20 13:07:02 +01:00
ljf 48ebdbb03a
[fix] LDAP connector with erland 24.3+ 2023-03-20 12:26:06 +01:00
Jakobus Schürz 8cae6d30c0 add static location for img
fixes not shown mobilizon image on "sorry-page" when service not
running.
2022-01-19 21:47:34 +01:00
1035 changed files with 52322 additions and 25634 deletions

View file

@ -22,7 +22,7 @@
# In the latter case `**/*.{ex,exs}` will be used. # In the latter case `**/*.{ex,exs}` will be used.
# #
included: ["lib/", "src/", "test/"], included: ["lib/", "src/", "test/"],
excluded: [~r"/_build/", ~r"/deps/", ~r"/js/"] excluded: [~r"/_build/", ~r"/deps/", ~r"/src/"]
}, },
# #
# If you create your own checks, you must specify the source files for # If you create your own checks, you must specify the source files for
@ -33,7 +33,7 @@
# If you want to enforce a style guide and need a more traditional linting # If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below: # experience, you can change `strict` to `true` below:
# #
strict: false, strict: true,
# #
# If you want to use uncolored output by default, you can change `color` # If you want to use uncolored output by default, you can change `color`
# to `false` below: # to `false` below:
@ -160,6 +160,7 @@
# #
{Credo.Check.Warning.LazyLogging, false}, {Credo.Check.Warning.LazyLogging, false},
{Credo.Check.Refactor.MapInto, false}, {Credo.Check.Refactor.MapInto, false},
{Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, false}
] ]
} }
] ]

View file

@ -1,44 +1,46 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/elixir-phoenix-postgres // https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/elixir-phoenix-postgres
{ {
"name": "Elixir, Phoenix, Node.js & PostgresSQL (Community)", "name": "Elixir, Phoenix, Node.js & PostgresSQL (Community)",
"dockerComposeFile": "docker-compose.yml", "dockerComposeFile": "docker-compose.yml",
"service": "elixir", "service": "elixir",
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
// Set *default* container specific settings.json values on container create. // Set *default* container specific settings.json values on container create.
"settings": { "settings": {
"sqltools.connections": [{ "sqltools.connections": [
"name": "Container database", {
"driver": "PostgreSQL", "name": "Container database",
"previewLimit": 50, "driver": "PostgreSQL",
"server": "localhost", "previewLimit": 50,
"port": 5432, "server": "localhost",
"database": "postgres", "port": 5432,
"username": "postgres", "database": "postgres",
"password": "postgres" "username": "postgres",
}] "password": "postgres"
}, }
]
},
// Add the IDs of extensions you want installed when the container is created. // Add the IDs of extensions you want installed when the container is created.
"extensions": [ "extensions": [
"jakebecker.elixir-ls", "jakebecker.elixir-ls",
"mtxr.sqltools", "mtxr.sqltools",
"mtxr.sqltools-driver-pg" "mtxr.sqltools-driver-pg"
], ],
// Use 'forwardPorts' to make a list of ports inside the container available locally. // Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [4000, 4001, 5432], "forwardPorts": [4000, 4001, 5432],
// Use 'postCreateCommand' to run commands after the container is created. // Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "mix deps.get", // "postCreateCommand": "mix deps.get",
// "runArgs": ["--userns=keep-id", "--privileged"], // "runArgs": ["--userns=keep-id", "--privileged"],
// "containerUser": "vscode", // "containerUser": "vscode",
// "containerEnv": { // "containerEnv": {
// "HOME": "/home/vscode", // "HOME": "/home/vscode",
// }, // },
// "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,Z", // "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,Z",
// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode" "remoteUser": "vscode"
} }

View file

@ -1,6 +1,10 @@
_build _build
CONTRIBUTING.md CONTRIBUTING.md
coverage
demo
deps deps
doc
docs
docker-compose.yml docker-compose.yml
Dockerfile Dockerfile
.elixir_ls .elixir_ls
@ -15,5 +19,8 @@ Makefile
README.md README.md
SECURITY.md SECURITY.md
ssh_match_hostname ssh_match_hostname
.js/package-lock.json package-lock.json
js/node_modules node_modules
playwright-report
test
tests

View file

@ -22,3 +22,6 @@ MOBILIZON_SMTP_PORT=25
MOBILIZON_SMTP_USERNAME=noreply@mobilizon.lan MOBILIZON_SMTP_USERNAME=noreply@mobilizon.lan
MOBILIZON_SMTP_PASSWORD=password MOBILIZON_SMTP_PASSWORD=password
MOBILIZON_SMTP_SSL=false MOBILIZON_SMTP_SSL=false
# When using docker for development, VITE_HOST must be set to 0.0.0.0
VITE_HOST=localhost

11
.gitignore vendored
View file

@ -46,7 +46,16 @@ release/
.weblate .weblate
docker/production/.env docker/production/.env
test-junit-report.xml test-junit-report.xml
js/junit.xml junit.xml
.env .env
demo/ demo/
codeclimate.json codeclimate.json
node_modules
stats.html
/coverage
/playwright-report/
.histoire
# Nix out-links
result*

View file

@ -6,14 +6,12 @@ stages:
- build-js - build-js
- sentry - sentry
- test - test
- docker - build
- package
- upload - upload
- deploy - deploy
variables: variables:
MIX_ENV: "test" MIX_ENV: "test"
YARN_CACHE_FOLDER: "js/.yarn"
# DB Variables for Postgres / Postgis # DB Variables for Postgres / Postgis
POSTGRES_DB: mobilizon_test POSTGRES_DB: mobilizon_test
POSTGRES_USER: postgres POSTGRES_USER: postgres
@ -32,17 +30,14 @@ variables:
EXPORT_FORMATS: "csv,ods,pdf" EXPORT_FORMATS: "csv,ods,pdf"
APP_VERSION: "${CI_COMMIT_REF_NAME}" APP_VERSION: "${CI_COMMIT_REF_NAME}"
APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz" APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz"
CYPRESS_INSTALL_BINARY: 0
cache: cache:
key: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}" key: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"
paths: paths:
- ~/.cache/Cypress
- cache/Cypress
- deps/ - deps/
- _build/ - _build/
- js/node_modules - node_modules
- js/.yarn - .npm
# Installed dependencies are cached across the pipeline # Installed dependencies are cached across the pipeline
# So there is no need to reinstall them all the time # So there is no need to reinstall them all the time
@ -50,7 +45,7 @@ cache:
install: install:
stage: install stage: install
script: script:
- yarn --cwd "js" install --frozen-lockfile - npm ci
- mix deps.get - mix deps.get
- mix compile - mix compile
@ -71,27 +66,26 @@ lint-elixir:
reports: reports:
codequality: codeclimate.json codequality: codeclimate.json
lint-front: lint-front:
image: node:16 image: node:20
stage: check stage: check
before_script: before_script:
- export EXITVALUE=0 - export EXITVALUE=0
- yarn --cwd "js" install --frozen-lockfile - npm ci
script: script:
- yarn --cwd "js" run lint || export EXITVALUE=1 - npm run lint || export EXITVALUE=1
- yarn --cwd "js" run prettier -c . || export EXITVALUE=1 - npx prettier -c . || export EXITVALUE=1
- exit $EXITVALUE - exit $EXITVALUE
build-frontend: build-frontend:
stage: build-js stage: build-js
image: node:16 image: node:20
before_script: before_script:
- apt update - apt update
- apt install -y --no-install-recommends python build-essential webp imagemagick gifsicle jpegoptim optipng pngquant - apt install -y --no-install-recommends python3 build-essential webp imagemagick gifsicle jpegoptim optipng pngquant
script: script:
- yarn --cwd "js" install --frozen-lockfile - npm install --frozen-lockfile
- yarn --cwd "js" run build - npm run build
artifacts: artifacts:
expire_in: 5 days expire_in: 5 days
paths: paths:
@ -121,7 +115,7 @@ deps:
script: script:
- export EXITVALUE=0 - export EXITVALUE=0
- mix hex.outdated || export EXITVALUE=1 - mix hex.outdated || export EXITVALUE=1
- yarn --cwd "js" outdated || export EXITVALUE=1 - npm outdated || export EXITVALUE=1
- exit $EXITVALUE - exit $EXITVALUE
allow_failure: true allow_failure: true
needs: needs:
@ -130,12 +124,14 @@ deps:
exunit: exunit:
stage: test stage: test
services: services:
- name: postgis/postgis:14-3.2 - name: postgis/postgis:16-3.4
alias: postgres alias: postgres
variables: variables:
MIX_ENV: test MIX_ENV: test
before_script: before_script:
- mix deps.get && mix tz_world.update - mix deps.get
- mix compile
- mix tz_world.update
- mix ecto.create - mix ecto.create
- mix ecto.migrate - mix ecto.migrate
script: script:
@ -152,22 +148,22 @@ vitest:
needs: needs:
- lint-front - lint-front
before_script: before_script:
- yarn --cwd "js" install --frozen-lockfile - npm install --frozen-lockfile
script: script:
- yarn --cwd "js" run coverage --reporter=default --reporter=junit --outputFile.junit=./junit.xml - npm run coverage --reporter=default --reporter=junit --outputFile.junit=./junit.xml
artifacts: artifacts:
when: always when: always
paths: paths:
- js/coverage - coverage
reports: reports:
junit: junit:
- js/junit.xml - junit.xml
expire_in: 30 days expire_in: 30 days
e2e: e2e:
stage: test stage: test
services: services:
- name: postgis/postgis:14-3.2 - name: postgis/postgis:16-3.4
alias: postgres alias: postgres
variables: variables:
MIX_ENV: "e2e" MIX_ENV: "e2e"
@ -176,31 +172,31 @@ e2e:
- mix ecto.create - mix ecto.create
- mix ecto.migrate - mix ecto.migrate
- mix run priv/repo/e2e.seed.exs - mix run priv/repo/e2e.seed.exs
- cd js && yarn install && yarn run build && npx playwright install && cd ../ - npm install && npm run build && npx playwright install
- mix phx.digest - mix phx.digest
script: script:
- mix phx.server & - mix phx.server &
- cd js
- npx wait-on http://localhost:4000 - npx wait-on http://localhost:4000
- npx playwright test --project $BROWSER - npx playwright test --project $BROWSER
parallel: parallel:
matrix: matrix:
- BROWSER: ['firefox', 'chromium'] - BROWSER: ["firefox", "chromium"]
artifacts: artifacts:
expire_in: 2 days expire_in: 2 days
paths: paths:
- js/playwright-report/ - playwright-report/
- js/test-results/ - test-results/
pages: pages:
stage: deploy stage: deploy
script: script:
- mv public public-mbz
- mkdir public - mkdir public
- mix deps.get - mix deps.get
- mix docs - mix docs
- mv doc public/backend - mv doc public/backend
# #- yarn run --cwd "js" styleguide:build # #- npm run styleguide:build
# #- mv js/styleguide public/frontend # #- mv styleguide public/frontend
rules: rules:
- if: '$CI_COMMIT_BRANCH == "main"' - if: '$CI_COMMIT_BRANCH == "main"'
artifacts: artifacts:
@ -209,22 +205,23 @@ pages:
- public - public
.docker: &docker .docker: &docker
stage: docker stage: build
image: docker:20.10.18 image: docker:24
variables: variables:
DOCKER_TLS_CERTDIR: "/certs" DOCKER_TLS_CERTDIR: "/certs"
DOCKER_HOST: tcp://docker:2376 DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client" DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
DOCKER_CLI_EXPERIMENTAL: enabled
services: services:
- docker:20.10.18-dind - docker:24-dind
cache: {} cache: {}
before_script: before_script:
# Install buildx # Install buildx
- wget https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-amd64 - wget https://github.com/docker/buildx/releases/download/v0.11.2/buildx-v0.11.2.linux-amd64
- mkdir -p ~/.docker/cli-plugins/ - mkdir -p ~/.docker/cli-plugins/
- mv buildx-v0.9.1.linux-amd64 ~/.docker/cli-plugins/docker-buildx - mv buildx-v0.11.2.linux-amd64 ~/.docker/cli-plugins/docker-buildx
- chmod a+x ~/.docker/cli-plugins/docker-buildx - chmod a+x ~/.docker/cli-plugins/docker-buildx
# Create env # Create env
- docker context create tls-environment - docker context create tls-environment
@ -232,6 +229,8 @@ pages:
# Install qemu/binfmt # Install qemu/binfmt
- docker pull tonistiigi/binfmt:latest - docker pull tonistiigi/binfmt:latest
- docker run --rm --privileged tonistiigi/binfmt:latest --install all - docker run --rm --privileged tonistiigi/binfmt:latest --install all
# Install jq
- apk --no-cache add jq
# Login to DockerHub # Login to DockerHub
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$CI_REGISTRY_AUTH\",\"email\":\"$CI_REGISTRY_EMAIL\"}}}" > ~/.docker/config.json - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$CI_REGISTRY_AUTH\",\"email\":\"$CI_REGISTRY_EMAIL\"}}}" > ~/.docker/config.json
@ -245,65 +244,70 @@ build-docker-main:
when: never when: never
- if: '$CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_TRIGGERED == "true"' - if: '$CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_TRIGGERED == "true"'
script: script:
- docker buildx build --push --platform linux/amd64 -t framasoft/mobilizon:main -f docker/production/Dockerfile . - docker buildx build --platform linux/amd64 -t framasoft/mobilizon:main -f docker/production/Dockerfile .
build-and-push-to-latest-docker-tag: build-docker-tag:
<<: *docker <<: *docker
rules: &release-tag-rules
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
when: never
- if: $CI_COMMIT_TAG != null
when: on_success
timeout: 3 hours
script:
- >
docker buildx build
--push
--platform linux/${ARCH}
--provenance=false
--build-arg="${ERL_FLAGS}"
-t framasoft/mobilizon:${CI_COMMIT_TAG}-${ARCH}
-f docker/production/Dockerfile .
parallel:
matrix:
- ARCH: ["amd64"]
ERL_FLAGS: ["ERL_FLAGS="]
- ARCH: ["arm64"]
ERL_FLAGS: ["ERL_FLAGS=+JMsingle true"]
# Create manifest and push
docker-manifest-push:
<<: *docker
needs: ["build-docker-tag"]
rules: &release-tag-rules
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
when: never
- if: $CI_COMMIT_TAG != null
when: on_success
script:
- >
docker manifest create framasoft/mobilizon:${CI_COMMIT_TAG}
--amend framasoft/mobilizon:${CI_COMMIT_TAG}-amd64
--amend framasoft/mobilizon:${CI_COMMIT_TAG}-arm64
- docker manifest push --purge framasoft/mobilizon:${CI_COMMIT_TAG}
###
# Simply creating an alias to the tag doesn't work:
# « xxx is a manifest list »
# https://joonas.fi/2021/02/docker-multi-arch-image-tooling-buildx/
###
docker-latest:
<<: *docker
needs: ["docker-manifest-push"]
rules: &release-tag-rules rules: &release-tag-rules
- if: '$CI_PROJECT_NAMESPACE != "framasoft"' - if: '$CI_PROJECT_NAMESPACE != "framasoft"'
when: never when: never
- if: $CI_COMMIT_TAG != null && $CI_COMMIT_TAG !~ /alpha|beta|rc/ - if: $CI_COMMIT_TAG != null && $CI_COMMIT_TAG !~ /alpha|beta|rc/
when: on_success when: on_success
timeout: 3 hours
script: script:
- > - echo docker manifest create framasoft/mobilizon:latest $(docker manifest inspect framasoft/mobilizon:$CI_COMMIT_TAG | jq '.manifests[] | .digest' | xargs -I {} echo framasoft/mobilizon@{})
docker buildx build - docker manifest create framasoft/mobilizon:latest $(docker manifest inspect framasoft/mobilizon:$CI_COMMIT_TAG | jq -r '.manifests[] | .digest' | xargs -I {} echo framasoft/mobilizon@{})
--push - docker manifest push --purge framasoft/mobilizon:latest
--platform linux/amd64
-t framasoft/mobilizon:$CI_COMMIT_TAG
-t framasoft/mobilizon:latest
-f docker/production/Dockerfile .
build-and-push-to-latest-docker-tag-cross:
<<: *docker
rules: &release-tag-rules
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
when: never
- if: $CI_COMMIT_TAG != null && $CI_COMMIT_TAG !~ /alpha|beta|rc/
when: on_success
timeout: 3 hours
allow_failure: true
script:
- >
docker buildx build
--push
--platform linux/arm, linux/arm64
-t framasoft/mobilizon:$CI_COMMIT_TAG
-t framasoft/mobilizon:latest
-f docker/production/Dockerfile .
# Don't push to latest when building beta/rc tags
build-and-push-docker-tag:
<<: *docker
rules: &pre-release-tag-rules
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
when: never
- if: $CI_COMMIT_TAG =~ /alpha|beta|rc/
when: on_success
timeout: 3 hours
script:
- >
docker buildx build
--push
--platform linux/amd64
-t framasoft/mobilizon:$CI_COMMIT_TAG
-f docker/production/Dockerfile .
# Packaging app for amd64 # Packaging app for amd64
package-app: package-app:
image: mobilizon/buildpack:1.14.1-erlang-25.1.1-debian-buster image: mobilizon/buildpack:1.15.7-erlang-26.1.2-${SYSTEM}
stage: package stage: build
variables: &release-variables variables: &release-variables
MIX_ENV: "prod" MIX_ENV: "prod"
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
@ -327,9 +331,22 @@ package-app:
expire_in: 2 days expire_in: 2 days
paths: paths:
- ${APP_ASSET} - ${APP_ASSET}
parallel:
matrix:
- SYSTEM:
[
"debian-bookworm",
"debian-bullseye",
"debian-buster",
"ubuntu-jammy",
"ubuntu-focal",
"ubuntu-bionic",
"fedora-38",
"fedora-39",
]
package-app-dev: package-app-dev:
stage: package stage: build
variables: *release-variables variables: *release-variables
script: *release-script script: *release-script
except: except:
@ -340,9 +357,9 @@ package-app-dev:
- ${APP_ASSET} - ${APP_ASSET}
# Packaging app for multi-arch # Packaging app for multi-arch
multi-arch-release: package-multi-arch-release:
stage: package stage: build
image: docker:20.10.21 image: docker:24
variables: variables:
DOCKER_TLS_CERTDIR: "/certs" DOCKER_TLS_CERTDIR: "/certs"
DOCKER_HOST: tcp://docker:2376 DOCKER_HOST: tcp://docker:2376
@ -352,13 +369,13 @@ multi-arch-release:
APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz" APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz"
OS: debian-buster OS: debian-buster
services: services:
- docker:20.10.21-dind - docker:24-dind
cache: {} cache: {}
before_script: before_script:
# Install buildx # Install buildx
- wget https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-amd64 - wget https://github.com/docker/buildx/releases/download/v0.11.2/buildx-v0.11.2.linux-amd64
- mkdir -p ~/.docker/cli-plugins/ - mkdir -p ~/.docker/cli-plugins/
- mv buildx-v0.9.1.linux-amd64 ~/.docker/cli-plugins/docker-buildx - mv buildx-v0.11.2.linux-amd64 ~/.docker/cli-plugins/docker-buildx
- chmod a+x ~/.docker/cli-plugins/docker-buildx - chmod a+x ~/.docker/cli-plugins/docker-buildx
# Create env # Create env
- docker context create tls-environment - docker context create tls-environment
@ -367,7 +384,7 @@ multi-arch-release:
- docker pull tonistiigi/binfmt:latest - docker pull tonistiigi/binfmt:latest
- docker run --rm --privileged tonistiigi/binfmt:latest --install all - docker run --rm --privileged tonistiigi/binfmt:latest --install all
script: script:
- docker buildx build --platform linux/${ARCH} --output type=local,dest=releases --build-arg APP_ASSET=${APP_ASSET} -f docker/multiarch/Dockerfile . - docker buildx build --platform linux/${ARCH} --output type=local,dest=releases --build-arg="ERL_FLAGS=+JMsingle true" --build-arg APP_ASSET=${APP_ASSET} -f docker/multiarch/Dockerfile .
- ls -alh releases/mobilizon/ - ls -alh releases/mobilizon/
- du -sh releases/mobilizon/${APP_ASSET} - du -sh releases/mobilizon/${APP_ASSET}
- mv releases/mobilizon/${APP_ASSET} . - mv releases/mobilizon/${APP_ASSET} .
@ -380,7 +397,20 @@ multi-arch-release:
- erl_crash.dump # if there's a memory issue - erl_crash.dump # if there's a memory issue
parallel: parallel:
matrix: matrix:
- ARCH: ["arm", "arm64"] - ARCH: ["arm64"]
## Currently not used as the hexpm base images do not have support for other architectures than amd64
# SYSTEM:
# [
# "debian-bookworm",
# "debian-bullseye",
# "ubuntu-jammy",
# "ubuntu-focal",
# "ubuntu-bionic",
# "alpine-3.17.5",
# "alpine-3.18.4",
# "fedora-38",
# "fedora-39",
# ]
rules: rules:
- if: '$CI_COMMIT_TAG != null || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_TRIGGERED == "true"' - if: '$CI_COMMIT_TAG != null || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_TRIGGERED == "true"'
timeout: 3h timeout: 3h

View file

@ -1,4 +1,3 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
cd js npm run pre-commit
yarn run lint-staged

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
src/i18n/*.json
coverage/
**/*.md
test/fixtures

View file

@ -1,44 +1,20 @@
02CE4963DFD1B0D6D5C567357CAFFE97
155A1FB53DE39EC8EFCFD7FB94EA823D 155A1FB53DE39EC8EFCFD7FB94EA823D
2262742E5C8944D5BF6698EC61F5DE50 1C29EE70E90ECED01AF28EC58D2575B5
25BEE162A99754480967216281E9EF33 26ED12A8E03D044BEDC08749BAA5E357
2A6F71CD6F1246F0B152C2376E2E398A 2BB1D36656B423758A470021718FCB09
30552A09D485A6AA73401C1D54F63C21 31CE26BC979C57B9E3CC97B40C290CE5
52900CE4EE3598F6F178A651FB256770 3529E7A4CECC24D02678820E6F521162
6151F44368FC19F2394274F513C29151 3644C4E850300482AA409471EFE1EFB3
765526195D4C6D770EAF4DC944A8CBF4 4E7C044C59E0BCB76AA826789998F624
B2FF1A12F13B873507C85091688C1D6D 53CBBEB6243FAF5C37249CBA17DE6F4C
B9AF8A342CD7FF39E10CC10A408C28E1 5BCE3651A03711295046DE48BDFE007E
C042E87389F7BDCFF4E076E95731AE69 5C4CED447689F00D9D1ACEB9B895ED29
C42BFAEF7100F57BED75998B217C857A 94ACF7B17C3FF42F64E57DD1DA936BD8
D11958E86F1B6D37EF656B63405CA8A4 A32E125003F1EDFAD95C487C6A969725
F16F054F2628609A726B9FF2F089D484 ACF6272A1DBB3A2ABD96C0C120B5CA69
26E816A7B054CB0347A2C6451F03B92B C46C4893B2F702ACADC4CAA5683FE370
2B76BDDB2BB4D36D69FAE793EBD63894 CDF2CCE0CF10F49CDFAE22FE26208155
301A837DE24C6AEE1DA812DF9E5486C1 E720CB13C50FF3ADEE7C522531E11217
395A2740CB468F93F6EBE6E90EE08291 F3D5851D3FB050939841ED2F14307A27
4013C9866943B9381D9F9F97027F88A9 FD1C9756370A195B74E95CE504C45E9E
4C796DD588A4B1C98E86BBCD0349949A
51289D8D7BDB59CB6473E0DED0591ED7
5A70DC86895DB3610C605EA9F31ED300
705C17F9C852F546D886B20DB2C4D0D1
75D2074B6F771BA8C032008EC18CABDF
7B1C6E35A374C38FF5F07DBF23B3EAE2
955ACF52ADD8FCAA450FB8138CB1FD1A
A092A563729E1F2C1C8D5D809A31F754
BFA12FDEDEAD7DEAB6D44DF6FDFBD5E1
D9A08930F140F9BA494BB90B3F812C87
FE1EEB91EA633570F703B251AE2D4D4E
02B15A0FE85181E2470E4E1E6740DFF6
128653EA565172F81FD177D1D6491CF3
2EB031217231C480C89EA0C1576EF3CA
39CFFBCF3FD4F6DB0E4DE4A9A78D3961
40C6EAD7C05ABB6A85BB904589DEF72F
49DE9560D506F9E7EF3AFD8DA6E5564B
759F752FA0768CCC7871895DC2A5CD51
7EEC79571F3F7CEEB04A8B86D908382A
E7967805C1EA5301F2722C7BDB2F25F3
BDFB0FB1AAF69C18212CBCFD42F8B717
40220A533CCACB3A1CE9DBF1A8A430A1
EEB29D1DDA3A3015BC645A989B5BD38E

View file

@ -1,2 +1,2 @@
elixir 1.14.4-otp-25 elixir 1.15.5-otp-26
erlang 25.3.1 erlang 26.0.2

View file

@ -5,6 +5,418 @@ 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).
## 4.0.2 (2023-12-07)
### Security issues
This release fixes different security issues reported by the potsda.mn collective. Please make sure to upgrade as soon as possible.
### Fixed
- Fixes XSS issues in notifier and participant and event contacts list formatting
* fix(front-end): add more security fixes for formatted lists and notifier ([1af8e37](https://framagit.org/framasoft/mobilizon/commits/1af8e37))
## 4.0.1 (2023-12-07)
### Security issues
This release fixes different security issues reported by the potsda.mn collective. Please make sure to upgrade as soon as possible.
### Added
- Added a CLI task to test if emails configuration works properly
### Fixed
- Fixes XSS issues in groups descriptions, report contents, messages from anonymous participations and resources descriptions
- Fixes Docker configuration that prevented the image to launch
### Changed
- Added back Debian Buster builds
### Complete changelog
* build(packages): add back Debian Buster as it seems people are still using it ([795ef24](https://framagit.org/framasoft/mobilizon/commits/795ef24))
* build(packages): remove alpine packages as there's no demand for it ([0caaf2b](https://framagit.org/framasoft/mobilizon/commits/0caaf2b))
* Translated using Weblate (Croatian) ([9c88fae](https://framagit.org/framasoft/mobilizon/commits/9c88fae))
* Translated using Weblate (Croatian) ([623f4ee](https://framagit.org/framasoft/mobilizon/commits/623f4ee))
* Translated using Weblate (Croatian) ([1162dd0](https://framagit.org/framasoft/mobilizon/commits/1162dd0))
* Translated using Weblate (Galician) ([97c53bb](https://framagit.org/framasoft/mobilizon/commits/97c53bb))
* Translated using Weblate (Galician) ([e08b057](https://framagit.org/framasoft/mobilizon/commits/e08b057))
* Translated using Weblate (Galician) ([ec5e436](https://framagit.org/framasoft/mobilizon/commits/ec5e436))
* Translated using Weblate (Korean) ([1a1ad52](https://framagit.org/framasoft/mobilizon/commits/1a1ad52))
* Translated using Weblate (Korean) ([7b4c31d](https://framagit.org/framasoft/mobilizon/commits/7b4c31d))
* fix: always consider report content as text ([ffff379](https://framagit.org/framasoft/mobilizon/commits/ffff379))
* fix: sanitize descriptions from resources ([dc6647f](https://framagit.org/framasoft/mobilizon/commits/dc6647f))
* fix(config): fix setting path for Mobilizon.Service.SiteMap ([7d725bd](https://framagit.org/framasoft/mobilizon/commits/7d725bd))
* fix(docker): fix getting configuration value from env MOBILIZON_SMTP_TLS ([28063bd](https://framagit.org/framasoft/mobilizon/commits/28063bd)), closes [#1381](https://framagit.org/framasoft/mobilizon/issues/1381)
* fix(docker): fix getting default value for MOBILIZON_SMTP_SSL env ([126727b](https://framagit.org/framasoft/mobilizon/commits/126727b))
* fix(docker): use separate env for tzdata dir path ([9907f88](https://framagit.org/framasoft/mobilizon/commits/9907f88))
* fix(emails): use tls_certificate_check to add tls config for mailer ([db38550](https://framagit.org/framasoft/mobilizon/commits/db38550))
* fix(front): anonymous participant text is plain text, avoid using v-html ([2c12fbf](https://framagit.org/framasoft/mobilizon/commits/2c12fbf))
* fix(front): fix editing group ([935799f](https://framagit.org/framasoft/mobilizon/commits/935799f))
* fix(front): fix XSS because of bad operations when setting the group's summary ([ded59be](https://framagit.org/framasoft/mobilizon/commits/ded59be))
* fix(front): put correct value for CONVERSATION_LIST enum value ([94bf2e5](https://framagit.org/framasoft/mobilizon/commits/94bf2e5))
* fix(graphql): set default value for resource type parameter ([09f4132](https://framagit.org/framasoft/mobilizon/commits/09f4132))
* feat(cli): add command to test emails send correctly ([7210f86](https://framagit.org/framasoft/mobilizon/commits/7210f86))
* feat(docker): allow to configure loglevel at runtime through env variable ([4855af8](https://framagit.org/framasoft/mobilizon/commits/4855af8))
* test: add new tests for XSS in actors summary ([58e50e3](https://framagit.org/framasoft/mobilizon/commits/58e50e3))
* style: linting front-end ([41227d9](https://framagit.org/framasoft/mobilizon/commits/41227d9))
* refactor(activitypub): handle failure finding public key in actor keys ([5b337f9](https://framagit.org/framasoft/mobilizon/commits/5b337f9))
## 4.0.0 (2023-12-05)
### Breaking changes
#### Release (binary package) installations
- We now produce packages for different distributions targets (Debian Bookworm, Debian Bullseye, Ubuntu Jammy, Ubuntu Focal, Ubuntu Bionic, Fedora 38 and Fedora 39). Be sure to pick the right one for your system, as there can be issues with OpenSSL versions differing from inside the Mobilizon package and on your system.
- The `https://joinmobilizon.org/latest-package` URL now links to the latest package builded against Debian Bookworm. Make sure to follow the documentation if you're not using this.
- There's also an `arm64` package build on Debian Bullseye for now.
#### Source installations
- Elixir 15 is now required
- The content of the `js` directory is now at the root of the repository, so you don't need to `cd js` anymore
- No need for `yarn` anymore, simply use `npm` instead for `npm i` and `npm run build`
### New features
- Event organizers and groups can be contacted through private messages (including PMs from 3rd-party micro-blogging fediverse services)
- Event organizers can send private announcements to event participants (approved or not)
### Improvements
- Anonymous participation e-mails now contain links to cancel your participation
- ActivityPub improvements for compatibility with https://event-federation.eu
- ICS export fixes for descriptions and adding event status
### Changes since 4.0.0-rc.1
* refactor: to lower cyclomatic complexity ([147096c](https://framagit.org/framasoft/mobilizon/commits/147096c))
* fix(activitypub): compact ical:status in activitystream data ([5e8f9af](https://framagit.org/framasoft/mobilizon/commits/5e8f9af)), closes [#1378](https://framagit.org/framasoft/mobilizon/issues/1378)
* fix(activitypub): fix receiving comments ([f1084c1](https://framagit.org/framasoft/mobilizon/commits/f1084c1))
* fix(backend): handle ecto errors when fetching and create entities ([89d1ee4](https://framagit.org/framasoft/mobilizon/commits/89d1ee4))
* fix(front): fix tag loading ([f81472e](https://framagit.org/framasoft/mobilizon/commits/f81472e))
* fix(front): make recipient field placeholder translatable ([10ce812](https://framagit.org/framasoft/mobilizon/commits/10ce812))
* fix(front): only show participants & announcements menu items to organizers ([c4d2ec6](https://framagit.org/framasoft/mobilizon/commits/c4d2ec6))
* Translated using Weblate (Croatian) ([a26ff98](https://framagit.org/framasoft/mobilizon/commits/a26ff98))
* Translated using Weblate (Croatian) ([1683f01](https://framagit.org/framasoft/mobilizon/commits/1683f01))
* Translated using Weblate (Croatian) ([aa7f870](https://framagit.org/framasoft/mobilizon/commits/aa7f870))
* Translated using Weblate (Croatian) ([1ce34ea](https://framagit.org/framasoft/mobilizon/commits/1ce34ea))
* Translated using Weblate (Croatian) ([5e7edc0](https://framagit.org/framasoft/mobilizon/commits/5e7edc0))
* Translated using Weblate (Croatian) ([d777d88](https://framagit.org/framasoft/mobilizon/commits/d777d88))
* Translated using Weblate (Croatian) ([0118d97](https://framagit.org/framasoft/mobilizon/commits/0118d97))
* Translated using Weblate (Croatian) ([805e931](https://framagit.org/framasoft/mobilizon/commits/805e931))
## 4.0.0-rc.1 (2023-12-04)
* fix: prevent sending group physical address if it's empty and allow empty text for timezone ([32caebb](https://framagit.org/framasoft/mobilizon/commits/32caebb)), closes [#1357](https://framagit.org/framasoft/mobilizon/issues/1357)
* fix(activitypub): add missing externalParticipationUrl context ([8795576](https://framagit.org/framasoft/mobilizon/commits/8795576)), closes [#1376](https://framagit.org/framasoft/mobilizon/issues/1376)
* fix(backend): only send suspension notification emails when actor's suspended and not just deleted ([9e41bc1](https://framagit.org/framasoft/mobilizon/commits/9e41bc1))
* docs(nginx): improve nginx configuration ([6c992ca](https://framagit.org/framasoft/mobilizon/commits/6c992ca))
## 4.0.0-beta.2 (2023-12-01)
* test: fix tests using verified routes ([5fcf3d5](https://framagit.org/framasoft/mobilizon/commits/5fcf3d5))
* feat: add links to cancel anonymous participations in emails ([9e6b232](https://framagit.org/framasoft/mobilizon/commits/9e6b232))
* feat(background): add a job to refresh participant stats ([11e42d6](https://framagit.org/framasoft/mobilizon/commits/11e42d6))
* feat(front): add dedicated page and route for event announcements ([d831dff](https://framagit.org/framasoft/mobilizon/commits/d831dff))
* chore(i18n): update backend translations ([6df16ef](https://framagit.org/framasoft/mobilizon/commits/6df16ef))
* fix: fix creating participant stats ([3f2a88f](https://framagit.org/framasoft/mobilizon/commits/3f2a88f))
* refactor: use Phoenix verified routes ([b315e1d](https://framagit.org/framasoft/mobilizon/commits/b315e1d))
## 4.0.0-beta.1 (2023-11-30)
* fix: add a final fallback if we have default_language: nil in instance config ([cd53062](https://framagit.org/framasoft/mobilizon/commits/cd53062))
* fix: build pictures at correct location and fix Plug.Static ([3c288c5](https://framagit.org/framasoft/mobilizon/commits/3c288c5))
* fix: don't show passed/finished events in related events section ([69e4a5c](https://framagit.org/framasoft/mobilizon/commits/69e4a5c))
* fix: fix Dockerfile copying assets path ([16cd377](https://framagit.org/framasoft/mobilizon/commits/16cd377))
* fix: normalize suggested username ([4960387](https://framagit.org/framasoft/mobilizon/commits/4960387))
* fix: set correct watcher config for E2E tests ([f47889b](https://framagit.org/framasoft/mobilizon/commits/f47889b))
* fix: various fixes ([b635937](https://framagit.org/framasoft/mobilizon/commits/b635937))
* fix(announcements): load group announcements ([7ef85fe](https://framagit.org/framasoft/mobilizon/commits/7ef85fe))
* fix(api): allow localhost as a valid uri host for applications ([49b070d](https://framagit.org/framasoft/mobilizon/commits/49b070d))
* fix(api): fix allowing posting event private announcement ([1831495](https://framagit.org/framasoft/mobilizon/commits/1831495))
* fix(docker): add sitemap folder ([bd38449](https://framagit.org/framasoft/mobilizon/commits/bd38449))
* fix(docker): allow to configure SMTP TLS ([2ecdf05](https://framagit.org/framasoft/mobilizon/commits/2ecdf05))
* fix(docker): convert smtp tls sni to char list ([b3be7c6](https://framagit.org/framasoft/mobilizon/commits/b3be7c6))
* fix(export): fix iCalendar export description HTML conversion ([d7daafc](https://framagit.org/framasoft/mobilizon/commits/d7daafc)), closes [#888](https://framagit.org/framasoft/mobilizon/issues/888)
* fix(front): hide all categories card if we don't have even one ([5e86ef1](https://framagit.org/framasoft/mobilizon/commits/5e86ef1))
* fix(histoire): fix URL to Framapiaf avatars ([0613f7f](https://framagit.org/framasoft/mobilizon/commits/0613f7f))
* fix(i18n): fix typos in translation sources ([2ecd55d](https://framagit.org/framasoft/mobilizon/commits/2ecd55d))
* fix(i18n): update spanish translations ([cfebc35](https://framagit.org/framasoft/mobilizon/commits/cfebc35))
* add simplified Chinese mapping ([02af9a4](https://framagit.org/framasoft/mobilizon/commits/02af9a4))
* Added translation using Weblate (Korean) ([a11fab6](https://framagit.org/framasoft/mobilizon/commits/a11fab6))
* Added translation using Weblate (Korean) ([c529a83](https://framagit.org/framasoft/mobilizon/commits/c529a83))
* Added translation using Weblate (Tatar) ([cefdaf8](https://framagit.org/framasoft/mobilizon/commits/cefdaf8))
* Fix docker development: ([9705978](https://framagit.org/framasoft/mobilizon/commits/9705978))
* fix fullAddressAutocomplete component not loading results ([83da88c](https://framagit.org/framasoft/mobilizon/commits/83da88c))
* Fix typo in ctl help text ([495d163](https://framagit.org/framasoft/mobilizon/commits/495d163))
* Fix typos ([66e89b9](https://framagit.org/framasoft/mobilizon/commits/66e89b9))
* introduce VITE_HOST env var and pass it to the node watcher vite --host ([bfb7e3c](https://framagit.org/framasoft/mobilizon/commits/bfb7e3c))
* remove unnecessary function ([8a1b122](https://framagit.org/framasoft/mobilizon/commits/8a1b122))
* resolve result promise in a shorter way ([f81804d](https://framagit.org/framasoft/mobilizon/commits/f81804d))
* Translated using Weblate (Croatian) ([e510e09](https://framagit.org/framasoft/mobilizon/commits/e510e09))
* Translated using Weblate (Czech) ([d702ca2](https://framagit.org/framasoft/mobilizon/commits/d702ca2))
* Translated using Weblate (Czech) ([9224f89](https://framagit.org/framasoft/mobilizon/commits/9224f89))
* Translated using Weblate (Czech) ([c14dffb](https://framagit.org/framasoft/mobilizon/commits/c14dffb))
* Translated using Weblate (Czech) ([a7d70d5](https://framagit.org/framasoft/mobilizon/commits/a7d70d5))
* Translated using Weblate (French) ([c7ba003](https://framagit.org/framasoft/mobilizon/commits/c7ba003))
* Translated using Weblate (German) ([7732f87](https://framagit.org/framasoft/mobilizon/commits/7732f87))
* Translated using Weblate (Indonesian) ([d065193](https://framagit.org/framasoft/mobilizon/commits/d065193))
* Translated using Weblate (Italian) ([b5e9f62](https://framagit.org/framasoft/mobilizon/commits/b5e9f62))
* Translated using Weblate (Italian) ([e8e1a62](https://framagit.org/framasoft/mobilizon/commits/e8e1a62))
* Translated using Weblate (Italian) ([84fc175](https://framagit.org/framasoft/mobilizon/commits/84fc175))
* Translated using Weblate (Italian) ([5b64388](https://framagit.org/framasoft/mobilizon/commits/5b64388))
* Translated using Weblate (Italian) ([5e3dedb](https://framagit.org/framasoft/mobilizon/commits/5e3dedb))
* Translated using Weblate (Italian) ([afe4dd2](https://framagit.org/framasoft/mobilizon/commits/afe4dd2))
* Translated using Weblate (Italian) ([fa0ae83](https://framagit.org/framasoft/mobilizon/commits/fa0ae83))
* Translated using Weblate (Italian) ([181a5a7](https://framagit.org/framasoft/mobilizon/commits/181a5a7))
* Translated using Weblate (Italian) ([827caa3](https://framagit.org/framasoft/mobilizon/commits/827caa3))
* Translated using Weblate (Italian) ([d08d350](https://framagit.org/framasoft/mobilizon/commits/d08d350))
* Translated using Weblate (Italian) ([e9d38c2](https://framagit.org/framasoft/mobilizon/commits/e9d38c2))
* Translated using Weblate (Italian) ([a4578f3](https://framagit.org/framasoft/mobilizon/commits/a4578f3))
* Translated using Weblate (Polish) ([d62c31e](https://framagit.org/framasoft/mobilizon/commits/d62c31e))
* Translated using Weblate (Polish) ([a8ea217](https://framagit.org/framasoft/mobilizon/commits/a8ea217))
* Translated using Weblate (Polish) ([42537af](https://framagit.org/framasoft/mobilizon/commits/42537af))
* Translated using Weblate (Polish) ([fb0a74e](https://framagit.org/framasoft/mobilizon/commits/fb0a74e))
* Translated using Weblate (Polish) ([2458076](https://framagit.org/framasoft/mobilizon/commits/2458076))
* Translated using Weblate (Polish) ([46ffc8c](https://framagit.org/framasoft/mobilizon/commits/46ffc8c))
* Translated using Weblate (Polish) ([f0d7807](https://framagit.org/framasoft/mobilizon/commits/f0d7807))
* Translated using Weblate (Portuguese (Brazil)) ([9f78c73](https://framagit.org/framasoft/mobilizon/commits/9f78c73))
* Translated using Weblate (Portuguese (Brazil)) ([802ab78](https://framagit.org/framasoft/mobilizon/commits/802ab78))
* Translated using Weblate (Spanish) ([ee5ee8d](https://framagit.org/framasoft/mobilizon/commits/ee5ee8d))
* Translated using Weblate (Spanish) ([66c49e4](https://framagit.org/framasoft/mobilizon/commits/66c49e4))
* Translated using Weblate (Tatar) ([ba5f8f8](https://framagit.org/framasoft/mobilizon/commits/ba5f8f8))
* Update translation files ([9aa9cd2](https://framagit.org/framasoft/mobilizon/commits/9aa9cd2))
* WIP ([b5672ce](https://framagit.org/framasoft/mobilizon/commits/b5672ce))
* build: downgrade Sentry since it doesn't want to compile ([b2bacbf](https://framagit.org/framasoft/mobilizon/commits/b2bacbf))
* build: only run ecto create & migrate & tz_world update on prepare_test task, not main test one ([8d11073](https://framagit.org/framasoft/mobilizon/commits/8d11073))
* build: replace @pluralsh/socket with @framasoft/socket ([435bd9d](https://framagit.org/framasoft/mobilizon/commits/435bd9d))
* build: replace @vueuse/head with @unhead/vue ([5602164](https://framagit.org/framasoft/mobilizon/commits/5602164))
* build: switch from yarn to npm to manage js dependencies and move js contents to root ([2e72f6f](https://framagit.org/framasoft/mobilizon/commits/2e72f6f))
* build(deps): replace absinthe socket library with fork ([ec397aa](https://framagit.org/framasoft/mobilizon/commits/ec397aa))
* build(docker): optimize image size ([f34099d](https://framagit.org/framasoft/mobilizon/commits/f34099d)), closes [#1012](https://framagit.org/framasoft/mobilizon/issues/1012)
* ci: bump node version in CI ([3205512](https://framagit.org/framasoft/mobilizon/commits/3205512))
* ci: fix handling pages deploy with existing public folder ([1228ec1](https://framagit.org/framasoft/mobilizon/commits/1228ec1))
* ci: install python3 instead of python ([5d65981](https://framagit.org/framasoft/mobilizon/commits/5d65981))
* ci: Release on multiple distributions & fix Docker multiple-step build ([262d1fc](https://framagit.org/framasoft/mobilizon/commits/262d1fc))
* test: fix ActivityPub headers test ([f248660](https://framagit.org/framasoft/mobilizon/commits/f248660))
* test: fix front-end tests ([105d3b5](https://framagit.org/framasoft/mobilizon/commits/105d3b5))
* test: fix histoire configuration ([bfbc299](https://framagit.org/framasoft/mobilizon/commits/bfbc299))
* test: fix tests ([c731f0f](https://framagit.org/framasoft/mobilizon/commits/c731f0f))
* test: fix unit backend tests ([e051df1](https://framagit.org/framasoft/mobilizon/commits/e051df1))
* chore: fix prettier configuration and run it ([c255cea](https://framagit.org/framasoft/mobilizon/commits/c255cea))
* chore: update Sobelow security ignores ([1d0398d](https://framagit.org/framasoft/mobilizon/commits/1d0398d))
* chore: upgrade deps ([99c80c6](https://framagit.org/framasoft/mobilizon/commits/99c80c6))
* chore(deps): update geo_postgis to 3.5.0 for Elixir 1.15 compat ([3936eb4](https://framagit.org/framasoft/mobilizon/commits/3936eb4))
* chore(deps): upgrade dependencies ([3d9beaa](https://framagit.org/framasoft/mobilizon/commits/3d9beaa))
* chore(i18n): add missing translation key ([6ecfa48](https://framagit.org/framasoft/mobilizon/commits/6ecfa48))
* chore(i18n): update gettext dependency and regenerate translation files ([d7ad934](https://framagit.org/framasoft/mobilizon/commits/d7ad934))
* chore(i18n): update translation templates ([70e9ce0](https://framagit.org/framasoft/mobilizon/commits/70e9ce0))
* refactor: use dedicated email for event announcements ([b97f1c9](https://framagit.org/framasoft/mobilizon/commits/b97f1c9))
* docs(dev.md): keep some info about structure ([d130b15](https://framagit.org/framasoft/mobilizon/commits/d130b15))
* feat(export): add event status in iCalendar exports ([7a1bfca](https://framagit.org/framasoft/mobilizon/commits/7a1bfca))
* feat(federation): expose public activities as announcements in relay outbx & rfrsh profile aftr fllw ([85e4715](https://framagit.org/framasoft/mobilizon/commits/85e4715))
## 3.2.0 (2023-09-07)
### Features
* **cli:** allow the mobilizon.users.delete command to delete multiple users by email domain or ip ([bc50ab6](https://framagit.org/framasoft/mobilizon/commit/bc50ab66f3a44df220a7daa3cb1d917bd02487ba))
* **export:** add date of participant creation in participant exports ([fef60ed](https://framagit.org/framasoft/mobilizon/commit/fef60ed0f92fc4e09ee261ff03f1139aff2449c3)), closes [#1343](https://framagit.org/framasoft/mobilizon/issues/1343)
* **notifications:** add missing notifications when an user registers to an event ([da532c7](https://framagit.org/framasoft/mobilizon/commit/da532c7059bea5fcd47e2f42210e8ba842a11d63)), closes [#1344](https://framagit.org/framasoft/mobilizon/issues/1344)
* **reports:** allow reports to hold multiple events ([f2ac3e2](https://framagit.org/framasoft/mobilizon/commit/f2ac3e2e5d28f4257a5e2d4870d339fecf3a5f1b))
* **reports:** allow to suspend a profile or a user account directly from the report view ([69588db](https://framagit.org/framasoft/mobilizon/commit/69588dbf4ce2f80cc5829a841135042fa73eb4fe))
* **reports:** improve reportview and allow removing content + resolve report automatically ([b105c50](https://framagit.org/framasoft/mobilizon/commit/b105c508c03ce3cb96dd8342f96d3291aa197e22))
* **reports:** show suspended status next to reported profile ([b9a165a](https://framagit.org/framasoft/mobilizon/commit/b9a165a7fc565dc583cca81dd9c54570f73b4ca3))
* Add option to link an external registration provider for events ([2de6937](https://framagit.org/framasoft/mobilizon/commit/2de6937407743100daba1d397db4da32d4cb606b))
* **back:** add admin setting to disable external event feature ([f6611e8](https://framagit.org/framasoft/mobilizon/commit/f6611e8eb5a7e12dc0dc0c216b598e04144e07c6))
* improve group creation view [3f601748](https://framagit.org/framasoft/mobilizon/-/commit/3f60174877bbe05773b1d1b2ceb91749adec7ed7)
* **auth:** pre-initialize registration fields with information from 3rd-party provider ([7e49345](https://framagit.org/framasoft/mobilizon/commit/7e4934513a0ca4a5f95e8c8e4a600459911899d5)), closes [#1105](https://framagit.org/framasoft/mobilizon/issues/1105)
### Bug Fixes
* add inets and ssl to extra_applications in test env ([af46bea](https://framagit.org/framasoft/mobilizon/commit/af46bea7f730f4479bb31518a9fa53de7302049a))
* **apps:** add missing app scopes ([7e98097](https://framagit.org/framasoft/mobilizon/commit/7e98097c710663609274200564fca9eff1ea4d20))
* **apps:** make sure we can set status for an application token ([1a6095d](https://framagit.org/framasoft/mobilizon/commit/1a6095d27aeb440379d27c3894c302f831214822))
* **backend:** fix config cache not being used everytime ([ed3cd58](https://framagit.org/framasoft/mobilizon/commit/ed3cd5858cd27a90d4724a95ee660bbc08e92e80))
* **backend:** handle email not being sent when resending registration instructions ([b2492a3](https://framagit.org/framasoft/mobilizon/commit/b2492a387086528598da36f11e53569c5bdb164c))
* create event time/date allignment ([3de90a3](https://framagit.org/framasoft/mobilizon/commit/3de90a3c73414105becdcb24899016178b1c6f02))
* **docker:** fix Qemu segfaulting on arm64 ([8e3f90f](https://framagit.org/framasoft/mobilizon/commit/8e3f90f7135e2a8a8ac46464420c9d57b2e02534)), closes [#1241](https://framagit.org/framasoft/mobilizon/issues/1241) [#1249](https://framagit.org/framasoft/mobilizon/issues/1249)
* **federation:** fix getting pictures from Gruppe actors ([7c5f8b2](https://framagit.org/framasoft/mobilizon/commit/7c5f8b24311253ef89c7e47cd7ce22ebe6cf2ec9))
* fix Elixir 1.15 depreciations ([da70427](https://framagit.org/framasoft/mobilizon/commit/da70427e3292be8943167bbad73d5a782a98c6b5))
* fix some typescript issues with pwa ([e351d3c](https://framagit.org/framasoft/mobilizon/commit/e351d3cb2f8183bb4335b3b21e154f46d9237a76))
* **front:** avoid crashing if we don't have configuration data in time when in guard ([7916261](https://framagit.org/framasoft/mobilizon/commit/7916261c5c8c680d064fba106619d733575bc39c))
* **front:** fix alignment of some input elements on event edition form ([50695fc](https://framagit.org/framasoft/mobilizon/commit/50695fcfd5e0dc6fd55185f4399d45ed1852f880))
* **front:** fix changing language not being saved to the user's settings ([010a5e4](https://framagit.org/framasoft/mobilizon/commit/010a5e426def0a0b7f2658234f3c9d6eec46a68e))
* **front:** fix comment not showing up when replying in a discussion ([cc8f02d](https://framagit.org/framasoft/mobilizon/commit/cc8f02d0a6354c49437e7ff1780912a71bed03f4))
* **front:** fix confirm anonymous participation ([f99267c](https://framagit.org/framasoft/mobilizon/commit/f99267c6115601fce6eadd6ee54893fde0d6fd84))
* **front:** fix discussion edition panel always showing up ([fee0e38](https://framagit.org/framasoft/mobilizon/commit/fee0e388af798f14d4da8cbd9f037137f6be9f85))
* **front:** fix display of participants list ([c6b83c4](https://framagit.org/framasoft/mobilizon/commit/c6b83c42d6fbb2e6a93175479ef1620913c6532f))
* **front:** fix map ([8f84ba1](https://framagit.org/framasoft/mobilizon/commit/8f84ba1d08ce8d2d266010ee3166106eed66116d)), closes [#1314](https://framagit.org/framasoft/mobilizon/issues/1314)
* **front:** fix missing type causing eslint error ([c76dba3](https://framagit.org/framasoft/mobilizon/commit/c76dba3dbfe4fb0ab9ed24f71a6f64681c643fca))
* **front:** fix selecting all participants in participant view ([beef3ff](https://framagit.org/framasoft/mobilizon/commit/beef3ff16d12f5d5710e302b739dd724ad4b0cb5))
* **front:** fix showing error message when app to approve doesn't exist ([12cbff1](https://framagit.org/framasoft/mobilizon/commit/12cbff154ae5cdd72a1a7e882cb99e943010222b))
* **front:** fix some alignment of some UI elements in mobile event view ([8c313b5](https://framagit.org/framasoft/mobilizon/commit/8c313b53977493792c113b5191443515f8aeae78))
* **front:** properly handle error when approving app ([086d208](https://framagit.org/framasoft/mobilizon/commit/086d208ee50ae1f9ecb30196e758fdc7687714ae))
* **front:** properly handle post not found ([8db31c9](https://framagit.org/framasoft/mobilizon/commit/8db31c99df668389db4c6651fa71a8c1420484cf))
* **front:** reduce horizontal padding on main element ([f3c218f](https://framagit.org/framasoft/mobilizon/commit/f3c218f841292a28ec6d1284a205e2c7fd7d8f6e))
* **lint:** fix lint after upgrades ([60aceb4](https://framagit.org/framasoft/mobilizon/commit/60aceb442ae49458e31a1f38d277eca7af248a36))
* **mail:** fix sending mail on OTP26 ([f54fff5](https://framagit.org/framasoft/mobilizon/commit/f54fff56fc5c94408b1fd16b1eb9dd0f91bc2dfd)), closes [#1341](https://framagit.org/framasoft/mobilizon/issues/1341)
* **push:** fix push subscriptions registration ([fdf87ea](https://framagit.org/framasoft/mobilizon/commit/fdf87ea991b1d406b28dbd0c8807908939070c8b))
* **pwa:** improvements to the PWA configuration ([04c5ac1](https://framagit.org/framasoft/mobilizon/commit/04c5ac11636a4ffb5d3ac0c510b028edfb7fc057))
* **reports:** make front-end handle nullified reported_id and reported_id ([afd2ffe](https://framagit.org/framasoft/mobilizon/commit/afd2ffe72294baedc9dd15dc89d57301831545cc))
* **reports:** remove on delete cascade for reports ([4f530ca](https://framagit.org/framasoft/mobilizon/commit/4f530cabcf1bcadc09399a728975d329f3c9fdbf))
* **front:** fix behavior of local toggle for profiles & groups view depending on domain value ([84f62cd](https://framagit.org/framasoft/mobilizon/commit/84f62cd043d5cf5d186fea6f24a1a9dff5fc64ce))
* **i18n:** add missing translations ([af670f3](https://framagit.org/framasoft/mobilizon/commit/af670f39478b11465205fbea9b9268bab401bbb6))
* **back:** allow any other type of actor to be suspended ([92b222b](https://framagit.org/framasoft/mobilizon/commit/92b222b091cf6248969b0206e7c052b725a1286b))
* **back:** only try to insert activities for groups ([cfc9843](https://framagit.org/framasoft/mobilizon/commit/cfc984345e90b2960077956858606395f37ef9b9))
* **front:** don't return promise if result is not finished loading for tags ([8c14ba4](https://framagit.org/framasoft/mobilizon/commit/8c14ba441c6f0fadb3c59f80ff4e3abb2e625752))
* **front:** fix getting result from interactable object in InteractView ([31b2d06](https://framagit.org/framasoft/mobilizon/commit/31b2d065a904453580731133cd3dfd545a5816fa))
* **docker:** make Docker entrypoint port configurable via $MOBILIZON_DATABASE_PORT ([13099e0](https://framagit.org/framasoft/mobilizon/commit/13099e0f118b727a1472282c6419ef9b1842c191))
* **front:** fix fetching and rendering profile mentions and fetching tags ([895378a](https://framagit.org/framasoft/mobilizon/commit/895378a96bf8a6c7662ed02509c37b8d8a95db0b))
* **sitemap:** save generated sitemaps in configurable directory ([f28109a](https://framagit.org/framasoft/mobilizon/commit/f28109ad50d85143e38c8e9f5d09c28f80566462)), closes [#1321](https://framagit.org/framasoft/mobilizon/issues/1321)
* **auth:** small front fixes in 3rd-party auth provider callback ([bde7206](https://framagit.org/framasoft/mobilizon/commit/bde7206a1ca44fdf96d817921bb1efc497dcae40))
* **config:** rollback Mailer tls setting to :never by default ([3d63c12](https://framagit.org/framasoft/mobilizon/commit/3d63c12e88ca31f582489f126d1ef5677af79721))
* **docker:** fix entrypoint PostgreSQL extensions creations not using MOBILIZON_DATABASE_PORT ([9b49918](https://framagit.org/framasoft/mobilizon/commit/9b4991844ecaf7c1f1287ae62d1dfd463c2ea26b)), closes [#1321](https://framagit.org/framasoft/mobilizon/issues/1321) [#1321](https://framagit.org/framasoft/mobilizon/issues/1321)
* **front:** fixes in EditIdentity view ([7e13e2b](https://framagit.org/framasoft/mobilizon/commit/7e13e2baa7690d5dfc4a8b12097a4ed85ea825d7))
## 3.2.0-beta.5 (2023-09-06)
### Bug Fixes
* **docker:** make Docker entrypoint port configurable via $MOBILIZON_DATABASE_PORT ([13099e0](https://framagit.org/framasoft/mobilizon/commit/13099e0f118b727a1472282c6419ef9b1842c191))
* **front:** fix fetching and rendering profile mentions and fetching tags ([895378a](https://framagit.org/framasoft/mobilizon/commit/895378a96bf8a6c7662ed02509c37b8d8a95db0b))
* **sitemap:** save generated sitemaps in configurable directory ([f28109a](https://framagit.org/framasoft/mobilizon/commit/f28109ad50d85143e38c8e9f5d09c28f80566462)), closes [#1321](https://framagit.org/framasoft/mobilizon/issues/1321)
## 3.2.0-beta.4 (2023-09-05)
### Bug Fixes
* **back:** allow any other type of actor to be suspended ([92b222b](https://framagit.org/framasoft/mobilizon/commit/92b222b091cf6248969b0206e7c052b725a1286b))
* **back:** only try to insert activities for groups ([cfc9843](https://framagit.org/framasoft/mobilizon/commit/cfc984345e90b2960077956858606395f37ef9b9))
* **front:** don't return promise if result is not finished loading for tags ([8c14ba4](https://framagit.org/framasoft/mobilizon/commit/8c14ba441c6f0fadb3c59f80ff4e3abb2e625752))
* **front:** fix getting result from interactable object in InteractView ([31b2d06](https://framagit.org/framasoft/mobilizon/commit/31b2d065a904453580731133cd3dfd545a5816fa))
## 3.2.0-beta.3 (2023-09-04)
### Bug Fixes
* **i18n:** add missing translations ([af670f3](https://framagit.org/framasoft/mobilizon/commit/af670f39478b11465205fbea9b9268bab401bbb6))
### Features
* Add option to link an external registration provider for events ([2de6937](https://framagit.org/framasoft/mobilizon/commit/2de6937407743100daba1d397db4da32d4cb606b))
* **back:** add admin setting to disable external event feature ([f6611e8](https://framagit.org/framasoft/mobilizon/commit/f6611e8eb5a7e12dc0dc0c216b598e04144e07c6))
* improve group creation view [3f601748](https://framagit.org/framasoft/mobilizon/-/commit/3f60174877bbe05773b1d1b2ceb91749adec7ed7)
## 3.2.0-beta.2 (2023-09-01)
Fixes a CI issue that prevented 3.2.0-beta.2 being released.
### Bug Fixes
* **front:** fix behavior of local toggle for profiles & groups view depending on domain value ([84f62cd](https://framagit.org/framasoft/mobilizon/commit/84f62cd043d5cf5d186fea6f24a1a9dff5fc64ce))
## 3.2.0-beta.1 (2023-09-01)
### Features
* **cli:** allow the mobilizon.users.delete command to delete multiple users by email domain or ip ([bc50ab6](https://framagit.org/framasoft/mobilizon/commit/bc50ab66f3a44df220a7daa3cb1d917bd02487ba))
* **export:** add date of participant creation in participant exports ([fef60ed](https://framagit.org/framasoft/mobilizon/commit/fef60ed0f92fc4e09ee261ff03f1139aff2449c3)), closes [#1343](https://framagit.org/framasoft/mobilizon/issues/1343)
* **notifications:** add missing notifications when an user registers to an event ([da532c7](https://framagit.org/framasoft/mobilizon/commit/da532c7059bea5fcd47e2f42210e8ba842a11d63)), closes [#1344](https://framagit.org/framasoft/mobilizon/issues/1344)
* **reports:** allow reports to hold multiple events ([f2ac3e2](https://framagit.org/framasoft/mobilizon/commit/f2ac3e2e5d28f4257a5e2d4870d339fecf3a5f1b))
* **reports:** allow to suspend a profile or a user account directly from the report view ([69588db](https://framagit.org/framasoft/mobilizon/commit/69588dbf4ce2f80cc5829a841135042fa73eb4fe))
* **reports:** improve reportview and allow removing content + resolve report automatically ([b105c50](https://framagit.org/framasoft/mobilizon/commit/b105c508c03ce3cb96dd8342f96d3291aa197e22))
* **reports:** show suspended status next to reported profile ([b9a165a](https://framagit.org/framasoft/mobilizon/commit/b9a165a7fc565dc583cca81dd9c54570f73b4ca3))
### Bug Fixes
* add inets and ssl to extra_applications in test env ([af46bea](https://framagit.org/framasoft/mobilizon/commit/af46bea7f730f4479bb31518a9fa53de7302049a))
* **apps:** add missing app scopes ([7e98097](https://framagit.org/framasoft/mobilizon/commit/7e98097c710663609274200564fca9eff1ea4d20))
* **apps:** make sure we can set status for an application token ([1a6095d](https://framagit.org/framasoft/mobilizon/commit/1a6095d27aeb440379d27c3894c302f831214822))
* **backend:** fix config cache not being used everytime ([ed3cd58](https://framagit.org/framasoft/mobilizon/commit/ed3cd5858cd27a90d4724a95ee660bbc08e92e80))
* **backend:** handle email not being sent when resending registration instructions ([b2492a3](https://framagit.org/framasoft/mobilizon/commit/b2492a387086528598da36f11e53569c5bdb164c))
* create event time/date allignment ([3de90a3](https://framagit.org/framasoft/mobilizon/commit/3de90a3c73414105becdcb24899016178b1c6f02))
* **docker:** fix Qemu segfaulting on arm64 ([8e3f90f](https://framagit.org/framasoft/mobilizon/commit/8e3f90f7135e2a8a8ac46464420c9d57b2e02534)), closes [#1241](https://framagit.org/framasoft/mobilizon/issues/1241) [#1249](https://framagit.org/framasoft/mobilizon/issues/1249)
* **federation:** fix getting pictures from Gruppe actors ([7c5f8b2](https://framagit.org/framasoft/mobilizon/commit/7c5f8b24311253ef89c7e47cd7ce22ebe6cf2ec9))
* fix Elixir 1.15 depreciations ([da70427](https://framagit.org/framasoft/mobilizon/commit/da70427e3292be8943167bbad73d5a782a98c6b5))
* fix some typescript issues with pwa ([e351d3c](https://framagit.org/framasoft/mobilizon/commit/e351d3cb2f8183bb4335b3b21e154f46d9237a76))
* **front:** avoid crashing if we don't have configuration data in time when in guard ([7916261](https://framagit.org/framasoft/mobilizon/commit/7916261c5c8c680d064fba106619d733575bc39c))
* **front:** fix alignment of some input elements on event edition form ([50695fc](https://framagit.org/framasoft/mobilizon/commit/50695fcfd5e0dc6fd55185f4399d45ed1852f880))
* **front:** fix changing language not being saved to the user's settings ([010a5e4](https://framagit.org/framasoft/mobilizon/commit/010a5e426def0a0b7f2658234f3c9d6eec46a68e))
* **front:** fix comment not showing up when replying in a discussion ([cc8f02d](https://framagit.org/framasoft/mobilizon/commit/cc8f02d0a6354c49437e7ff1780912a71bed03f4))
* **front:** fix confirm anonymous participation ([f99267c](https://framagit.org/framasoft/mobilizon/commit/f99267c6115601fce6eadd6ee54893fde0d6fd84))
* **front:** fix discussion edition panel always showing up ([fee0e38](https://framagit.org/framasoft/mobilizon/commit/fee0e388af798f14d4da8cbd9f037137f6be9f85))
* **front:** fix display of participants list ([c6b83c4](https://framagit.org/framasoft/mobilizon/commit/c6b83c42d6fbb2e6a93175479ef1620913c6532f))
* **front:** fix map ([8f84ba1](https://framagit.org/framasoft/mobilizon/commit/8f84ba1d08ce8d2d266010ee3166106eed66116d)), closes [#1314](https://framagit.org/framasoft/mobilizon/issues/1314)
* **front:** fix missing type causing eslint error ([c76dba3](https://framagit.org/framasoft/mobilizon/commit/c76dba3dbfe4fb0ab9ed24f71a6f64681c643fca))
* **front:** fix selecting all participants in participant view ([beef3ff](https://framagit.org/framasoft/mobilizon/commit/beef3ff16d12f5d5710e302b739dd724ad4b0cb5))
* **front:** fix showing error message when app to approve doesn't exist ([12cbff1](https://framagit.org/framasoft/mobilizon/commit/12cbff154ae5cdd72a1a7e882cb99e943010222b))
* **front:** fix some alignment of some UI elements in mobile event view ([8c313b5](https://framagit.org/framasoft/mobilizon/commit/8c313b53977493792c113b5191443515f8aeae78))
* **front:** properly handle error when approving app ([086d208](https://framagit.org/framasoft/mobilizon/commit/086d208ee50ae1f9ecb30196e758fdc7687714ae))
* **front:** properly handle post not found ([8db31c9](https://framagit.org/framasoft/mobilizon/commit/8db31c99df668389db4c6651fa71a8c1420484cf))
* **front:** reduce horizontal padding on main element ([f3c218f](https://framagit.org/framasoft/mobilizon/commit/f3c218f841292a28ec6d1284a205e2c7fd7d8f6e))
* **lint:** fix lint after upgrades ([60aceb4](https://framagit.org/framasoft/mobilizon/commit/60aceb442ae49458e31a1f38d277eca7af248a36))
* **mail:** fix sending mail on OTP26 ([f54fff5](https://framagit.org/framasoft/mobilizon/commit/f54fff56fc5c94408b1fd16b1eb9dd0f91bc2dfd)), closes [#1341](https://framagit.org/framasoft/mobilizon/issues/1341)
* **push:** fix push subscriptions registration ([fdf87ea](https://framagit.org/framasoft/mobilizon/commit/fdf87ea991b1d406b28dbd0c8807908939070c8b))
* **pwa:** improvements to the PWA configuration ([04c5ac1](https://framagit.org/framasoft/mobilizon/commit/04c5ac11636a4ffb5d3ac0c510b028edfb7fc057))
* **reports:** make front-end handle nullified reported_id and reported_id ([afd2ffe](https://framagit.org/framasoft/mobilizon/commit/afd2ffe72294baedc9dd15dc89d57301831545cc))
* **reports:** remove on delete cascade for reports ([4f530ca](https://framagit.org/framasoft/mobilizon/commit/4f530cabcf1bcadc09399a728975d329f3c9fdbf))
## 3.1.3 (2023-06-21)
### Bug Fixes
* **groups:** fix unauthenticated access to groups because of missing read:group:members permission ([3714925](https://framagit.org/framasoft/mobilizon/commit/3714925896ad0415496352b9901ebec199afa0f2)), closes [#1311](https://framagit.org/framasoft/mobilizon/issues/1311)
## 3.1.2 (2023-06-21)
### Bug Fixes
* **activity settings:** fix saving activity settings ([6c1e1e9](https://framagit.org/framasoft/mobilizon/commit/6c1e1e98d81c7469f41beed17cfa1d4b718b5d13)), closes [#1251](https://framagit.org/framasoft/mobilizon/issues/1251)
* **apps:** fix pruning old application device activations ([dd00620](https://framagit.org/framasoft/mobilizon/commit/dd00620b9a54b2b1356855d280e03c82befe15e4))
* **backend:** filter out nil tags before starting looking for existing ones ([f04d2b9](https://framagit.org/framasoft/mobilizon/commit/f04d2b9225b80333f03a3cc9366df4a05af88a73))
* **deps:** fix compatibility with elixir-plug/mime 2.0.5 ([d63999c](https://framagit.org/framasoft/mobilizon/commit/d63999c081bcbb5923af17b71edbfd13a3720d7d))
* **discussions:** handle changeset errors when updating discussion ([ca06ec3](https://framagit.org/framasoft/mobilizon/commit/ca06ec397fbd6848e340dfae12c635736069a9f3))
* **exports:** properly handle export format not being handled ([a76b1ca](https://framagit.org/framasoft/mobilizon/commit/a76b1ca66d776fbe4566d7f23b38b087ae32530b))
* **federation:** allow federated usernames with capitals ([d502164](https://framagit.org/framasoft/mobilizon/commit/d5021647d753e6457e459b1f992da60876292428))
* **federation:** handle fetch_actor with a map ([552ab4c](https://framagit.org/framasoft/mobilizon/commit/552ab4c80b2f99095028ab3685c71ff9efdb94eb))
* **federation:** handle string values for tags when constructing mentions ([2729d5e](https://framagit.org/framasoft/mobilizon/commit/2729d5ed7acef7c20a4388f019152e80a9db163c))
* **federation:** ignore mentions from everything that's not a AP Person ([56f341e](https://framagit.org/framasoft/mobilizon/commit/56f341e960b7ae0a5fe78d7174f0e05d14add3f2))
* **federation:** only refresh instances once a day ([6745590](https://framagit.org/framasoft/mobilizon/commit/6745590e54dce236dc7a2319f9c49c4aa6858306))
* **federation:** prevent fetching own relay actor ([b981f91](https://framagit.org/framasoft/mobilizon/commit/b981f91cf748079847ae7a71b68f98b6914c951f))
* **federation:** restrict fetch_group first arg to binaries ([e8d34b4](https://framagit.org/framasoft/mobilizon/commit/e8d34b4ea9f06d16a5982da8e5ff5140852c985d))
* **federation:** rotate relay keys on startup if missing private keys ([5381eaa](https://framagit.org/framasoft/mobilizon/commit/5381eaae22248cdc6585d19c10be7fe2b7f5709f))
* **front:** add missing title to Participants View page ([a5a86a5](https://framagit.org/framasoft/mobilizon/commit/a5a86a5e1be08cf9123ee7ad0979974bc2be1cb4))
* **front:** fix displaying user activity settings checkboxes ([8e21c30](https://framagit.org/framasoft/mobilizon/commit/8e21c30f92f47dcb742d8f7df2aed59191158d80)), closes [#1251](https://framagit.org/framasoft/mobilizon/issues/1251)
* **front:** fix wrong key name for dialog.confirm() option ([c8f49e1](https://framagit.org/framasoft/mobilizon/commit/c8f49e1837d719cd737c3e1ae976f14b20345e2b))
* **front:** fix wrong value for timezone when it has no prefix ([2dd0e13](https://framagit.org/framasoft/mobilizon/commit/2dd0e13eba8bb5c04af45bae0de059deb93c2efa)), closes [#1275](https://framagit.org/framasoft/mobilizon/issues/1275)
* **group:** fix getting group members count ([f749518](https://framagit.org/framasoft/mobilizon/commit/f749518bf7a29a86da559bfe6aba6d7485e7cfeb)), closes [#1303](https://framagit.org/framasoft/mobilizon/issues/1303)
* **participant exports:** fix participants by returning the export type as well as the file path ([49b04c9](https://framagit.org/framasoft/mobilizon/commit/49b04c9b19517daa0a07656779d53001b39ab803))
* **participant:** handle re-confirming participation ([5cc5c99](https://framagit.org/framasoft/mobilizon/commit/5cc5c9943cbc9a53246dda98958e99d004f0dfa9))
### Features
* **graphql:** validate timezone id as a GraphQL Scalar ([845bb6a](https://framagit.org/framasoft/mobilizon/commit/845bb6ac90081ef8cb4cff8d6ec3d11bfc19857c)), closes [#1299](https://framagit.org/framasoft/mobilizon/issues/1299)
## 3.1.1 (2023-06-02) ## 3.1.1 (2023-06-02)
### Features ### Features

View file

@ -1,9 +1,10 @@
FROM elixir:alpine FROM elixir:1.15-alpine
RUN apk add --no-cache inotify-tools postgresql-client yarn file make gcc libc-dev argon2 imagemagick cmake build-base libwebp-tools bash ncurses git python3 RUN apk add --no-cache inotify-tools postgresql-client file make gcc libc-dev argon2 imagemagick cmake build-base libwebp-tools bash ncurses git python3 npm
RUN mix local.hex --force && mix local.rebar --force RUN mix local.hex --force && mix local.rebar --force
WORKDIR /app WORKDIR /app
EXPOSE 4000 EXPOSE 4000
EXPOSE 5173

View file

@ -4,24 +4,25 @@ init:
setup: stop setup: stop
@bash docker/message.sh "Compiling everything" @bash docker/message.sh "Compiling everything"
docker-compose run --rm api bash -c 'mix deps.get; yarn --cwd "js"; yarn --cwd "js" build:pictures; mix ecto.create; mix ecto.migrate' docker compose run --rm api bash -c 'mix deps.get; npm ci; npm run build:pictures; mix ecto.create; mix ecto.migrate'
migrate: migrate:
docker-compose run --rm api mix ecto.migrate docker compose run --rm api mix ecto.migrate
logs: logs:
docker-compose logs -f docker compose logs -f
start: stop start: stop
@bash docker/message.sh "Starting Mobilizon with Docker" @bash docker/message.sh "Starting Mobilizon with Docker"
docker-compose up -d api docker compose up -d api
@bash docker/message.sh "Docker server started" @bash docker/message.sh "Docker server started"
stop: stop:
@bash docker/message.sh "Stopping Mobilizon" @bash docker/message.sh "Stopping Mobilizon"
docker-compose down docker compose down
@bash docker/message.sh "Mobilizon is stopped" @bash docker/message.sh "Mobilizon is stopped"
test: stop test: stop
@bash docker/message.sh "Running tests" @bash docker/message.sh "Running tests"
docker-compose -f docker-compose.yml -f docker-compose.test.yml run api mix test $(only) docker compose -f docker compose.yml -f docker compose.test.yml run api mix prepare_test
docker compose -f docker compose.yml -f docker compose.test.yml run api mix test $(only)
@bash docker/message.sh "Done running tests" @bash docker/message.sh "Done running tests"
format: format:
docker-compose run --rm api bash -c "mix format && mix credo --strict" docker compose run --rm api bash -c "mix format && mix credo --strict"
@bash docker/message.sh "Code is now ready to commit :)" @bash docker/message.sh "Code is now ready to commit :)"
target: init target: init

View file

@ -1,3 +1,5 @@
*You can learn about [what we plan to do with this fork](https://framacolibri.org/t/using-mobilizon-for-regional-leftist-subculture-calendar-platforms/18772) in this post in the Mobilizon forum.*
<h1 align="center"> <h1 align="center">
<a href="https://joinmobilizon.org"> <a href="https://joinmobilizon.org">
<img src="https://lutim.cpy.re/qVYC86G9.png" alt="Mobilizon"> <img src="https://lutim.cpy.re/qVYC86G9.png" alt="Mobilizon">
@ -16,6 +18,20 @@ Mobilizon is your federated organization and mobilization platform. Gather peopl
</a> </a>
</p> </p>
## Notes about this fork
The currently deployed `main` branch can be tested at [https://rotes.potsda.mn/](https://rotes.potsda.mn/).
### Building with Nix
For building this locally, you can use Nix (with Flakes enabled):
```
$ nix build git+ssh://git@git.potsda.mn/potsda.mn/mobilizon.git?ref=main#mobilizon
```
The built package is then located in `result/`.
## Introduction ## Introduction
Mobilizon is a tool designed to create platforms for managing communities and events. Its purpose is to help as many people as possible to free themselves from Facebook groups and events, from Meetup, etc. Mobilizon is a tool designed to create platforms for managing communities and events. Its purpose is to help as many people as possible to free themselves from Facebook groups and events, from Meetup, etc.
@ -50,6 +66,7 @@ We appreciate any contribution to Mobilizon. Check [our contributing page](https
* 🔢 Pick an instance [https://mobilizon.org](https://mobilizon.org) * 🔢 Pick an instance [https://mobilizon.org](https://mobilizon.org)
* 💻 Source: [https://framagit.org/framasoft/mobilizon](https://framagit.org/framasoft/mobilizon) * 💻 Source: [https://framagit.org/framasoft/mobilizon](https://framagit.org/framasoft/mobilizon)
* 📜 Documentation [https://docs.joinmobilizon.org](https://docs.joinmobilizon.org) * 📜 Documentation [https://docs.joinmobilizon.org](https://docs.joinmobilizon.org)
* A summarized description of structure of sources is done in [`docs/dev.md`](./docs/dev.md)
### Discuss ### Discuss
* 💬 Element/Matrix: [https://matrix.to/#/#Mobilizon:matrix.org](https://matrix.to/#/#Mobilizon:matrix.org) * 💬 Element/Matrix: [https://matrix.to/#/#Mobilizon:matrix.org](https://matrix.to/#/#Mobilizon:matrix.org)

View file

@ -7,6 +7,6 @@ module.exports = {
localSchemaFile: "./schema.graphql", localSchemaFile: "./schema.graphql",
}, },
// Files processed by the extension // Files processed by the extension
includes: ["js/src/**/*.vue", "js/src/**/*.js"], includes: ["src/**/*.vue", "src/**/*.js"],
}, },
}; };

View file

@ -41,7 +41,10 @@ config :mobilizon, :instance,
email_reply_to: "noreply@localhost" email_reply_to: "noreply@localhost"
config :mobilizon, :groups, enabled: true config :mobilizon, :groups, enabled: true
config :mobilizon, :events, creation: true
config :mobilizon, :events,
creation: true,
external: true
config :mobilizon, :restrictions, only_admin_can_create_groups: false config :mobilizon, :restrictions, only_admin_can_create_groups: false
config :mobilizon, :restrictions, only_groups_can_create_events: false config :mobilizon, :restrictions, only_groups_can_create_events: false
@ -65,6 +68,10 @@ config :mime, :types, %{
"application/xrd+xml" => ["xrd-xml"] "application/xrd+xml" => ["xrd-xml"]
} }
config :mime, :extensions, %{
"activity-json" => "application/activity+json"
}
# Upload configuration # Upload configuration
config :mobilizon, Mobilizon.Web.Upload, config :mobilizon, Mobilizon.Web.Upload,
uploader: Mobilizon.Web.Upload.Uploader.Local, uploader: Mobilizon.Web.Upload.Uploader.Local,
@ -109,17 +116,14 @@ config :mobilizon, :media_proxy,
config :mobilizon, Mobilizon.Web.Email.Mailer, config :mobilizon, Mobilizon.Web.Email.Mailer,
adapter: Swoosh.Adapters.SMTP, adapter: Swoosh.Adapters.SMTP,
relay: "localhost", relay: "localhost",
# usually 25, 465 or 587
port: 25,
username: "", username: "",
password: "", password: "",
# can be `:always` or `:never` # can be `:always` or `:never`
auth: :if_available, auth: :if_available,
# can be `true` # can be `true`
ssl: false, # ssl: false,
# can be `:always` or `:never` # can be `:always` or `:never`
tls: :if_available, tls: :never,
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
retries: 1, retries: 1,
# can be `true` # can be `true`
no_mx_lookups: false no_mx_lookups: false
@ -148,13 +152,12 @@ config :mobilizon, Mobilizon.Web.Auth.Guardian,
} }
config :guardian, Guardian.DB, config :guardian, Guardian.DB,
adapter: Guardian.DB.EctoAdapter,
repo: Mobilizon.Storage.Repo, repo: Mobilizon.Storage.Repo,
# default # default
schema_name: "guardian_tokens", schema_name: "guardian_tokens",
# store all token types if not set # store all token types if not set
token_types: ["refresh"], token_types: ["refresh"]
# default: 60 minutes
sweep_interval: 60
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
@ -313,6 +316,7 @@ config :mobilizon, Oban,
{"@hourly", Mobilizon.Service.Workers.ExportCleanerWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.ExportCleanerWorker, queue: :background},
{"@hourly", Mobilizon.Service.Workers.SendActivityRecapWorker, queue: :notifications}, {"@hourly", Mobilizon.Service.Workers.SendActivityRecapWorker, queue: :notifications},
{"@daily", Mobilizon.Service.Workers.CleanOldActivityWorker, queue: :background}, {"@daily", Mobilizon.Service.Workers.CleanOldActivityWorker, queue: :background},
{"@daily", Mobilizon.Service.Workers.RefreshParticipantStats, queue: :background},
{"@hourly", Mobilizon.Service.Workers.CleanApplicationData, {"@hourly", Mobilizon.Service.Workers.CleanApplicationData,
queue: :background, args: %{type: :application_token}}, queue: :background, args: %{type: :application_token}},
{"@hourly", Mobilizon.Service.Workers.CleanApplicationData, {"@hourly", Mobilizon.Service.Workers.CleanApplicationData,
@ -380,6 +384,8 @@ config :mobilizon, Mobilizon.Service.GlobalSearch.SearchMobilizon,
config :mobilizon, Mobilizon.Service.AntiSpam, service: Mobilizon.Service.AntiSpam.Akismet config :mobilizon, Mobilizon.Service.AntiSpam, service: Mobilizon.Service.AntiSpam.Akismet
config :mobilizon, Mobilizon.Service.SiteMap, path: "/var/lib/mobilizon/sitemap"
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs" import_config "#{config_env()}.exs"

View file

@ -16,7 +16,8 @@ config :mobilizon, Mobilizon.Web.Endpoint,
watchers: [ watchers: [
node: [ node: [
"node_modules/.bin/vite", "node_modules/.bin/vite",
cd: Path.expand("../js", __DIR__) "--host",
System.get_env("VITE_HOST", "localhost")
] ]
] ]
@ -92,6 +93,9 @@ config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, uploads: "uploads"
config :mobilizon, :exports, path: "uploads/exports" config :mobilizon, :exports, path: "uploads/exports"
config :mobilizon, Mobilizon.Service.SiteMap,
path: System.get_env("MOBILIZON_SITEMAP_PATH", "priv/static")
config :tz_world, data_dir: "_build/dev/lib/tz_world/priv" config :tz_world, data_dir: "_build/dev/lib/tz_world/priv"
config :mobilizon, :anonymous, config :mobilizon, :anonymous,

View file

@ -2,6 +2,28 @@
import Config import Config
{:ok, _} = Application.ensure_all_started(:tls_certificate_check)
loglevels = [
:emergency,
:alert,
:critical,
:error,
:warning,
:notice,
:info,
:debug
]
loglevel_env = System.get_env("MOBILIZON_LOGLEVEL", "error")
loglevel =
if loglevel_env in Enum.map(loglevels, &to_string/1) do
String.to_existing_atom(loglevel_env)
else
:error
end
listen_ip = System.get_env("MOBILIZON_INSTANCE_LISTEN_IP", "0.0.0.0") listen_ip = System.get_env("MOBILIZON_INSTANCE_LISTEN_IP", "0.0.0.0")
listen_ip = listen_ip =
@ -43,14 +65,17 @@ config :mobilizon, Mobilizon.Storage.Repo,
ssl: System.get_env("MOBILIZON_DATABASE_SSL", "false") == "true", ssl: System.get_env("MOBILIZON_DATABASE_SSL", "false") == "true",
pool_size: 10 pool_size: 10
config :logger, level: loglevel
config :mobilizon, Mobilizon.Web.Email.Mailer, config :mobilizon, Mobilizon.Web.Email.Mailer,
adapter: Swoosh.Adapters.SMTP, adapter: Swoosh.Adapters.SMTP,
relay: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"), relay: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"),
port: System.get_env("MOBILIZON_SMTP_PORT", "25"), port: System.get_env("MOBILIZON_SMTP_PORT", "25"),
username: System.get_env("MOBILIZON_SMTP_USERNAME", nil), username: System.get_env("MOBILIZON_SMTP_USERNAME", nil),
password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil), password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil),
tls: :if_available, tls: System.get_env("MOBILIZON_SMTP_TLS", "if_available"),
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], tls_options:
:tls_certificate_check.options(System.get_env("MOBILIZON_SMTP_SERVER", "localhost")),
ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"), ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"),
retries: 1, retries: 1,
no_mx_lookups: false, no_mx_lookups: false,
@ -79,4 +104,9 @@ config :mobilizon, :exports,
config :tz_world, config :tz_world,
data_dir: System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/timezones") data_dir: System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/timezones")
config :tzdata, :data_dir, System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/tzdata") config :tzdata, :data_dir, System.get_env("MOBILIZON_TZDATA_DIR", "/var/lib/mobilizon/tzdata")
config :web_push_encryption, :vapid_details,
subject: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_SUBJECT", nil),
public_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PUBLIC_KEY", nil),
private_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PRIVATE_KEY", nil)

View file

@ -16,7 +16,9 @@ config :mobilizon, Mobilizon.Web.Endpoint,
check_origin: false, check_origin: false,
# Somehow this can't be merged properly with the dev config so we got this… # Somehow this can't be merged properly with the dev config so we got this…
watchers: [ watchers: [
yarn: [cd: Path.expand("../js", __DIR__)] node: [
"node_modules/.bin/vite"
]
] ]
config :vite_phx, config :vite_phx,

View file

@ -54,6 +54,11 @@ config :mobilizon, :ldap,
bind_uid: System.get_env("LDAP_BIND_UID"), bind_uid: System.get_env("LDAP_BIND_UID"),
bind_password: System.get_env("LDAP_BIND_PASSWORD") bind_password: System.get_env("LDAP_BIND_PASSWORD")
# Faster runs in test environment
config :argon2_elixir,
t_cost: 1,
m_cost: 8
config :mobilizon, Mobilizon.Web.Email.Mailer, adapter: Swoosh.Adapters.Test config :mobilizon, Mobilizon.Web.Email.Mailer, adapter: Swoosh.Adapters.Test
config :mobilizon, Mobilizon.Web.Upload, filters: [], link_name: false config :mobilizon, Mobilizon.Web.Upload, filters: [], link_name: false
@ -62,6 +67,9 @@ config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, uploads: "test/uploads"
config :mobilizon, :exports, path: "test/uploads/exports" config :mobilizon, :exports, path: "test/uploads/exports"
config :mobilizon, Mobilizon.Service.SiteMap,
path: System.get_env("MOBILIZON_SITEMAP_PATH", "test/sitemap")
config :tz_world, data_dir: "_build/test/lib/tz_world/priv" config :tz_world, data_dir: "_build/test/lib/tz_world/priv"
config :tesla, Mobilizon.Service.HTTP.ActivityPub, config :tesla, Mobilizon.Service.HTTP.ActivityPub,

139
default.nix Normal file
View file

@ -0,0 +1,139 @@
{ lib
, beamPackages
, fetchFromGitHub
, git
, cmake
, nixosTests
, src
, src-config
, mobilizon-js
}:
let
inherit (beamPackages) mixRelease buildMix;
in
mixRelease rec {
pname = "mobilizon";
version = "4.0.2";
inherit src;
# See https://github.com/whitfin/cachex/issues/205
# This circumvents a startup error for now
stripDebug = false;
nativeBuildInputs = [ git cmake ];
mixNixDeps = import ./mix.nix {
inherit beamPackages lib;
overrides = (final: prev:
(lib.mapAttrs
(_: value: value.override {
appConfigPath = src-config;
})
prev) // {
fast_html = prev.fast_html.override {
nativeBuildInputs = [ cmake ];
};
ex_cldr = prev.ex_cldr.overrideAttrs (old: {
# We have to use the GitHub sources, as it otherwise tries to download
# the locales at build time.
src = fetchFromGitHub {
owner = "elixir-cldr";
repo = "cldr";
rev = "v${old.version}";
sha256 = assert old.version == "2.37.5";
"sha256-T5Qvuo+xPwpgBsqHNZYnTCA4loToeBn1LKTMsDcCdYs=";
};
postInstall = ''
cp $src/priv/cldr/locales/* $out/lib/erlang/lib/ex_cldr-${old.version}/priv/cldr/locales/
'';
});
# Upstream issue: https://github.com/bryanjos/geo_postgis/pull/87
geo_postgis = prev.geo_postgis.overrideAttrs (old: {
propagatedBuildInputs = old.propagatedBuildInputs ++ [ final.ecto ];
});
# The remainder are Git dependencies (and their deps) that are not supported by mix2nix currently.
web_push_encryption = buildMix rec {
name = "web_push_encryption";
version = "0.3.1";
src = fetchFromGitHub {
owner = "danhper";
repo = "elixir-web-push-encryption";
rev = "70f00d06cbd88c9ac382e0ad2539e54448e9d8da";
sha256 = "sha256-b4ZMrt/8n2sPUFtCDRTwXS1qWm5VlYdbx8qC0R0boOA=";
};
beamDeps = with final; [ httpoison jose ];
};
icalendar = buildMix rec {
name = "icalendar";
version = "unstable-2022-04-10";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "1033d922c82a7223db0ec138e2316557b70ff49f";
sha256 = "sha256-N3bJZznNazLewHS4c2B7LP1lgxd1wev+EWVlQ7rOwfU=";
};
beamDeps = with final; [ mix_test_watch ex_doc timex ];
};
rajska = buildMix rec {
name = "rajska";
version = "1.3.3";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "0c036448e261e8be6a512581c592fadf48982d84";
sha256 = "sha256-4pfply1vTAIT2Xvm3kONmrCK05xKfXFvcb8EKoSCXBE=";
};
beamDeps = with final; [ ex_doc credo absinthe excoveralls hammer mock ];
};
exkismet = buildMix rec {
name = "exkismet";
version = "0.0.3";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "8b5485fde00fafbde20f315bec387a77f7358334";
sha256 = "sha256-ttgCWoBKU7VTjZJBhZNtqVF4kN7psBr/qOeR65MbTqw=";
};
beamDeps = with final; [ httpoison ex_doc credo doctor dialyxir ];
};
});
};
preConfigure = ''
export LANG=C.UTF-8 # fix elixir locale warning
'';
# Install the compiled js part
preBuild =
''
cp -a "${mobilizon-js}/_napalm-install/priv/static" ./priv
chmod 770 -R ./priv
'';
postBuild = ''
mix phx.digest --no-deps-check
'';
# Just a hack to reduce path size by 60MB
postInstall =
let
inherit (mixNixDeps) ex_cldr;
in
''
rm -r $out/lib/ex_cldr-${ex_cldr.version}/priv/cldr/locales
ln -s ${ex_cldr.src}/priv/cldr/locales $out/lib/ex_cldr-${ex_cldr.version}/priv/cldr/locales
'';
passthru.elixirPackage = beamPackages.elixir;
meta = with lib; {
description = "Mobilizon is an online tool to help manage your events, your profiles and your groups";
homepage = "https://joinmobilizon.org/";
license = licenses.agpl3Plus;
maintainers = with maintainers; [ minijackson erictapen ];
};
}

View file

@ -11,7 +11,7 @@ services:
MIX_ENV: "test" MIX_ENV: "test"
MOBILIZON_DATABASE_DBNAME: mobilizon_test MOBILIZON_DATABASE_DBNAME: mobilizon_test
MOBILIZON_INSTANCE_HOST: mobilizon.test MOBILIZON_INSTANCE_HOST: mobilizon.test
command: "mix test" command: "mix prepare_test && mix test"
volumes: volumes:
pgdata: pgdata:
.: .:

View file

@ -19,6 +19,7 @@ services:
- ".:/app" - ".:/app"
ports: ports:
- 4000:4000 - 4000:4000
- 5173:5173
depends_on: depends_on:
- postgres - postgres
environment: environment:
@ -35,6 +36,7 @@ services:
MOBILIZON_DATABASE_DBNAME: ${POSTGRES_DB} MOBILIZON_DATABASE_DBNAME: ${POSTGRES_DB}
MOBILIZON_DATABASE_HOST: postgres MOBILIZON_DATABASE_HOST: postgres
MOBILIZON_DATABASE_PORT: ${POSTGRES_PORT} MOBILIZON_DATABASE_PORT: ${POSTGRES_PORT}
VITE_HOST: ${VITE_HOST:-localhost}
command: sh -c "mix phx.server" command: sh -c "mix phx.server"
volumes: volumes:
pgdata: pgdata:

View file

@ -1,21 +1,27 @@
FROM elixir as build ARG IMAGE="elixir:1.15"
FROM ${IMAGE} as build
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
ENV MIX_ENV prod ENV MIX_ENV prod
# ENV LANG en_US.UTF-8 # ENV LANG en_US.UTF-8
ARG APP_ASSET ARG APP_ASSET
# Fix qemu segfault on arm64
# See https://github.com/plausible/analytics/pull/2879 and https://github.com/erlang/otp/pull/6340
ARG ERL_FLAGS=""
ENV ERL_FLAGS=$ERL_FLAGS
# Set the right versions # Set the right versions
ENV ELIXIR_VERSION latest ENV ELIXIR_VERSION latest
ENV ERLANG_VERSION latest ENV ERLANG_VERSION latest
ENV NODE_VERSION 16 ENV NODE_VERSION 20
# Install system dependencies # Install system dependencies
RUN apt-get update -yq && apt-get install -yq build-essential cmake postgresql-client git curl gnupg unzip exiftool webp imagemagick gifsicle RUN apt-get update -yq && apt-get install -yq build-essential cmake postgresql-client git curl gnupg unzip exiftool webp imagemagick gifsicle
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# # Install Node & yarn # # Install Node
# RUN curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq # RUN curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq
# RUN npm install -g yarn
# Install build tools # Install build tools
RUN source /root/.bashrc && \ RUN source /root/.bashrc && \
@ -27,8 +33,8 @@ COPY ./ /mobilizon
WORKDIR /mobilizon WORKDIR /mobilizon
# # Build front-end # # Build front-end
# RUN yarn --cwd "js" install --frozen-lockfile # RUN npm install
# RUN yarn --cwd "js" run build # RUN npm run build
# Elixir release # Elixir release
RUN source /root/.bashrc && \ RUN source /root/.bashrc && \

View file

@ -1,16 +1,20 @@
# First build the application assets # First build the application assets
FROM node:18-alpine as assets FROM node:20-alpine as assets
RUN apk add --no-cache python3 build-base libwebp-tools bash imagemagick ncurses RUN apk add --no-cache python3 build-base libwebp-tools bash imagemagick ncurses
WORKDIR /build WORKDIR /build
COPY js . COPY . .
# Network timeout because it's slow when cross-compiling # Network timeout because it's slow when cross-compiling
RUN yarn install --network-timeout 100000 \ RUN npm install && npm run build
&& yarn run build
# Then, build the application binary # Then, build the application binary
FROM elixir:1.14-alpine AS builder FROM elixir:1.15-alpine AS builder
# Fix qemu segfault on arm64
# See https://github.com/plausible/analytics/pull/2879 and https://github.com/erlang/otp/pull/6340
ARG ERL_FLAGS=""
ENV ERL_FLAGS=$ERL_FLAGS
RUN apk add --no-cache build-base git cmake RUN apk add --no-cache build-base git cmake
@ -26,7 +30,7 @@ COPY config/config.exs config/prod.exs ./config/
COPY config/docker.exs ./config/runtime.exs COPY config/docker.exs ./config/runtime.exs
COPY rel ./rel COPY rel ./rel
COPY support ./support COPY support ./support
COPY --from=assets ./priv/static ./priv/static COPY --from=assets /build/priv/static ./priv/static
RUN mix phx.digest.clean --all && mix phx.digest && mix release RUN mix phx.digest.clean --all && mix phx.digest && mix release
@ -46,23 +50,24 @@ LABEL org.opencontainers.image.title="mobilizon" \
org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.created=$BUILD_DATE org.opencontainers.image.created=$BUILD_DATE
RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell openssl1.1-compat RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell
RUN pip install weasyprint pyexcel-ods3 RUN pip --no-cache-dir install --break-system-packages weasyprint pyexcel-ods3
# Create every data directory # Create every data directory
RUN mkdir -p /var/lib/mobilizon/uploads && chown nobody:nobody /var/lib/mobilizon/uploads RUN mkdir -p /var/lib/mobilizon/uploads && chown nobody:nobody /var/lib/mobilizon/uploads
RUN mkdir -p /var/lib/mobilizon/timezones && chown nobody:nobody /var/lib/mobilizon/timezones RUN mkdir -p /var/lib/mobilizon/timezones && chown nobody:nobody /var/lib/mobilizon/timezones
RUN mkdir -p /var/lib/mobilizon/tzdata && chown nobody:nobody /var/lib/mobilizon/tzdata RUN mkdir -p /var/lib/mobilizon/tzdata && chown nobody:nobody /var/lib/mobilizon/tzdata
RUN mkdir -p /var/lib/mobilizon/sitemap && chown nobody:nobody /var/lib/mobilizon/sitemap
RUN mkdir -p /var/lib/mobilizon/uploads/exports/{csv,pdf,ods} && chown -R nobody:nobody /var/lib/mobilizon/uploads/exports RUN mkdir -p /var/lib/mobilizon/uploads/exports/{csv,pdf,ods} && chown -R nobody:nobody /var/lib/mobilizon/uploads/exports
# Get timezone geodata
RUN curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets
RUN chown -R nobody:nobody /var/lib/mobilizon/timezones
# Create configuration directory # Create configuration directory
RUN mkdir -p /etc/mobilizon && chown nobody:nobody /etc/mobilizon RUN mkdir -p /etc/mobilizon && chown nobody:nobody /etc/mobilizon
USER nobody USER nobody
# Get timezone geodata
RUN curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets
EXPOSE 4000 EXPOSE 4000
ENV MOBILIZON_DOCKER=true ENV MOBILIZON_DOCKER=true

View file

@ -3,12 +3,12 @@
set -e set -e
echo "-- Waiting for database..." echo "-- Waiting for database..."
while ! pg_isready -U ${MOBILIZON_DATABASE_USERNAME} -d postgres://${MOBILIZON_DATABASE_HOST}:5432/${MOBILIZON_DATABASE_DBNAME} -t 1; do while ! pg_isready -U ${MOBILIZON_DATABASE_USERNAME} -d postgres://${MOBILIZON_DATABASE_HOST}:${MOBILIZON_DATABASE_PORT:-5432}/${MOBILIZON_DATABASE_DBNAME} -t 1; do
sleep 1s sleep 1s
done done
PGPASSWORD=$MOBILIZON_DATABASE_PASSWORD psql -U $MOBILIZON_DATABASE_USERNAME -d $MOBILIZON_DATABASE_DBNAME -h $MOBILIZON_DATABASE_HOST -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;' PGPASSWORD=$MOBILIZON_DATABASE_PASSWORD psql -U $MOBILIZON_DATABASE_USERNAME -d $MOBILIZON_DATABASE_DBNAME -h $MOBILIZON_DATABASE_HOST -p ${MOBILIZON_DATABASE_PORT:-5432} -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;'
PGPASSWORD=$MOBILIZON_DATABASE_PASSWORD psql -U $MOBILIZON_DATABASE_USERNAME -d $MOBILIZON_DATABASE_DBNAME -h $MOBILIZON_DATABASE_HOST -c 'CREATE EXTENSION IF NOT EXISTS unaccent;' PGPASSWORD=$MOBILIZON_DATABASE_PASSWORD psql -U $MOBILIZON_DATABASE_USERNAME -d $MOBILIZON_DATABASE_DBNAME -h $MOBILIZON_DATABASE_HOST -p ${MOBILIZON_DATABASE_PORT:-5432} -c 'CREATE EXTENSION IF NOT EXISTS unaccent;'
echo "-- Running migrations..." echo "-- Running migrations..."
/bin/mobilizon_ctl migrate /bin/mobilizon_ctl migrate

View file

@ -1,11 +1,11 @@
FROM elixir:latest FROM elixir:latest
LABEL maintainer="Thomas Citharel <tcit@tcit.fr>" LABEL maintainer="Thomas Citharel <thomas.citharel@framasoft.org>"
ENV REFRESHED_AT=2023-05-22 ENV REFRESHED_AT=2023-11-20
RUN apt-get update -yq && apt-get install -yq build-essential inotify-tools postgresql-client git curl gnupg xvfb libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 cmake exiftool python3-pip python3-setuptools RUN apt-get update -yq && apt-get install -yq ca-certificates build-essential inotify-tools postgresql-client git curl gnupg xvfb libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 cmake exiftool python3-pip python3-setuptools
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash && apt-get install nodejs -yq RUN mkdir -p /etc/apt/keyrings && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -yq
RUN npm install -g yarn wait-on RUN npm install -g wait-on
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN mix local.hex --force && mix local.rebar --force RUN mix local.hex --force && mix local.rebar --force
RUN pip3 install -Iv weasyprint pyexcel_ods3 RUN pip3 --no-cache-dir install -Iv weasyprint pyexcel_ods3
RUN curl https://dbip.mirror.framasoft.org/files/dbip-city-lite-latest.mmdb --output GeoLite2-City.mmdb -s && mkdir -p /usr/share/GeoIP && mv GeoLite2-City.mmdb /usr/share/GeoIP/ RUN curl https://dbip.mirror.framasoft.org/files/dbip-city-lite-latest.mmdb --output GeoLite2-City.mmdb -s && mkdir -p /usr/share/GeoIP && mv GeoLite2-City.mmdb /usr/share/GeoIP/

30
docs/dev.md Normal file
View file

@ -0,0 +1,30 @@
# Documentation for developpers
_This file is a summary of the documentation for developpers. As explained in [CONTRIBUTING.md](../CONTRIBUTING.md), the main documentation is available at <https://docs.joinmobilizon.org/contribute/>_
## Technologies
Mobilizon is an app that uses:
* [Elixir](https://elixir-lang.org/) for backend,
* [VueJS](https://vuejs.org/) for front-end
* [GraphQL](https://graphql.org/) as it's API layer
[GraphQL](https://graphql.org/) is managed using:
* [Absinthe](https://absinthe-graphql.org/) on the backend
* [VueApollo](https://apollo.vuejs.org/) on the front-end.
[UI](https://en.wikipedia.org/wiki/User_interface) is handled with [Tailwind](https://tailwindcss.com/) and [Oruga](https://oruga.io/).
## Structure of sources
* `config` backend compile-time and runtime configuration
* `docker` 🐳
* `src` Front-end
* `lib/federation` Handling all the federation stuff (sending and receving activities, converting activities, signatures, helpers…)
* `lib/graphql/schema` The schema declarations for the GraphQL API
* `lib/graphql/resolvers` The logic behind the GraphQL API
* `lib/mix/tasks` CLI
* `lib/mobilizon` model structures, database queries
* `lib/service` various services
* `lib/web` controllers, middlewares, auth-related stuff
* `test` tests

View file

98
flake.lock Normal file
View file

@ -0,0 +1,98 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"napalm": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1703102458,
"narHash": "sha256-3pOV731qi34Q2G8e2SqjUXqnftuFrbcq+NdagEZXISo=",
"owner": "nix-community",
"repo": "napalm",
"rev": "edcb26c266ca37c9521f6a97f33234633cbec186",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "napalm",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1705332318,
"narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "3449dc925982ad46246cfc36469baf66e1b64f17",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1706550542,
"narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"napalm": "napalm",
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

241
flake.nix Normal file
View file

@ -0,0 +1,241 @@
{
description = "Mobilizon fork for potsda.mn";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nix-filter.url = "github:numtide/nix-filter";
napalm.url = "github:nix-community/napalm";
napalm.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, nix-filter, napalm }:
let
forAllSystems = f: nixpkgs.lib.genAttrs
[ "x86_64-linux" "aarch64-linux" ]
(system: f system);
nixpkgsFor = forAllSystems (
system:
import nixpkgs { inherit system; }
);
filter = nix-filter.lib;
in
{
packages = forAllSystems (system:
let
pkgs = nixpkgsFor.${system};
# Directories that are neither needed for building the frontend nor the backend.
# For better build caching.
unrelatedDirs = [
"flake.lock"
(filter.matchExt "nix")
"docs"
"docker"
"docker-compose.test.yml"
"docker-compose.yml"
"generate-test-data"
];
in
{
mobilizon = pkgs.callPackage ./. {
src = filter {
root = ./.;
exclude = [
"src"
(filter.matchExt "js")
(filter.matchExt "ts")
(filter.matchExt "json")
"tests"
"scripts"
"public"
] ++ unrelatedDirs;
};
src-config = ./config;
mobilizon-js = self.packages."${system}".mobilizon-frontend;
};
mobilizon-frontend =
let
nodejs = pkgs.nodejs-18_x;
in
napalm.legacyPackages."${system}".buildPackage
(filter {
root = ./.;
exclude = [
"lib"
"config"
"test"
"rel"
"support"
] ++ unrelatedDirs;
})
{
inherit nodejs;
nativeBuildInputs = [ pkgs.imagemagick ];
npmCommands = [ "npm install" "npm run build" ];
};
default = self.packages."${system}".mobilizon;
# Update local Mobilizon definition
update =
pkgs.writeShellScriptBin "update" ''
set -eou pipefail
${pkgs.mix2nix}/bin/mix2nix ./mix.lock > mix.nix
'';
});
devShells = forAllSystems (system:
let pkgs = nixpkgsFor.${system};
in {
default =
pkgs.mkShell {
buildInputs = with pkgs; [
elixir
mix2nix
cmake
imagemagick
nodejs-18_x
];
};
});
overlays.default = final: prev: {
inherit (self.packages."${prev.system}") mobilizon;
};
checks = forAllSystems (system: {
inherit (self.packages.${system}) mobilizon update;
nixosTest =
let
pkgsMobilizon = import nixpkgs {
inherit system;
overlays = [ self.overlays.default ];
};
certs = import "${nixpkgs}/nixos/tests/common/acme/server/snakeoil-certs.nix";
test = import ./nixos-test.nix { inherit certs; };
in
pkgsMobilizon.nixosTest test;
});
lib = {
# Patch the logos in the source tree of a mobilizon-frontend package before building.
# Can be used to construct the argument for .overrideAttrs on mobilizon-frontend.
mobilizonLogosOverride = icons:
let
inherit (icons) logo favicon;
in
old: {
postPatch = ''
cp '${logo}' src/assets/logo.svg
magick convert \
-resize x16 \
-gravity center \
-crop 16x16+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
public/img/icons/favicon-16x16.png
magick convert \
-resize x32 \
-gravity center \
-crop 32x32+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
public/img/icons/favicon-32x32.png
magick convert \
-resize x16 \
-gravity center \
-crop 16x16+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-16x16.ico
magick convert \
-resize x32 \
-gravity center \
-crop 32x32+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-32x32.ico
magick convert \
-resize x48 \
-gravity center \
-crop 48x48+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-48x48.ico
magick convert \
favicon-16x16.ico \
favicon-32x32.ico \
favicon-48x48.ico \
public/favicon.ico
rm favicon-16x16.ico favicon-32x32.ico favicon-48x48.ico
cp '${favicon}' public/img/icons/favicon.svg
cp '${favicon}' public/img/icons/safari-pinned-tab.svg
magick convert \
'${favicon}' \
-gravity center \
-extent 630x350 \
public/img/mobilizon_default_card.png
magick convert \
-background '#e08c96' \
'${logo}' \
-resize 366x108 \
public/img/mobilizon_logo.png
'' + nixpkgs.lib.concatMapStrings
({ resize, filename }: ''
magick convert \
-resize x${resize} \
'${favicon}' \
public/img/icons/${filename}
'')
[
{ resize = "180"; filename = "apple-touch-icon.png"; }
{ resize = "180"; filename = "apple-touch-icon-180x180.png"; }
{ resize = "152"; filename = "apple-touch-icon-152x152.png"; }
{ resize = "120"; filename = "apple-touch-icon-120x120.png"; }
{ resize = "76"; filename = "apple-touch-icon-76x76.png"; }
{ resize = "60"; filename = "apple-touch-icon-60x60.png"; }
{ resize = "192"; filename = "android-chrome-192x192.png"; }
{ resize = "512"; filename = "android-chrome-512x512.png"; }
{ resize = "192"; filename = "android-chrome-maskable-192x192.png"; }
{ resize = "512"; filename = "android-chrome-maskable-512x512.png"; }
{ resize = "128"; filename = "badge-128x128.png"; }
{ resize = "144"; filename = "icon-144x144.png"; }
{ resize = "168"; filename = "icon-168x168.png"; }
{ resize = "256"; filename = "icon-256x256.png"; }
{ resize = "48"; filename = "icon-48x48.png"; }
{ resize = "72"; filename = "icon-72x72.png"; }
{ resize = "96"; filename = "icon-96x96.png"; }
{ resize = "144"; filename = "msapplication-icon-144x144.png"; }
{ resize = "150"; filename = "mstile-150x150.png"; }
{ resize = "192"; filename = "android-chrome-192x192.png"; }
{ resize = "512"; filename = "android-chrome-512x512.png"; }
{ resize = "192"; filename = "android-chrome-maskable-192x192.png"; }
{ resize = "512"; filename = "android-chrome-maskable-512x512.png"; }
];
};
};
};
}

View file

@ -8,6 +8,7 @@ export default defineConfig({
plugins: [HstVue()], plugins: [HstVue()],
setupFile: path.resolve(__dirname, "./src/histoire.setup.ts"), setupFile: path.resolve(__dirname, "./src/histoire.setup.ts"),
viteNodeInlineDeps: [/date-fns/], viteNodeInlineDeps: [/date-fns/],
// viteIgnorePlugins: ['vite-plugin-pwa', 'vite-plugin-pwa:build', 'vite-plugin-pwa:info'],
tree: { tree: {
groups: [ groups: [
{ {

27
js/.gitignore vendored
View file

@ -1,27 +0,0 @@
.DS_Store
node_modules
/dist
/coverage
stats.html
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/test-results/
/playwright-report/
/playwright/.cache/

View file

@ -1,2 +0,0 @@
src/i18n/*.json
coverage/

View file

@ -1,94 +0,0 @@
<template>
<div class="items">
<button
class="item"
:class="{ 'is-selected': index === selectedIndex }"
v-for="(item, index) in items"
:key="index"
@click="selectItem(index)"
>
<actor-inline :actor="item" />
</button>
</div>
</template>
<script lang="ts" setup>
import { usernameWithDomain } from "@/types/actor/actor.model";
import { IPerson } from "@/types/actor";
import ActorInline from "../../components/Account/ActorInline.vue";
import { ref, watch } from "vue";
const props = defineProps<{
items: IPerson[];
command: ({ id }: { id: string }) => any;
}>();
// @Prop({ type: Function, required: true }) command!: any;
const selectedIndex = ref(0);
watch(props.items, () => {
selectedIndex.value = 0;
});
// const onKeyDown = ({ event }: { event: KeyboardEvent }): boolean => {
// if (event.key === "ArrowUp") {
// upHandler();
// return true;
// }
// if (event.key === "ArrowDown") {
// downHandler();
// return true;
// }
// if (event.key === "Enter") {
// enterHandler();
// return true;
// }
// return false;
// };
// const upHandler = (): void => {
// selectedIndex.value =
// (selectedIndex.value + props.items.length - 1) % props.items.length;
// };
// const downHandler = (): void => {
// selectedIndex.value = (selectedIndex.value + 1) % props.items.length;
// };
// const enterHandler = (): void => {
// selectItem(selectedIndex.value);
// };
const selectItem = (index: number): void => {
const item = props.items[index];
if (item) {
props.command({ id: usernameWithDomain(item) });
}
};
</script>
<style lang="scss" scoped>
.items {
position: relative;
border-radius: 0.25rem;
background: white;
color: rgba(black, 0.8);
overflow: hidden;
font-size: 0.9rem;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0px 10px 20px rgba(0, 0, 0, 0.1);
}
.item {
display: block;
width: 100%;
text-align: left;
background: transparent;
border: none;
padding: 0.5rem 0.75rem;
}
</style>

View file

@ -1,118 +0,0 @@
<template>
<div class="relative pt-10 px-2">
<div class="mb-2">
<div class="w-full flex flex-wrap gap-3 items-center">
<h2
class="text-xl font-bold tracking-tight text-gray-900 dark:text-gray-100 mt-0"
>
<slot name="title" />
</h2>
<o-button
:disabled="doingGeoloc"
v-if="suggestGeoloc"
class="inline-flex bg-primary rounded text-white flex-initial px-4 py-2 justify-center w-full md:w-min whitespace-nowrap"
@click="emit('doGeoLoc')"
>
{{ t("Geolocate me") }}
</o-button>
</div>
<slot name="subtitle" />
</div>
<div class="" v-show="showScrollLeftButton">
<button
@click="scrollLeft"
class="absolute inset-y-0 my-auto z-10 rounded-full bg-white dark:bg-transparent w-10 h-10 border border-shadowColor -left-5 ml-2"
>
<span class="">&lt;</span>
</button>
</div>
<div class="overflow-hidden">
<div
class="relative w-full snap-x snap-always snap-mandatory overflow-x-auto flex pb-6 gap-x-5 gap-y-8 p-1"
ref="scrollContainer"
@scroll="scrollHandler"
>
<slot name="content" />
</div>
</div>
<div class="" v-show="showScrollRightButton">
<button
@click="scrollRight"
class="absolute inset-y-0 my-auto z-10 rounded-full bg-white dark:bg-transparent w-10 h-10 border border-shadowColor -right-5 mr-2"
>
<span class="">&gt;</span>
</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue";
import { useI18n } from "vue-i18n";
withDefaults(
defineProps<{
suggestGeoloc?: boolean;
doingGeoloc?: boolean;
}>(),
{ suggestGeoloc: true, doingGeoloc: false }
);
const emit = defineEmits(["doGeoLoc"]);
const { t } = useI18n({ useScope: "global" });
const showScrollRightButton = ref(false);
const showScrollLeftButton = ref(false);
const scrollContainer = ref<any>();
const scrollHandler = () => {
if (scrollContainer.value) {
showScrollRightButton.value =
scrollContainer.value.scrollLeft <
scrollContainer.value.scrollWidth - scrollContainer.value.clientWidth;
showScrollLeftButton.value = scrollContainer.value.scrollLeft > 0;
}
};
const doScroll = (e: Event, left: number) => {
e.preventDefault();
if (scrollContainer.value) {
scrollContainer.value.scrollBy({
left,
behavior: "smooth",
});
}
};
const scrollLeft = (e: Event) => {
doScroll(e, -300);
};
const scrollRight = (e: Event) => {
doScroll(e, 300);
};
const scrollHorizontalToVertical = (evt: WheelEvent) => {
evt.deltaY > 0 ? doScroll(evt, 300) : doScroll(evt, -300);
};
onMounted(async () => {
// Make sure everything is mounted properly
setTimeout(() => {
scrollHandler();
}, 1500);
scrollContainer.value.addEventListener("wheel", scrollHorizontalToVertical);
});
onUnmounted(() => {
if (scrollContainer.value) {
scrollContainer.value.removeEventListener(
"wheel",
scrollHorizontalToVertical
);
}
});
</script>

View file

@ -1,42 +0,0 @@
<template>
<svg
class="bg-white dark:bg-zinc-900 dark:fill-white"
:class="{ 'bg-gray-900': invert }"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 248.16 46.78"
>
<title>Mobilizon Logo</title>
<g data-name="header">
<path
d="M0 45.82l3.18-40.8a29.88 29.88 0 015.07-.36 27.74 27.74 0 014.95.36l4.86 17.16a92.19 92.19 0 012.34 10.08h.36a92.19 92.19 0 012.34-10.08L28 5.02a29.23 29.23 0 015-.36 29.23 29.23 0 015 .36l3.18 40.8a13.61 13.61 0 01-3.63.42 23.41 23.41 0 01-3.63-.24l-1.2-19.92q-.36-5.52-.48-12.84h-.44l-7.32 26.51a25.62 25.62 0 01-4 .3 23.36 23.36 0 01-3.84-.3L9.36 13.24H9q-.3 8.94-.48 12.84L7.26 46a22.47 22.47 0 01-3.6.24A13.75 13.75 0 010 45.82zM74 31.06q0 8-4.26 12.3a12.21 12.21 0 01-9 3.42 12.21 12.21 0 01-9-3.42q-4.26-4.26-4.26-12.3t4.24-12.31a12.21 12.21 0 019-3.42 12.21 12.21 0 019 3.42Q74 23.02 74 31.06zM60.75 20.98q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM103.2 19.75q2.7 4.11 2.7 11.28T102 42.31a13.18 13.18 0 01-10 4.11 31.41 31.41 0 01-11.34-2V2.2l.4-.45h2.76A4 4 0 0187 2.83a5.38 5.38 0 01.93 3.57v11.94a12.08 12.08 0 017.56-2.7 8.71 8.71 0 017.71 4.11zm-9.72 2a7.28 7.28 0 00-5.58 2.82v16a15 15 0 004.08.54 5.25 5.25 0 004.68-2.67q1.68-2.67 1.68-7.59 0-9.03-4.86-9.1zM121 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014 1.62A6.27 6.27 0 01121 22z"
/>
<path
d="M119.82.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M139.08 40.42h2a10.23 10.23 0 01.6 3.18 9.24 9.24 0 01-.18 2.1 38.47 38.47 0 01-5.64.54q-6.48 0-6.48-7v-37l.36-.42h2.88a3.94 3.94 0 013.12 1.05 5.52 5.52 0 01.9 3.57v31.31q-.02 2.67 2.44 2.67zM155.94 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014.05 1.62 6.27 6.27 0 011.43 4.39z"
/>
<path
d="M154.8 2.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M163.08 39.22l8.76-11.82q1.32-1.8 4.8-5.7l-.18-.3a63.09 63.09 0 01-7.74.42H163a9.79 9.79 0 01-.24-2.34 15.8 15.8 0 01.42-3.3h20.4a16.31 16.31 0 011 4.26 4.1 4.1 0 01-.78 2.34L175 34.66a64.65 64.65 0 01-4.56 5.7l.18.24q3.12-.3 5.22-.3h2.58a15.35 15.35 0 006.12-.9 9.4 9.4 0 01.72 3.12q0 3.42-4.32 3.42h-18a14.27 14.27 0 01-.9-3.93 5.08 5.08 0 011.04-2.79zM215.88 31.06q0 8-4.26 12.3a13.63 13.63 0 01-18.06 0q-4.26-4.26-4.26-12.3t4.26-12.31a13.63 13.63 0 0118.06 0q4.26 4.27 4.26 12.31zm-13.29-10.08q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM247 25.84v13.32a11 11 0 001.2 5.64 7 7 0 01-4.41 1.56q-2.43 0-3.33-1.14a5.69 5.69 0 01-.9-3.54V27.4a7.74 7.74 0 00-.72-3.87 2.78 2.78 0 00-2.58-1.17 8.62 8.62 0 00-6.3 3v20.58a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3v-29.7l.42-.36h2.76q3.42 0 4.08 3.6 4.38-3.84 8.73-3.84t6.42 2.82a12.17 12.17 0 012.07 7.38z"
/>
<path
d="M57.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84zM198.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84z"
fill="currentColor"
/>
</g>
</svg>
</template>
<script lang="ts" setup>
withDefaults(
defineProps<{
invert?: boolean;
}>(),
{ invert: false }
);
</script>

View file

@ -1,144 +0,0 @@
<template>
<div class="flex items-center">
<figure class="image" v-if="imageSrc && !imagePreviewLoadingError">
<img :src="imageSrc" @error="showImageLoadingError" />
</figure>
<figure class="image is-128x128" v-else>
<div
class="image-placeholder"
:class="{ error: imagePreviewLoadingError }"
>
<span class="has-text-centered" v-if="imagePreviewLoadingError">{{
$t("Error while loading the preview")
}}</span>
<span class="has-text-centered" v-else>{{
textFallbackWithDefault
}}</span>
</div>
</figure>
<div class="flex flex-col">
<p v-if="modelValue" class="inline-flex">
<span class="block truncate max-w-[200px]" :title="modelValue.name">{{
modelValue.name
}}</span>
<span>({{ formatBytes(modelValue.size) }})</span>
</p>
<p v-if="pictureTooBig" class="text-mbz-danger">
{{
$t(
"The selected picture is too heavy. You need to select a file smaller than {size}.",
{ size: formatBytes(maxSize) }
)
}}
</p>
<o-field class="justify-center" variant="primary">
<o-upload @update:modelValue="onFileChanged" :accept="accept" drag-drop>
<span>
<Upload />
<span>{{ $t("Click to upload") }}</span>
</span>
</o-upload>
</o-field>
<o-button
variant="text"
v-if="imageSrc"
@click="removeOrClearPicture"
@keyup.enter="removeOrClearPicture"
>
{{ $t("Clear") }}
</o-button>
</div>
</div>
</template>
<style scoped lang="scss">
@use "@/styles/_mixins" as *;
figure.image {
// @include margin-right(30px);
max-height: 200px;
max-width: 200px;
overflow: hidden;
}
.image-placeholder {
background-color: grey;
width: 100%;
height: 100%;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
&.error {
border: 2px solid red;
}
span {
flex: 1;
color: #eee;
}
}
</style>
<script lang="ts" setup>
import { IMedia } from "@/types/media.model";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import Upload from "vue-material-design-icons/Upload.vue";
import { formatBytes } from "@/utils/datetime";
const { t } = useI18n({ useScope: "global" });
const props = withDefaults(
defineProps<{
modelValue: File | null;
defaultImage?: IMedia | null;
accept?: string;
textFallback?: string;
maxSize?: number;
}>(),
{
accept: "image/gif,image/png,image/jpeg,image/webp",
maxSize: 10_485_760,
}
);
const textFallbackWithDefault = props.textFallback ?? t("Avatar");
const emit = defineEmits(["update:modelValue"]);
const imagePreviewLoadingError = ref(false);
const pictureTooBig = computed((): boolean => {
return props.modelValue != null && props.modelValue?.size > props.maxSize;
});
const imageSrc = computed((): string | null | undefined => {
if (props.modelValue !== undefined) {
if (props.modelValue === null) return null;
try {
return URL.createObjectURL(props.modelValue);
} catch (e) {
console.error(e, props.modelValue);
}
}
return props.defaultImage?.url;
});
const onFileChanged = (file: File | null): void => {
emit("update:modelValue", file);
};
const removeOrClearPicture = async (): Promise<void> => {
onFileChanged(null);
};
watch(imageSrc, () => {
imagePreviewLoadingError.value = false;
});
const showImageLoadingError = (): void => {
imagePreviewLoadingError.value = true;
};
</script>

View file

@ -1,18 +0,0 @@
import { FILTER_TAGS } from "@/graphql/tags";
import { ITag } from "@/types/tag.model";
import { apolloClient } from "@/vue-apollo";
import { provideApolloClient, useQuery } from "@vue/apollo-composable";
export function fetchTags(text: string): Promise<ITag[]> {
return new Promise((resolve, reject) => {
const { onResult, onError } = provideApolloClient(apolloClient)(() =>
useQuery<{ tags: ITag[] }, { filter: string }>(FILTER_TAGS, {
filter: text,
})
);
onResult(({ data }) => resolve(data.tags));
onError((error) => reject(error));
});
}

View file

@ -1,9 +0,0 @@
/**
* New Line to <br>
*
* @param {string} str Input text
* @return {string} Filtered text
*/
export default function nl2br(str: string): string {
return `${str}`.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1<br>");
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
declare module "@absinthe/socket";
declare module "@absinthe/socket-apollo-link";
declare module "apollo-absinthe-upload-link";

View file

@ -1,12 +0,0 @@
export function nl2br(text: string): string {
return text.replace(/(?:\r\n|\r|\n)/g, "<br>");
}
export function htmlToText(html: string) {
const template = document.createElement("template");
const trimmedHTML = html.trim();
template.innerHTML = trimmedHTML;
const text = template.content.textContent;
template.remove();
return text;
}

View file

@ -1,65 +0,0 @@
import { AUTH_USER_ACTOR_ID } from "@/constants";
import { UPDATE_CURRENT_ACTOR_CLIENT, IDENTITIES } from "@/graphql/actor";
import { IPerson } from "@/types/actor";
import { ICurrentUser } from "@/types/current-user.model";
import { apolloClient } from "@/vue-apollo";
import {
provideApolloClient,
useLazyQuery,
useMutation,
} from "@vue/apollo-composable";
import { computed } from "vue";
export class NoIdentitiesException extends Error {}
function saveActorData(obj: IPerson): void {
localStorage.setItem(AUTH_USER_ACTOR_ID, `${obj.id}`);
}
export async function changeIdentity(identity: IPerson): Promise<void> {
if (!identity.id) return;
const { mutate: updateCurrentActorClient } = provideApolloClient(
apolloClient
)(() => useMutation(UPDATE_CURRENT_ACTOR_CLIENT));
updateCurrentActorClient(identity);
if (identity.id) {
saveActorData(identity);
}
}
const { onResult: setIdentities, load: loadIdentities } = provideApolloClient(
apolloClient
)(() => useLazyQuery<{ loggedUser: Pick<ICurrentUser, "actors"> }>(IDENTITIES));
/**
* We fetch from localStorage the latest actor ID used,
* then fetch the current identities to set in cache
* the current identity used
*/
export async function initializeCurrentActor(): Promise<void> {
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
loadIdentities();
setIdentities(async ({ data }) => {
const identities = computed(() => data?.loggedUser?.actors);
console.debug(
"initializing current actor based on identities",
identities.value
);
if (identities.value && identities.value.length < 1) {
console.warn("Logged user has no identities!");
throw new NoIdentitiesException();
}
const activeIdentity =
(identities.value || []).find(
(identity: IPerson | undefined) => identity?.id === actorId
) || ((identities.value || [])[0] as IPerson);
if (activeIdentity) {
await changeIdentity(activeIdentity);
}
});
}

View file

@ -1,259 +0,0 @@
<template>
<div v-if="instance">
<breadcrumbs-nav
:links="[
{ name: RouteName.ADMIN, text: $t('Admin') },
{ name: RouteName.INSTANCES, text: $t('Instances') },
{ text: instance.domain },
]"
/>
<h1 class="text-2xl">{{ instance.domain }}</h1>
<div
class="grid md:grid-cols-2 xl:grid-cols-4 gap-2 content-center text-center mt-2"
>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<router-link
:to="{
name: RouteName.PROFILES,
query: { domain: instance.domain },
}"
>
<span class="mb-4 text-xl font-semibold block">{{
instance.personCount
}}</span>
<span class="text-sm block">{{ $t("Profiles") }}</span>
</router-link>
</div>
<div class="bg-gray-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<router-link
:to="{
name: RouteName.ADMIN_GROUPS,
query: { domain: instance.domain },
}"
>
<span class="mb-4 text-xl font-semibold block">{{
instance.groupCount
}}</span>
<span class="text-sm block">{{ $t("Groups") }}</span>
</router-link>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 text-xl font-semibold block">{{
instance.followingsCount
}}</span>
<span class="text-sm block">{{ $t("Followings") }}</span>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 text-xl font-semibold block">{{
instance.followersCount
}}</span>
<span class="text-sm block">{{ $t("Followers") }}</span>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<router-link
:to="{ name: RouteName.REPORTS, query: { domain: instance.domain } }"
>
<span class="mb-4 text-xl font-semibold block">{{
instance.reportsCount
}}</span>
<span class="text-sm block">{{ $t("Reports") }}</span>
</router-link>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 font-semibold block">{{
formatBytes(instance.mediaSize)
}}</span>
<span class="text-sm block">{{ $t("Uploaded media size") }}</span>
</div>
</div>
<div class="mt-3 grid xl:grid-cols-2 gap-4">
<div
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md"
v-if="instance.hasRelay"
>
<button
@click="
removeInstanceFollow({
address: instance?.relayAddress,
})
"
v-if="instance.followedStatus == InstanceFollowStatus.APPROVED"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Stop following instance") }}
</button>
<button
@click="
removeInstanceFollow({
address: instance?.relayAddress,
})
"
v-else-if="instance.followedStatus == InstanceFollowStatus.PENDING"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Cancel follow request") }}
</button>
<button
@click="followInstance"
v-else
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Follow instance") }}
</button>
</div>
<div v-else class="md:h-48 py-16 text-center opacity-50">
{{ $t("Only Mobilizon instances can be followed") }}
</div>
<div
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2"
>
<button
@click="
acceptInstance({
address: instance?.relayAddress,
})
"
v-if="instance.followerStatus == InstanceFollowStatus.PENDING"
class="bg-green-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Accept follow") }}
</button>
<button
@click="
rejectInstance({
address: instance?.relayAddress,
})
"
v-if="instance.followerStatus != InstanceFollowStatus.NONE"
class="bg-red-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Reject follow") }}
</button>
<p v-if="instance.followerStatus == InstanceFollowStatus.NONE">
{{ $t("This instance doesn't follow yours.") }}
</p>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {
ACCEPT_RELAY,
ADD_INSTANCE,
INSTANCE,
REJECT_RELAY,
REMOVE_RELAY,
} from "@/graphql/admin";
import { formatBytes } from "@/utils/datetime";
import RouteName from "@/router/name";
import { IInstance } from "@/types/instance.model";
import { ApolloCache, gql, Reference } from "@apollo/client/core";
import { InstanceFollowStatus } from "@/types/enums";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { computed, inject } from "vue";
import { Notifier } from "@/plugins/notifier";
const props = defineProps<{ domain: string }>();
const { result: instanceResult } = useQuery<{ instance: IInstance }>(
INSTANCE,
() => ({ domain: props.domain })
);
const instance = computed(() => instanceResult.value?.instance);
const notifier = inject<Notifier>("notifier");
const { mutate: acceptInstance, onError: onAcceptInstanceError } = useMutation(
ACCEPT_RELAY,
() => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance as unknown as Reference),
fragment: gql`
fragment InstanceFollowerStatus on Instance {
followerStatus
}
`,
data: {
followerStatus: InstanceFollowStatus.APPROVED,
},
});
},
})
);
onAcceptInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
notifier?.error(error.graphQLErrors[0].message);
}
});
/**
* Reject instance follow
*/
const { mutate: rejectInstance, onError: onRejectInstanceError } = useMutation(
REJECT_RELAY,
() => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance as unknown as Reference),
fragment: gql`
fragment InstanceFollowerStatus on Instance {
followerStatus
}
`,
data: {
followerStatus: InstanceFollowStatus.NONE,
},
});
},
})
);
onRejectInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
notifier?.error(error.graphQLErrors[0].message);
}
});
const { mutate: followInstanceMutation, onError: onFollowInstanceError } =
useMutation<{ addInstance: IInstance }>(ADD_INSTANCE);
onFollowInstanceError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
notifier?.error(error.graphQLErrors[0].message);
}
});
const followInstance = async (e: Event): Promise<void> => {
e.preventDefault();
followInstanceMutation({ domain: props.domain });
};
/**
* Stop following instance
*/
const { mutate: removeInstanceFollow, onError: onRemoveInstanceFollowError } =
useMutation(REMOVE_RELAY, () => ({
update(cache: ApolloCache<any>) {
cache.writeFragment({
id: cache.identify(instance.value as unknown as Reference),
fragment: gql`
fragment InstanceFollowedStatus on Instance {
followedStatus
}
`,
data: {
followedStatus: InstanceFollowStatus.NONE,
},
});
},
}));
onRemoveInstanceFollowError((error) => {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
notifier?.error(error.graphQLErrors[0].message);
}
});
</script>

View file

@ -1,16 +0,0 @@
import "./specs/mocks/matchMedia";
import { config } from "@vue/test-utils";
import { createHead } from "@vueuse/head";
import { createI18n } from "vue-i18n";
import en_US from "@/i18n/en_US.json";
const i18n = createI18n({
legacy: false,
messages: { en_US },
locale: "en_US",
});
const head = createHead();
config.global.plugins.push(head);
config.global.plugins.push(i18n);

View file

@ -1,67 +0,0 @@
// Vitest Snapshot v1
exports[`CommentTree > renders a comment tree with comments 1`] = `
"<div data-v-5d0380ab=\\"\\">
<form class=\\"\\" data-v-5d0380ab=\\"\\">
<!--v-if-->
<article class=\\"flex flex-wrap items-start gap-2\\" data-v-5d0380ab=\\"\\">
<figure class=\\"\\" data-v-5d0380ab=\\"\\">
<identity-picker-wrapper-stub modelvalue=\\"[object Object]\\" inline=\\"false\\" masked=\\"false\\" data-v-5d0380ab=\\"\\"></identity-picker-wrapper-stub>
</figure>
<div class=\\"flex-1\\" data-v-5d0380ab=\\"\\">
<div class=\\"flex flex-col gap-2\\" data-v-5d0380ab=\\"\\">
<div class=\\"editor-wrapper\\" data-v-5d0380ab=\\"\\">
<editor-stub currentactor=\\"[object Object]\\" mode=\\"comment\\" modelvalue=\\"\\" aria-label=\\"Comment body\\" data-v-5d0380ab=\\"\\"></editor-stub>
<!--v-if-->
</div>
<!--v-if-->
</div>
</div>
<div class=\\"\\" data-v-5d0380ab=\\"\\">
<o-button-stub variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\" data-v-5d0380ab=\\"\\"></o-button-stub>
</div>
</article>
</form>
<transition-group-stub data-v-5d0380ab=\\"\\">
<transition-group-stub data-v-5d0380ab=\\"\\">
<comment-stub comment=\\"[object Object]\\" event=\\"[object Object]\\" currentactor=\\"[object Object]\\" rootcomment=\\"true\\" class=\\"root-comment\\" data-v-5d0380ab=\\"\\"></comment-stub>
<comment-stub comment=\\"[object Object]\\" event=\\"[object Object]\\" currentactor=\\"[object Object]\\" rootcomment=\\"true\\" class=\\"root-comment\\" data-v-5d0380ab=\\"\\"></comment-stub>
</transition-group-stub>
</transition-group-stub>
</div>"
`;
exports[`CommentTree > renders a loading comment tree 1`] = `
"<div data-v-5d0380ab=\\"\\">
<!--v-if-->
<p class=\\"text-center\\" data-v-5d0380ab=\\"\\">Loading comments…</p>
</div>"
`;
exports[`CommentTree > renders an empty comment tree 1`] = `
"<div data-v-5d0380ab=\\"\\">
<form class=\\"\\" data-v-5d0380ab=\\"\\">
<!--v-if-->
<article class=\\"flex flex-wrap items-start gap-2\\" data-v-5d0380ab=\\"\\">
<figure class=\\"\\" data-v-5d0380ab=\\"\\">
<identity-picker-wrapper-stub modelvalue=\\"[object Object]\\" inline=\\"false\\" masked=\\"false\\" data-v-5d0380ab=\\"\\"></identity-picker-wrapper-stub>
</figure>
<div class=\\"flex-1\\" data-v-5d0380ab=\\"\\">
<div class=\\"flex flex-col gap-2\\" data-v-5d0380ab=\\"\\">
<div class=\\"editor-wrapper\\" data-v-5d0380ab=\\"\\">
<editor-stub currentactor=\\"[object Object]\\" mode=\\"comment\\" modelvalue=\\"\\" aria-label=\\"Comment body\\" data-v-5d0380ab=\\"\\"></editor-stub>
<!--v-if-->
</div>
<!--v-if-->
</div>
</div>
<div class=\\"\\" data-v-5d0380ab=\\"\\">
<o-button-stub variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\" data-v-5d0380ab=\\"\\"></o-button-stub>
</div>
</article>
</form>
<transition-group-stub data-v-5d0380ab=\\"\\">
<empty-content-stub icon=\\"comment\\" descriptionclasses=\\"\\" inline=\\"true\\" center=\\"false\\" data-v-5d0380ab=\\"\\"></empty-content-stub>
</transition-group-stub>
</div>"
`;

View file

@ -1,37 +0,0 @@
// Vitest Snapshot v1
exports[`PostListItem > renders post list item with basic informations 1`] = `
"<a href=\\"/p/my-blog-post-some-uuid\\" class=\\"block md:flex bg-white dark:bg-violet-2 dark:text-white dark:hover:text-white rounded-lg shadow-md\\" dir=\\"auto\\" data-v-6ca7cc69=\\"\\">
<!--v-if-->
<div class=\\"flex flex-col gap-1 bg-inherit p-2 rounded-lg flex-1\\" data-v-6ca7cc69=\\"\\">
<h3 class=\\"text-xl color-violet-3 line-clamp-3 mb-2 font-bold\\" lang=\\"en\\" data-v-6ca7cc69=\\"\\">My Blog Post</h3>
<p class=\\"flex gap-2\\" data-v-6ca7cc69=\\"\\"><span aria-hidden=\\"true\\" class=\\"material-design-icon clock-icon\\" role=\\"img\\" data-v-6ca7cc69=\\"\\"><svg fill=\\"currentColor\\" class=\\"material-design-icon__svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\"><path d=\\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.9L16.2,16.2Z\\"><!--v-if--></path></svg></span><span dir=\\"auto\\" class=\\"\\" data-v-6ca7cc69=\\"\\">Dec 2, 2020</span></p>
<!--v-if-->
<!--v-if-->
</div>
</a>"
`;
exports[`PostListItem > renders post list item with publisher name 1`] = `
"<a href=\\"/p/my-blog-post-some-uuid\\" class=\\"block md:flex bg-white dark:bg-violet-2 dark:text-white dark:hover:text-white rounded-lg shadow-md\\" dir=\\"auto\\" data-v-6ca7cc69=\\"\\">
<!--v-if-->
<div class=\\"flex flex-col gap-1 bg-inherit p-2 rounded-lg flex-1\\" data-v-6ca7cc69=\\"\\">
<h3 class=\\"text-xl color-violet-3 line-clamp-3 mb-2 font-bold\\" lang=\\"en\\" data-v-6ca7cc69=\\"\\">My Blog Post</h3>
<p class=\\"flex gap-2\\" data-v-6ca7cc69=\\"\\"><span aria-hidden=\\"true\\" class=\\"material-design-icon clock-icon\\" role=\\"img\\" data-v-6ca7cc69=\\"\\"><svg fill=\\"currentColor\\" class=\\"material-design-icon__svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\"><path d=\\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.9L16.2,16.2Z\\"><!--v-if--></path></svg></span><span dir=\\"auto\\" class=\\"\\" data-v-6ca7cc69=\\"\\">Dec 2, 2020</span></p>
<!--v-if-->
<p class=\\"flex gap-1\\" data-v-6ca7cc69=\\"\\"><span aria-hidden=\\"true\\" class=\\"material-design-icon account-edit-icon\\" role=\\"img\\" data-v-6ca7cc69=\\"\\"><svg fill=\\"currentColor\\" class=\\"material-design-icon__svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\"><path d=\\"M21.7,13.35L20.7,14.35L18.65,12.3L19.65,11.3C19.86,11.09 20.21,11.09 20.42,11.3L21.7,12.58C21.91,12.79 21.91,13.14 21.7,13.35M12,18.94L18.06,12.88L20.11,14.93L14.06,21H12V18.94M12,14C7.58,14 4,15.79 4,18V20H10V18.11L14,14.11C13.34,14.03 12.67,14 12,14M12,4A4,4 0 0,0 8,8A4,4 0 0,0 12,12A4,4 0 0,0 16,8A4,4 0 0,0 12,4Z\\"><!--v-if--></path></svg></span>Published by <b class=\\"\\" data-v-6ca7cc69=\\"\\">An author</b></p>
</div>
</a>"
`;
exports[`PostListItem > renders post list item with tags 1`] = `
"<a href=\\"/p/my-blog-post-some-uuid\\" class=\\"block md:flex bg-white dark:bg-violet-2 dark:text-white dark:hover:text-white rounded-lg shadow-md\\" dir=\\"auto\\" data-v-6ca7cc69=\\"\\">
<!--v-if-->
<div class=\\"flex flex-col gap-1 bg-inherit p-2 rounded-lg flex-1\\" data-v-6ca7cc69=\\"\\">
<h3 class=\\"text-xl color-violet-3 line-clamp-3 mb-2 font-bold\\" lang=\\"en\\" data-v-6ca7cc69=\\"\\">My Blog Post</h3>
<p class=\\"flex gap-2\\" data-v-6ca7cc69=\\"\\"><span aria-hidden=\\"true\\" class=\\"material-design-icon clock-icon\\" role=\\"img\\" data-v-6ca7cc69=\\"\\"><svg fill=\\"currentColor\\" class=\\"material-design-icon__svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\"><path d=\\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.9L16.2,16.2Z\\"><!--v-if--></path></svg></span><span dir=\\"auto\\" class=\\"\\" data-v-6ca7cc69=\\"\\">Dec 2, 2020</span></p>
<div class=\\"flex flex-wrap gap-y-0 gap-x-2\\" data-v-6ca7cc69=\\"\\"><span aria-hidden=\\"true\\" class=\\"material-design-icon tag-icon\\" role=\\"img\\" data-v-6ca7cc69=\\"\\"><svg fill=\\"currentColor\\" class=\\"material-design-icon__svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\"><path d=\\"M5.5,7A1.5,1.5 0 0,1 4,5.5A1.5,1.5 0 0,1 5.5,4A1.5,1.5 0 0,1 7,5.5A1.5,1.5 0 0,1 5.5,7M21.41,11.58L12.41,2.58C12.05,2.22 11.55,2 11,2H4C2.89,2 2,2.89 2,4V11C2,11.55 2.22,12.05 2.59,12.41L11.58,21.41C11.95,21.77 12.45,22 13,22C13.55,22 14.05,21.77 14.41,21.41L21.41,14.41C21.78,14.05 22,13.55 22,13C22,12.44 21.77,11.94 21.41,11.58Z\\"><!--v-if--></path></svg></span><span class=\\"rounded-md truncate text-sm text-violet-title px-2 py-1 bg-purple-3 dark:text-violet-3\\" data-v-6955ca87=\\"\\" data-v-6ca7cc69=\\"\\">A tag</span></div>
<!--v-if-->
</div>
</a>"
`;

View file

@ -1,29 +0,0 @@
// Vitest Snapshot v1
exports[`ReportModal > renders report modal with basic informations and submits it 1`] = `
"<div class=\\"p-2\\" data-v-e0cceef3=\\"\\">
<!--v-if-->
<section data-v-e0cceef3=\\"\\">
<div class=\\"flex gap-1 flex-row mb-3\\" data-v-e0cceef3=\\"\\"><span class=\\"o-icon o-icon--warning hidden md:block flex-1\\" data-v-e0cceef3=\\"\\"><i class=\\"mdi mdi-alert 48\\"></i></span>
<p data-v-e0cceef3=\\"\\">The report will be sent to the moderators of your instance. You can explain why you report this content below.</p>
</div>
<div class=\\"\\" data-v-e0cceef3=\\"\\">
<!--v-if-->
<div class=\\"o-field o-field--filled\\" data-v-e0cceef3=\\"\\"><label for=\\"additional-comments\\" class=\\"o-field__label\\">Additional comments</label>
<div class=\\"o-ctrl-input\\" data-v-e0cceef3=\\"\\"><textarea id=\\"additional-comments\\" class=\\"o-input o-input__textarea\\"></textarea>
<!--v-if-->
<!--v-if-->
<!--v-if-->
</div>
<!--v-if-->
</div>
<!--v-if-->
</div>
</section>
<footer class=\\"flex gap-2 py-3\\" data-v-e0cceef3=\\"\\"><button type=\\"button\\" class=\\"o-btn o-btn--outlined\\" data-v-e0cceef3=\\"\\"><span class=\\"o-btn__wrapper\\"><!--v-if--><span class=\\"o-btn__label\\">Cancel</span>
<!--v-if--></span>
</button><button type=\\"button\\" class=\\"o-btn o-btn--primary\\" data-v-e0cceef3=\\"\\"><span class=\\"o-btn__wrapper\\"><!--v-if--><span class=\\"o-btn__label\\">Send the report</span>
<!--v-if--></span>
</button></footer>
</div>"
`;

View file

@ -1,196 +0,0 @@
// Vitest Snapshot v1
exports[`App component > renders a Vue component 1`] = `
"<nav class=\\"bg-white border-gray-200 px-2 sm:px-4 py-2.5 dark:bg-zinc-900\\" id=\\"navbar\\">
<div class=\\"container mx-auto flex flex-wrap items-center mx-auto gap-4\\">
<router-link to=\\"[object Object]\\" class=\\"flex items-center\\">
<mobilizon-logo-stub invert=\\"false\\" class=\\"w-40\\"></mobilizon-logo-stub>
</router-link>
<!--v-if--><button type=\\"button\\" class=\\"inline-flex items-center p-2 ml-1 text-sm text-zinc-500 rounded-lg md:hidden hover:bg-zinc-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-zinc-400 dark:hover:bg-zinc-700 dark:focus:ring-gray-600\\" aria-controls=\\"mobile-menu-2\\" aria-expanded=\\"false\\"><span class=\\"sr-only\\">Open main menu</span><svg class=\\"w-6 h-6\\" aria-hidden=\\"true\\" fill=\\"currentColor\\" viewBox=\\"0 0 20 20\\" xmlns=\\"http://www.w3.org/2000/svg\\">
<path fill-rule=\\"evenodd\\" d=\\"M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z\\" clip-rule=\\"evenodd\\"></path>
</svg></button>
<div class=\\"justify-between items-center w-full md:flex md:w-auto md:order-1 hidden\\" id=\\"mobile-menu-2\\">
<ul class=\\"flex flex-col md:flex-row md:space-x-8 mt-2 md:mt-0 md:font-lightbold\\">
<!--v-if-->
<!--v-if-->
<li>
<router-link to=\\"[object Object]\\" class=\\"block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700\\">Login</router-link>
</li>
<li>
<router-link to=\\"[object Object]\\" class=\\"block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700\\">Register</router-link>
</li>
</ul>
</div>
</div>
</nav>
<!-- <o-navbar
id=\\"navbar\\"
type=\\"is-secondary\\"
wrapper-class=\\"container mx-auto\\"
v-model:active=\\"mobileNavbarActive\\"
>
<template #brand>
<o-navbar-item
tag=\\"router-link\\"
:to=\\"{ name: RouteName.HOME }\\"
:aria-label=\\"$t('Home')\\"
>
<logo />
</o-navbar-item>
</template>
<template #start>
<o-navbar-item tag=\\"router-link\\" :to=\\"{ name: RouteName.SEARCH }\\">{{
$t(\\"Explore\\")
}}</o-navbar-item>
<o-navbar-item
v-if=\\"currentActor.id && currentUser?.isLoggedIn\\"
tag=\\"router-link\\"
:to=\\"{ name: RouteName.MY_EVENTS }\\"
>{{ $t(\\"My events\\") }}</o-navbar-item
>
<o-navbar-item
tag=\\"router-link\\"
:to=\\"{ name: RouteName.MY_GROUPS }\\"
v-if=\\"
config &&
config.features.groups &&
currentActor.id &&
currentUser?.isLoggedIn
\\"
>{{ $t(\\"My groups\\") }}</o-navbar-item
>
<o-navbar-item
tag=\\"span\\"
v-if=\\"
config &&
config.features.eventCreation &&
currentActor.id &&
currentUser?.isLoggedIn
\\"
>
<o-button
v-if=\\"!hideCreateEventsButton\\"
tag=\\"router-link\\"
:to=\\"{ name: RouteName.CREATE_EVENT }\\"
variant=\\"primary\\"
>{{ $t(\\"Create\\") }}</o-button
>
</o-navbar-item>
</template>
<template #end>
<o-navbar-item tag=\\"div\\">
<search-field @navbar-search=\\"mobileNavbarActive = false\\" />
</o-navbar-item>
<o-navbar-dropdown
v-if=\\"currentActor.id && currentUser?.isLoggedIn\\"
right
collapsible
ref=\\"user-dropdown\\"
tabindex=\\"0\\"
tag=\\"span\\"
@keyup.enter=\\"toggleMenu\\"
>
<template #label v-if=\\"currentActor\\">
<div class=\\"identity-wrapper\\">
<div>
<figure class=\\"image is-32x32\\" v-if=\\"currentActor.avatar\\">
<img
class=\\"is-rounded\\"
alt=\\"avatarUrl\\"
:src=\\"currentActor.avatar.url\\"
/>
</figure>
<o-icon v-else icon=\\"account-circle\\" />
</div>
<div class=\\"media-content is-hidden-desktop\\">
<span>{{ displayName(currentActor) }}</span>
<span class=\\"has-text-grey-dark\\" v-if=\\"currentActor.name\\"
>@{{ currentActor.preferredUsername }}</span
>
</div>
</div>
</template>
No identities dropdown if no identities
<span v-if=\\"identities.length <= 1\\"></span>
<o-navbar-item
tag=\\"span\\"
v-for=\\"identity in identities\\"
v-else
:active=\\"identity.id === currentActor.id\\"
:key=\\"identity.id\\"
tabindex=\\"0\\"
@click=\\"setIdentity({
preferredUsername: identity.preferredUsername,
})\\"
@keyup.enter=\\"setIdentity({
preferredUsername: identity.preferredUsername,
})\\"
>
<span>
<div class=\\"media-left\\">
<figure class=\\"image is-32x32\\" v-if=\\"identity.avatar\\">
<img
class=\\"is-rounded\\"
loading=\\"lazy\\"
:src=\\"identity.avatar.url\\"
alt
/>
</figure>
<o-icon v-else size=\\"is-medium\\" icon=\\"account-circle\\" />
</div>
<div class=\\"media-content\\">
<span>{{ displayName(identity) }}</span>
<span class=\\"has-text-grey-dark\\" v-if=\\"identity.name\\"
>@{{ identity.preferredUsername }}</span
>
</div>
</span>
<hr class=\\"navbar-divider\\" role=\\"presentation\\" />
</o-navbar-item>
<o-navbar-item
tag=\\"router-link\\"
:to=\\"{ name: RouteName.UPDATE_IDENTITY }\\"
>{{ $t(\\"My account\\") }}</o-navbar-item
>
<o-navbar-item
v-if=\\"currentUser.role === ICurrentUserRole.ADMINISTRATOR\\"
tag=\\"router-link\\"
:to=\\"{ name: RouteName.ADMIN_DASHBOARD }\\"
>{{ $t(\\"Administration\\") }}</o-navbar-item
>
<o-navbar-item
tag=\\"span\\"
tabindex=\\"0\\"
@click=\\"logout\\"
@keyup.enter=\\"logout\\"
>
<span>{{ $t(\\"Log out\\") }}</span>
</o-navbar-item>
</o-navbar-dropdown>
<o-navbar-item v-else tag=\\"div\\">
<div class=\\"buttons\\">
<router-link
class=\\"button is-primary\\"
v-if=\\"config && config.registrationsOpen\\"
:to=\\"{ name: RouteName.REGISTER }\\"
>
<strong>{{ $t(\\"Sign up\\") }}</strong>
</router-link>
<router-link
class=\\"button is-light\\"
:to=\\"{ name: RouteName.LOGIN }\\"
>{{ $t(\\"Log in\\") }}</router-link
>
</div>
</o-navbar-item>
</template>
</o-navbar> -->"
`;

View file

@ -1,40 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env", "jest", "vite/client"],
"typeRoots": ["./@types", "./node_modules/@types"],
"paths": {
"@/*": ["src/*"]
},
"lib": [
"esnext",
"dom",
"es2017.intl",
"dom.iterable",
"scripthost",
"webworker"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx",
"env.d.ts"
],
"exclude": ["node_modules"]
}

View file

@ -1,80 +0,0 @@
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import path from "path";
import { VitePWA } from "vite-plugin-pwa";
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig(({ command }) => {
const isDev = command !== "build";
if (isDev) {
// Terminate the watcher when Phoenix quits
process.stdin.on("close", () => {
process.exit(0);
});
process.stdin.resume();
}
return {
plugins: [
vue(),
VitePWA({
// registerType: "autoUpdate",
strategies: "injectManifest",
srcDir: "src",
filename: "service-worker.ts",
// injectRegister: "auto",
// devOptions: {
// enabled: true,
// },
}),
visualizer(),
],
build: {
manifest: true,
outDir: path.resolve(__dirname, "../priv/static"),
emptyOutDir: true,
sourcemap: true,
rollupOptions: {
// overwrite default .html entry
input: {
main: "src/main.ts",
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
unfetch: path.resolve(
__dirname,
"node_modules",
"unfetch",
"dist",
"unfetch.mjs"
),
},
},
css: {
preprocessorOptions: {
scss: {
sassOptions: {
quietDeps: true,
},
},
},
},
test: {
environment: "jsdom",
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
coverage: {
reporter: ["text", "json", "html"],
},
setupFiles: path.resolve(__dirname, "./tests/unit/setup.ts"),
include: [path.resolve(__dirname, "./tests/unit/specs/**/*.spec.ts")],
},
};
});

File diff suppressed because it is too large Load diff

View file

@ -82,6 +82,11 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Accept do
) )
Scheduler.trigger_notifications_for_participant(participant) Scheduler.trigger_notifications_for_participant(participant)
Mobilizon.Service.Activity.Participant.insert_activity(participant,
subject: "event_new_participation"
)
participant_as_data = Convertible.model_to_as(participant) participant_as_data = Convertible.model_to_as(participant)
audience = Audience.get_audience(participant) audience = Audience.get_audience(participant)

View file

@ -14,7 +14,15 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Create do
] ]
@type create_entities :: @type create_entities ::
:event | :comment | :discussion | :actor | :todo_list | :todo | :resource | :post :event
| :comment
| :discussion
| :conversation
| :actor
| :todo_list
| :todo
| :resource
| :post
@doc """ @doc """
Create an activity of type `Create` Create an activity of type `Create`
@ -50,18 +58,27 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Create do
end end
end end
@map_types %{
:event => Types.Events,
:comment => Types.Comments,
:discussion => Types.Discussions,
:conversation => Types.Conversations,
:actor => Types.Actors,
:todo_list => Types.TodoLists,
:todo => Types.Todos,
:resource => Types.Resources,
:post => Types.Posts
}
@spec do_create(create_entities(), map(), map()) :: @spec do_create(create_entities(), map(), map()) ::
{:ok, Entity.t(), Activity.t()} | {:error, Ecto.Changeset.t() | atom()} {:ok, Entity.t(), Activity.t()} | {:error, Ecto.Changeset.t() | atom()}
defp do_create(type, args, additional) do defp do_create(type, args, additional) do
case type do mod = Map.get(@map_types, type)
:event -> Types.Events.create(args, additional)
:comment -> Types.Comments.create(args, additional) if is_nil(mod) do
:discussion -> Types.Discussions.create(args, additional) {:error, :type_not_supported}
:actor -> Types.Actors.create(args, additional) else
:todo_list -> Types.TodoLists.create(args, additional) mod.create(args, additional)
:todo -> Types.Todos.create(args, additional)
:resource -> Types.Resources.create(args, additional)
:post -> Types.Posts.create(args, additional)
end end
end end

View file

@ -25,7 +25,7 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Invite do
) do ) do
Logger.debug("Handling #{actor_url} invite to #{group_url} sent to #{target_actor_url}") Logger.debug("Handling #{actor_url} invite to #{group_url} sent to #{target_actor_url}")
if is_able_to_invite?(actor, group) do if able_to_invite?(actor, group) do
with {:ok, %Member{url: member_url} = member} <- with {:ok, %Member{url: member_url} = member} <-
Actors.create_member(%{ Actors.create_member(%{
parent_id: group_id, parent_id: group_id,
@ -64,8 +64,8 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Invite do
end end
end end
@spec is_able_to_invite?(Actor.t(), Actor.t()) :: boolean @spec able_to_invite?(Actor.t(), Actor.t()) :: boolean
defp is_able_to_invite?(%Actor{domain: actor_domain, id: actor_id}, %Actor{ defp able_to_invite?(%Actor{domain: actor_domain, id: actor_id}, %Actor{
domain: group_domain, domain: group_domain,
id: group_id id: group_id
}) do }) do
@ -76,7 +76,7 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Invite do
# If local group, we'll send the invite # If local group, we'll send the invite
case Actors.get_member(actor_id, group_id) do case Actors.get_member(actor_id, group_id) do
{:ok, %Member{} = admin_member} -> {:ok, %Member{} = admin_member} ->
Member.is_administrator(admin_member) Member.administrator?(admin_member)
_ -> _ ->
false false

View file

@ -34,7 +34,7 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Leave do
local, local,
additional additional
) do ) do
if Participant.is_not_only_organizer(event_id, actor_id) do if Participant.not_only_organizer?(event_id, actor_id) do
{:error, :is_only_organizer} {:error, :is_only_organizer}
else else
case Mobilizon.Events.get_participant( case Mobilizon.Events.get_participant(
@ -83,7 +83,7 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Leave do
case Actors.get_member(actor_id, group_id) do case Actors.get_member(actor_id, group_id) do
{:ok, %Member{id: member_id} = member} -> {:ok, %Member{id: member_id} = member} ->
if Map.get(additional, :force_member_removal, false) || group_domain != actor_domain || if Map.get(additional, :force_member_removal, false) || group_domain != actor_domain ||
!Actors.is_only_administrator?(member_id, group_id) do !Actors.only_administrator?(member_id, group_id) do
with {:ok, %Member{} = member} <- Actors.delete_member(member) do with {:ok, %Member{} = member} <- Actors.delete_member(member) do
Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_quit") Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_quit")

View file

@ -70,7 +70,7 @@ defmodule Mobilizon.Federation.ActivityPub do
handle_existing_entity(url, entity, options) handle_existing_entity(url, entity, options)
{:error, e} -> {:error, e} ->
Logger.warn("Something failed while fetching url #{url} #{inspect(e)}") Logger.warning("Something failed while fetching url #{url} #{inspect(e)}")
{:error, e} {:error, e}
end end
else else
@ -135,7 +135,7 @@ defmodule Mobilizon.Federation.ActivityPub do
%Page{total: total_events, elements: events} = %Page{total: total_events, elements: events} =
if actor_id == relay_actor_id do if actor_id == relay_actor_id do
Events.list_public_local_events(page, limit) Events.list_public_local_events(page, limit, :publish_at, :desc)
else else
Events.list_public_events_for_actor(actor, page, limit) Events.list_public_events_for_actor(actor, page, limit)
end end

View file

@ -5,6 +5,7 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
alias Mobilizon.{Actors, Discussions, Events, Share} alias Mobilizon.{Actors, Discussions, Events, Share}
alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Conversations.Conversation
alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.{Event, Participant} alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
@ -38,6 +39,10 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
%{"to" => maybe_add_group_members([], actor), "cc" => []} %{"to" => maybe_add_group_members([], actor), "cc" => []}
end end
def get_audience(%Conversation{participants: participants}) do
%{"to" => Enum.map(participants, & &1.url), "cc" => []}
end
# Deleted comments are just like tombstones # Deleted comments are just like tombstones
def get_audience(%Comment{deleted_at: deleted_at}) when not is_nil(deleted_at) do def get_audience(%Comment{deleted_at: deleted_at}) when not is_nil(deleted_at) do
%{"to" => [@ap_public], "cc" => []} %{"to" => [@ap_public], "cc" => []}

View file

@ -42,30 +42,14 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
end end
@spec fetch_and_create(String.t(), Keyword.t()) :: @spec fetch_and_create(String.t(), Keyword.t()) ::
{:ok, map(), struct()} | {:error, atom()} | :error {:ok, map(), struct()} | {:error, atom()} | {:error, Ecto.Changeset.t()} | :error
def fetch_and_create(url, options \\ []) do def fetch_and_create(url, options \\ []) do
case fetch(url, options) do case fetch(url, options) do
{:ok, data} when is_map(data) -> {:ok, data} when is_map(data) ->
if origin_check?(url, data) do if origin_check?(url, data) do
case Transmogrifier.handle_incoming(%{ pass_to_transmogrifier(data)
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["actor"] || data["attributedTo"],
"attributedTo" => data["attributedTo"] || data["actor"],
"object" => data
}) do
{:ok, entity, structure} ->
{:ok, entity, structure}
{:error, error} when is_atom(error) ->
{:error, error}
:error ->
{:error, :transmogrifier_error}
end
else else
Logger.warn("Object origin check failed") Logger.warning("Object origin check failed")
{:error, :object_origin_check_failed} {:error, :object_origin_check_failed}
end end
@ -89,7 +73,7 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
"object" => data "object" => data
}) })
else else
Logger.warn("Object origin check failed") Logger.warning("Object origin check failed")
{:error, :object_origin_check_failed} {:error, :object_origin_check_failed}
end end
@ -98,6 +82,34 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
end end
end end
@spec pass_to_transmogrifier(map()) ::
{:ok, map(), struct()}
| {:error, atom()}
| {:error, Ecto.Changeset.t()}
| {:error, :transmogrifier_error}
defp pass_to_transmogrifier(data) do
case Transmogrifier.handle_incoming(%{
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["actor"] || data["attributedTo"],
"attributedTo" => data["attributedTo"] || data["actor"],
"object" => data
}) do
{:ok, entity, structure} ->
{:ok, entity, structure}
{:error, error} when is_atom(error) ->
{:error, error}
{:error, %Ecto.Changeset{} = err} ->
{:error, err}
:error ->
{:error, :transmogrifier_error}
end
end
@type fetch_actor_errors :: @type fetch_actor_errors ::
:json_decode_error | :actor_deleted | :http_error | :actor_not_allowed_type :json_decode_error | :actor_deleted | :http_error | :actor_not_allowed_type
@ -177,7 +189,7 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
{:error, :content_not_json} {:error, :content_not_json}
{:ok, %Tesla.Env{} = res} -> {:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code #{inspect(res)}") Logger.debug("Resource returned bad HTTP code (#{res.status}) #{inspect(res)}")
{:error, :http_error} {:error, :http_error}
{:error, err} -> {:error, err} ->

View file

@ -44,13 +44,13 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
) do ) do
case object |> Ownable.permissions() |> get_in([:create]) do case object |> Ownable.permissions() |> get_in([:create]) do
:member -> :member ->
Actors.is_member?(actor_id, group_id) Actors.member?(actor_id, group_id)
:moderator -> :moderator ->
Actors.is_moderator?(actor_id, group_id) Actors.moderator?(actor_id, group_id)
:administrator -> :administrator ->
Actors.is_administrator?(actor_id, group_id) Actors.administrator?(actor_id, group_id)
_ -> _ ->
false false
@ -89,13 +89,13 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
_ -> _ ->
case permission do case permission do
:access -> :access ->
Logger.warn("Actor #{actor_url} can't access #{object.url}") Logger.warning("Actor #{actor_url} can't access #{object.url}")
:update -> :update ->
Logger.warn("Actor #{actor_url} can't update #{object.url}") Logger.warning("Actor #{actor_url} can't update #{object.url}")
:delete -> :delete ->
Logger.warn("Actor #{actor_url} can't delete #{object.url}") Logger.warning("Actor #{actor_url} can't delete #{object.url}")
end end
false false
@ -122,21 +122,21 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
"Checking if activity actor #{actor_url} is a moderator from group from #{object.url}" "Checking if activity actor #{actor_url} is a moderator from group from #{object.url}"
) )
Actors.is_moderator?(actor_id, group_id) Actors.moderator?(actor_id, group_id)
:administrator -> :administrator ->
Logger.debug( Logger.debug(
"Checking if activity actor #{actor_url} is an administrator from group from #{object.url}" "Checking if activity actor #{actor_url} is an administrator from group from #{object.url}"
) )
Actors.is_administrator?(actor_id, group_id) Actors.administrator?(actor_id, group_id)
_ -> _ ->
Logger.debug( Logger.debug(
"Checking if activity actor #{actor_url} is a member from group from #{object.url}" "Checking if activity actor #{actor_url} is a member from group from #{object.url}"
) )
Actors.is_member?(actor_id, group_id) Actors.member?(actor_id, group_id)
end end
_ -> _ ->

View file

@ -21,10 +21,10 @@ defmodule Mobilizon.Federation.ActivityPub.Publisher do
Logger.debug("Publishing an activity") Logger.debug("Publishing an activity")
Logger.debug(inspect(activity, pretty: true)) Logger.debug(inspect(activity, pretty: true))
public = Visibility.is_public?(activity) public = Visibility.public?(activity)
Logger.debug("is public ? #{public}") Logger.debug("is public ? #{public}")
if public && is_create_activity?(activity) && Config.get([:instance, :allow_relay]) do if public && create_activity?(activity) && Config.get([:instance, :allow_relay]) do
Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
Relay.publish(activity) Relay.publish(activity)
@ -125,9 +125,9 @@ defmodule Mobilizon.Federation.ActivityPub.Publisher do
end) end)
end end
@spec is_create_activity?(Activity.t()) :: boolean @spec create_activity?(Activity.t()) :: boolean
defp is_create_activity?(%Activity{data: %{"type" => "Create"}}), do: true defp create_activity?(%Activity{data: %{"type" => "Create"}}), do: true
defp is_create_activity?(_), do: false defp create_activity?(_), do: false
@spec convert_members_in_recipients(list(String.t())) :: {list(String.t()), list(Actor.t())} @spec convert_members_in_recipients(list(String.t())) :: {list(String.t()), list(Actor.t())}
defp convert_members_in_recipients(recipients) do defp convert_members_in_recipients(recipients) do

View file

@ -13,10 +13,9 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier} alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier}
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.WebFinger alias Mobilizon.Federation.{NodeInfo, WebFinger}
alias Mobilizon.GraphQL.API.Follows alias Mobilizon.GraphQL.API.Follows
alias Mobilizon.Service.Workers.Background alias Mobilizon.Service.Workers.Background
import Mobilizon.Federation.ActivityPub.Utils, only: [create_full_domain_string: 1]
require Logger require Logger
@ -26,7 +25,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
relay = get_actor() relay = get_actor()
unless Regex.match?(~r/BEGIN RSA PRIVATE KEY/, relay.keys) do unless Regex.match?(~r/BEGIN RSA PRIVATE KEY/, relay.keys) do
{:ok, relay} = Actors.actor_key_rotation(relay) {:ok, _relay} = Actors.actor_key_rotation(relay)
end end
relay relay
@ -49,18 +48,25 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
%Actor{} = local_actor = get_actor() %Actor{} = local_actor = get_actor()
with {:ok, target_instance} <- fetch_actor(address), with {:ok, target_instance} <- fetch_actor(address),
{:ok, %Actor{} = target_actor} <- {:ok, %Actor{id: target_actor_id} = target_actor} <-
ActivityPubActor.get_or_fetch_actor_by_url(target_instance), ActivityPubActor.get_or_fetch_actor_by_url(target_instance),
{:ok, activity, follow} <- Follows.follow(local_actor, target_actor) do {:ok, activity, follow} <- Follows.follow(local_actor, target_actor) do
Logger.info("Relay: followed instance #{target_instance}; id=#{activity.data["id"]}") Logger.info("Relay: followed instance #{target_instance}; id=#{activity.data["id"]}")
Background.enqueue("refresh_profile", %{
"actor_id" => target_actor_id
})
Logger.info("Relay: schedule refreshing instance #{target_instance} after follow")
{:ok, activity, follow} {:ok, activity, follow}
else else
{:error, :person_no_follow} -> {:error, :person_no_follow} ->
Logger.warn("Only group and instances can be followed") Logger.warning("Only group and instances can be followed")
{:error, :person_no_follow} {:error, :person_no_follow}
{:error, e} -> {:error, e} ->
Logger.warn("Error while following remote instance: #{inspect(e)}") Logger.warning("Error while following remote instance: #{inspect(e)}")
{:error, e} {:error, e}
end end
end end
@ -78,7 +84,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
{:ok, activity, follow} {:ok, activity, follow}
else else
{:error, e} -> {:error, e} ->
Logger.warn("Error while unfollowing remote instance: #{inspect(e)}") Logger.warning("Error while unfollowing remote instance: #{inspect(e)}")
{:error, e} {:error, e}
end end
end end
@ -96,7 +102,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
{:ok, activity, follow} {:ok, activity, follow}
else else
{:error, e} -> {:error, e} ->
Logger.warn("Error while accepting remote instance follow: #{inspect(e)}") Logger.warning("Error while accepting remote instance follow: #{inspect(e)}")
{:error, e} {:error, e}
end end
end end
@ -114,7 +120,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
{:ok, activity, follow} {:ok, activity, follow}
else else
{:error, e} -> {:error, e} ->
Logger.warn("Error while rejecting remote instance follow: #{inspect(e)}") Logger.warning("Error while rejecting remote instance follow: #{inspect(e)}")
{:error, e} {:error, e}
end end
end end
@ -137,7 +143,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
}) })
else else
{:error, e} -> {:error, e} ->
Logger.warn("Error while refreshing remote instance: #{inspect(e)}") Logger.warning("Error while refreshing remote instance: #{inspect(e)}")
{:error, e} {:error, e}
end end
end end
@ -178,17 +184,17 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
defp fetch_actor("http://" <> address), do: fetch_actor(address) defp fetch_actor("http://" <> address), do: fetch_actor(address)
defp fetch_actor(address) do defp fetch_actor(address) do
%URI{host: host} = uri = URI.parse("http://" <> address) %URI{host: host} = URI.parse("http://" <> address)
cond do cond do
String.contains?(address, "@") -> String.contains?(address, "@") ->
check_actor(address) check_actor(address)
!is_nil(host) -> !is_nil(host) ->
uri case NodeInfo.application_actor(host) do
|> create_full_domain_string() nil -> check_actor("relay@#{host}")
|> then(&Kernel.<>("relay@", &1)) actor_url when is_binary(actor_url) -> {:ok, actor_url}
|> check_actor() end
true -> true ->
{:error, :bad_url} {:error, :bad_url}

View file

@ -68,24 +68,24 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object}) do def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object}) do
Logger.info("Handle incoming to create notes") Logger.info("Handle incoming to create notes")
case Converter.Comment.as_to_model_data(object) do case Discussions.get_comment_from_url_with_preload(object["id"]) do
%{visibility: visibility, event_id: event_id} {:error, :comment_not_found} ->
when visibility != :public and event_id != nil -> case Converter.Comment.as_to_model_data(object) do
Logger.info("Tried to reply to an event with a private comment - ignore") %{visibility: visibility, attributed_to_id: attributed_to_id} = object_data
:error when visibility === :private and is_nil(attributed_to_id) ->
Actions.Create.create(:conversation, object_data, false)
object_data when is_map(object_data) -> object_data when is_map(object_data) ->
case Discussions.get_comment_from_url_with_preload(object_data.url) do handle_comment_or_discussion(object_data)
{:error, :comment_not_found} ->
object_data
|> transform_object_data_for_discussion()
|> save_comment_or_discussion()
{:ok, %Comment{} = comment} -> {:error, err} ->
# Object already exists {:error, err}
{:ok, nil, comment}
end end
{:ok, %Comment{} = comment} ->
# Object already exists
{:ok, nil, comment}
{:error, err} -> {:error, err} ->
{:error, err} {:error, err}
end end
@ -203,7 +203,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
Actions.Delete.delete(entity, Relay.get_actor(), false) Actions.Delete.delete(entity, Relay.get_actor(), false)
{:error, err} -> {:error, err} ->
Logger.warn("Error while fetching object from URL: #{inspect(err)}") Logger.warning("Error while fetching object from URL: #{inspect(err)}")
:error :error
end end
end end
@ -219,11 +219,11 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, object} {:ok, activity, object}
else else
{:error, :person_no_follow} -> {:error, :person_no_follow} ->
Logger.warn("Only group and instances can be followed") Logger.warning("Only group and instances can be followed")
:error :error
{:error, e} -> {:error, e} ->
Logger.warn("Unable to handle Follow activity #{inspect(e)}") Logger.warning("Unable to handle Follow activity #{inspect(e)}")
:error :error
end end
end end
@ -285,7 +285,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
object_data when is_map(object_data) <- object_data when is_map(object_data) <-
object |> Converter.Resource.as_to_model_data(), object |> Converter.Resource.as_to_model_data(),
{:member, true} <- {:member, true} <-
{:member, Actors.is_member?(object_data.creator_id, object_data.actor_id)}, {:member, Actors.member?(object_data.creator_id, object_data.actor_id)},
{:ok, %Activity{} = activity, %Resource{} = resource} <- {:ok, %Activity{} = activity, %Resource{} = resource} <-
Actions.Create.create(:resource, object_data, false) do Actions.Create.create(:resource, object_data, false) do
{:ok, activity, resource} {:ok, activity, resource}
@ -325,14 +325,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
:error :error
{:object_not_found, nil} -> {:object_not_found, nil} ->
Logger.warn( Logger.warning(
"Unable to process Accept activity #{inspect(id)}. Object #{inspect(accepted_object)} wasn't found." "Unable to process Accept activity #{inspect(id)}. Object #{inspect(accepted_object)} wasn't found."
) )
:error :error
e -> e ->
Logger.warn( Logger.warning(
"Unable to process Accept activity #{inspect(id)} for object #{inspect(accepted_object)} only returned #{inspect(e)}" "Unable to process Accept activity #{inspect(id)} for object #{inspect(accepted_object)} only returned #{inspect(e)}"
) )
@ -353,14 +353,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, object} {:ok, activity, object}
else else
{:object_not_found, nil} -> {:object_not_found, nil} ->
Logger.warn( Logger.warning(
"Unable to process Reject activity #{inspect(id)}. Object #{inspect(rejected_object)} wasn't found." "Unable to process Reject activity #{inspect(id)}. Object #{inspect(rejected_object)} wasn't found."
) )
:error :error
e -> e ->
Logger.warn( Logger.warning(
"Unable to process Reject activity #{inspect(id)} for object #{inspect(rejected_object)} only returned #{inspect(e)}" "Unable to process Reject activity #{inspect(id)} for object #{inspect(rejected_object)} only returned #{inspect(e)}"
) )
@ -405,7 +405,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, new_actor} {:ok, activity, new_actor}
else else
{:error, :update_not_allowed} -> {:error, :update_not_allowed} ->
Logger.warn( Logger.warning(
"Activity tried to update an actor that's local or not a group: #{inspect(params)}" "Activity tried to update an actor that's local or not a group: #{inspect(params)}"
) )
@ -485,7 +485,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, new_post} {:ok, activity, new_post}
else else
{:origin_check, _} -> {:origin_check, _} ->
Logger.warn("Actor tried to update a post but doesn't has the required role") Logger.warning("Actor tried to update a post but doesn't has the required role")
:error :error
_e -> _e ->
@ -692,7 +692,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, object} {:ok, activity, object}
else else
{:only_organizer, true} -> {:only_organizer, true} ->
Logger.warn( Logger.warning(
"Actor #{inspect(actor)} tried to leave event #{inspect(object)} but it was the only organizer so we didn't detach it" "Actor #{inspect(actor)} tried to leave event #{inspect(object)} but it was the only organizer so we didn't detach it"
) )
@ -742,14 +742,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
Actions.Remove.remove(member, group, moderator, false) Actions.Remove.remove(member, group, moderator, false)
else else
{:is_admin, {:ok, %Member{}}} -> {:is_admin, {:ok, %Member{}}} ->
Logger.warn( Logger.warning(
"Person #{inspect(actor)} is not an admin from #{inspect(origin)} and can't remove member #{inspect(object)}" "Person #{inspect(actor)} is not an admin from #{inspect(origin)} and can't remove member #{inspect(object)}"
) )
{:error, "Member already removed"} {:error, "Member already removed"}
{:is_member, {:ok, %Member{role: :rejected}}} -> {:is_member, {:ok, %Member{role: :rejected}}} ->
Logger.warn("Member #{inspect(object)} already removed from #{inspect(origin)}") Logger.warning("Member #{inspect(object)} already removed from #{inspect(origin)}")
{:error, "Member already removed"} {:error, "Member already removed"}
end end
end end
@ -949,7 +949,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, activity, participant} {:ok, activity, participant}
else else
{:join_event, {:ok, _activity, %Participant{role: :rejected}}} -> {:join_event, {:ok, _activity, %Participant{role: :rejected}}} ->
Logger.warn( Logger.warning(
"Tried to handle an Reject activity on a Join activity with a event object but the participant is already rejected" "Tried to handle an Reject activity on a Join activity with a event object but the participant is already rejected"
) )
@ -979,19 +979,19 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
# If the object has been announced by a group let's use one of our members to fetch it # If the object has been announced by a group let's use one of our members to fetch it
@spec fetch_object_optionnally_authenticated(String.t(), Actor.t() | any()) :: @spec fetch_object_optionally_authenticated(String.t(), Actor.t() | any()) ::
{:ok, struct()} | {:error, any()} {:ok, struct()} | {:error, any()}
defp fetch_object_optionnally_authenticated(url, %Actor{type: :Group, id: group_id}) do defp fetch_object_optionally_authenticated(url, %Actor{type: :Group, id: group_id}) do
case Actors.get_single_group_member_actor(group_id) do case Actors.get_single_group_member_actor(group_id) do
%Actor{} = actor -> %Actor{} = actor ->
ActivityPub.fetch_object_from_url(url, on_behalf_of: actor, force: true) ActivityPub.fetch_object_from_url(url, on_behalf_of: actor, force: true)
_err -> _err ->
fetch_object_optionnally_authenticated(url, nil) fetch_object_optionally_authenticated(url, nil)
end end
end end
defp fetch_object_optionnally_authenticated(url, _), defp fetch_object_optionally_authenticated(url, _),
do: ActivityPub.fetch_object_from_url(url, force: true) do: ActivityPub.fetch_object_from_url(url, force: true)
defp eventually_create_share(object, entity, actor_id) do defp eventually_create_share(object, entity, actor_id) do
@ -1005,23 +1005,36 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
# Comment initiates a whole discussion only if it has full title # Comment initiates a whole discussion only if it has full title
@spec is_data_for_comment_or_discussion?(map()) :: boolean() @spec data_for_comment_or_discussion?(map()) :: boolean()
defp is_data_for_comment_or_discussion?(object_data) do defp data_for_comment_or_discussion?(object_data) do
is_data_a_discussion_initialization?(object_data) and data_a_discussion_initialization?(object_data) and
is_nil(object_data.discussion_id) is_nil(object_data.discussion_id)
end end
# Comment initiates a whole discussion only if it has full title # Comment initiates a whole discussion only if it has full title
defp is_data_a_discussion_initialization?(object_data) do defp data_a_discussion_initialization?(object_data) do
not Map.has_key?(object_data, :title) or not Map.has_key?(object_data, :title) or
is_nil(object_data.title) or object_data.title == "" is_nil(object_data.title) or object_data.title == ""
end end
defp handle_comment_or_discussion(object_data) do
case Discussions.get_comment_from_url_with_preload(object_data.url) do
{:error, :comment_not_found} ->
object_data
|> transform_object_data_for_discussion()
|> save_comment_or_discussion()
{:ok, %Comment{} = comment} ->
# Object already exists
{:ok, nil, comment}
end
end
# Comment and conversations have different attributes for actor and groups # Comment and conversations have different attributes for actor and groups
@spec transform_object_data_for_discussion(map()) :: map() @spec transform_object_data_for_discussion(map()) :: map()
defp transform_object_data_for_discussion(object_data) do defp transform_object_data_for_discussion(object_data) do
# Basic comment # Basic comment
if is_data_a_discussion_initialization?(object_data) do if data_a_discussion_initialization?(object_data) do
object_data object_data
else else
# Conversation # Conversation
@ -1100,7 +1113,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, object} {:ok, object}
err -> err ->
Logger.warn("Error while fetching #{inspect(object)}") Logger.warning("Error while fetching #{inspect(object)}")
{:error, err} {:error, err}
end end
end end
@ -1121,12 +1134,12 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
if Utils.are_same_origin?(url, Endpoint.url()) do if Utils.are_same_origin?(url, Endpoint.url()) do
ActivityPub.fetch_object_from_url(url, force: false) ActivityPub.fetch_object_from_url(url, force: false)
else else
fetch_object_optionnally_authenticated(url, actor) fetch_object_optionally_authenticated(url, actor)
end end
end end
defp is_group_object_gone(object_id) do defp group_object_gone_check(object_id) do
Logger.debug("is_group_object_gone #{object_id}") Logger.debug("Checking if group object #{object_id} is gone")
case ActivityPub.fetch_object_from_url(object_id, force: true) do case ActivityPub.fetch_object_from_url(object_id, force: true) do
# comments are just emptied # comments are just emptied
@ -1150,14 +1163,10 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
end end
# Before 1.0.4 the object of a "Remove" activity was an actor's URL
# instead of the member's URL.
# TODO: Remove in 1.2
@spec get_remove_object(map() | String.t()) :: {:ok, integer()} @spec get_remove_object(map() | String.t()) :: {:ok, integer()}
defp get_remove_object(object) do defp get_remove_object(object) do
case object |> Utils.get_url() |> ActivityPub.fetch_object_from_url() do case object |> Utils.get_url() |> ActivityPub.fetch_object_from_url() do
{:ok, %Member{actor: %Actor{id: person_id}}} -> {:ok, person_id} {:ok, %Member{actor: %Actor{id: person_id}}} -> {:ok, person_id}
{:ok, %Actor{id: person_id}} -> {:ok, person_id}
_ -> {:error, :remove_object_not_found} _ -> {:error, :remove_object_not_found}
end end
end end
@ -1183,7 +1192,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
@spec create_comment_or_discussion(map()) :: @spec create_comment_or_discussion(map()) ::
{:ok, Activity.t(), struct()} | {:error, atom() | Ecto.Changeset.t()} {:ok, Activity.t(), struct()} | {:error, atom() | Ecto.Changeset.t()}
defp create_comment_or_discussion(object_data) do defp create_comment_or_discussion(object_data) do
if is_data_for_comment_or_discussion?(object_data) do if data_for_comment_or_discussion?(object_data) do
Logger.debug("Chosing to create a regular comment") Logger.debug("Chosing to create a regular comment")
Actions.Create.create(:comment, object_data, false) Actions.Create.create(:comment, object_data, false)
else else
@ -1235,14 +1244,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
defp handle_group_being_gone(actor, actor_url, object_id) do defp handle_group_being_gone(actor, actor_url, object_id) do
case is_group_object_gone(object_id) do case group_object_gone_check(object_id) do
# The group object is no longer there, we can remove the element # The group object is no longer there, we can remove the element
{:ok, entity} -> {:ok, entity} ->
if Utils.origin_check_from_id?(actor_url, object_id) || if Utils.origin_check_from_id?(actor_url, object_id) ||
Permission.can_delete_group_object?(actor, entity) do Permission.can_delete_group_object?(actor, entity) do
Actions.Delete.delete(entity, actor, false) Actions.Delete.delete(entity, actor, false)
else else
Logger.warn("Object origin check failed") Logger.warning("Object origin check failed")
:error :error
end end

View file

@ -301,7 +301,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
) do ) do
%Actor{id: relay_id} = Relay.get_actor() %Actor{id: relay_id} = Relay.get_actor()
unless follower.target_actor.manually_approves_followers or unless follower.target_actor.manually_approves_followers == true or
follower.target_actor.id == relay_id do follower.target_actor.id == relay_id do
require Logger require Logger
Logger.debug("Target doesn't manually approves followers, we can accept right away") Logger.debug("Target doesn't manually approves followers, we can accept right away")

View file

@ -0,0 +1,219 @@
defmodule Mobilizon.Federation.ActivityPub.Types.Conversations do
@moduledoc false
# alias Mobilizon.Conversations.ConversationParticipant
alias Mobilizon.{Actors, Conversations, Discussions}
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Conversation
alias Mobilizon.Discussions.Comment
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream
alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API.Utils, as: APIUtils
alias Mobilizon.Service.Activity.Conversation, as: ConversationActivity
alias Mobilizon.Web.Endpoint
import Mobilizon.Federation.ActivityPub.Utils, only: [make_create_data: 2, make_update_data: 2]
require Logger
@behaviour Entity
@impl Entity
@spec create(map(), map()) ::
{:ok, Conversation.t(), ActivityStream.t()}
| {:error,
:conversation_not_found
| :last_comment_not_found
| :empty_participants
| Ecto.Changeset.t()}
def create(%{conversation_id: conversation_id} = args, additional)
when not is_nil(conversation_id) do
Logger.debug("Creating a reply to a conversation #{inspect(args, pretty: true)}")
args = prepare_args(args)
Logger.debug("Creating a reply to a conversation #{inspect(args, pretty: true)}")
with args when is_map(args) <- prepare_args(args) do
case Conversations.get_conversation(conversation_id) do
%Conversation{} = conversation ->
case Conversations.reply_to_conversation(conversation, args) do
{:ok, %Conversation{last_comment_id: last_comment_id} = conversation} ->
ConversationActivity.insert_activity(conversation, subject: "conversation_replied")
maybe_publish_graphql_subscription(conversation)
case Discussions.get_comment_with_preload(last_comment_id) do
%Comment{} = last_comment ->
comment_as_data = Convertible.model_to_as(last_comment)
audience = Audience.get_audience(conversation)
create_data = make_create_data(comment_as_data, Map.merge(audience, additional))
{:ok, conversation, create_data}
nil ->
{:error, :last_comment_not_found}
end
{:error, _, %Ecto.Changeset{} = err, _} ->
{:error, err}
end
nil ->
{:error, :discussion_not_found}
end
end
end
@impl Entity
def create(args, additional) do
with args when is_map(args) <- prepare_args(args) do
case Conversations.create_conversation(args) do
{:ok, %Conversation{} = conversation} ->
ConversationActivity.insert_activity(conversation, subject: "conversation_created")
conversation_as_data = Convertible.model_to_as(conversation)
audience = Audience.get_audience(conversation)
create_data = make_create_data(conversation_as_data, Map.merge(audience, additional))
{:ok, conversation, create_data}
{:error, _, %Ecto.Changeset{} = err, _} ->
{:error, err}
end
end
end
@impl Entity
@spec update(Conversation.t(), map(), map()) ::
{:ok, Conversation.t(), ActivityStream.t()} | {:error, Ecto.Changeset.t()}
def update(%Conversation{} = old_conversation, args, additional) do
case Conversations.update_conversation(old_conversation, args) do
{:ok, %Conversation{} = new_conversation} ->
# ConversationActivity.insert_activity(new_conversation,
# subject: "conversation_renamed",
# old_conversation: old_conversation
# )
conversation_as_data = Convertible.model_to_as(new_conversation)
audience = Audience.get_audience(new_conversation)
update_data = make_update_data(conversation_as_data, Map.merge(audience, additional))
{:ok, new_conversation, update_data}
{:error, %Ecto.Changeset{} = err} ->
{:error, err}
end
end
@impl Entity
@spec delete(Conversation.t(), Actor.t(), boolean, map()) ::
{:error, Ecto.Changeset.t()} | {:ok, ActivityStream.t(), Actor.t(), Conversation.t()}
def delete(
%Conversation{} = _conversation,
%Actor{} = _actor,
_local,
_additionnal
) do
{:error, :not_applicable}
end
# @spec actor(Conversation.t()) :: Actor.t() | nil
# def actor(%ConversationParticipant{actor_id: actor_id}), do: Actors.get_actor(actor_id)
# @spec group_actor(Conversation.t()) :: Actor.t() | nil
# def group_actor(%Conversation{actor_id: actor_id}), do: Actors.get_actor(actor_id)
@spec permissions(Conversation.t()) :: Permission.t()
def permissions(%Conversation{}) do
%Permission{access: :member, create: :member, update: :moderator, delete: :moderator}
end
@spec maybe_publish_graphql_subscription(Conversation.t()) :: :ok
defp maybe_publish_graphql_subscription(%Conversation{} = conversation) do
Absinthe.Subscription.publish(Endpoint, conversation,
conversation_comment_changed: conversation.id
)
:ok
end
@spec prepare_args(map) :: map | {:error, :empty_participants}
defp prepare_args(args) do
{text, mentions, _tags} =
APIUtils.make_content_html(
args |> Map.get(:text, "") |> String.trim(),
# Can't put additional tags on a comment
[],
"text/html"
)
mentions =
(args |> Map.get(:mentions, []) |> prepare_mentions()) ++
ConverterUtils.fetch_mentions(mentions)
# Can't create a conversation with just ourselves
mentions =
Enum.filter(mentions, fn %{actor_id: actor_id} ->
to_string(actor_id) != to_string(args.actor_id)
end)
if Enum.empty?(mentions) do
{:error, :empty_participants}
else
event = Map.get(args, :event, get_event(Map.get(args, :event_id)))
participants =
(mentions ++
[
%{actor_id: args.actor_id},
%{
actor_id:
if(is_nil(event),
do: nil,
else: event.attributed_to_id || event.organizer_actor_id
)
}
])
|> Enum.reduce(
[],
fn %{actor_id: actor_id}, acc ->
case Actors.get_actor(actor_id) do
nil -> acc
actor -> acc ++ [actor]
end
end
)
|> Enum.uniq_by(& &1.id)
args
|> Map.put(:text, text)
|> Map.put(:mentions, mentions)
|> Map.put(:participants, participants)
end
end
@spec prepare_mentions(list(String.t())) :: list(%{actor_id: String.t()})
defp prepare_mentions(mentions) do
Enum.reduce(mentions, [], &prepare_mention/2)
end
@spec prepare_mention(String.t() | map(), list()) :: list(%{actor_id: String.t()})
defp prepare_mention(%{actor_id: _} = mention, mentions) do
mentions ++ [mention]
end
defp prepare_mention(mention, mentions) do
case ActivityPubActor.find_or_make_actor_from_nickname(mention) do
{:ok, %Actor{id: actor_id}} ->
mentions ++ [%{actor_id: actor_id}]
{:error, _} ->
mentions
end
end
defp get_event(nil), do: nil
defp get_event(event_id) do
case Mobilizon.Events.get_event(event_id) do
{:ok, event} -> event
_ -> nil
end
end
end

View file

@ -224,6 +224,10 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
cond do cond do
Mobilizon.Events.get_default_participant_role(event) == :participant && Mobilizon.Events.get_default_participant_role(event) == :participant &&
role == :participant -> role == :participant ->
Mobilizon.Service.Activity.Participant.insert_activity(participant,
subject: "event_new_participation"
)
{:accept, {:accept,
Actions.Accept.accept( Actions.Accept.accept(
:join, :join,

View file

@ -93,7 +93,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Members do
atom() atom()
) :: boolean ) :: boolean
defp check_admins_left?(member_id, group_id, current_role, updated_role) do defp check_admins_left?(member_id, group_id, current_role, updated_role) do
Actors.is_only_administrator?(member_id, group_id) && current_role == :administrator && Actors.only_administrator?(member_id, group_id) && current_role == :administrator &&
updated_role != :administrator updated_role != :administrator
end end
end end

View file

@ -2,7 +2,6 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Reports do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Discussions, Events, Reports} alias Mobilizon.{Actors, Discussions, Events, Reports}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityStream alias Mobilizon.Federation.ActivityStream
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Reports.Report alias Mobilizon.Reports.Report
@ -26,15 +25,19 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Reports do
%Actor{} = reported_actor = Actors.get_actor!(args.reported_id) %Actor{} = reported_actor = Actors.get_actor!(args.reported_id)
content = HTML.strip_tags(args.content) content = HTML.strip_tags(args.content)
event_id = Map.get(args, :event_id) events =
args
event = |> Map.get(:events_ids, [])
if is_nil(event_id) do |> Enum.map(fn event_id ->
nil case Events.get_event(event_id) do
else {:ok, event} -> event
{:ok, %Event{} = event} = Events.get_event(event_id) {:error, :event_not_found} -> nil
event end
end end)
|> Enum.filter(fn event ->
is_struct(event) and
Enum.member?([event.organizer_actor_id, event.attributed_to_id], reported_actor.id)
end)
comments = comments =
Discussions.list_comments_by_actor_and_ids( Discussions.list_comments_by_actor_and_ids(
@ -46,7 +49,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Reports do
reporter: reporter_actor, reporter: reporter_actor,
reported: reported_actor, reported: reported_actor,
content: content, content: content,
event: event, events: events,
comments: comments comments: comments
}) })
end end

View file

@ -8,6 +8,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Resources.Resource alias Mobilizon.Resources.Resource
alias Mobilizon.Service.Activity.Resource, as: ResourceActivity alias Mobilizon.Service.Activity.Resource, as: ResourceActivity
alias Mobilizon.Service.Formatter.HTML
alias Mobilizon.Service.RichMedia.Parser alias Mobilizon.Service.RichMedia.Parser
require Logger require Logger
@ -20,21 +21,8 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
@spec create(map(), map()) :: @spec create(map(), map()) ::
{:ok, Resource.t(), ActivityStream.t()} {:ok, Resource.t(), ActivityStream.t()}
| {:error, Ecto.Changeset.t() | :creator_not_found | :group_not_found} | {:error, Ecto.Changeset.t() | :creator_not_found | :group_not_found}
def create(%{type: type} = args, additional) do def create(args, additional) do
args = args = prepare_args(args)
case type do
:folder ->
args
_ ->
case Parser.parse(Map.get(args, :resource_url)) do
{:ok, metadata} ->
Map.put(args, :metadata, metadata)
_ ->
args
end
end
with {:ok, with {:ok,
%Resource{actor_id: group_id, creator_id: creator_id, parent_id: parent_id} = resource} <- %Resource{actor_id: group_id, creator_id: creator_id, parent_id: parent_id} = resource} <-
@ -76,7 +64,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
additional additional
) )
when old_parent_id != parent_id do when old_parent_id != parent_id do
move(old_resource, args, additional) move(old_resource, prepare_args(args), additional)
end end
# Simple rename # Simple rename
@ -218,4 +206,23 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
defp parents(old_parent_id, new_parent_id) do defp parents(old_parent_id, new_parent_id) do
{:ok, Resources.get_resource(old_parent_id), Resources.get_resource(new_parent_id)} {:ok, Resources.get_resource(old_parent_id), Resources.get_resource(new_parent_id)}
end end
defp prepare_args(args) do
args =
case Map.get(args, :type, :link) do
:folder ->
args
_ ->
case Parser.parse(Map.get(args, :resource_url)) do
{:ok, metadata} ->
Map.put(args, :metadata, metadata)
_ ->
args
end
end
Map.update(args, :description, nil, &HTML.strip_tags/1)
end
end end

View file

@ -22,6 +22,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
@actor_types ["Group", "Person", "Application"] @actor_types ["Group", "Person", "Application"]
@all_actor_types @actor_types ++ ["Organization", "Service"] @all_actor_types @actor_types ++ ["Organization", "Service"]
@ap_public_audience "https://www.w3.org/ns/activitystreams#Public"
# Wraps an object into an activity # Wraps an object into an activity
@spec create_activity(map(), boolean()) :: {:ok, Activity.t()} @spec create_activity(map(), boolean()) :: {:ok, Activity.t()}
@ -125,6 +126,10 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
"@type" => "sc:Boolean", "@type" => "sc:Boolean",
"@id" => "mz:isOnline" "@id" => "mz:isOnline"
}, },
"externalParticipationUrl" => %{
"@id" => "mz:externalParticipationUrl",
"@type" => "sc:URL"
},
"PropertyValue" => "sc:PropertyValue", "PropertyValue" => "sc:PropertyValue",
"value" => "sc:value", "value" => "sc:value",
"propertyID" => "sc:propertyID", "propertyID" => "sc:propertyID",
@ -160,6 +165,10 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
"todos" => %{ "todos" => %{
"@id" => "mz:todos", "@id" => "mz:todos",
"@type" => "@id" "@type" => "@id"
},
"status" => %{
"@id" => "ical:status",
"@type" => "ical:status"
} }
} }
] ]
@ -491,8 +500,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
if public do if public do
Logger.debug("Making announce data for a public object") Logger.debug("Making announce data for a public object")
{[actor.followers_url, object_actor_url], {[actor.followers_url, object_actor_url], [@ap_public_audience]}
["https://www.w3.org/ns/activitystreams#Public"]}
else else
Logger.debug("Making announce data for a private object") Logger.debug("Making announce data for a private object")
@ -539,7 +547,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
"actor" => url, "actor" => url,
"object" => activity, "object" => activity,
"to" => [actor.followers_url, actor.url], "to" => [actor.followers_url, actor.url],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"] "cc" => [@ap_public_audience]
} }
if activity_id, do: Map.put(data, "id", activity_id), else: data if activity_id, do: Map.put(data, "id", activity_id), else: data
@ -672,19 +680,22 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
@doc """ @doc """
Converts PEM encoded keys to a public key representation Converts PEM encoded keys to a public key representation
""" """
@spec pem_to_public_key_pem(String.t()) :: String.t() @spec pem_to_public_key_pem(String.t()) :: String.t() | {:error, :no_publickey_found}
def pem_to_public_key_pem(pem) do def pem_to_public_key_pem(pem) do
public_key = pem_to_public_key(pem) case :public_key.pem_decode(pem) do
public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key) [key_code] ->
:public_key.pem_encode([public_key]) public_key = pem_to_public_key(key_code)
public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key)
:public_key.pem_encode([public_key])
_ ->
{:error, :no_publickey_found}
end
end end
@spec pem_to_public_key(String.t()) :: {:RSAPublicKey, any(), any()} @spec pem_to_public_key(String.t()) :: {:RSAPublicKey, any(), any()}
defp pem_to_public_key(pem) do defp pem_to_public_key(key_code) do
[key_code] = :public_key.pem_decode(pem) case :public_key.pem_entry_decode(key_code) do
key = :public_key.pem_entry_decode(key_code)
case key do
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} -> {:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} ->
{:RSAPublicKey, modulus, exponent} {:RSAPublicKey, modulus, exponent}

View file

@ -14,17 +14,17 @@ defmodule Mobilizon.Federation.ActivityPub.Visibility do
@public "https://www.w3.org/ns/activitystreams#Public" @public "https://www.w3.org/ns/activitystreams#Public"
@spec is_public?(Activity.t() | map()) :: boolean() @spec public?(Activity.t() | map()) :: boolean()
def is_public?(%{data: %{"type" => "Tombstone"}}), do: false def public?(%{data: %{"type" => "Tombstone"}}), do: false
def is_public?(%{data: data}), do: is_public?(data) def public?(%{data: data}), do: public?(data)
def is_public?(%Activity{data: data}), do: is_public?(data) def public?(%Activity{data: data}), do: public?(data)
def is_public?(data) when is_map(data) do def public?(data) when is_map(data) do
@public in make_list(Map.get(data, "to", [])) @public in make_list(Map.get(data, "to", []))
end end
def is_public?(%Comment{deleted_at: deleted_at}), do: !is_nil(deleted_at) def public?(%Comment{deleted_at: deleted_at}), do: !is_nil(deleted_at)
def is_public?(err), do: raise(ArgumentError, message: "Invalid argument #{inspect(err)}") def public?(err), do: raise(ArgumentError, message: "Invalid argument #{inspect(err)}")
defp make_list(data) when is_list(data), do: data defp make_list(data) when is_list(data), do: data
defp make_list(data), do: [data] defp make_list(data), do: [data]

View file

@ -36,10 +36,18 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
@spec as_to_model_data(map()) :: map() | {:error, :actor_not_allowed_type} @spec as_to_model_data(map()) :: map() | {:error, :actor_not_allowed_type}
def as_to_model_data(%{"type" => type} = data) when type in @allowed_types do def as_to_model_data(%{"type" => type} = data) when type in @allowed_types do
avatar = avatar =
download_picture(get_in(data, ["icon", "url"]), get_in(data, ["icon", "name"]), "avatar") download_picture(
get_picture(data, ["icon", "url"]),
get_picture(data, ["icon", "name"]),
"avatar"
)
banner = banner =
download_picture(get_in(data, ["image", "url"]), get_in(data, ["image", "name"]), "banner") download_picture(
get_picture(data, ["image", "url"]),
get_picture(data, ["image", "name"]),
"banner"
)
address = get_address(data["location"]) address = get_address(data["location"])
@ -68,14 +76,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
def as_to_model_data(_), do: {:error, :actor_not_allowed_type} def as_to_model_data(_), do: {:error, :actor_not_allowed_type}
defp add_endpoints_to_model(actor, data) do defp add_endpoints_to_model(actor, data) do
# TODO: Remove fallbacks in 3.0
endpoints = %{ endpoints = %{
members_url: get_in(data, ["endpoints", "members"]) || data["members"], members_url: get_in(data, ["endpoints", "members"]),
resources_url: get_in(data, ["endpoints", "resources"]) || data["resources"], resources_url: get_in(data, ["endpoints", "resources"]),
todos_url: get_in(data, ["endpoints", "todos"]) || data["todos"], todos_url: get_in(data, ["endpoints", "todos"]),
events_url: get_in(data, ["endpoints", "events"]) || data["events"], events_url: get_in(data, ["endpoints", "events"]),
posts_url: get_in(data, ["endpoints", "posts"]) || data["posts"], posts_url: get_in(data, ["endpoints", "posts"]),
discussions_url: get_in(data, ["endpoints", "discussions"]) || data["discussions"], discussions_url: get_in(data, ["endpoints", "discussions"]),
shared_inbox_url: data["endpoints"]["sharedInbox"] shared_inbox_url: data["endpoints"]["sharedInbox"]
} }
@ -104,19 +111,11 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
}, },
"discoverable" => actor.visibility == :public, "discoverable" => actor.visibility == :public,
"openness" => actor.openness, "openness" => actor.openness,
"manuallyApprovesFollowers" => actor.manually_approves_followers, "manuallyApprovesFollowers" => actor.manually_approves_followers
"publicKey" => %{
"id" => "#{actor.url}#main-key",
"owner" => actor.url,
"publicKeyPem" =>
if(is_nil(actor.domain) and not is_nil(actor.keys),
do: Utils.pem_to_public_key_pem(actor.keys),
else: actor.keys
)
}
} }
actor_data actor_data
|> add_keys(actor)
|> add_endpoints(actor) |> add_endpoints(actor)
|> maybe_add_members(actor) |> maybe_add_members(actor)
|> maybe_add_avatar_picture(actor) |> maybe_add_avatar_picture(actor)
@ -124,6 +123,28 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
|> maybe_add_physical_address(actor) |> maybe_add_physical_address(actor)
end end
@spec add_keys(map(), ActorModel.t()) :: map()
defp add_keys(actor_data, %ActorModel{} = actor) do
keys =
if is_nil(actor.domain) and not is_nil(actor.keys) do
case Utils.pem_to_public_key_pem(actor.keys) do
{:error, :no_publickey_found} ->
raise "No publickey found in private keys"
public_key when is_binary(public_key) ->
public_key
end
else
actor.keys
end
Map.put(actor_data, "publicKey", %{
"id" => "#{actor.url}#main-key",
"owner" => actor.url,
"publicKeyPem" => keys
})
end
defp add_endpoints(%{"endpoints" => endpoints} = actor_data, %ActorModel{} = actor) do defp add_endpoints(%{"endpoints" => endpoints} = actor_data, %ActorModel{} = actor) do
new_endpoints = %{ new_endpoints = %{
"members" => actor.members_url, "members" => actor.members_url,
@ -193,4 +214,10 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
end end
defp maybe_add_physical_address(res, _), do: res defp maybe_add_physical_address(res, _), do: res
defp get_picture(nil, _keys), do: nil
defp get_picture(url, _keys) when is_binary(url), do: url
defp get_picture(data, [key | rest] = keys) when is_map(data) and is_list(keys),
do: get_picture(data[key], rest)
end end

View file

@ -24,16 +24,23 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Address do
} }
res = res =
if is_nil(object["address"]) or not is_map(object["address"]) do cond do
res is_binary(object["address"]) ->
else Map.merge(res, %{
Map.merge(res, %{ "street" => object["address"]
"country" => object["address"]["addressCountry"], })
"postal_code" => object["address"]["postalCode"],
"region" => object["address"]["addressRegion"], is_map(object["address"]) ->
"street" => object["address"]["streetAddress"], Map.merge(res, %{
"locality" => object["address"]["addressLocality"] "country" => object["address"]["addressCountry"],
}) "postal_code" => object["address"]["postalCode"],
"region" => object["address"]["addressRegion"],
"street" => object["address"]["streetAddress"],
"locality" => object["address"]["addressLocality"]
})
is_nil(object["address"]) ->
res
end end
latitude = Map.get(object, "latitude") latitude = Map.get(object, "latitude")

View file

@ -47,9 +47,6 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
case maybe_fetch_actor_and_attributed_to_id(object) do case maybe_fetch_actor_and_attributed_to_id(object) do
{:ok, %Actor{id: actor_id, domain: actor_domain}, attributed_to} -> {:ok, %Actor{id: actor_id, domain: actor_domain}, attributed_to} ->
Logger.debug("Inserting full comment")
Logger.debug(inspect(object))
data = %{ data = %{
text: object["content"], text: object["content"],
url: object["id"], url: object["id"],
@ -65,19 +62,12 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
tags: fetch_tags(tag_object), tags: fetch_tags(tag_object),
mentions: fetch_mentions(tag_object), mentions: fetch_mentions(tag_object),
local: is_nil(actor_domain), local: is_nil(actor_domain),
visibility: if(Visibility.is_public?(object), do: :public, else: :private), visibility: if(Visibility.public?(object), do: :public, else: :private),
published_at: object["published"], published_at: object["published"],
is_announcement: Map.get(object, "isAnnouncement", false) is_announcement: Map.get(object, "isAnnouncement", false)
} }
Logger.debug("Converted object before fetching parents") maybe_fetch_parent_object(object, data)
Logger.debug(inspect(data))
data = maybe_fetch_parent_object(object, data)
Logger.debug("Converted object after fetching parents")
Logger.debug(inspect(data))
data
{:error, err} -> {:error, err} ->
{:error, err} {:error, err}
@ -147,19 +137,22 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
end end
@spec determine_to(CommentModel.t()) :: [String.t()] @spec determine_to(CommentModel.t()) :: [String.t()]
defp determine_to(%CommentModel{} = comment) do defp determine_to(%CommentModel{visibility: :private, mentions: mentions} = _comment) do
cond do Enum.map(mentions, fn mention -> mention.actor.url end)
not is_nil(comment.attributed_to) -> end
[comment.attributed_to.url]
comment.visibility == :public -> defp determine_to(%CommentModel{visibility: :public} = comment) do
["https://www.w3.org/ns/activitystreams#Public"] if is_nil(comment.attributed_to) do
["https://www.w3.org/ns/activitystreams#Public"]
true -> else
[comment.actor.followers_url] [comment.attributed_to.url]
end end
end end
defp determine_to(%CommentModel{} = comment) do
[comment.actor.followers_url]
end
defp maybe_fetch_parent_object(object, data) do defp maybe_fetch_parent_object(object, data) do
# We fetch the parent object # We fetch the parent object
Logger.debug("We're fetching the parent object") Logger.debug("We're fetching the parent object")
@ -170,9 +163,12 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
case ActivityPub.fetch_object_from_url(object["inReplyTo"]) do case ActivityPub.fetch_object_from_url(object["inReplyTo"]) do
# Reply to an event (Event) # Reply to an event (Event)
{:ok, %Event{id: id}} -> {:ok, %Event{id: id} = event} ->
Logger.debug("Parent object is an event") Logger.debug("Parent object is an event")
data |> Map.put(:event_id, id)
data
|> Map.put(:event_id, id)
|> Map.put(:event, event)
# Reply to a comment (Comment) # Reply to a comment (Comment)
{:ok, %CommentModel{id: id} = comment} -> {:ok, %CommentModel{id: id} = comment} ->
@ -182,6 +178,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|> Map.put(:in_reply_to_comment_id, id) |> Map.put(:in_reply_to_comment_id, id)
|> Map.put(:origin_comment_id, comment |> CommentModel.get_thread_id()) |> Map.put(:origin_comment_id, comment |> CommentModel.get_thread_id())
|> Map.put(:event_id, comment.event_id) |> Map.put(:event_id, comment.event_id)
|> Map.put(:conversation_id, comment.conversation_id)
# Reply to a discucssion (Discussion) # Reply to a discucssion (Discussion)
{:ok, {:ok,
@ -202,7 +199,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
# Anything else is kind of a MP # Anything else is kind of a MP
{:error, parent} -> {:error, parent} ->
Logger.warn("Parent object is something we don't handle") Logger.warning("Parent object is something we don't handle")
Logger.debug(inspect(parent)) Logger.debug(inspect(parent))
data data
end end

View file

@ -0,0 +1,68 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.Conversation do
@moduledoc """
Comment converter.
This module allows to convert conversations from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Conversation
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Federation.ActivityStream.Converter.Conversation, as: ConversationConverter
alias Mobilizon.Storage.Repo
import Mobilizon.Service.Guards, only: [is_valid_string: 1]
require Logger
@behaviour Converter
defimpl Convertible, for: Conversation do
defdelegate model_to_as(comment), to: ConversationConverter
end
@doc """
Make an AS comment object from an existing `conversation` structure.
"""
@impl Converter
@spec model_to_as(Conversation.t()) :: map
def model_to_as(%Conversation{} = conversation) do
conversation = Repo.preload(conversation, [:participants, last_comment: [:actor]])
%{
"type" => "Note",
"to" => Enum.map(conversation.participants, & &1.url),
"cc" => [],
"content" => conversation.last_comment.text,
"mediaType" => "text/html",
"actor" => conversation.last_comment.actor.url,
"id" => conversation.last_comment.url,
"publishedAt" => conversation.inserted_at
}
end
@impl Converter
@spec as_to_model_data(map) :: map() | {:error, atom()}
def as_to_model_data(%{"type" => "Note", "name" => name} = object) when is_valid_string(name) do
with %{actor_id: actor_id, creator_id: creator_id} <- extract_actors(object) do
%{actor_id: actor_id, creator_id: creator_id, title: name, url: object["id"]}
end
end
@spec extract_actors(map()) ::
%{actor_id: String.t(), creator_id: String.t()} | {:error, atom()}
defp extract_actors(%{"actor" => creator_url, "attributedTo" => actor_url} = _object)
when is_valid_string(creator_url) and is_valid_string(actor_url) do
with {:ok, %Actor{id: creator_id, suspended: false}} <-
ActivityPubActor.get_or_fetch_actor_by_url(creator_url),
{:ok, %Actor{id: actor_id, suspended: false}} <-
ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do
%{actor_id: actor_id, creator_id: creator_id}
else
{:error, error} -> {:error, error}
{:ok, %Actor{url: ^creator_url}} -> {:error, :creator_suspended}
{:ok, %Actor{url: ^actor_url}} -> {:error, :actor_suspended}
end
end
end

View file

@ -77,10 +77,15 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
category: Categories.get_category(object["category"]), category: Categories.get_category(object["category"]),
visibility: visibility, visibility: visibility,
join_options: Map.get(object, "joinMode", "free"), join_options: Map.get(object, "joinMode", "free"),
local: is_local?(object["id"]), local: local?(object["id"]),
external_participation_url: object["externalParticipationUrl"],
options: options, options: options,
metadata: metadata, metadata: metadata,
status: object |> Map.get("ical:status", "CONFIRMED") |> String.downcase(), # Remove fallback in MBZ 5.x
status:
object
|> Map.get("status", Map.get(object, "ical:status", "CONFIRMED"))
|> String.downcase(),
online_address: object |> Map.get("attachment", []) |> get_online_address(), online_address: object |> Map.get("attachment", []) |> get_online_address(),
phone_address: object["phoneAddress"], phone_address: object["phoneAddress"],
draft: object["draft"] == true, draft: object["draft"] == true,
@ -129,6 +134,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"mediaType" => "text/html", "mediaType" => "text/html",
"startTime" => event.begins_on |> shift_tz(event.options.timezone) |> date_to_string(), "startTime" => event.begins_on |> shift_tz(event.options.timezone) |> date_to_string(),
"joinMode" => to_string(event.join_options), "joinMode" => to_string(event.join_options),
"externalParticipationUrl" => event.external_participation_url,
"endTime" => event.ends_on |> shift_tz(event.options.timezone) |> date_to_string(), "endTime" => event.ends_on |> shift_tz(event.options.timezone) |> date_to_string(),
"tag" => event.tags |> build_tags(), "tag" => event.tags |> build_tags(),
"maximumAttendeeCapacity" => event.options.maximum_attendee_capacity, "maximumAttendeeCapacity" => event.options.maximum_attendee_capacity,
@ -140,7 +146,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"anonymousParticipationEnabled" => event.options.anonymous_participation, "anonymousParticipationEnabled" => event.options.anonymous_participation,
"attachment" => Enum.map(event.metadata, &EventMetadataConverter.metadata_to_as/1), "attachment" => Enum.map(event.metadata, &EventMetadataConverter.metadata_to_as/1),
"draft" => event.draft, "draft" => event.draft,
# Remove me in MBZ 5.x
"ical:status" => event.status |> to_string |> String.upcase(), "ical:status" => event.status |> to_string |> String.upcase(),
"status" => event.status |> to_string |> String.upcase(),
"id" => event.url, "id" => event.url,
"url" => event.url, "url" => event.url,
"inLanguage" => event.language, "inLanguage" => event.language,
@ -190,6 +198,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
defp calculate_timezone(_object, nil), do: nil defp calculate_timezone(_object, nil), do: nil
defp calculate_timezone(_object, %Address{geom: nil}), do: nil
defp calculate_timezone(_object, %Address{geom: geom}) do defp calculate_timezone(_object, %Address{geom: geom}) do
TimezoneDetector.detect( TimezoneDetector.detect(
nil, nil,
@ -295,8 +305,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
) )
end end
@spec is_local?(String.t()) :: boolean() @spec local?(String.t()) :: boolean()
defp is_local?(url) do defp local?(url) do
%URI{host: url_domain} = URI.parse(url) %URI{host: url_domain} = URI.parse(url)
%URI{host: local_domain} = URI.parse(Endpoint.url()) %URI{host: local_domain} = URI.parse(Endpoint.url())
url_domain == local_domain url_domain == local_domain

View file

@ -9,11 +9,11 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
""" """
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions alias Mobilizon.Discussions.Comment
alias Mobilizon.Events
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
alias Mobilizon.Reports.Report alias Mobilizon.Reports.Report
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.Relay alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
@ -38,7 +38,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
"uri" => params["uri"], "uri" => params["uri"],
"content" => params["content"], "content" => params["content"],
"reported_id" => params["reported"].id, "reported_id" => params["reported"].id,
"event_id" => (!is_nil(params["event"]) && params["event"].id) || nil, "events" => params["events"],
"comments" => params["comments"] "comments" => params["comments"]
} }
end end
@ -50,15 +50,17 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
@impl Converter @impl Converter
@spec model_to_as(Report.t()) :: map @spec model_to_as(Report.t()) :: map
def model_to_as(%Report{} = report) do def model_to_as(%Report{} = report) do
object = [report.reported.url] ++ Enum.map(report.comments, fn comment -> comment.url end) object =
[report.reported.url] ++
object = if report.event, do: object ++ [report.event.url], else: object Enum.map(report.comments, fn comment -> comment.url end) ++
Enum.map(report.events, & &1.url)
%{ %{
"type" => "Flag", "type" => "Flag",
"actor" => Relay.get_actor().url, "actor" => Relay.get_actor().url,
"id" => report.url, "id" => report.url,
"content" => report.content, "content" => report.content,
"mediaType" => "text/plain",
"object" => object "object" => object
} }
end end
@ -68,14 +70,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
with {:ok, %Actor{} = reporter} <- with {:ok, %Actor{} = reporter} <-
ActivityPubActor.get_or_fetch_actor_by_url(object["actor"]), ActivityPubActor.get_or_fetch_actor_by_url(object["actor"]),
%Actor{} = reported <- find_reported(objects), %Actor{} = reported <- find_reported(objects),
event <- find_event(objects), %{events: events, comments: comments} <- find_events_and_comments(objects) do
comments <- find_comments(objects, reported, event) do
%{ %{
"reporter" => reporter, "reporter" => reporter,
"uri" => object["id"], "uri" => object["id"],
"content" => object["content"], "content" => object["content"],
"reported" => reported, "reported" => reported,
"event" => event, "events" => events,
"comments" => comments "comments" => comments
} }
end end
@ -94,26 +95,19 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
end) end)
end end
# Remove the reported actor and the event from the object list. defp find_events_and_comments(objects) do
@spec find_comments(list(String.t()), Actor.t() | nil, Event.t() | nil) :: list(Comment.t())
defp find_comments(objects, reported, event) do
objects objects
|> Enum.filter(fn url -> |> Enum.map(&ActivityPub.fetch_object_from_url/1)
!((!is_nil(reported) && url == reported.url) || (!is_nil(event) && event.url == url)) |> Enum.reduce(%{comments: [], events: []}, fn res, acc ->
end) case res do
|> Enum.map(&Discussions.get_comment_from_url/1) {:ok, %Event{} = event} ->
|> Enum.filter(& &1) Map.put(acc, :events, [event | acc.events])
end
@spec find_event(list(String.t())) :: Event.t() | nil {:ok, %Comment{} = comment} ->
defp find_event(objects) do Map.put(acc, :comments, [comment | acc.comments])
Enum.reduce_while(objects, nil, fn url, _ ->
case Events.get_event_by_url(url) do
%Event{} = event ->
{:halt, event}
_ -> _ ->
{:cont, nil} acc
end end
end) end)
end end

View file

@ -26,7 +26,11 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
Logger.debug("fetching tags") Logger.debug("fetching tags")
Logger.debug(inspect(tags)) Logger.debug(inspect(tags))
tags |> Enum.flat_map(&fetch_tag/1) |> Enum.uniq() |> Enum.map(&existing_tag_or_data/1) tags
|> Enum.flat_map(&fetch_tag/1)
|> Enum.uniq()
|> Enum.filter(& &1)
|> Enum.map(&existing_tag_or_data/1)
end end
def fetch_tags(_), do: [] def fetch_tags(_), do: []
@ -122,6 +126,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
create_mention(mention, acc) create_mention(mention, acc)
end end
defp create_mention(_, acc), do: acc
@spec maybe_fetch_actor_and_attributed_to_id(map()) :: @spec maybe_fetch_actor_and_attributed_to_id(map()) ::
{:ok, Actor.t(), Actor.t() | nil} | {:error, atom()} {:ok, Actor.t(), Actor.t() | nil} | {:error, atom()}
def maybe_fetch_actor_and_attributed_to_id(%{ def maybe_fetch_actor_and_attributed_to_id(%{
@ -179,8 +185,10 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
def maybe_fetch_actor_and_attributed_to_id(_), do: {:error, :no_actor_found} def maybe_fetch_actor_and_attributed_to_id(_), do: {:error, :no_actor_found}
@spec fetch_actor(String.t()) :: {:ok, Actor.t()} | {:error, atom()} @spec fetch_actor(String.t() | map()) :: {:ok, Actor.t()} | {:error, atom()}
def fetch_actor(actor_url) do def fetch_actor(%{"id" => actor_url}) when is_binary(actor_url), do: fetch_actor(actor_url)
def fetch_actor(actor_url) when is_binary(actor_url) do
case ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do case ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do
{:ok, %Actor{suspended: false} = actor} -> {:ok, %Actor{suspended: false} = actor} ->
{:ok, actor} {:ok, actor}
@ -193,6 +201,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
end end
end end
def fetch_actor(_), do: {:error, :no_actor_found}
@spec process_pictures(map(), integer()) :: Keyword.t() @spec process_pictures(map(), integer()) :: Keyword.t()
def process_pictures(object, actor_id) do def process_pictures(object, actor_id) do
attachements = Map.get(object, "attachment", []) attachements = Map.get(object, "attachment", [])
@ -268,8 +278,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
end end
@spec get_address(map | binary | nil) :: Address.t() | nil @spec get_address(map | binary | nil) :: Address.t() | nil
def get_address(address_url) when is_binary(address_url) do def get_address(text_address) when is_binary(text_address) do
get_address(%{"id" => address_url}) get_address(%{"type" => "Place", "name" => text_address})
end end
def get_address(%{"id" => url} = map) when is_map(map) and is_binary(url) do def get_address(%{"id" => url} = map) when is_map(map) and is_binary(url) do

123
lib/federation/node_info.ex Normal file
View file

@ -0,0 +1,123 @@
defmodule Mobilizon.Federation.NodeInfo do
@moduledoc """
Performs NodeInfo requests
"""
alias Mobilizon.Service.HTTP.WebfingerClient
require Logger
import Mobilizon.Service.HTTP.Utils, only: [content_type_matches?: 2]
@application_uri "https://www.w3.org/ns/activitystreams#Application"
@nodeinfo_rel_2_0 "http://nodeinfo.diaspora.software/ns/schema/2.0"
@nodeinfo_rel_2_1 "http://nodeinfo.diaspora.software/ns/schema/2.1"
@env Application.compile_env(:mobilizon, :env)
@spec application_actor(String.t()) :: String.t() | nil
def application_actor(host) do
Logger.debug("Fetching application actor from NodeInfo data for domain #{host}")
case fetch_nodeinfo_endpoint(host) do
{:ok, body} ->
extract_application_actor(body)
{:error, _err} ->
nil
end
end
@spec nodeinfo(String.t()) :: {:ok, map()} | {:error, atom()}
def nodeinfo(host) do
Logger.debug("Fetching NodeInfo details for domain #{host}")
with {:ok, endpoint} when is_binary(endpoint) <- fetch_nodeinfo_details(host),
:ok <- Logger.debug("Going to get NodeInfo information from URL #{endpoint}"),
{:ok, %{body: body, status: code, headers: headers}} when code in 200..299 <-
WebfingerClient.get(endpoint),
{:ok, body} <- validate_json_response(body, headers) do
Logger.debug("Found nodeinfo information for domain #{host}")
{:ok, body}
else
{:error, err} ->
{:error, err}
err ->
Logger.debug("Failed to fetch NodeInfo data from endpoint #{inspect(err)}")
{:error, :node_info_endpoint_http_error}
end
end
defp extract_application_actor(body) do
body
|> Map.get("links", [])
|> Enum.find(%{"rel" => @application_uri, "href" => nil}, fn %{"rel" => rel, "href" => href} ->
rel == @application_uri and is_binary(href)
end)
|> Map.get("href")
end
@spec fetch_nodeinfo_endpoint(String.t()) :: {:ok, map()} | {:error, atom()}
defp fetch_nodeinfo_endpoint(host) do
prefix = if @env !== :dev, do: "https", else: "http"
case WebfingerClient.get("#{prefix}://#{host}/.well-known/nodeinfo") do
{:ok, %{body: body, status: code, headers: headers}} when code in 200..299 ->
validate_json_response(body, headers)
err ->
Logger.debug("Failed to fetch NodeInfo data #{inspect(err)}")
{:error, :node_info_meta_http_error}
end
end
@spec fetch_nodeinfo_details(String.t()) :: {:ok, String.t()} | {:error, atom()}
defp fetch_nodeinfo_details(host) do
with {:ok, body} <- fetch_nodeinfo_endpoint(host) do
extract_nodeinfo_endpoint(body)
end
end
@spec extract_nodeinfo_endpoint(map()) ::
{:ok, String.t()}
| {:error, :no_node_info_endpoint_found | :no_valid_node_info_endpoint_found}
defp extract_nodeinfo_endpoint(body) do
links = Map.get(body, "links", [])
relation =
find_nodeinfo_relation(links, @nodeinfo_rel_2_1) ||
find_nodeinfo_relation(links, @nodeinfo_rel_2_0)
if is_nil(relation) do
{:error, :no_node_info_endpoint_found}
else
endpoint = Map.get(relation, "href")
if is_nil(endpoint) do
{:error, :no_valid_node_info_endpoint_found}
else
{:ok, endpoint}
end
end
end
defp find_nodeinfo_relation(links, relation) do
Enum.find(links, fn %{"rel" => rel, "href" => href} ->
rel == relation and is_binary(href)
end)
end
@spec validate_json_response(map() | String.t(), list()) ::
{:ok, String.t()} | {:error, :bad_content_type | :body_not_json}
defp validate_json_response(body, headers) do
cond do
!content_type_matches?(headers, "application/json") ->
{:error, :bad_content_type}
!is_map(body) ->
{:error, :body_not_json}
true ->
{:ok, body}
end
end
end

View file

@ -14,7 +14,7 @@ defmodule Mobilizon.Federation.WebFinger do
alias Mobilizon.Federation.WebFinger.XmlBuilder alias Mobilizon.Federation.WebFinger.XmlBuilder
alias Mobilizon.Service.HTTP.{HostMetaClient, WebfingerClient} alias Mobilizon.Service.HTTP.{HostMetaClient, WebfingerClient}
alias Mobilizon.Web.Endpoint alias Mobilizon.Web.Endpoint
alias Mobilizon.Web.Router.Helpers, as: Routes use Mobilizon.Web, :verified_routes
require Jason require Jason
require Logger require Logger
import SweetXml import SweetXml
@ -85,7 +85,7 @@ defmodule Mobilizon.Federation.WebFinger do
%{"rel" => "self", "type" => "application/activity+json", "href" => actor.url}, %{"rel" => "self", "type" => "application/activity+json", "href" => actor.url},
%{ %{
"rel" => "http://ostatus.org/schema/1.0/subscribe", "rel" => "http://ostatus.org/schema/1.0/subscribe",
"template" => "#{Routes.page_url(Endpoint, :interact, uri: nil)}{uri}" "template" => "#{url(~p"/interact?#{[uri: nil]}")}{uri}"
} }
] ]
|> maybe_add_avatar(actor) |> maybe_add_avatar(actor)
@ -242,12 +242,15 @@ defmodule Mobilizon.Federation.WebFinger do
@spec domain_from_federated_actor(String.t()) :: {:ok, String.t()} | {:error, :host_not_found} @spec domain_from_federated_actor(String.t()) :: {:ok, String.t()} | {:error, :host_not_found}
defp domain_from_federated_actor(actor) do defp domain_from_federated_actor(actor) do
case String.split(actor, "@") do case String.split(actor, "@") do
[_name, ""] ->
{:error, :host_not_found}
[_name, domain] -> [_name, domain] ->
{:ok, domain} {:ok, domain}
_e -> _e ->
host = URI.parse(actor).host host = URI.parse(actor).host
if is_nil(host), do: {:error, :host_not_found}, else: {:ok, host} if is_nil(host) or host == "", do: {:error, :host_not_found}, else: {:ok, host}
end end
end end

View file

@ -4,7 +4,8 @@ defmodule Mobilizon.GraphQL.API.Comments do
""" """
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment alias Mobilizon.Conversations.Conversation
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub.{Actions, Activity} alias Mobilizon.Federation.ActivityPub.{Actions, Activity}
alias Mobilizon.GraphQL.API.Utils alias Mobilizon.GraphQL.API.Utils
@ -53,6 +54,22 @@ defmodule Mobilizon.GraphQL.API.Comments do
) )
end end
@doc """
Creates a conversation (or reply to a conversation)
"""
@spec create_conversation(map()) ::
{:ok, Activity.t(), Conversation.t()}
| {:error, :entity_tombstoned | atom | Ecto.Changeset.t()}
def create_conversation(args) do
args = extract_pictures_from_comment_body(args)
Actions.Create.create(
:conversation,
args,
true
)
end
@spec extract_pictures_from_comment_body(map()) :: map() @spec extract_pictures_from_comment_body(map()) :: map()
defp extract_pictures_from_comment_body(%{text: text, actor_id: actor_id} = args) do defp extract_pictures_from_comment_body(%{text: text, actor_id: actor_id} = args) do
pictures = Utils.extract_pictures_from_body(text, actor_id) pictures = Utils.extract_pictures_from_body(text, actor_id)

View file

@ -4,8 +4,8 @@ defmodule Mobilizon.GraphQL.API.Events do
""" """
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Utils} alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Utils}
alias Mobilizon.GraphQL.API.Utils, as: APIUtils alias Mobilizon.GraphQL.API.Utils, as: APIUtils
@ -36,6 +36,12 @@ defmodule Mobilizon.GraphQL.API.Events do
Actions.Delete.delete(event, actor, true) Actions.Delete.delete(event, actor, true)
end end
@spec send_private_message_to_participants(map()) ::
{:ok, Activity.t(), Comment.t()} | {:error, atom() | Ecto.Changeset.t()}
def send_private_message_to_participants(args) do
Actions.Create.create(:comment, args, true)
end
@spec prepare_args(map) :: map @spec prepare_args(map) :: map
defp prepare_args(args) do defp prepare_args(args) do
organizer_actor = Map.get(args, :organizer_actor) organizer_actor = Map.get(args, :organizer_actor)

View file

@ -57,7 +57,7 @@ defmodule Mobilizon.GraphQL.API.Reports do
end end
if antispam_response != :ok do if antispam_response != :ok do
Logger.warn("Antispam response has been #{inspect(antispam_response)}") Logger.warning("Antispam response has been #{inspect(antispam_response)}")
end end
{:ok, report} {:ok, report}

View file

@ -25,8 +25,8 @@ defmodule Mobilizon.GraphQL.API.Search do
cond do cond do
# Some URLs could be domain.tld/@username, so keep this condition above # Some URLs could be domain.tld/@username, so keep this condition above
# the `is_handle` function # the `handle?` function
is_url(term) -> url?(term) ->
# skip, if it's not an actor # skip, if it's not an actor
case process_from_url(term) do case process_from_url(term) do
%Page{total: _total, elements: [%Actor{} = _actor]} = page -> %Page{total: _total, elements: [%Actor{} = _actor]} = page ->
@ -36,11 +36,11 @@ defmodule Mobilizon.GraphQL.API.Search do
{:ok, %{total: 0, elements: []}} {:ok, %{total: 0, elements: []}}
end end
is_handle(term) -> handle?(term) ->
{:ok, process_from_username(term)} {:ok, process_from_username(term)}
true -> true ->
if is_global_search(args) do if global_search?(args) do
service = GlobalSearch.service() service = GlobalSearch.service()
{:ok, service.search_groups(Keyword.new(args, fn {k, v} -> {k, v} end))} {:ok, service.search_groups(Keyword.new(args, fn {k, v} -> {k, v} end))}
@ -75,7 +75,7 @@ defmodule Mobilizon.GraphQL.API.Search do
def search_events(%{term: term} = args, page \\ 1, limit \\ 10) do def search_events(%{term: term} = args, page \\ 1, limit \\ 10) do
term = String.trim(term) term = String.trim(term)
if is_url(term) do if url?(term) do
# skip, if it's not an event # skip, if it's not an event
case process_from_url(term) do case process_from_url(term) do
%Page{total: _total, elements: [%Event{} = event]} = page -> %Page{total: _total, elements: [%Event{} = event]} = page ->
@ -89,7 +89,7 @@ defmodule Mobilizon.GraphQL.API.Search do
{:ok, %{total: 0, elements: []}} {:ok, %{total: 0, elements: []}}
end end
else else
if is_global_search(args) do if global_search?(args) do
service = GlobalSearch.service() service = GlobalSearch.service()
{:ok, service.search_events(Keyword.new(args, fn {k, v} -> {k, v} end))} {:ok, service.search_events(Keyword.new(args, fn {k, v} -> {k, v} end))}
@ -116,13 +116,9 @@ defmodule Mobilizon.GraphQL.API.Search do
@spec process_from_username(String.t()) :: Page.t(Actor.t()) @spec process_from_username(String.t()) :: Page.t(Actor.t())
defp process_from_username(search) do defp process_from_username(search) do
case ActivityPubActor.find_or_make_actor_from_nickname(search) do case ActivityPubActor.find_or_make_actor_from_nickname(search) do
{:ok, %Actor{type: :Group} = actor} -> {:ok, %Actor{} = actor} ->
%Page{total: 1, elements: [actor]} %Page{total: 1, elements: [actor]}
# Don't return anything else than groups
{:ok, %Actor{}} ->
%Page{total: 0, elements: []}
{:error, _err} -> {:error, _err} ->
Logger.debug(fn -> "Unable to find or make actor '#{search}'" end) Logger.debug(fn -> "Unable to find or make actor '#{search}'" end)
@ -144,17 +140,17 @@ defmodule Mobilizon.GraphQL.API.Search do
end end
end end
@spec is_url(String.t()) :: boolean @spec url?(String.t()) :: boolean
defp is_url(search), do: String.starts_with?(search, ["http://", "https://"]) defp url?(search), do: String.starts_with?(search, ["http://", "https://"])
@spec is_handle(String.t()) :: boolean @spec handle?(String.t()) :: boolean
defp is_handle(search), do: String.match?(search, ~r/@/) defp handle?(search), do: String.match?(search, ~r/@/)
defp is_global_search(%{search_target: :global}) do defp global_search?(%{search_target: :global}) do
global_search_enabled?() global_search_enabled?()
end end
defp is_global_search(_), do: global_search_enabled?() && global_search_default?() defp global_search?(_), do: global_search_enabled?() && global_search_default?()
defp global_search_enabled? do defp global_search_enabled? do
Application.get_env(:mobilizon, :search) |> get_in([:global]) |> get_in([:is_enabled]) Application.get_env(:mobilizon, :search) |> get_in([:global]) |> get_in([:is_enabled])

View file

@ -68,6 +68,6 @@ defmodule Mobilizon.GraphQL.API.Utils do
@spec check_actor_owns_media?(integer() | String.t(), integer() | String.t()) :: boolean() @spec check_actor_owns_media?(integer() | String.t(), integer() | String.t()) :: boolean()
defp check_actor_owns_media?(actor_id, media_actor_id) do defp check_actor_owns_media?(actor_id, media_actor_id) do
actor_id == media_actor_id || Mobilizon.Actors.is_member?(media_actor_id, actor_id) actor_id == media_actor_id || Mobilizon.Actors.member?(media_actor_id, actor_id)
end end
end end

View file

@ -16,11 +16,13 @@ defmodule Mobilizon.GraphQL.Authorization do
@impl true @impl true
def has_user_access?(%User{}, _scope, _rule), do: true def has_user_access?(%User{}, _scope, _rule), do: true
@impl true
def has_user_access?(%ApplicationToken{scope: scope} = _current_app_token, _struct, rule) def has_user_access?(%ApplicationToken{scope: scope} = _current_app_token, _struct, rule)
when rule != :forbid_app_access do when rule != :forbid_app_access do
AppScope.has_app_access?(scope, rule) AppScope.has_app_access?(scope, rule)
end end
@impl true
def has_user_access?(_current_user, _scoped_struct, _rule), do: false def has_user_access?(_current_user, _scoped_struct, _rule), do: false
@impl true @impl true

View file

@ -55,7 +55,14 @@ defmodule Mobilizon.GraphQL.Authorization.AppScope do
:"read:event", :"read:event",
:"read:event:participants", :"read:event:participants",
:"read:event:participants:export", :"read:event:participants:export",
# User permissions
:"read:user:media",
:"read:user:settings", :"read:user:settings",
:"read:user:activity_settings",
:"read:user:participations",
:"read:user:memberships",
:"read:user:draft_events",
:"read:user:group_suggested_events",
# Profile permissions # Profile permissions
:"read:profile", :"read:profile",
:"read:profile:organized_events", :"read:profile:organized_events",
@ -67,7 +74,6 @@ defmodule Mobilizon.GraphQL.Authorization.AppScope do
:"read:group:events", :"read:group:events",
:"read:group:discussions", :"read:group:discussions",
:"read:group:resources", :"read:group:resources",
:"read:group:members",
:"read:group:followers", :"read:group:followers",
:"read:group:todo_lists", :"read:group:todo_lists",
:"read:group:activities" :"read:group:activities"

View file

@ -5,8 +5,8 @@ defmodule Mobilizon.GraphQL.Error do
require Logger require Logger
alias __MODULE__ alias __MODULE__
alias Mobilizon.Web.Gettext, as: GettextBackend
import Mobilizon.Web.Gettext, only: [dgettext: 2] import Mobilizon.Web.Gettext, only: [dgettext: 2]
import Mobilizon.Storage.Ecto, only: [convert_ecto_errors: 1]
@type t :: %{code: atom(), message: String.t(), status_code: pos_integer(), field: atom()} @type t :: %{code: atom(), message: String.t(), status_code: pos_integer(), field: atom()}
@ -64,7 +64,7 @@ defmodule Mobilizon.GraphQL.Error do
defp handle(%Ecto.Changeset{} = changeset) do defp handle(%Ecto.Changeset{} = changeset) do
changeset changeset
|> Ecto.Changeset.traverse_errors(&translate_error/1) |> convert_ecto_errors()
|> Enum.map(fn {k, v} -> |> Enum.map(fn {k, v} ->
%Error{ %Error{
code: :validation, code: :validation,
@ -123,30 +123,7 @@ defmodule Mobilizon.GraphQL.Error do
defp metadata(:unknown), do: {500, dgettext("errors", "Something went wrong")} defp metadata(:unknown), do: {500, dgettext("errors", "Something went wrong")}
defp metadata(code) do defp metadata(code) do
Logger.warn("Unhandled error code: #{inspect(code)}") Logger.warning("Unhandled error code: #{inspect(code)}")
{422, to_string(code)} {422, to_string(code)}
end end
# Translates an error message using gettext.
defp translate_error({msg, opts}) do
# Because error messages were defined within Ecto, we must
# call the Gettext module passing our Gettext backend. We
# also use the "errors" domain as translations are placed
# in the errors.po file.
# Ecto will pass the :count keyword if the error message is
# meant to be pluralized.
# On your own code and templates, depending on whether you
# need the message to be pluralized or not, this could be
# written simply as:
#
# dngettext "errors", "1 file", "%{count} files", count
# dgettext "errors", "is invalid"
#
if count = opts[:count] do
Gettext.dngettext(GettextBackend, "errors", msg, msg, count, opts)
else
Gettext.dgettext(GettextBackend, "errors", msg, opts)
end
end
end end

View file

@ -18,7 +18,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Activity do
def group_activity(%Actor{type: :Group, id: group_id}, %{page: page, limit: limit} = args, %{ def group_activity(%Actor{type: :Group, id: group_id}, %{page: page, limit: limit} = args, %{
context: %{current_user: %User{role: role}, current_actor: %Actor{id: actor_id}} context: %{current_user: %User{role: role}, current_actor: %Actor{id: actor_id}}
}) do }) do
if Actors.is_member?(actor_id, group_id) or is_moderator(role) do if Actors.member?(actor_id, group_id) or is_moderator(role) do
%Page{total: total, elements: elements} = %Page{total: total, elements: elements} =
Activities.list_group_activities_for_member( Activities.list_group_activities_for_member(
group_id, group_id,

View file

@ -16,6 +16,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
alias Mobilizon.Reports.{Note, Report} alias Mobilizon.Reports.{Note, Report}
alias Mobilizon.Service.Auth.Authenticator alias Mobilizon.Service.Auth.Authenticator
alias Mobilizon.Service.Statistics alias Mobilizon.Service.Statistics
alias Mobilizon.Service.Workers.RefreshInstances
alias Mobilizon.Storage.Page alias Mobilizon.Storage.Page
alias Mobilizon.Users.User alias Mobilizon.Users.User
alias Mobilizon.Web.Email alias Mobilizon.Web.Email
@ -273,7 +274,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
|> Enum.into(%{}), |> Enum.into(%{}),
:ok <- eventually_update_instance_actor(res) do :ok <- eventually_update_instance_actor(res) do
Config.clear_config_cache() Config.clear_config_cache()
Cachex.put(:config, :admin_config, res)
{:ok, res} {:ok, res}
end end
@ -491,19 +491,35 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
context: %{current_user: %User{role: role}} context: %{current_user: %User{role: role}}
}) })
when is_admin(role) do when is_admin(role) do
remote_relay = Actors.get_relay(domain) remote_relay = Instances.get_instance_actor(domain)
local_relay = Relay.get_actor() local_relay = Relay.get_actor()
result = %{ result =
has_relay: !is_nil(remote_relay), if is_nil(remote_relay) do
relay_address: %{
if(is_nil(remote_relay), has_relay: false,
do: nil, relay_address: nil,
else: "#{remote_relay.preferred_username}@#{remote_relay.domain}" follower_status: nil,
), followed_status: nil,
follower_status: follow_status(remote_relay, local_relay), software: nil,
followed_status: follow_status(local_relay, remote_relay) software_version: nil
} }
else
%{
has_relay: !is_nil(remote_relay.actor),
relay_address:
if(is_nil(remote_relay.actor),
do: nil,
else: Actor.preferred_username_and_domain(remote_relay.actor)
),
follower_status: follow_status(remote_relay.actor, local_relay),
followed_status: follow_status(local_relay, remote_relay.actor),
instance_name: remote_relay.instance_name,
instance_description: remote_relay.instance_description,
software: remote_relay.software,
software_version: remote_relay.software_version
}
end
case Instances.instance(domain) do case Instances.instance(domain) do
nil -> {:error, :not_found} nil -> {:error, :not_found}
@ -531,6 +547,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
case Relay.follow(domain) do case Relay.follow(domain) do
{:ok, _activity, _follow} -> {:ok, _activity, _follow} ->
Instances.refresh() Instances.refresh()
RefreshInstances.refresh_instance_actor(domain)
get_instance(parent, args, resolution) get_instance(parent, args, resolution)
{:error, :follow_pending} -> {:error, :follow_pending} ->

View file

@ -12,7 +12,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
""" """
@spec get_config(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} @spec get_config(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()}
def get_config(_parent, _params, %{context: %{ip: ip}}) do def get_config(_parent, _params, %{context: %{ip: ip}}) do
# ip = "2a01:e0a:184:2000:1112:e19d:9779:88c8"
geolix = Geolix.lookup(ip) geolix = Geolix.lookup(ip)
country_code = country_code =
@ -86,6 +85,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
@spec build_config_cache :: map() @spec build_config_cache :: map()
defp build_config_cache do defp build_config_cache do
webpush_public_key =
get_in(Application.get_env(:web_push_encryption, :vapid_details), [:public_key])
%{ %{
name: Config.instance_name(), name: Config.instance_name(),
registrations_open: Config.instance_registrations_open?(), registrations_open: Config.instance_registrations_open?(),
@ -146,6 +148,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
features: %{ features: %{
groups: Config.instance_group_feature_enabled?(), groups: Config.instance_group_feature_enabled?(),
event_creation: Config.instance_event_creation_enabled?(), event_creation: Config.instance_event_creation_enabled?(),
event_external: Config.instance_event_external_enabled?(),
antispam: AntiSpam.service().ready?() antispam: AntiSpam.service().ready?()
}, },
restrictions: %{ restrictions: %{
@ -170,9 +173,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
enabled: Config.get([:instance, :enable_instance_feeds]) enabled: Config.get([:instance, :enable_instance_feeds])
}, },
web_push: %{ web_push: %{
enabled: !is_nil(Application.get_env(:web_push_encryption, :vapid_details)), enabled: is_binary(webpush_public_key) && String.trim(webpush_public_key) != "",
public_key: public_key:
get_in(Application.get_env(:web_push_encryption, :vapid_details), [:public_key]) if(is_binary(webpush_public_key), do: String.trim(webpush_public_key), else: nil)
}, },
export_formats: Config.instance_export_formats(), export_formats: Config.instance_export_formats(),
analytics: FrontEndAnalytics.config(), analytics: FrontEndAnalytics.config(),

View file

@ -0,0 +1,277 @@
defmodule Mobilizon.GraphQL.Resolvers.Conversation do
@moduledoc """
Handles the group-related GraphQL calls.
"""
alias Mobilizon.{Actors, Conversations}
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.{Conversation, ConversationParticipant, ConversationView}
alias Mobilizon.Events.Event
alias Mobilizon.GraphQL.API.Comments
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
alias Mobilizon.Web.Endpoint
import Mobilizon.Web.Gettext, only: [dgettext: 2]
require Logger
@spec find_conversations_for_event(Event.t(), map(), Absinthe.Resolution.t()) ::
{:ok, Page.t(ConversationView.t())} | {:error, :unauthenticated}
def find_conversations_for_event(
%Event{id: event_id, attributed_to_id: attributed_to_id},
%{page: page, limit: limit},
%{
context: %{
current_actor: %Actor{id: actor_id}
}
}
)
when not is_nil(attributed_to_id) do
if Actors.member?(actor_id, attributed_to_id) do
{:ok,
event_id
|> Conversations.find_conversations_for_event(attributed_to_id, page, limit)
|> conversation_participant_to_view()}
else
{:ok, %Page{total: 0, elements: []}}
end
end
@spec find_conversations_for_event(Event.t(), map(), Absinthe.Resolution.t()) ::
{:ok, Page.t(ConversationView.t())} | {:error, :unauthenticated}
def find_conversations_for_event(
%Event{id: event_id, organizer_actor_id: organizer_actor_id},
%{page: page, limit: limit},
%{
context: %{
current_actor: %Actor{id: actor_id}
}
}
) do
if organizer_actor_id == actor_id do
{:ok,
event_id
|> Conversations.find_conversations_for_event(actor_id, page, limit)
|> conversation_participant_to_view()}
else
{:ok, %Page{total: 0, elements: []}}
end
end
def list_conversations(%Actor{id: actor_id}, %{page: page, limit: limit}, %{
context: %{
current_actor: %Actor{id: _current_actor_id}
}
}) do
{:ok,
actor_id
|> Conversations.list_conversation_participants_for_actor(page, limit)
|> conversation_participant_to_view()}
end
def list_conversations(%User{id: user_id}, %{page: page, limit: limit}, %{
context: %{
current_actor: %Actor{id: _current_actor_id}
}
}) do
{:ok,
user_id
|> Conversations.list_conversation_participants_for_user(page, limit)
|> conversation_participant_to_view()}
end
def unread_conversations_count(%Actor{id: actor_id}, _args, %{
context: %{
current_user: %User{} = user
}
}) do
case User.owns_actor(user, actor_id) do
{:is_owned, %Actor{}} ->
{:ok, Conversations.count_unread_conversation_participants_for_person(actor_id)}
_ ->
{:error, :unauthorized}
end
end
def get_conversation(_parent, %{id: conversation_participant_id}, %{
context: %{
current_actor: %Actor{id: performing_actor_id}
}
}) do
case Conversations.get_conversation_participant(conversation_participant_id) do
nil ->
{:error, :not_found}
%ConversationParticipant{actor_id: actor_id} = conversation_participant ->
if actor_id == performing_actor_id or Actors.member?(performing_actor_id, actor_id) do
{:ok, conversation_participant_to_view(conversation_participant)}
else
{:error, :not_found}
end
end
end
def get_comments_for_conversation(
%ConversationView{origin_comment_id: origin_comment_id, actor_id: conversation_actor_id},
%{page: page, limit: limit},
%{
context: %{
current_actor: %Actor{id: performing_actor_id}
}
}
) do
if conversation_actor_id == performing_actor_id or
Actors.member?(performing_actor_id, conversation_actor_id) do
{:ok,
Mobilizon.Discussions.get_comments_in_reply_to_comment_id(origin_comment_id, page, limit)}
else
{:error, :unauthorized}
end
end
def create_conversation(
_parent,
%{actor_id: actor_id} = args,
%{
context: %{
current_actor: %Actor{} = current_actor
}
}
) do
if authorized_to_reply?(
Map.get(args, :conversation_id),
Map.get(args, :attributed_to_id),
current_actor.id
) do
case Comments.create_conversation(args) do
{:ok, _activity, %Conversation{} = conversation} ->
Absinthe.Subscription.publish(
Endpoint,
Conversations.count_unread_conversation_participants_for_person(current_actor.id),
person_unread_conversations_count: current_actor.id
)
conversation_participant_actor =
args |> Map.get(:attributed_to_id, actor_id) |> Actors.get_actor()
{:ok, conversation_to_view(conversation, conversation_participant_actor)}
{:error, :empty_participants} ->
{:error,
dgettext(
"errors",
"Conversation needs to mention at least one participant that's not yourself"
)}
end
else
Logger.debug(
"Actor #{current_actor.id} is not authorized to reply to conversation #{inspect(Map.get(args, :conversation_id))}"
)
{:error, :unauthorized}
end
end
def update_conversation(_parent, %{conversation_id: conversation_participant_id, read: read}, %{
context: %{
current_actor: %Actor{id: current_actor_id}
}
}) do
with {:no_participant,
%ConversationParticipant{actor_id: actor_id} = conversation_participant} <-
{:no_participant,
Conversations.get_conversation_participant(conversation_participant_id)},
{:valid_actor, true} <-
{:valid_actor,
actor_id == current_actor_id or
Actors.member?(current_actor_id, actor_id)},
{:ok, %ConversationParticipant{} = conversation_participant} <-
Conversations.update_conversation_participant(conversation_participant, %{
unread: !read
}) do
Absinthe.Subscription.publish(
Endpoint,
Conversations.count_unread_conversation_participants_for_person(actor_id),
person_unread_conversations_count: actor_id
)
{:ok, conversation_participant_to_view(conversation_participant)}
else
{:no_participant, _} ->
{:error, :not_found}
{:valid_actor, _} ->
{:error, :unauthorized}
end
end
def delete_conversation(_, _, _), do: :ok
defp conversation_participant_to_view(%Page{elements: elements} = page) do
%Page{page | elements: Enum.map(elements, &conversation_participant_to_view/1)}
end
defp conversation_participant_to_view(%ConversationParticipant{} = conversation_participant) do
value =
conversation_participant
|> Map.from_struct()
|> Map.merge(Map.from_struct(conversation_participant.conversation))
|> Map.delete(:conversation)
|> Map.put(
:participants,
Enum.map(
conversation_participant.conversation.participants,
&conversation_participant_to_actor/1
)
)
|> Map.put(:conversation_participant_id, conversation_participant.id)
struct(ConversationView, value)
end
defp conversation_to_view(
%Conversation{id: conversation_id} = conversation,
%Actor{id: actor_id} = actor,
unread \\ true
) do
value =
conversation
|> Map.from_struct()
|> Map.put(:actor, actor)
|> Map.put(:unread, unread)
|> Map.put(
:conversation_participant_id,
Conversations.get_participant_by_conversation_and_actor(conversation_id, actor_id).id
)
struct(ConversationView, value)
end
defp conversation_participant_to_actor(%Actor{} = actor), do: actor
defp conversation_participant_to_actor(%ConversationParticipant{} = conversation_participant),
do: conversation_participant.actor
@spec authorized_to_reply?(String.t() | nil, String.t() | nil, String.t()) :: boolean()
# Not a reply
defp authorized_to_reply?(conversation_id, _attributed_to_id, _current_actor_id)
when is_nil(conversation_id),
do: true
# We are authorized to reply if we are one of the participants, or if we a a member of a participant group
defp authorized_to_reply?(conversation_id, attributed_to_id, current_actor_id) do
case Conversations.get_conversation(conversation_id) do
nil ->
false
%Conversation{participants: participants} ->
participant_ids = Enum.map(participants, fn participant -> to_string(participant.id) end)
to_string(current_actor_id) in participant_ids or
Enum.any?(participant_ids, fn participant_id ->
Actors.member?(current_actor_id, participant_id) and
attributed_to_id == participant_id
end)
end
end
end

Some files were not shown because too many files have changed in this diff Show more