diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f3867a2..f4f3bddb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute. - Admin API: An endpoint to manage frontends. - Streaming API: Add follow relationships updates. -- WebPush: Introduce `pleroma:chat_mention` and `pleroma:emoji_reaction` notification types +- WebPush: Introduce `pleroma:chat_mention` and `pleroma:emoji_reaction` notification types. +- Mastodon API: Add monthly active users to `/api/v1/instance` (`pleroma.stats.mau`). +- Mastodon API: Home, public, hashtag & list timelines accept `only_media`, `remote` & `local` parameters for filtration. ### Fixed @@ -58,6 +60,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Creating incorrect IPv4 address-style HTTP links when encountering certain numbers. - Reblog API Endpoint: Do not set visibility parameter to public by default and let CommonAPI to infer it from status, so a user can reblog their private status without explicitly setting reblog visibility to private. - Tag URLs in statuses are now absolute +- Removed duplicate jobs to purge expired activities
API Changes diff --git a/config/description.exs b/config/description.exs index fac5a006e..f84b52a4f 100644 --- a/config/description.exs +++ b/config/description.exs @@ -3224,6 +3224,12 @@ type: :string, description: "S3 host", suggestions: ["s3.eu-central-1.amazonaws.com"] + }, + %{ + key: :region, + type: :string, + description: "S3 region (for AWS)", + suggestions: ["us-east-1"] } ] }, diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md index 84430408b..c83be2faa 100644 --- a/docs/development/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -16,6 +16,12 @@ Adding the parameter `reply_visibility` to the public and home timelines queries Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance). +Home, public, hashtag & list timelines accept these parameters: + +- `only_media`: show only statuses with media attached +- `local`: show only local statuses +- `remote`: show only remote statuses + ## Statuses - `visibility`: has additional possible values `list` and `local` (for local-only statuses) @@ -54,6 +60,16 @@ The `id` parameter can also be the `nickname` of the user. This only works in th - `/api/v1/accounts/:id` - `/api/v1/accounts/:id/statuses` +`/api/v1/accounts/:id/statuses` endpoint accepts these parameters: + +- `pinned`: include only pinned statuses +- `tagged`: with tag +- `only_media`: include only statuses with media attached +- `with_muted`: include statuses/reactions from muted accounts +- `exclude_reblogs`: exclude reblogs +- `exclude_replies`: exclude replies +- `exclude_visibilities`: exclude visibilities + Has these additional fields under the `pleroma` object: - `ap_id`: nullable URL string, ActivityPub id of the user diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e422b59f1..06cdb42af 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -146,6 +146,7 @@ defmodule Pleroma.User do field(:inbox, :string) field(:shared_inbox, :string) field(:accepts_chat_messages, :boolean, default: nil) + field(:last_active_at, :naive_datetime) embeds_one( :notification_settings, @@ -2444,4 +2445,19 @@ def sanitize_html(%User{} = user, filter) do def get_host(%User{ap_id: ap_id} = _user) do URI.parse(ap_id).host end + + def update_last_active_at(%__MODULE__{local: true} = user) do + user + |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at]) + |> update_and_set_cache() + end + + def active_user_count(weeks \\ 4) do + active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks) + + __MODULE__ + |> where([u], u.last_active_at >= ^active_after) + |> where([u], u.local == true) + |> Repo.aggregate(:count) + end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d0bb07aab..98051032a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -735,6 +735,12 @@ defp restrict_local(query, %{local_only: true}) do defp restrict_local(query, _), do: query + defp restrict_remote(query, %{remote: true}) do + from(activity in query, where: activity.local == false) + end + + defp restrict_remote(query, _), do: query + defp restrict_actor(query, %{actor_id: actor_id}) do from(activity in query, where: activity.actor == ^actor_id) end @@ -1111,6 +1117,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do |> restrict_tag_all(opts) |> restrict_since(opts) |> restrict_local(opts) + |> restrict_remote(opts) |> restrict_actor(opts) |> restrict_type(opts) |> restrict_state(opts) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 80acee2f7..a301ce090 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -130,7 +130,7 @@ def statuses_operation do :with_muted, :query, BooleanLike, - "Include statuses from muted acccounts." + "Include statuses from muted accounts." ), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"), @@ -144,7 +144,7 @@ def statuses_operation do :with_muted, :query, BooleanLike, - "Include reactions from muted acccounts." + "Include reactions from muted accounts." ) ] ++ pagination_params(), responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index e1ebdab38..01396642c 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -25,6 +25,8 @@ def home_operation do security: [%{"oAuth" => ["read:statuses"]}], parameters: [ local_param(), + remote_param(), + only_media_param(), with_muted_param(), exclude_visibilities_param(), reply_visibility_param() | pagination_params() @@ -61,6 +63,7 @@ def public_operation do local_param(), instance_param(), only_media_param(), + remote_param(), with_muted_param(), exclude_visibilities_param(), reply_visibility_param() | pagination_params() @@ -107,6 +110,7 @@ def hashtag_operation do ), local_param(), only_media_param(), + remote_param(), with_muted_param(), exclude_visibilities_param() | pagination_params() ], @@ -132,6 +136,9 @@ def list_operation do required: true ), with_muted_param(), + local_param(), + remote_param(), + only_media_param(), exclude_visibilities_param() | pagination_params() ], operationId: "TimelineController.list", @@ -198,4 +205,13 @@ defp only_media_param do "Show only statuses with media attached?" ) end + + defp remote_param do + Operation.parameter( + :remote, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Show only remote statuses?" + ) + end end diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 94703cd05..8e274de88 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -23,6 +23,18 @@ defmodule Pleroma.Web.Endpoint do # InstanceStatic needs to be before Plug.Static to be able to override shipped-static files # If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well # Cache-control headers are duplicated in case we turn off etags in the future + plug( + Pleroma.Web.Plugs.InstanceStatic, + at: "/", + from: :pleroma, + only: ["emoji", "images"], + gzip: true, + cache_control_for_etags: "public, max-age=1209600", + headers: %{ + "cache-control" => "public, max-age=1209600" + } + ) + plug(Pleroma.Web.Plugs.InstanceStatic, at: "/", gzip: true, diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 08e6f23b9..cef299aa4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -51,6 +51,8 @@ def home(%{assigns: %{user: user}} = conn, params) do |> Map.put(:reply_filtering_user, user) |> Map.put(:announce_filtering_user, user) |> Map.put(:user, user) + |> Map.put(:local_only, params[:local]) + |> Map.delete(:local) activities = [user.ap_id | User.following(user)] @@ -190,6 +192,7 @@ def list(%{assigns: %{user: user}} = conn, %{list_id: id} = params) do |> Map.put(:blocking_user, user) |> Map.put(:user, user) |> Map.put(:muting_user, user) + |> Map.put(:local_only, params[:local]) # we must filter the following list for the user to avoid leaking statuses the user # does not actually have permission to see (for more info, peruse security issue #270). diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 1edbdbe11..73205fb6d 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -45,6 +45,7 @@ def render("show.json", _) do fields_limits: fields_limits(), post_formats: Config.get([:instance, :allowed_post_formats]) }, + stats: %{mau: Pleroma.User.active_user_count()}, vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) } } diff --git a/lib/pleroma/web/plugs/user_tracking_plug.ex b/lib/pleroma/web/plugs/user_tracking_plug.ex new file mode 100644 index 000000000..c9a988f00 --- /dev/null +++ b/lib/pleroma/web/plugs/user_tracking_plug.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserTrackingPlug do + alias Pleroma.User + + import Plug.Conn, only: [assign: 3] + + @update_interval :timer.hours(24) + + def init(opts), do: opts + + def call(%{assigns: %{user: %User{id: id} = user}} = conn, _) when not is_nil(id) do + with true <- needs_update?(user), + {:ok, user} <- User.update_last_active_at(user) do + assign(conn, :user, user) + else + _ -> conn + end + end + + def call(conn, _), do: conn + + defp needs_update?(%User{last_active_at: nil}), do: true + + defp needs_update?(%User{last_active_at: last_active_at}) do + NaiveDateTime.diff(NaiveDateTime.utc_now(), last_active_at, :millisecond) >= @update_interval + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index a9e332fa1..7521f5dc3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -56,6 +56,7 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.UserEnabledPlug) plug(Pleroma.Web.Plugs.SetUserSessionIdPlug) plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug) + plug(Pleroma.Web.Plugs.UserTrackingPlug) end pipeline :base_api do diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index a2373ebb9..f5090dae7 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -17,12 +17,14 @@ def perform(%Job{ "object" => %{"data" => %{"attachment" => [_ | _] = attachments, "actor" => actor}} } }) do - attachments - |> Enum.flat_map(fn item -> Enum.map(item["url"], & &1["href"]) end) - |> fetch_objects - |> prepare_objects(actor, Enum.map(attachments, & &1["name"])) - |> filter_objects - |> do_clean + if Pleroma.Config.get([:instance, :cleanup_attachments], false) do + attachments + |> Enum.flat_map(fn item -> Enum.map(item["url"], & &1["href"]) end) + |> fetch_objects + |> prepare_objects(actor, Enum.map(attachments, & &1["name"])) + |> filter_objects + |> do_clean + end {:ok, :success} end diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index 01256831b..027171c1e 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do Worker which purges expired activity. """ - use Oban.Worker, queue: :activity_expiration, max_attempts: 1 + use Oban.Worker, queue: :activity_expiration, max_attempts: 1, unique: [period: :infinity] import Ecto.Query diff --git a/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs b/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs new file mode 100644 index 000000000..9671e495b --- /dev/null +++ b/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddLastActiveAtToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:last_active_at, :naive_datetime) + end + + create_if_not_exists(index(:users, [:last_active_at])) + end +end diff --git a/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs b/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs new file mode 100644 index 000000000..309009205 --- /dev/null +++ b/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs @@ -0,0 +1,29 @@ +defmodule Pleroma.Repo.Migrations.RemoveDuplicatesFromActivityExpirationQueue do + use Ecto.Migration + + import Ecto.Query, only: [from: 2] + + def up do + duplicate_ids = + from(j in Oban.Job, + where: j.queue == "activity_expiration", + where: j.worker == "Pleroma.Workers.PurgeExpiredActivity", + where: j.state == "scheduled", + select: + {fragment("(?)->>'activity_id'", j.args), fragment("array_agg(?)", j.id), count(j.id)}, + group_by: fragment("(?)->>'activity_id'", j.args), + having: count(j.id) > 1 + ) + |> Pleroma.Repo.all() + |> Enum.map(fn {_, ids, _} -> + max_id = Enum.max(ids) + List.delete(ids, max_id) + end) + |> List.flatten() + + from(j in Oban.Job, where: j.id in ^duplicate_ids) + |> Pleroma.Repo.delete_all() + end + + def down, do: :noop +end diff --git a/priv/static/index.html b/priv/static/index.html index c4dcf5d37..79d67c4c2 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/priv/static/static/js/10.a11a612e4c1ef51ded17.js b/priv/static/static/js/10.8702741bef65422a8655.js similarity index 99% rename from priv/static/static/js/10.a11a612e4c1ef51ded17.js rename to priv/static/static/js/10.8702741bef65422a8655.js index 2a1ffcc2b..0a0795bcd 100644 Binary files a/priv/static/static/js/10.a11a612e4c1ef51ded17.js and b/priv/static/static/js/10.8702741bef65422a8655.js differ diff --git a/priv/static/static/js/12.c6df5166dc6cdcf749e5.js.map b/priv/static/static/js/10.8702741bef65422a8655.js.map similarity index 56% rename from priv/static/static/js/12.c6df5166dc6cdcf749e5.js.map rename to priv/static/static/js/10.8702741bef65422a8655.js.map index c0bac6f0f..cb936cec1 100644 Binary files a/priv/static/static/js/12.c6df5166dc6cdcf749e5.js.map and b/priv/static/static/js/10.8702741bef65422a8655.js.map differ diff --git a/priv/static/static/js/11.22872a1f83121e70a148.js b/priv/static/static/js/11.a3e462fd9190986433f8.js similarity index 99% rename from priv/static/static/js/11.22872a1f83121e70a148.js rename to priv/static/static/js/11.a3e462fd9190986433f8.js index a2e9cee51..6b49bb02d 100644 Binary files a/priv/static/static/js/11.22872a1f83121e70a148.js and b/priv/static/static/js/11.a3e462fd9190986433f8.js differ diff --git a/priv/static/static/js/10.a11a612e4c1ef51ded17.js.map b/priv/static/static/js/11.a3e462fd9190986433f8.js.map similarity index 56% rename from priv/static/static/js/10.a11a612e4c1ef51ded17.js.map rename to priv/static/static/js/11.a3e462fd9190986433f8.js.map index fd81b28be..496d6a6f1 100644 Binary files a/priv/static/static/js/10.a11a612e4c1ef51ded17.js.map and b/priv/static/static/js/11.a3e462fd9190986433f8.js.map differ diff --git a/priv/static/static/js/12.c6df5166dc6cdcf749e5.js b/priv/static/static/js/12.7d5889019e7177d19bc2.js similarity index 99% rename from priv/static/static/js/12.c6df5166dc6cdcf749e5.js rename to priv/static/static/js/12.7d5889019e7177d19bc2.js index 441071f37..6dc87809f 100644 Binary files a/priv/static/static/js/12.c6df5166dc6cdcf749e5.js and b/priv/static/static/js/12.7d5889019e7177d19bc2.js differ diff --git a/priv/static/static/js/13.77214c18c6d2a9865281.js.map b/priv/static/static/js/12.7d5889019e7177d19bc2.js.map similarity index 56% rename from priv/static/static/js/13.77214c18c6d2a9865281.js.map rename to priv/static/static/js/12.7d5889019e7177d19bc2.js.map index 3d7abf273..cf9631696 100644 Binary files a/priv/static/static/js/13.77214c18c6d2a9865281.js.map and b/priv/static/static/js/12.7d5889019e7177d19bc2.js.map differ diff --git a/priv/static/static/js/13.77214c18c6d2a9865281.js b/priv/static/static/js/13.bb129366e7d54b5678d4.js similarity index 99% rename from priv/static/static/js/13.77214c18c6d2a9865281.js rename to priv/static/static/js/13.bb129366e7d54b5678d4.js index 08e356de2..8c73a0022 100644 Binary files a/priv/static/static/js/13.77214c18c6d2a9865281.js and b/priv/static/static/js/13.bb129366e7d54b5678d4.js differ diff --git a/priv/static/static/js/11.22872a1f83121e70a148.js.map b/priv/static/static/js/13.bb129366e7d54b5678d4.js.map similarity index 56% rename from priv/static/static/js/11.22872a1f83121e70a148.js.map rename to priv/static/static/js/13.bb129366e7d54b5678d4.js.map index 6467c58a5..b5a0af8a3 100644 Binary files a/priv/static/static/js/11.22872a1f83121e70a148.js.map and b/priv/static/static/js/13.bb129366e7d54b5678d4.js.map differ diff --git a/priv/static/static/js/14.e560f5e2f902b9ad2d0d.js b/priv/static/static/js/14.3546063198fc4cb3852c.js similarity index 99% rename from priv/static/static/js/14.e560f5e2f902b9ad2d0d.js rename to priv/static/static/js/14.3546063198fc4cb3852c.js index d2d291725..8897a50ce 100644 Binary files a/priv/static/static/js/14.e560f5e2f902b9ad2d0d.js and b/priv/static/static/js/14.3546063198fc4cb3852c.js differ diff --git a/priv/static/static/js/14.3546063198fc4cb3852c.js.map b/priv/static/static/js/14.3546063198fc4cb3852c.js.map new file mode 100644 index 000000000..e7596b961 Binary files /dev/null and b/priv/static/static/js/14.3546063198fc4cb3852c.js.map differ diff --git a/priv/static/static/js/14.e560f5e2f902b9ad2d0d.js.map b/priv/static/static/js/14.e560f5e2f902b9ad2d0d.js.map deleted file mode 100644 index f9797f1b6..000000000 Binary files a/priv/static/static/js/14.e560f5e2f902b9ad2d0d.js.map and /dev/null differ diff --git a/priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js.map b/priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js.map deleted file mode 100644 index 00cab138d..000000000 Binary files a/priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js.map and /dev/null differ diff --git a/priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js b/priv/static/static/js/15.e0cc6ce336f523c26f4d.js similarity index 98% rename from priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js rename to priv/static/static/js/15.e0cc6ce336f523c26f4d.js index 82318f797..ec162d862 100644 Binary files a/priv/static/static/js/15.2893c12f1ca2bcdc3cbf.js and b/priv/static/static/js/15.e0cc6ce336f523c26f4d.js differ diff --git a/priv/static/static/js/15.e0cc6ce336f523c26f4d.js.map b/priv/static/static/js/15.e0cc6ce336f523c26f4d.js.map new file mode 100644 index 000000000..d6ec98aaf Binary files /dev/null and b/priv/static/static/js/15.e0cc6ce336f523c26f4d.js.map differ diff --git a/priv/static/static/js/16.be7f4b788716bec25023.js b/priv/static/static/js/16.67b2bcf7dd3271e31643.js similarity index 99% rename from priv/static/static/js/16.be7f4b788716bec25023.js rename to priv/static/static/js/16.67b2bcf7dd3271e31643.js index ea5b554f1..b4f1fcb57 100644 Binary files a/priv/static/static/js/16.be7f4b788716bec25023.js and b/priv/static/static/js/16.67b2bcf7dd3271e31643.js differ diff --git a/priv/static/static/js/16.67b2bcf7dd3271e31643.js.map b/priv/static/static/js/16.67b2bcf7dd3271e31643.js.map new file mode 100644 index 000000000..31f00875c Binary files /dev/null and b/priv/static/static/js/16.67b2bcf7dd3271e31643.js.map differ diff --git a/priv/static/static/js/16.be7f4b788716bec25023.js.map b/priv/static/static/js/16.be7f4b788716bec25023.js.map deleted file mode 100644 index 121a49be1..000000000 Binary files a/priv/static/static/js/16.be7f4b788716bec25023.js.map and /dev/null differ diff --git a/priv/static/static/js/17.4ddba89b4f8c284f6392.js.map b/priv/static/static/js/17.4ddba89b4f8c284f6392.js.map deleted file mode 100644 index 322db8c6b..000000000 Binary files a/priv/static/static/js/17.4ddba89b4f8c284f6392.js.map and /dev/null differ diff --git a/priv/static/static/js/17.4ddba89b4f8c284f6392.js b/priv/static/static/js/17.a8395e49508cd81ecdf4.js similarity index 94% rename from priv/static/static/js/17.4ddba89b4f8c284f6392.js rename to priv/static/static/js/17.a8395e49508cd81ecdf4.js index 39283f245..0b90485ff 100644 Binary files a/priv/static/static/js/17.4ddba89b4f8c284f6392.js and b/priv/static/static/js/17.a8395e49508cd81ecdf4.js differ diff --git a/priv/static/static/js/17.a8395e49508cd81ecdf4.js.map b/priv/static/static/js/17.a8395e49508cd81ecdf4.js.map new file mode 100644 index 000000000..33b1c8e34 Binary files /dev/null and b/priv/static/static/js/17.a8395e49508cd81ecdf4.js.map differ diff --git a/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js b/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js new file mode 100644 index 000000000..621616a3f Binary files /dev/null and b/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js differ diff --git a/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js.map b/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js.map new file mode 100644 index 000000000..46f4d2a0c Binary files /dev/null and b/priv/static/static/js/18.1b9a9aedd06803dbb3e4.js.map differ diff --git a/priv/static/static/js/18.990b88b57bf3a6809098.js b/priv/static/static/js/18.990b88b57bf3a6809098.js deleted file mode 100644 index 96de50c61..000000000 Binary files a/priv/static/static/js/18.990b88b57bf3a6809098.js and /dev/null differ diff --git a/priv/static/static/js/18.990b88b57bf3a6809098.js.map b/priv/static/static/js/18.990b88b57bf3a6809098.js.map deleted file mode 100644 index b0fb3b629..000000000 Binary files a/priv/static/static/js/18.990b88b57bf3a6809098.js.map and /dev/null differ diff --git a/priv/static/static/js/19.783715f17e3f98e8898e.js.map b/priv/static/static/js/19.783715f17e3f98e8898e.js.map deleted file mode 100644 index d3bd148d5..000000000 Binary files a/priv/static/static/js/19.783715f17e3f98e8898e.js.map and /dev/null differ diff --git a/priv/static/static/js/19.783715f17e3f98e8898e.js b/priv/static/static/js/19.af8826ed7cd146d80620.js similarity index 99% rename from priv/static/static/js/19.783715f17e3f98e8898e.js rename to priv/static/static/js/19.af8826ed7cd146d80620.js index bf4fd22fd..d941e222e 100644 Binary files a/priv/static/static/js/19.783715f17e3f98e8898e.js and b/priv/static/static/js/19.af8826ed7cd146d80620.js differ diff --git a/priv/static/static/js/19.af8826ed7cd146d80620.js.map b/priv/static/static/js/19.af8826ed7cd146d80620.js.map new file mode 100644 index 000000000..886699ead Binary files /dev/null and b/priv/static/static/js/19.af8826ed7cd146d80620.js.map differ diff --git a/priv/static/static/js/2.88fa7ac80b2020ac2b46.js b/priv/static/static/js/2.88fa7ac80b2020ac2b46.js deleted file mode 100644 index b2c2eeb25..000000000 Binary files a/priv/static/static/js/2.88fa7ac80b2020ac2b46.js and /dev/null differ diff --git a/priv/static/static/js/2.88fa7ac80b2020ac2b46.js.map b/priv/static/static/js/2.88fa7ac80b2020ac2b46.js.map deleted file mode 100644 index f6aafd426..000000000 Binary files a/priv/static/static/js/2.88fa7ac80b2020ac2b46.js.map and /dev/null differ diff --git a/priv/static/static/js/2.cac6da00a889ad330fef.js b/priv/static/static/js/2.cac6da00a889ad330fef.js new file mode 100644 index 000000000..0e34c12d2 Binary files /dev/null and b/priv/static/static/js/2.cac6da00a889ad330fef.js differ diff --git a/priv/static/static/js/2.cac6da00a889ad330fef.js.map b/priv/static/static/js/2.cac6da00a889ad330fef.js.map new file mode 100644 index 000000000..05f611b86 Binary files /dev/null and b/priv/static/static/js/2.cac6da00a889ad330fef.js.map differ diff --git a/priv/static/static/js/20.96c40f6c9db8c08633bd.js.map b/priv/static/static/js/20.96c40f6c9db8c08633bd.js.map deleted file mode 100644 index d7d40ed07..000000000 Binary files a/priv/static/static/js/20.96c40f6c9db8c08633bd.js.map and /dev/null differ diff --git a/priv/static/static/js/20.96c40f6c9db8c08633bd.js b/priv/static/static/js/20.c45b976fb08603acced8.js similarity index 99% rename from priv/static/static/js/20.96c40f6c9db8c08633bd.js rename to priv/static/static/js/20.c45b976fb08603acced8.js index a3b3d7894..6012aebb1 100644 Binary files a/priv/static/static/js/20.96c40f6c9db8c08633bd.js and b/priv/static/static/js/20.c45b976fb08603acced8.js differ diff --git a/priv/static/static/js/20.c45b976fb08603acced8.js.map b/priv/static/static/js/20.c45b976fb08603acced8.js.map new file mode 100644 index 000000000..c0cc39285 Binary files /dev/null and b/priv/static/static/js/20.c45b976fb08603acced8.js.map differ diff --git a/priv/static/static/js/21.5a9f8e39a7833c1aa117.js b/priv/static/static/js/21.11c34dd4260444732ab0.js similarity index 99% rename from priv/static/static/js/21.5a9f8e39a7833c1aa117.js rename to priv/static/static/js/21.11c34dd4260444732ab0.js index 4114db7db..b5b0d7403 100644 Binary files a/priv/static/static/js/21.5a9f8e39a7833c1aa117.js and b/priv/static/static/js/21.11c34dd4260444732ab0.js differ diff --git a/priv/static/static/js/21.11c34dd4260444732ab0.js.map b/priv/static/static/js/21.11c34dd4260444732ab0.js.map new file mode 100644 index 000000000..11b0f1cdb Binary files /dev/null and b/priv/static/static/js/21.11c34dd4260444732ab0.js.map differ diff --git a/priv/static/static/js/21.5a9f8e39a7833c1aa117.js.map b/priv/static/static/js/21.5a9f8e39a7833c1aa117.js.map deleted file mode 100644 index 898948286..000000000 Binary files a/priv/static/static/js/21.5a9f8e39a7833c1aa117.js.map and /dev/null differ diff --git a/priv/static/static/js/22.d65671b9e5e00a0eb625.js b/priv/static/static/js/22.6155d82624c0297d5694.js similarity index 99% rename from priv/static/static/js/22.d65671b9e5e00a0eb625.js rename to priv/static/static/js/22.6155d82624c0297d5694.js index 3748a53b2..7054f1a7c 100644 Binary files a/priv/static/static/js/22.d65671b9e5e00a0eb625.js and b/priv/static/static/js/22.6155d82624c0297d5694.js differ diff --git a/priv/static/static/js/22.6155d82624c0297d5694.js.map b/priv/static/static/js/22.6155d82624c0297d5694.js.map new file mode 100644 index 000000000..721b74faf Binary files /dev/null and b/priv/static/static/js/22.6155d82624c0297d5694.js.map differ diff --git a/priv/static/static/js/22.d65671b9e5e00a0eb625.js.map b/priv/static/static/js/22.d65671b9e5e00a0eb625.js.map deleted file mode 100644 index 110cadd41..000000000 Binary files a/priv/static/static/js/22.d65671b9e5e00a0eb625.js.map and /dev/null differ diff --git a/priv/static/static/js/23.bf697d60801d277815e0.js.map b/priv/static/static/js/23.bf697d60801d277815e0.js.map deleted file mode 100644 index 20c74e93b..000000000 Binary files a/priv/static/static/js/23.bf697d60801d277815e0.js.map and /dev/null differ diff --git a/priv/static/static/js/23.bf697d60801d277815e0.js b/priv/static/static/js/23.d89535c0e277447a45a7.js similarity index 99% rename from priv/static/static/js/23.bf697d60801d277815e0.js rename to priv/static/static/js/23.d89535c0e277447a45a7.js index e61cf01d7..8979bc0fe 100644 Binary files a/priv/static/static/js/23.bf697d60801d277815e0.js and b/priv/static/static/js/23.d89535c0e277447a45a7.js differ diff --git a/priv/static/static/js/23.d89535c0e277447a45a7.js.map b/priv/static/static/js/23.d89535c0e277447a45a7.js.map new file mode 100644 index 000000000..336c6ecd4 Binary files /dev/null and b/priv/static/static/js/23.d89535c0e277447a45a7.js.map differ diff --git a/priv/static/static/js/24.914e51bfcfc620a93c0e.js b/priv/static/static/js/24.4693bde7d2a49831dbe2.js similarity index 99% rename from priv/static/static/js/24.914e51bfcfc620a93c0e.js rename to priv/static/static/js/24.4693bde7d2a49831dbe2.js index abdad101e..7faf73baa 100644 Binary files a/priv/static/static/js/24.914e51bfcfc620a93c0e.js and b/priv/static/static/js/24.4693bde7d2a49831dbe2.js differ diff --git a/priv/static/static/js/24.4693bde7d2a49831dbe2.js.map b/priv/static/static/js/24.4693bde7d2a49831dbe2.js.map new file mode 100644 index 000000000..1b2573a33 Binary files /dev/null and b/priv/static/static/js/24.4693bde7d2a49831dbe2.js.map differ diff --git a/priv/static/static/js/24.914e51bfcfc620a93c0e.js.map b/priv/static/static/js/24.914e51bfcfc620a93c0e.js.map deleted file mode 100644 index 1ddfced9a..000000000 Binary files a/priv/static/static/js/24.914e51bfcfc620a93c0e.js.map and /dev/null differ diff --git a/priv/static/static/js/25.fa8acda1a0ba7de2ab58.js b/priv/static/static/js/25.8f7cea2eb70da626b21d.js similarity index 99% rename from priv/static/static/js/25.fa8acda1a0ba7de2ab58.js rename to priv/static/static/js/25.8f7cea2eb70da626b21d.js index 719148fcd..726304c49 100644 Binary files a/priv/static/static/js/25.fa8acda1a0ba7de2ab58.js and b/priv/static/static/js/25.8f7cea2eb70da626b21d.js differ diff --git a/priv/static/static/js/25.8f7cea2eb70da626b21d.js.map b/priv/static/static/js/25.8f7cea2eb70da626b21d.js.map new file mode 100644 index 000000000..c8e52eac5 Binary files /dev/null and b/priv/static/static/js/25.8f7cea2eb70da626b21d.js.map differ diff --git a/priv/static/static/js/25.fa8acda1a0ba7de2ab58.js.map b/priv/static/static/js/25.fa8acda1a0ba7de2ab58.js.map deleted file mode 100644 index ec5910108..000000000 Binary files a/priv/static/static/js/25.fa8acda1a0ba7de2ab58.js.map and /dev/null differ diff --git a/priv/static/static/js/26.3f806866a23f516b7e87.js b/priv/static/static/js/26.3f806866a23f516b7e87.js new file mode 100644 index 000000000..48273248b Binary files /dev/null and b/priv/static/static/js/26.3f806866a23f516b7e87.js differ diff --git a/priv/static/static/js/26.3f806866a23f516b7e87.js.map b/priv/static/static/js/26.3f806866a23f516b7e87.js.map new file mode 100644 index 000000000..68cc924a8 Binary files /dev/null and b/priv/static/static/js/26.3f806866a23f516b7e87.js.map differ diff --git a/priv/static/static/js/26.5233739c17e00ab514f7.js b/priv/static/static/js/26.5233739c17e00ab514f7.js deleted file mode 100644 index 9adba8a0c..000000000 Binary files a/priv/static/static/js/26.5233739c17e00ab514f7.js and /dev/null differ diff --git a/priv/static/static/js/26.5233739c17e00ab514f7.js.map b/priv/static/static/js/26.5233739c17e00ab514f7.js.map deleted file mode 100644 index 9aad55492..000000000 Binary files a/priv/static/static/js/26.5233739c17e00ab514f7.js.map and /dev/null differ diff --git a/priv/static/static/js/27.79a2337abb067d8a36ce.js b/priv/static/static/js/27.2d655ddddf874f532191.js similarity index 94% rename from priv/static/static/js/27.79a2337abb067d8a36ce.js rename to priv/static/static/js/27.2d655ddddf874f532191.js index 07b8fbea4..b52d610aa 100644 Binary files a/priv/static/static/js/27.79a2337abb067d8a36ce.js and b/priv/static/static/js/27.2d655ddddf874f532191.js differ diff --git a/priv/static/static/js/27.2d655ddddf874f532191.js.map b/priv/static/static/js/27.2d655ddddf874f532191.js.map new file mode 100644 index 000000000..0042ffa62 Binary files /dev/null and b/priv/static/static/js/27.2d655ddddf874f532191.js.map differ diff --git a/priv/static/static/js/27.79a2337abb067d8a36ce.js.map b/priv/static/static/js/27.79a2337abb067d8a36ce.js.map deleted file mode 100644 index a55aeae77..000000000 Binary files a/priv/static/static/js/27.79a2337abb067d8a36ce.js.map and /dev/null differ diff --git a/priv/static/static/js/28.ed355decbad274c26485.js b/priv/static/static/js/28.ed355decbad274c26485.js deleted file mode 100644 index e4cfd3d70..000000000 Binary files a/priv/static/static/js/28.ed355decbad274c26485.js and /dev/null differ diff --git a/priv/static/static/js/28.ed355decbad274c26485.js.map b/priv/static/static/js/28.ed355decbad274c26485.js.map deleted file mode 100644 index 0349f2c68..000000000 Binary files a/priv/static/static/js/28.ed355decbad274c26485.js.map and /dev/null differ diff --git a/priv/static/static/js/28.f738a8b568b00299a569.js b/priv/static/static/js/28.f738a8b568b00299a569.js new file mode 100644 index 000000000..64de7926b Binary files /dev/null and b/priv/static/static/js/28.f738a8b568b00299a569.js differ diff --git a/priv/static/static/js/28.f738a8b568b00299a569.js.map b/priv/static/static/js/28.f738a8b568b00299a569.js.map new file mode 100644 index 000000000..1e1aa98e3 Binary files /dev/null and b/priv/static/static/js/28.f738a8b568b00299a569.js.map differ diff --git a/priv/static/static/js/29.d3d8f3c066d579644c9a.js b/priv/static/static/js/29.64d5389501dc6e6c77f2.js similarity index 99% rename from priv/static/static/js/29.d3d8f3c066d579644c9a.js rename to priv/static/static/js/29.64d5389501dc6e6c77f2.js index 8a8a3b51f..6d1246a86 100644 Binary files a/priv/static/static/js/29.d3d8f3c066d579644c9a.js and b/priv/static/static/js/29.64d5389501dc6e6c77f2.js differ diff --git a/priv/static/static/js/29.64d5389501dc6e6c77f2.js.map b/priv/static/static/js/29.64d5389501dc6e6c77f2.js.map new file mode 100644 index 000000000..075022565 Binary files /dev/null and b/priv/static/static/js/29.64d5389501dc6e6c77f2.js.map differ diff --git a/priv/static/static/js/29.d3d8f3c066d579644c9a.js.map b/priv/static/static/js/29.d3d8f3c066d579644c9a.js.map deleted file mode 100644 index 0ef69d368..000000000 Binary files a/priv/static/static/js/29.d3d8f3c066d579644c9a.js.map and /dev/null differ diff --git a/priv/static/static/js/3.0b1cb0c49b906b834801.js b/priv/static/static/js/3.91e3846705ce522e8366.js similarity index 99% rename from priv/static/static/js/3.0b1cb0c49b906b834801.js rename to priv/static/static/js/3.91e3846705ce522e8366.js index 5b79d06b1..a01c4760a 100644 Binary files a/priv/static/static/js/3.0b1cb0c49b906b834801.js and b/priv/static/static/js/3.91e3846705ce522e8366.js differ diff --git a/priv/static/static/js/3.0b1cb0c49b906b834801.js.map b/priv/static/static/js/3.91e3846705ce522e8366.js.map similarity index 99% rename from priv/static/static/js/3.0b1cb0c49b906b834801.js.map rename to priv/static/static/js/3.91e3846705ce522e8366.js.map index 08e6ffdfe..dba83509c 100644 Binary files a/priv/static/static/js/3.0b1cb0c49b906b834801.js.map and b/priv/static/static/js/3.91e3846705ce522e8366.js.map differ diff --git a/priv/static/static/js/30.04694ca04ca2fb3b9695.js b/priv/static/static/js/30.04694ca04ca2fb3b9695.js deleted file mode 100644 index cc60c675d..000000000 Binary files a/priv/static/static/js/30.04694ca04ca2fb3b9695.js and /dev/null differ diff --git a/priv/static/static/js/30.04694ca04ca2fb3b9695.js.map b/priv/static/static/js/30.04694ca04ca2fb3b9695.js.map deleted file mode 100644 index b347f4f84..000000000 Binary files a/priv/static/static/js/30.04694ca04ca2fb3b9695.js.map and /dev/null differ diff --git a/priv/static/static/js/30.d0724c72975d6ce2243c.js b/priv/static/static/js/30.d0724c72975d6ce2243c.js new file mode 100644 index 000000000..04132ef83 Binary files /dev/null and b/priv/static/static/js/30.d0724c72975d6ce2243c.js differ diff --git a/priv/static/static/js/30.d0724c72975d6ce2243c.js.map b/priv/static/static/js/30.d0724c72975d6ce2243c.js.map new file mode 100644 index 000000000..330ad3596 Binary files /dev/null and b/priv/static/static/js/30.d0724c72975d6ce2243c.js.map differ diff --git a/priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js b/priv/static/static/js/31.31627923fc0b0d75672f.js similarity index 99% rename from priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js rename to priv/static/static/js/31.31627923fc0b0d75672f.js index 886c184d1..1dfae7798 100644 Binary files a/priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js and b/priv/static/static/js/31.31627923fc0b0d75672f.js differ diff --git a/priv/static/static/js/31.31627923fc0b0d75672f.js.map b/priv/static/static/js/31.31627923fc0b0d75672f.js.map new file mode 100644 index 000000000..52ae7f8af Binary files /dev/null and b/priv/static/static/js/31.31627923fc0b0d75672f.js.map differ diff --git a/priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js.map b/priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js.map deleted file mode 100644 index 1a4bd1a0a..000000000 Binary files a/priv/static/static/js/31.ef44f6a2b08f7f78dd8e.js.map and /dev/null differ diff --git a/priv/static/static/js/32.044555dd7095261d9faf.js.map b/priv/static/static/js/32.044555dd7095261d9faf.js.map deleted file mode 100644 index f7f4094ee..000000000 Binary files a/priv/static/static/js/32.044555dd7095261d9faf.js.map and /dev/null differ diff --git a/priv/static/static/js/32.044555dd7095261d9faf.js b/priv/static/static/js/32.f628f72f0c04549e3d56.js similarity index 68% rename from priv/static/static/js/32.044555dd7095261d9faf.js rename to priv/static/static/js/32.f628f72f0c04549e3d56.js index 6ca50349e..1fd7b588f 100644 Binary files a/priv/static/static/js/32.044555dd7095261d9faf.js and b/priv/static/static/js/32.f628f72f0c04549e3d56.js differ diff --git a/priv/static/static/js/32.f628f72f0c04549e3d56.js.map b/priv/static/static/js/32.f628f72f0c04549e3d56.js.map new file mode 100644 index 000000000..8a5717322 Binary files /dev/null and b/priv/static/static/js/32.f628f72f0c04549e3d56.js.map differ diff --git a/priv/static/static/js/4.15e71ac865c2606c30a6.js b/priv/static/static/js/4.14dd3a6fcb972eb61829.js similarity index 80% rename from priv/static/static/js/4.15e71ac865c2606c30a6.js rename to priv/static/static/js/4.14dd3a6fcb972eb61829.js index 3406cc065..a92d5cc42 100644 Binary files a/priv/static/static/js/4.15e71ac865c2606c30a6.js and b/priv/static/static/js/4.14dd3a6fcb972eb61829.js differ diff --git a/priv/static/static/js/4.15e71ac865c2606c30a6.js.map b/priv/static/static/js/4.14dd3a6fcb972eb61829.js.map similarity index 99% rename from priv/static/static/js/4.15e71ac865c2606c30a6.js.map rename to priv/static/static/js/4.14dd3a6fcb972eb61829.js.map index 023d90430..3a5561a41 100644 Binary files a/priv/static/static/js/4.15e71ac865c2606c30a6.js.map and b/priv/static/static/js/4.14dd3a6fcb972eb61829.js.map differ diff --git a/priv/static/static/js/5.e116ac5b71f5e62029a1.js b/priv/static/static/js/5.41ab92595cefc4c72fe0.js similarity index 98% rename from priv/static/static/js/5.e116ac5b71f5e62029a1.js rename to priv/static/static/js/5.41ab92595cefc4c72fe0.js index acd64094e..4a7b85b13 100644 Binary files a/priv/static/static/js/5.e116ac5b71f5e62029a1.js and b/priv/static/static/js/5.41ab92595cefc4c72fe0.js differ diff --git a/priv/static/static/js/5.e116ac5b71f5e62029a1.js.map b/priv/static/static/js/5.41ab92595cefc4c72fe0.js.map similarity index 57% rename from priv/static/static/js/5.e116ac5b71f5e62029a1.js.map rename to priv/static/static/js/5.41ab92595cefc4c72fe0.js.map index 0017a3bfd..74e16ebfa 100644 Binary files a/priv/static/static/js/5.e116ac5b71f5e62029a1.js.map and b/priv/static/static/js/5.41ab92595cefc4c72fe0.js.map differ diff --git a/priv/static/static/js/6.4e804674e0bff336a51b.js b/priv/static/static/js/6.22a79587289c1f1e1e99.js similarity index 99% rename from priv/static/static/js/6.4e804674e0bff336a51b.js rename to priv/static/static/js/6.22a79587289c1f1e1e99.js index b33bbd652..e1b663f59 100644 Binary files a/priv/static/static/js/6.4e804674e0bff336a51b.js and b/priv/static/static/js/6.22a79587289c1f1e1e99.js differ diff --git a/priv/static/static/js/6.4e804674e0bff336a51b.js.map b/priv/static/static/js/6.22a79587289c1f1e1e99.js.map similarity index 57% rename from priv/static/static/js/6.4e804674e0bff336a51b.js.map rename to priv/static/static/js/6.22a79587289c1f1e1e99.js.map index bbb049a88..aa2f9be2c 100644 Binary files a/priv/static/static/js/6.4e804674e0bff336a51b.js.map and b/priv/static/static/js/6.22a79587289c1f1e1e99.js.map differ diff --git a/priv/static/static/js/7.e8595e0b6e063c6d9478.js b/priv/static/static/js/7.cf211d851ab1c77ec4c3.js similarity index 99% rename from priv/static/static/js/7.e8595e0b6e063c6d9478.js rename to priv/static/static/js/7.cf211d851ab1c77ec4c3.js index 7622e0d7a..c013d64c7 100644 Binary files a/priv/static/static/js/7.e8595e0b6e063c6d9478.js and b/priv/static/static/js/7.cf211d851ab1c77ec4c3.js differ diff --git a/priv/static/static/js/7.e8595e0b6e063c6d9478.js.map b/priv/static/static/js/7.cf211d851ab1c77ec4c3.js.map similarity index 57% rename from priv/static/static/js/7.e8595e0b6e063c6d9478.js.map rename to priv/static/static/js/7.cf211d851ab1c77ec4c3.js.map index 40327d1fd..16461348e 100644 Binary files a/priv/static/static/js/7.e8595e0b6e063c6d9478.js.map and b/priv/static/static/js/7.cf211d851ab1c77ec4c3.js.map differ diff --git a/priv/static/static/js/8.2d08c6fbb6b6ef23752f.js b/priv/static/static/js/8.08dd17e532ddcdd39742.js similarity index 99% rename from priv/static/static/js/8.2d08c6fbb6b6ef23752f.js rename to priv/static/static/js/8.08dd17e532ddcdd39742.js index 085a9e004..bf83ae385 100644 Binary files a/priv/static/static/js/8.2d08c6fbb6b6ef23752f.js and b/priv/static/static/js/8.08dd17e532ddcdd39742.js differ diff --git a/priv/static/static/js/8.2d08c6fbb6b6ef23752f.js.map b/priv/static/static/js/8.08dd17e532ddcdd39742.js.map similarity index 57% rename from priv/static/static/js/8.2d08c6fbb6b6ef23752f.js.map rename to priv/static/static/js/8.08dd17e532ddcdd39742.js.map index 50222e2be..c4c701b5f 100644 Binary files a/priv/static/static/js/8.2d08c6fbb6b6ef23752f.js.map and b/priv/static/static/js/8.08dd17e532ddcdd39742.js.map differ diff --git a/priv/static/static/js/9.7d9dd95c4a1c9aa47453.js b/priv/static/static/js/9.1ea2330cb884e98f8257.js similarity index 99% rename from priv/static/static/js/9.7d9dd95c4a1c9aa47453.js rename to priv/static/static/js/9.1ea2330cb884e98f8257.js index 41ab62b92..35cc53089 100644 Binary files a/priv/static/static/js/9.7d9dd95c4a1c9aa47453.js and b/priv/static/static/js/9.1ea2330cb884e98f8257.js differ diff --git a/priv/static/static/js/9.1ea2330cb884e98f8257.js.map b/priv/static/static/js/9.1ea2330cb884e98f8257.js.map new file mode 100644 index 000000000..f72847ec6 Binary files /dev/null and b/priv/static/static/js/9.1ea2330cb884e98f8257.js.map differ diff --git a/priv/static/static/js/9.7d9dd95c4a1c9aa47453.js.map b/priv/static/static/js/9.7d9dd95c4a1c9aa47453.js.map deleted file mode 100644 index c215e9a03..000000000 Binary files a/priv/static/static/js/9.7d9dd95c4a1c9aa47453.js.map and /dev/null differ diff --git a/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js b/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js new file mode 100644 index 000000000..83b640a87 Binary files /dev/null and b/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js differ diff --git a/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js.map b/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js.map new file mode 100644 index 000000000..742d5229b Binary files /dev/null and b/priv/static/static/js/app.c6b8a1c86149ed63e6ff.js.map differ diff --git a/priv/static/static/js/app.eb8f7164fc75862a251d.js b/priv/static/static/js/app.eb8f7164fc75862a251d.js deleted file mode 100644 index 55414d124..000000000 Binary files a/priv/static/static/js/app.eb8f7164fc75862a251d.js and /dev/null differ diff --git a/priv/static/static/js/app.eb8f7164fc75862a251d.js.map b/priv/static/static/js/app.eb8f7164fc75862a251d.js.map deleted file mode 100644 index f1dbb68bb..000000000 Binary files a/priv/static/static/js/app.eb8f7164fc75862a251d.js.map and /dev/null differ diff --git a/priv/static/static/js/vendors~app.54838a79dee084ec3dad.js b/priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js similarity index 96% rename from priv/static/static/js/vendors~app.54838a79dee084ec3dad.js rename to priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js index 38dd65643..066573a52 100644 Binary files a/priv/static/static/js/vendors~app.54838a79dee084ec3dad.js and b/priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js differ diff --git a/priv/static/static/js/vendors~app.54838a79dee084ec3dad.js.map b/priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js.map similarity index 99% rename from priv/static/static/js/vendors~app.54838a79dee084ec3dad.js.map rename to priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js.map index 35b5fd52f..72d5e4e8a 100644 Binary files a/priv/static/static/js/vendors~app.54838a79dee084ec3dad.js.map and b/priv/static/static/js/vendors~app.3b02e2e5bd8cdca42216.js.map differ diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index 25879eb45..6731447d4 100644 Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ diff --git a/priv/static/sw-pleroma.js.map b/priv/static/sw-pleroma.js.map index 62cea8c08..ed747c6d6 100644 Binary files a/priv/static/sw-pleroma.js.map and b/priv/static/sw-pleroma.js.map differ diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index b4df22c2c..ae6fc4c0d 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -2248,4 +2248,43 @@ test "get_host/1" do user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain") assert User.get_host(user) == "lain.com" end + + test "update_last_active_at/1" do + user = insert(:user) + assert is_nil(user.last_active_at) + + test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + + assert {:ok, user} = User.update_last_active_at(user) + + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + + last_active_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(-:timer.hours(24), :millisecond) + |> NaiveDateTime.truncate(:second) + + assert {:ok, user} = + user + |> cast(%{last_active_at: last_active_at}, [:last_active_at]) + |> User.update_and_set_cache() + + assert user.last_active_at == last_active_at + assert {:ok, user} = User.update_last_active_at(user) + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + end + + test "active_user_count/1" do + insert(:user) + insert(:user, %{local: false}) + insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -5)}) + insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -3)}) + insert(:user, %{last_active_at: NaiveDateTime.utc_now()}) + + assert User.active_user_count() == 2 + assert User.active_user_count(6) == 3 + assert User.active_user_count(1) == 1 + end end diff --git a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs index 0d4eebb73..b99856659 100644 --- a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs @@ -47,6 +47,7 @@ test "get instance information", %{conn: conn} do assert result["pleroma"]["metadata"]["federation"] assert result["pleroma"]["metadata"]["fields_limits"] assert result["pleroma"]["vapid_public_key"] + assert result["pleroma"]["stats"]["mau"] == 0 assert email == from_config_email assert thumbnail == from_config_thumbnail diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index a647cd57f..3c73eb514 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -263,6 +263,7 @@ test "posting a fake status", %{conn: conn} do fake_conn = conn + |> assign(:user, refresh_record(conn.assigns.user)) |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{ "status" => diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index 664375fef..cc409451c 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -90,6 +90,65 @@ test "muted emotions", %{user: user, conn: conn} do } ] = result end + + test "filtering", %{conn: conn, user: user} do + local_user = insert(:user) + {:ok, user, local_user} = User.follow(user, local_user) + {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Status"}) + with_media = create_with_media_activity(local_user) + + remote_user = insert(:user, local: false) + {:ok, _user, remote_user} = User.follow(user, remote_user) + remote_activity = create_remote_activity(remote_user) + + without_filter_ids = + conn + |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in without_filter_ids + assert remote_activity.id in without_filter_ids + assert with_media.id in without_filter_ids + + only_local_ids = + conn + |> get("/api/v1/timelines/home?local=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in only_local_ids + refute remote_activity.id in only_local_ids + assert with_media.id in only_local_ids + + only_local_media_ids = + conn + |> get("/api/v1/timelines/home?local=true&only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_local_media_ids + refute remote_activity.id in only_local_media_ids + assert with_media.id in only_local_media_ids + + remote_ids = + conn + |> get("/api/v1/timelines/home?remote=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in remote_ids + assert remote_activity.id in remote_ids + refute with_media.id in remote_ids + + assert conn + |> get("/api/v1/timelines/home?remote=true&only_media=true") + |> json_response_and_validate_schema(200) == [] + + assert conn + |> get("/api/v1/timelines/home?remote=true&local=true") + |> json_response_and_validate_schema(200) == [] + end end describe "public" do @@ -98,27 +157,80 @@ test "the public timeline", %{conn: conn} do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + with_media = create_with_media_activity(user) - _activity = insert(:note_activity, local: false) + remote = insert(:note_activity, local: false) - conn = get(conn, "/api/v1/timelines/public?local=False") + assert conn + |> get("/api/v1/timelines/public?local=False") + |> json_response_and_validate_schema(:ok) + |> length == 3 - assert length(json_response_and_validate_schema(conn, :ok)) == 2 + local_ids = + conn + |> get("/api/v1/timelines/public?local=True") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - conn = get(build_conn(), "/api/v1/timelines/public?local=True") + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + local_ids = + conn + |> get("/api/v1/timelines/public?local=True") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - conn = get(build_conn(), "/api/v1/timelines/public?local=1") + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + local_ids = + conn + |> get("/api/v1/timelines/public?local=True&only_media=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids + + local_ids = + conn + |> get("/api/v1/timelines/public?local=1") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids + + remote_id = remote.id + + assert [%{"id" => ^remote_id}] = + conn + |> get("/api/v1/timelines/public?remote=true") + |> json_response_and_validate_schema(:ok) + + with_media_id = with_media.id + + assert [%{"id" => ^with_media_id}] = + conn + |> get("/api/v1/timelines/public?only_media=true") + |> json_response_and_validate_schema(:ok) + + assert conn + |> get("/api/v1/timelines/public?remote=true&only_media=true") + |> json_response_and_validate_schema(:ok) == [] # does not contain repeats {:ok, _} = CommonAPI.repeat(activity.id, user) - conn = get(build_conn(), "/api/v1/timelines/public?local=true") - - assert [_] = json_response_and_validate_schema(conn, :ok) + assert [_, _] = + conn + |> get("/api/v1/timelines/public?local=true") + |> json_response_and_validate_schema(:ok) end test "the public timeline includes only public statuses for an authenticated user" do @@ -544,6 +656,77 @@ test "muted emotions", %{user: user, conn: conn} do } ] = result end + + test "filtering", %{user: user, conn: conn} do + {:ok, list} = Pleroma.List.create("name", user) + + local_user = insert(:user) + {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Marisa is stupid."}) + with_media = create_with_media_activity(local_user) + {:ok, list} = Pleroma.List.follow(list, local_user) + + remote_user = insert(:user, local: false) + remote_activity = create_remote_activity(remote_user) + {:ok, list} = Pleroma.List.follow(list, remote_user) + + all_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids + + only_local_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?local=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in only_local_ids + assert with_media.id in only_local_ids + refute remote_activity.id in only_local_ids + + only_local_media_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?local=true&only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_local_media_ids + assert with_media.id in only_local_media_ids + refute remote_activity.id in only_local_media_ids + + remote_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?remote=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in remote_ids + refute with_media.id in remote_ids + assert remote_activity.id in remote_ids + + assert conn + |> get("/api/v1/timelines/list/#{list.id}?remote=true&only_media=true") + |> json_response_and_validate_schema(200) == [] + + only_media_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_media_ids + assert with_media.id in only_media_ids + refute remote_activity.id in only_media_ids + + assert conn + |> get("/api/v1/timelines/list/#{list.id}?only_media=true&local=true&remote=true") + |> json_response_and_validate_schema(200) == [] + end end describe "hashtag" do @@ -554,19 +737,85 @@ test "hashtag timeline", %{conn: conn} do following = insert(:user) {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) + with_media = create_with_media_activity(following) - nconn = get(conn, "/api/v1/timelines/tag/2hu") + remote = insert(:user, local: false) + remote_activity = create_remote_activity(remote) - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + all_ids = + conn + |> get("/api/v1/timelines/tag/2hu") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - assert id == to_string(activity.id) + assert activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids # works for different capitalization too - nconn = get(conn, "/api/v1/timelines/tag/2HU") + all_ids = + conn + |> get("/api/v1/timelines/tag/2HU") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + assert activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids - assert id == to_string(activity.id) + local_ids = + conn + |> get("/api/v1/timelines/tag/2hu?local=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote_activity.id in local_ids + + remote_ids = + conn + |> get("/api/v1/timelines/tag/2hu?remote=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in remote_ids + refute with_media.id in remote_ids + assert remote_activity.id in remote_ids + + media_ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in media_ids + assert with_media.id in media_ids + refute remote_activity.id in media_ids + + media_local_ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in media_local_ids + assert with_media.id in media_local_ids + refute remote_activity.id in media_local_ids + + ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true&remote=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in ids + refute with_media.id in ids + refute remote_activity.id in ids + + assert conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&remote=true") + |> json_response_and_validate_schema(:ok) == [] end test "multi-hashtag timeline", %{conn: conn} do @@ -726,4 +975,37 @@ test "with `%{local: true, federated: false}`, forbids unauthenticated access to ensure_authenticated_access(base_uri) end end + + defp create_remote_activity(user) do + obj = + insert(:note, %{ + data: %{ + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + User.ap_followers(user) + ] + }, + user: user + }) + + insert(:note_activity, %{ + note: obj, + recipients: [ + "https://www.w3.org/ns/activitystreams#Public", + User.ap_followers(user) + ], + user: user, + local: false + }) + end + + defp create_with_media_activity(user) do + obj = insert(:attachment_note, user: user) + + insert(:note_activity, %{ + note: obj, + recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(user)], + user: user + }) + end end diff --git a/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs index 98a23aaaa..54f2c5a58 100644 --- a/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs @@ -104,7 +104,7 @@ test "PATCH /api/v1/pleroma/conversations/:id" do [participation] = Participation.for_user(user) participation = Repo.preload(participation, :recipients) - assert user in participation.recipients + assert refresh_record(user) in participation.recipients assert other_user in participation.recipients end diff --git a/test/pleroma/web/plugs/user_tracking_plug_test.exs b/test/pleroma/web/plugs/user_tracking_plug_test.exs new file mode 100644 index 000000000..8e9d59b99 --- /dev/null +++ b/test/pleroma/web/plugs/user_tracking_plug_test.exs @@ -0,0 +1,58 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserTrackingPlugTest do + use Pleroma.Web.ConnCase, async: true + + import Pleroma.Factory + + alias Pleroma.Web.Plugs.UserTrackingPlug + + test "updates last_active_at for a new user", %{conn: conn} do + user = insert(:user) + + assert is_nil(user.last_active_at) + + test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + + %{assigns: %{user: user}} = + conn + |> assign(:user, user) + |> UserTrackingPlug.call(%{}) + + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + end + + test "doesn't update last_active_at if it was updated recently", %{conn: conn} do + last_active_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(-:timer.hours(1), :millisecond) + |> NaiveDateTime.truncate(:second) + + user = insert(:user, %{last_active_at: last_active_at}) + + %{assigns: %{user: user}} = + conn + |> assign(:user, user) + |> UserTrackingPlug.call(%{}) + + assert user.last_active_at == last_active_at + end + + test "skips updating last_active_at if user ID is nil", %{conn: conn} do + %{assigns: %{user: user}} = + conn + |> assign(:user, %Pleroma.User{}) + |> UserTrackingPlug.call(%{}) + + assert is_nil(user.last_active_at) + end + + test "does nothing if user is not present", %{conn: conn} do + %{assigns: assigns} = UserTrackingPlug.call(conn, %{}) + + refute Map.has_key?(assigns, :user) + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index bf9592064..436e19409 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -104,6 +104,37 @@ def note_factory(attrs \\ %{}) do } end + def attachment_note_factory(attrs \\ %{}) do + user = attrs[:user] || insert(:user) + {length, attrs} = Map.pop(attrs, :length, 1) + + data = %{ + "attachment" => + Stream.repeatedly(fn -> attachment_data(user.ap_id, attrs[:href]) end) + |> Enum.take(length) + } + + build(:note, Map.put(attrs, :data, data)) + end + + defp attachment_data(ap_id, href) do + href = href || sequence(:href, &"#{Pleroma.Web.Endpoint.url()}/media/#{&1}.jpg") + + %{ + "url" => [ + %{ + "href" => href, + "type" => "Link", + "mediaType" => "image/jpeg" + } + ], + "name" => "some name", + "type" => "Document", + "actor" => ap_id, + "mediaType" => "image/jpeg" + } + end + def audio_factory(attrs \\ %{}) do text = sequence(:text, &"lain radio episode #{&1}")