From 5dd24e1c9eb4c795f49921cb2e2546cf30384513 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 4 Oct 2021 17:38:37 +0200 Subject: [PATCH 1/4] Allow to change an user's password through the users.modify mix task Signed-off-by: Thomas Citharel --- lib/mix/tasks/mobilizon/users/modify.ex | 3 +++ lib/mobilizon/users/user.ex | 1 + test/tasks/users_test.exs | 15 +++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/lib/mix/tasks/mobilizon/users/modify.ex b/lib/mix/tasks/mobilizon/users/modify.ex index ae02ed56..cea9ad4e 100644 --- a/lib/mix/tasks/mobilizon/users/modify.ex +++ b/lib/mix/tasks/mobilizon/users/modify.ex @@ -16,6 +16,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do rest, strict: [ email: :string, + password: :string, disable: :boolean, enable: :boolean, user: :boolean, @@ -30,6 +31,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do disable? = Keyword.get(options, :disable, false) enable? = Keyword.get(options, :enable, false) new_email = Keyword.get(options, :email) + new_password = Keyword.get(options, :password) if disable? && enable? do shell_error("Can't use both --enable and --disable options at the same time.") @@ -41,6 +43,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do attrs <- %{}, role <- calculate_role(admin?, moderator?, user?), attrs <- process_new_value(attrs, :email, new_email, user.email), + attrs <- process_new_value(attrs, :password, new_password, nil), attrs <- process_new_value(attrs, :role, role, user.role), attrs <- if(disable? && !is_nil(user.confirmed_at), diff --git a/lib/mobilizon/users/user.ex b/lib/mobilizon/users/user.ex index 78427214..f30cd637 100644 --- a/lib/mobilizon/users/user.ex +++ b/lib/mobilizon/users/user.ex @@ -107,6 +107,7 @@ defmodule Mobilizon.Users.User do |> validate_required(@required_attrs) |> unique_constraint(:email, message: dgettext("errors", "This email is already used.")) |> Checker.validate_changeset() + |> hash_password() |> validate_length(:password, min: 6, max: 200, diff --git a/test/tasks/users_test.exs b/test/tasks/users_test.exs index bff6acce..775d4cec 100644 --- a/test/tasks/users_test.exs +++ b/test/tasks/users_test.exs @@ -310,5 +310,20 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do assert output_received == "An user has been modified with the following information:\n - email: #{@modified_email}\n - Role: #{user.role}\n - account status: Activated on #{confirmed_at} (UTC)\n" end + + @modified_password "new one" + + test "change an user's password" do + insert(:user, email: @email) + Modify.run([@email, "--password", @modified_password]) + + assert {:ok, %User{}} = + Mobilizon.Service.Auth.MobilizonAuthenticator.login(@email, @modified_password) + + Modify.run([@email, "--password", "changed again"]) + + assert {:error, :bad_password} = + Mobilizon.Service.Auth.MobilizonAuthenticator.login(@email, @modified_password) + end end end From 0c667b13ae0239fb05e0e7b162f1e9a888f61a4e Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 4 Oct 2021 18:59:41 +0200 Subject: [PATCH 2/4] Export participants to different formats * CSV * PDF (requires Python dependency `weasyprint`) * ODS (requires Python dependency `pyexcel_ods3`) Signed-off-by: Thomas Citharel --- .gitignore | 2 + .gitlab-ci.yml | 1 + .sobelow-skips | 3 +- config/config.exs | 6 + docker/tests/Dockerfile | 9 +- js/src/graphql/config.ts | 10 + js/src/graphql/event.ts | 10 + js/src/types/config.model.ts | 3 + js/src/views/Event/Participants.vue | 527 +++++++----- lib/federation/activity_pub/actions/accept.ex | 2 +- lib/graphql/api/participations.ex | 6 +- lib/graphql/resolvers/config.ex | 3 +- lib/graphql/resolvers/event/utils.ex | 4 +- lib/graphql/resolvers/participant.ex | 123 ++- lib/graphql/schema/config.ex | 11 + lib/graphql/schema/events/participant.ex | 21 + lib/mobilizon.ex | 1 + lib/mobilizon/config.ex | 8 + lib/mobilizon/events/events.ex | 29 +- lib/mobilizon/export.ex | 57 ++ lib/mobilizon/users/push_subscription.ex | 1 + lib/mobilizon/users/user.ex | 8 +- lib/mobilizon/users/users.ex | 6 +- lib/service/export/participants/common.ex | 120 +++ lib/service/export/participants/csv.ex | 100 +++ lib/service/export/participants/ods.ex | 106 +++ lib/service/export/participants/pdf.ex | 120 +++ lib/service/python_port.ex | 28 + lib/service/python_worker.ex | 65 ++ lib/service/workers/export_cleaner_worker.ex | 14 + lib/web/auth/guardian.ex | 10 +- lib/web/controllers/export_controller.ex | 35 + lib/web/endpoint.ex | 11 - lib/web/router.ex | 8 + .../export/event_participants.html.heex | 154 ++++ lib/web/views/export_view.ex | 17 + mix.exs | 2 + mix.lock | 5 + priv/gettext/activity.pot | 86 +- priv/gettext/ar/LC_MESSAGES/activity.po | 86 +- priv/gettext/ar/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/ar/LC_MESSAGES/errors.po | 67 +- priv/gettext/be/LC_MESSAGES/activity.po | 86 +- priv/gettext/be/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/be/LC_MESSAGES/errors.po | 67 +- priv/gettext/ca/LC_MESSAGES/activity.po | 86 +- priv/gettext/ca/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/ca/LC_MESSAGES/errors.po | 67 +- priv/gettext/cs/LC_MESSAGES/activity.po | 86 +- priv/gettext/cs/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/cs/LC_MESSAGES/errors.po | 67 +- priv/gettext/de/LC_MESSAGES/activity.po | 86 +- priv/gettext/de/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/de/LC_MESSAGES/errors.po | 67 +- priv/gettext/default.pot | 481 ++++++----- priv/gettext/en/LC_MESSAGES/activity.po | 86 +- priv/gettext/en/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/en/LC_MESSAGES/errors.po | 67 +- priv/gettext/errors.pot | 67 +- priv/gettext/es/LC_MESSAGES/activity.po | 86 +- priv/gettext/es/LC_MESSAGES/default.po | 691 ++++++++------- priv/gettext/es/LC_MESSAGES/errors.po | 361 ++++---- priv/gettext/fi/LC_MESSAGES/activity.po | 86 +- priv/gettext/fi/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/fi/LC_MESSAGES/errors.po | 67 +- priv/gettext/fr/LC_MESSAGES/activity.po | 86 +- priv/gettext/fr/LC_MESSAGES/default.po | 787 +++++++++++++----- priv/gettext/fr/LC_MESSAGES/errors.po | 259 ++---- priv/gettext/gl/LC_MESSAGES/activity.po | 86 +- priv/gettext/gl/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/gl/LC_MESSAGES/errors.po | 67 +- priv/gettext/hu/LC_MESSAGES/activity.po | 86 +- priv/gettext/hu/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/hu/LC_MESSAGES/errors.po | 67 +- priv/gettext/id/LC_MESSAGES/activity.po | 86 +- priv/gettext/id/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/id/LC_MESSAGES/errors.po | 67 +- priv/gettext/it/LC_MESSAGES/activity.po | 86 +- priv/gettext/it/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/it/LC_MESSAGES/errors.po | 67 +- priv/gettext/ja/LC_MESSAGES/activity.po | 86 +- priv/gettext/ja/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/ja/LC_MESSAGES/errors.po | 67 +- priv/gettext/nl/LC_MESSAGES/activity.po | 86 +- priv/gettext/nl/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/nl/LC_MESSAGES/errors.po | 67 +- priv/gettext/nn/LC_MESSAGES/activity.po | 86 +- priv/gettext/nn/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/nn/LC_MESSAGES/errors.po | 67 +- priv/gettext/oc/LC_MESSAGES/activity.po | 86 +- priv/gettext/oc/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/oc/LC_MESSAGES/errors.po | 67 +- priv/gettext/pl/LC_MESSAGES/activity.po | 86 +- priv/gettext/pl/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/pl/LC_MESSAGES/errors.po | 67 +- priv/gettext/pt/LC_MESSAGES/activity.po | 86 +- priv/gettext/pt/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/pt/LC_MESSAGES/errors.po | 67 +- priv/gettext/pt_BR/LC_MESSAGES/activity.po | 86 +- priv/gettext/pt_BR/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/pt_BR/LC_MESSAGES/errors.po | 67 +- priv/gettext/ru/LC_MESSAGES/activity.po | 86 +- priv/gettext/ru/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/ru/LC_MESSAGES/errors.po | 67 +- priv/gettext/sv/LC_MESSAGES/activity.po | 86 +- priv/gettext/sv/LC_MESSAGES/default.po | 487 ++++++----- priv/gettext/sv/LC_MESSAGES/errors.po | 67 +- priv/python/module.py | 8 + priv/python/ods.py | 12 + priv/python/pdf.py | 4 + .../20211001101902_create_exports.exs | 18 + schema.graphql | 98 +++ .../transmogrifier/follow_test.exs | 1 - .../activity_pub/transmogrifier/join_test.exs | 1 - test/service/export/icalendar_test.exs | 2 +- .../export/participants/common_test.exs | 28 + test/service/export/participants/csv_test.exs | 22 + test/service/export/participants/ods_test.exs | 62 ++ test/service/export/participants/pdf_test.exs | 62 ++ test/service/workers/export_cleaner_worker.ex | 14 + test/tasks/users_test.exs | 7 +- 121 files changed, 10817 insertions(+), 6872 deletions(-) create mode 100644 lib/mobilizon/export.ex create mode 100644 lib/service/export/participants/common.ex create mode 100644 lib/service/export/participants/csv.ex create mode 100644 lib/service/export/participants/ods.ex create mode 100644 lib/service/export/participants/pdf.ex create mode 100644 lib/service/python_port.ex create mode 100644 lib/service/python_worker.ex create mode 100644 lib/service/workers/export_cleaner_worker.ex create mode 100644 lib/web/controllers/export_controller.ex create mode 100644 lib/web/templates/export/event_participants.html.heex create mode 100644 lib/web/views/export_view.ex create mode 100644 priv/python/module.py create mode 100644 priv/python/ods.py create mode 100644 priv/python/pdf.py create mode 100644 priv/repo/migrations/20211001101902_create_exports.exs create mode 100644 test/service/export/participants/common_test.exs create mode 100644 test/service/export/participants/csv_test.exs create mode 100644 test/service/export/participants/ods_test.exs create mode 100644 test/service/export/participants/pdf_test.exs create mode 100644 test/service/workers/export_cleaner_worker.ex diff --git a/.gitignore b/.gitignore index 5b7ebafb..eb7d0c14 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ priv/data/* priv/errors/* !priv/errors/.gitkeep priv/cert/ +priv/python/__pycache__/ .vscode/ cover/ site/ @@ -37,6 +38,7 @@ test/uploads/ uploads/* release/ !uploads/.gitkeep +!uploads/exports/.gitkeep .idea *.mo *.po~ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 28f6f444..42ff2ce1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,6 +28,7 @@ variables: # Release elements PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${CI_PROJECT_NAME}" ARCH: "amd64" + EXPORT_FORMATS: "csv,ods,pdf" cache: key: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}" diff --git a/.sobelow-skips b/.sobelow-skips index 28a98366..0dda374a 100644 --- a/.sobelow-skips +++ b/.sobelow-skips @@ -8,4 +8,5 @@ 73B351E4CB3AF715AD450A085F5E6304 BBACD7F0BACD4A6D3010C26604671692 6D4D4A4821B93BCFAC9CDBB367B34C4B -5674F0D127852889ED0132DC2F442AAB \ No newline at end of file +5674F0D127852889ED0132DC2F442AAB +1600B7206E47F630D94AB54C360906F0 \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index 92d02872..0264d95c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -285,6 +285,7 @@ config :mobilizon, Oban, {"17 4 * * *", Mobilizon.Service.Workers.RefreshGroups, queue: :background}, {"@hourly", Mobilizon.Service.Workers.CleanOrphanMediaWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.CleanUnconfirmedUsersWorker, queue: :background}, + {"@hourly", Mobilizon.Service.Workers.ExportCleanerWorker, queue: :background}, {"@hourly", Mobilizon.Service.Workers.SendActivityRecapWorker, queue: :notifications}, {"@daily", Mobilizon.Service.Workers.CleanOldActivityWorker, queue: :background} ]}, @@ -320,6 +321,11 @@ config :mobilizon, Mobilizon.Service.Notifier.Email, enabled: true config :mobilizon, Mobilizon.Service.Notifier.Push, enabled: true +config :mobilizon, :exports, + formats: [ + Mobilizon.Service.Export.Participants.CSV + ] + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/docker/tests/Dockerfile b/docker/tests/Dockerfile index 3a6673b1..6a1d0975 100644 --- a/docker/tests/Dockerfile +++ b/docker/tests/Dockerfile @@ -1,10 +1,15 @@ FROM elixir:latest LABEL maintainer="Thomas Citharel " -ENV REFRESHED_AT=2021-06-07 -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 +ENV REFRESHED_AT=2021-10-04 +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 curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq RUN npm install -g yarn wait-on RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN mix local.hex --force && mix local.rebar --force +# Weasyprint 53 requires pango >= 1.44.0, which is not available in Stretch. +# TODO: Remove the version requirement when elixir:latest is based on Bullseye +# https://github.com/erlang/docker-erlang-otp/issues/362 +# https://github.com/Kozea/WeasyPrint/issues/1384 +RUN pip3 install -Iv weasyprint==52 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/ diff --git a/js/src/graphql/config.ts b/js/src/graphql/config.ts index 3089b6f8..99a54c7a 100644 --- a/js/src/graphql/config.ts +++ b/js/src/graphql/config.ts @@ -175,3 +175,13 @@ export const WEB_PUSH = gql` } } `; + +export const EVENT_PARTICIPANTS = gql` + query EventParticipants { + config { + exportFormats { + eventParticipants + } + } + } +`; diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index 4f1e8276..0e6ebcb7 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -574,3 +574,13 @@ export const CLOSE_EVENTS = gql` } } `; + +export const EXPORT_EVENT_PARTICIPATIONS = gql` + mutation ExportEventParticipants( + $eventId: ID! + $format: ExportFormatEnum + $roles: [ParticipantRoleEnum] + ) { + exportEventParticipants(eventId: $eventId, format: $format, roles: $roles) + } +`; diff --git a/js/src/types/config.model.ts b/js/src/types/config.model.ts index f135458f..cc4d65a1 100644 --- a/js/src/types/config.model.ts +++ b/js/src/types/config.model.ts @@ -102,4 +102,7 @@ export interface IConfig { enabled: boolean; publicKey: string; }; + exportFormats: { + eventParticipants: string[]; + }; } diff --git a/js/src/views/Event/Participants.vue b/js/src/views/Event/Participants.vue index 328a9dc7..90611309 100644 --- a/js/src/views/Event/Participants.vue +++ b/js/src/views/Event/Participants.vue @@ -1,235 +1,255 @@