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