diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c3776c8..575df42c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,45 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.0.0-beta.2 - 2021-11-15 + +This lists changes since 2.0.0-beta.1. +### Added + +- Group followers and members get an notification email by default when a group publishes a new event (subject to activity notification settings) +- Group admins can now approve or deny new memberships +- Added organizer actor name (profile or group) in the icalendar export +- Add initial support for federation with Gancio + +### Changed + +- Event update notification is send to participants ~30 minutes after the event update, so that successive edits are throttled. +- Event, post and comments titles and content now have expose their detected language in HTML, for improved screen reader experience + +### Fixed + +- Release front-end files are no longer in duplicate +- Only show datetime timezone toggle on event if the timezone offset is different from our own +- Fix error when determining audience for Discussion when deleting a comment +- Fix a couple of accessibility issues +- Limit to acceptable tags when pasting raw HTML into comment fields on front-end +- Fixed group map display +- Fixed updating group physical address +- Allow group members to access group drafts +- Improve group refreshment workflow +- Fixed date signature generation for federation +- Fixed an issue when duplicating a group event from another profile +- Fixed event metadata not saved on eventcreation +- Use a different pagination parameter for searched events and featured events on search page + +### Translations + +- Gaelic +- Spanish + ## 2.0.0-beta.1 - 2021-11-09 +Please read the [UPGRADE.md](https://framagit.org/framasoft/mobilizon/-/blob/main/UPGRADE.md#upgrading-from-13-to-20) file as well. ### Added - Added possibility to follow groups and be notified from new upcoming events @@ -40,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improve metadata on public page - Make sure some event action pages (participate remotely or without an account) don't get indexed by search engines - Only send `Tombstone` element in `Delete` activities, not the whole previous deleted element. +- Make sure `Delete` activity are send correctly to everyone - Only add address and tags to event icalendar export if they exist - `master` branch has been renamed to `main` diff --git a/UPGRADE.md b/UPGRADE.md index 58bb9087f..566f70c63 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,19 +1,69 @@ # Upgrading from 1.3 to 2.0 Requirements dependencies depend on the way Mobilizon is installed. -## New dependencies requirements - -### Release and Docker +## New Elixir version requirement +### Docker and Release install You are already using latest Elixir version in the release tarball and Docker images. ### Source install -* Elixir 1.12 and Erlang OTP 22 is now required. If your distribution doesn't provide these versions, you can uninstall them and install [Elixir](https://github.com/asdf-vm/asdf-elixir) through the [ASDF tool](https://asdf-vm.com/). +**Elixir 1.12 and Erlang OTP 22 are now required**. If your distribution doesn't provide these versions (which is likely), you must uninstall them and install [Elixir](https://github.com/asdf-vm/asdf-elixir) through the [ASDF tool](https://asdf-vm.com/). -## Optional dependencies +## Geographic timezone data -These are optional, installing them will allow Mobilizon to export to PDF and ODS as well. +Mobilizon 2.0 uses data based on [timezone-boundary-builder](https://github.com/evansiroky/timezone-boundary-builder) (which is based itself on OpenStreetMap data) to determine the timezone of an event automatically, based on it's geocoordinates. However, this needs ~700Mio of disk, so we don't redistribute data directly, depending on the case. It's possible to skip this part, but users will need to manually pick the timezone for every event they created when it has a different timezone from their own. + +### Docker install + +The geographic timezone data is already bundled into the image, you have nothing to do. +### Release install + +In order to keep the release tarballs light, the geographic timezone data is not bundled directly. You need to download the data : +* either raw from Github, but **requires an extra ~1Gio of memory** to process the data + + ```sh + sudo -u mobilizon mkdir /var/lib/mobilizon/timezones + sudo -u mobilizon ./bin/mobilizon_ctl tz_world.update + ``` + +* either already processed from our own distribution server + + ```sh + sudo -u mobilizon mkdir /var/lib/mobilizon/timezones + sudo -u mobilizon curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets + ``` + +In both cases, ~700Mio of disk will be used. You may use the following configuration to specify where the data is expected: +```elixir +config :tz_world, data_dir: "/some/place" +``` + +### Source install + +You need to download the data : +* either raw from Github, but **requires an extra ~1Gio of memory** to process the data + + ```sh + sudo -u mobilizon mkdir /var/lib/mobilizon/timezones + sudo -u mobilizon mix mobilizon.tz_world.update + ``` + +* either already processed from our own distribution server + + ```sh + sudo -u mobilizon mkdir /var/lib/mobilizon/timezones + sudo -u mobilizon curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets + ``` + +In both cases, ~700Mio of disk will be used. You may use the following configuration to specify where the data is expected: +```elixir +config :tz_world, data_dir: "/some/place" +``` + +## New optional dependencies + +These are optional, installing them will allow Mobilizon to export to PDF and ODS as well. Mobilizon 2.0 allows to export the participant list, but more is planned. ### Docker Everything is included in our Docker image. ### Release and source install @@ -23,7 +73,7 @@ New optional Python dependencies: * `weasyprint` for PDF export (with [a few extra dependencies](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html)) * `pyexcel-ods3` for ODS export (no extra dependencies) -Both can be installed through pip. +Both can be installed through pip. You need to enable exports for PDF and ODS in the configuration afterwards. Read [the dedicated docs page about this]() (*upcoming*). # Upgrading from 1.0 to 1.1 diff --git a/config/config.exs b/config/config.exs index 62ec966ec..17674049a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -90,6 +90,8 @@ config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, uploads: "/var/lib/mobil config :tz_world, data_dir: "/var/lib/mobilizon/timezones" +config :mobilizon, Timex.Gettext, default_locale: "en" + config :mobilizon, :media_proxy, enabled: true, proxy_opts: [ diff --git a/config/test.exs b/config/test.exs index 507e4cd0c..c6338b88e 100644 --- a/config/test.exs +++ b/config/test.exs @@ -62,15 +62,18 @@ config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, uploads: "test/uploads" config :tz_world, data_dir: "_build/test/lib/tz_world/priv" -config :exvcr, - vcr_cassette_library_dir: "test/fixtures/vcr_cassettes" - config :tesla, Mobilizon.Service.HTTP.ActivityPub, adapter: Mobilizon.Service.HTTP.ActivityPub.Mock +config :tesla, Mobilizon.Service.HTTP.WebfingerClient, + adapter: Mobilizon.Service.HTTP.WebfingerClient.Mock + config :tesla, Mobilizon.Service.HTTP.GeospatialClient, adapter: Mobilizon.Service.HTTP.GeospatialClient.Mock +config :tesla, Mobilizon.Service.HTTP.HostMetaClient, + adapter: Mobilizon.Service.HTTP.HostMetaClient.Mock + config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Mock config :mobilizon, Oban, queues: false, plugins: false diff --git a/js/package.json b/js/package.json index 8799976bf..aa07217b7 100644 --- a/js/package.json +++ b/js/package.json @@ -50,6 +50,7 @@ "p-debounce": "^4.0.0", "phoenix": "^1.6", "register-service-worker": "^1.7.2", + "sanitize-html": "^2.5.3", "tippy.js": "^6.2.3", "unfetch": "^4.2.0", "v-tooltip": "^2.1.3", @@ -74,6 +75,7 @@ "@types/prosemirror-model": "^1.7.2", "@types/prosemirror-state": "^1.2.4", "@types/prosemirror-view": "^1.11.4", + "@types/sanitize-html": "^2.5.0", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", "@vue/cli-plugin-babel": "~5.0.0-rc.0", diff --git a/js/src/components/Comment/Comment.vue b/js/src/components/Comment/Comment.vue index d450f99ed..569134f60 100644 --- a/js/src/components/Comment/Comment.vue +++ b/js/src/components/Comment/Comment.vue @@ -63,7 +63,12 @@
-
+
{{ $t("[This comment has been deleted]") }}

diff --git a/js/src/components/Editor.vue b/js/src/components/Editor.vue index ebdcf1556..6645de3ac 100644 --- a/js/src/components/Editor.vue +++ b/js/src/components/Editor.vue @@ -212,6 +212,7 @@ import Underline from "@tiptap/extension-underline"; import Link from "@tiptap/extension-link"; import CharacterCount from "@tiptap/extension-character-count"; import { AutoDir } from "./Editor/Autodir"; +import sanitizeHtml from "sanitize-html"; @Component({ components: { EditorContent, BubbleMenu }, @@ -265,6 +266,7 @@ export default class EditorComponent extends Vue { "aria-label": this.ariaLabel, role: "textbox", }, + transformPastedHTML: this.transformPastedHTML, }, extensions: [ StarterKit, @@ -292,6 +294,19 @@ export default class EditorComponent extends Vue { }); } + transformPastedHTML(html: string): string { + // When using comment mode, limit to acceptable tags + if (this.isCommentMode) { + return sanitizeHtml(html, { + allowedTags: ["b", "i", "em", "strong", "a"], + allowedAttributes: { + a: ["href", "rel", "target"], + }, + }); + } + return html; + } + @Watch("value") onValueChanged(val: string): void { if (!this.editor) return; diff --git a/js/src/components/Event/EventCard.vue b/js/src/components/Event/EventCard.vue index 3f8ba21f2..541b1c4a6 100644 --- a/js/src/components/Event/EventCard.vue +++ b/js/src/components/Event/EventCard.vue @@ -39,7 +39,12 @@ />

-

+

{{ event.title }}

diff --git a/js/src/components/Event/EventMinimalistCard.vue b/js/src/components/Event/EventMinimalistCard.vue index be0e47815..549c63db2 100644 --- a/js/src/components/Event/EventMinimalistCard.vue +++ b/js/src/components/Event/EventMinimalistCard.vue @@ -17,7 +17,7 @@
-

+

-

{{ participation.event.title }}

+

+ {{ participation.event.title }} +

-

{{ post.title }}

+

+ {{ post.title }} +

{{ diff --git a/js/src/graphql/actor.ts b/js/src/graphql/actor.ts index aa7f0d372..2b8771153 100644 --- a/js/src/graphql/actor.ts +++ b/js/src/graphql/actor.ts @@ -214,6 +214,9 @@ export const LOGGED_USER_DRAFTS = gql` } beginsOn visibility + attributedTo { + ...ActorFragment + } organizerActor { ...ActorFragment } diff --git a/js/src/graphql/comment.ts b/js/src/graphql/comment.ts index 350a9cf38..8fa835b1d 100644 --- a/js/src/graphql/comment.ts +++ b/js/src/graphql/comment.ts @@ -18,6 +18,7 @@ export const COMMENT_FIELDS_FRAGMENT = gql` updatedAt deletedAt isAnnouncement + language } ${ACTOR_FRAGMENT} `; diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index c45fd44d6..cafcbdcb4 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -22,6 +22,7 @@ const FULL_EVENT_FRAGMENT = gql` visibility joinOptions draft + language picture { id url @@ -60,6 +61,7 @@ const FULL_EVENT_FRAGMENT = gql` uuid title beginsOn + language picture { id url @@ -153,6 +155,7 @@ export const FETCH_EVENTS = gql` status visibility insertedAt + language picture { id url @@ -203,6 +206,7 @@ export const CREATE_EVENT = gql` $physicalAddress: AddressInput $options: EventOptionsInput $contacts: [Contact] + $metadata: EventMetadataInput ) { createEvent( organizerActorId: $organizerActorId @@ -223,6 +227,7 @@ export const CREATE_EVENT = gql` physicalAddress: $physicalAddress options: $options contacts: $contacts + metadata: $metadata ) { ...FullEvent } diff --git a/js/src/graphql/group.ts b/js/src/graphql/group.ts index 6ff9ac71e..fbc085d15 100644 --- a/js/src/graphql/group.ts +++ b/js/src/graphql/group.ts @@ -105,6 +105,7 @@ export const GROUP_FIELDS_FRAGMENTS = gql` title beginsOn draft + language options { maximumAttendeeCapacity } diff --git a/js/src/graphql/home.ts b/js/src/graphql/home.ts index ae2b6cfc0..9fda50247 100644 --- a/js/src/graphql/home.ts +++ b/js/src/graphql/home.ts @@ -37,6 +37,7 @@ export const HOME_USER_QUERIES = gql` } beginsOn visibility + language organizerActor { ...ActorFragment } @@ -79,6 +80,7 @@ export const HOME_USER_QUERIES = gql` picture { url } + language attributedTo { ...ActorFragment } @@ -129,6 +131,7 @@ export const CLOSE_CONTENT = gql` id url } + language tags { ...TagFragment } diff --git a/js/src/graphql/post.ts b/js/src/graphql/post.ts index b7ccc7bd4..0b125ccff 100644 --- a/js/src/graphql/post.ts +++ b/js/src/graphql/post.ts @@ -21,6 +21,7 @@ export const POST_FRAGMENT = gql` publishAt draft visibility + language tags { ...TagFragment } @@ -56,6 +57,7 @@ export const POST_BASIC_FIELDS = gql` publishAt draft visibility + language picture { id url diff --git a/js/src/types/event.model.ts b/js/src/types/event.model.ts index 35899be18..e091af117 100644 --- a/js/src/types/event.model.ts +++ b/js/src/types/event.model.ts @@ -90,6 +90,7 @@ export interface IEvent { options: IEventOptions; metadata: IEventMetadata[]; contacts: IActor[]; + language: string; toEditJSON(): IEventEditJSON; } @@ -134,6 +135,8 @@ export class EventModel implements IEvent { publishAt = new Date(); + language = "und"; + participantStats = { notApproved: 0, notConfirmed: 0, @@ -210,6 +213,7 @@ export class EventModel implements IEvent { this.tags = hash.tags; this.metadata = hash.metadata; + this.language = hash.language; if (hash.options) this.options = hash.options; } diff --git a/js/src/views/Event/Edit.vue b/js/src/views/Event/Edit.vue index ff23eb98f..6d1b4dd0b 100644 --- a/js/src/views/Event/Edit.vue +++ b/js/src/views/Event/Edit.vue @@ -669,7 +669,11 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10; }; }, update(data) { - return new EventModel(data.event); + let event = data.event; + if (this.isDuplicate) { + event = { ...event, organizerActor: this.currentActor }; + } + return new EventModel(event); }, skip() { return !this.eventId; diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index c4c51fcce..99a77a834 100755 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -10,7 +10,12 @@

-

+

{{ event.title }}

@@ -289,6 +294,7 @@

diff --git a/js/src/views/Posts/Post.vue b/js/src/views/Posts/Post.vue index c68f07d95..efc0f300c 100644 --- a/js/src/views/Posts/Post.vue +++ b/js/src/views/Posts/Post.vue @@ -15,7 +15,7 @@ v-if="post.draft" >{{ $t("Draft") }} -

{{ post.title }}

+

{{ post.title }}

+

{{ $t("Featured events") }}

- +