diff --git a/CHANGELOG.md b/CHANGELOG.md index 20af9badc..fbbaf18f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,14 +11,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired +- **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities. - Configuration: OpenGraph and TwitterCard providers enabled by default - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text +- Mastodon API: `pleroma.thread_muted` key in the Status entity - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option - NodeInfo: Return `mailerEnabled` in `metadata` - Mastodon API: Unsubscribe followers when they unfollow a user - AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses) - Improve digest email template +– Pagination: (optional) return `total` alongside with `items` when paginating ### Fixed - Following from Osada @@ -29,7 +32,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `federation_incoming_replies_max_depth` option being ignored in certain cases - Federation/MediaProxy not working with instances that have wrong certificate order - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`) +- Mastodon API: Misskey's endless polls being unable to render - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity +- Mastodon API: Notifications endpoint crashing if one notification failed to render - Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set - Mastodon API: `muted` in the Status entity, using author's account to determine if the tread was muted - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`) @@ -49,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances - MRF: fix use of unserializable keyword lists in describe() implementations - ActivityPub: Deactivated user deletion +- MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled ### Added - Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically. diff --git a/Dockerfile b/Dockerfile index 268ec61dc..c61dcfde9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rinpatch/elixir:1.9.0-rc.0-alpine as build +FROM elixir:1.9-alpine as build COPY . . @@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make &&\ mkdir release &&\ mix release --path release -FROM alpine:latest +FROM alpine:3.9 ARG HOME=/opt/pleroma ARG DATA=/var/lib/pleroma diff --git a/config/docker.exs b/config/docker.exs index 63ab4cdee..f9f27d141 100644 --- a/config/docker.exs +++ b/config/docker.exs @@ -10,7 +10,7 @@ notify_email: System.get_env("NOTIFY_EMAIL"), limit: 5000, registrations_open: false, - dynamic_configuration: true + healthcheck: true config :pleroma, Pleroma.Repo, adapter: Ecto.Adapters.Postgres, diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index f34e3dd72..02f90f3e8 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -26,6 +26,7 @@ Has these additional fields under the `pleroma` object: - `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain` - `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain` - `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire +- `thread_muted`: true if the thread the post belongs to is muted ## Attachments diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md index b134b31a8..7d343e97a 100644 --- a/docs/api/pleroma_api.md +++ b/docs/api/pleroma_api.md @@ -126,13 +126,14 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi ## `/api/pleroma/admin/`… See [Admin-API](Admin-API.md) -## `/api/pleroma/notifications/read` -### Mark a single notification as read +## `/api/v1/pleroma/notifications/read` +### Mark notifications as read * Method `POST` * Authentication: required -* Params: - * `id`: notification's id -* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}` +* Params (mutually exclusive): + * `id`: a single notification id to read + * `max_id`: read all notifications up to this id +* Response: Notification entity/Array of Notification entities that were read. In case of `max_id`, only the first 80 read notifications will be returned. ## `/api/v1/pleroma/accounts/:id/subscribe` ### Subscribe to receive notifications for all statuses posted by a user diff --git a/installation/pleroma.nginx b/installation/pleroma.nginx index e3c70de54..4da9918ca 100644 --- a/installation/pleroma.nginx +++ b/installation/pleroma.nginx @@ -71,26 +71,26 @@ server { proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; - # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only - # and `localhost.` resolves to [::0] on some systems: see issue #930 + # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only + # and `localhost.` resolves to [::0] on some systems: see issue #930 proxy_pass http://127.0.0.1:4000; client_max_body_size 16m; } location ~ ^/(media|proxy) { - proxy_cache pleroma_media_cache; + proxy_cache pleroma_media_cache; slice 1m; proxy_cache_key $host$uri$is_args$args$slice_range; proxy_set_header Range $slice_range; proxy_http_version 1.1; proxy_cache_valid 200 206 301 304 1h; - proxy_cache_lock on; + proxy_cache_lock on; proxy_ignore_client_abort on; - proxy_buffering on; + proxy_buffering on; chunked_transfer_encoding on; proxy_ignore_headers Cache-Control; - proxy_hide_header Cache-Control; - proxy_pass http://localhost:4000; + proxy_hide_header Cache-Control; + proxy_pass http://127.0.0.1:4000; } } diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 5d29af853..b7c880c51 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -102,15 +102,33 @@ def set_read_up_to(%{id: user_id} = _user, id) do n in Notification, where: n.user_id == ^user_id, where: n.id <= ^id, + where: n.seen == false, update: [ set: [ seen: true, updated_at: ^NaiveDateTime.utc_now() ] - ] + ], + # Ideally we would preload object and activities here + # but Ecto does not support preloads in update_all + select: n.id ) - Repo.update_all(query, []) + {_, notification_ids} = Repo.update_all(query, []) + + Notification + |> where([n], n.id in ^notification_ids) + |> join(:inner, [n], activity in assoc(n, :activity)) + |> join(:left, [n, a], object in Object, + on: + fragment( + "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", + object.data, + a.data + ) + ) + |> preload([n, a, o], activity: {a, object: o}) + |> Repo.all() end def read_one(%User{} = user, notification_id) do diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 2b869ccdc..b55379c4a 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -16,6 +16,15 @@ defmodule Pleroma.Pagination do def fetch_paginated(query, params, type \\ :keyset) + def fetch_paginated(query, %{"total" => true} = params, :keyset) do + total = Repo.aggregate(query, :count, :id) + + %{ + total: total, + items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset) + } + end + def fetch_paginated(query, params, :keyset) do options = cast_params(params) @@ -25,6 +34,15 @@ def fetch_paginated(query, params, :keyset) do |> enforce_order(options) end + def fetch_paginated(query, %{"total" => true} = params, :offset) do + total = Repo.aggregate(query, :count, :id) + + %{ + total: total, + items: fetch_paginated(query, Map.drop(params, ["total"]), :offset) + } + end + def fetch_paginated(query, params, :offset) do options = cast_params(params) diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index de1eb4aa5..b3547ecd4 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -25,11 +25,15 @@ defp score_displayname("fedibot"), do: 1.0 defp score_displayname(_), do: 0.0 defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do - # nickname will always be a binary string because it's generated by Pleroma. + # nickname will be a binary string except when following a relay nick_score = - nickname - |> String.downcase() - |> score_nickname() + if is_binary(nickname) do + nickname + |> String.downcase() + |> score_nickname() + else + 0.0 + end # displayname will either be a binary string or nil, if a displayname isn't set. name_score = diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 27e9cab06..ec8eadcaa 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do alias Pleroma.Web.MastodonAPI.StatusView def render("index.json", %{notifications: notifications, for: user}) do - render_many(notifications, NotificationView, "show.json", %{for: user}) + safe_render_many(notifications, NotificationView, "show.json", %{for: user}) end def render("show.json", %{ diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a4ee0b5dd..e71083b91 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -299,7 +299,8 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity content: %{"text/plain" => content_plaintext}, spoiler_text: %{"text/plain" => summary_plaintext}, expires_at: expires_at, - direct_conversation_id: direct_conversation_id + direct_conversation_id: direct_conversation_id, + thread_muted: thread_muted? } } end @@ -384,16 +385,27 @@ def render("poll.json", %{object: object} = opts) do end if options do - end_time = - (object.data["closed"] || object.data["endTime"]) - |> NaiveDateTime.from_iso8601!() + {end_time, expired} = + case object.data["closed"] || object.data["endTime"] do + end_time when is_binary(end_time) -> + end_time = + (object.data["closed"] || object.data["endTime"]) + |> NaiveDateTime.from_iso8601!() - expired = - end_time - |> NaiveDateTime.compare(NaiveDateTime.utc_now()) - |> case do - :lt -> true - _ -> false + expired = + end_time + |> NaiveDateTime.compare(NaiveDateTime.utc_now()) + |> case do + :lt -> true + _ -> false + end + + end_time = Utils.to_masto_date(end_time) + + {end_time, expired} + + _ -> + {nil, false} end voted = @@ -420,7 +432,7 @@ def render("poll.json", %{object: object} = opts) do # Mastodon uses separate ids for polls, but an object can't have # more than one poll embedded so object id is fine id: to_string(object.id), - expires_at: Utils.to_masto_date(end_time), + expires_at: end_time, expired: expired, multiple: multiple, votes_count: votes_count, diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex index b6d2bf86b..f4df3b024 100644 --- a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex @@ -8,8 +8,10 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7] alias Pleroma.Conversation.Participation + alias Pleroma.Notification alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.ConversationView + alias Pleroma.Web.MastodonAPI.NotificationView alias Pleroma.Web.MastodonAPI.StatusView def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do @@ -70,4 +72,27 @@ def update_conversation( |> render("participation.json", %{participation: participation, for: user}) end end + + def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do + with {:ok, notification} <- Notification.read_one(user, notification_id) do + conn + |> put_view(NotificationView) + |> render("show.json", %{notification: notification, for: user}) + else + {:error, message} -> + conn + |> put_status(:bad_request) + |> json(%{"error" => message}) + end + end + + def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do + with notifications <- Notification.set_read_up_to(user, max_id) do + notifications = Enum.take(notifications, 80) + + conn + |> put_view(NotificationView) + |> render("index.json", %{notifications: notifications, for: user}) + end + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 969dc66fd..44a4279f7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -236,12 +236,6 @@ defmodule Pleroma.Web.Router do post("/blocks_import", UtilController, :blocks_import) post("/follow_import", UtilController, :follow_import) end - - scope [] do - pipe_through(:oauth_read) - - post("/notifications/read", UtilController, :notifications_read) - end end scope "/oauth", Pleroma.Web.OAuth do @@ -277,6 +271,7 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:oauth_write) patch("/conversations/:id", PleromaAPIController, :update_conversation) + post("/notifications/read", PleromaAPIController, :read_notification) end end diff --git a/test/fixtures/tesla_mock/misskey_poll_no_end_date.json b/test/fixtures/tesla_mock/misskey_poll_no_end_date.json new file mode 100644 index 000000000..0e08de4de --- /dev/null +++ b/test/fixtures/tesla_mock/misskey_poll_no_end_date.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"Hashtag":"as:Hashtag"}],"id":"https://skippers-bin.com/notes/7x9tmrp97i","type":"Question","attributedTo":"https://skippers-bin.com/users/7v1w1r8ce6","summary":null,"content":"
@march@marchgenso.me How are your notifications now?
リモートで結果を表示
@march@marchgenso.me How are your notifications now?
リモートで結果を表示
----------------------------------------
0: Working
1: Broken af
----------------------------------------
番号を返信して投票
Admin of skippers-bin.com and neckbeard.xyz
For the most part I'm just a normal user. I mostly post animu, lewds, may-mays, and shitposts.
Not an alt of @sjw@neckbeard.xyz but another main.
Email/XMPP: neckbeard@rape.lol
PGP: d016 b622 75ba bcbc 5b3a fced a7d9 4824 0eb3 9c4e