Merge branch 'develop' into issue/1276
authorMaksim Pechnikov <parallel588@gmail.com>
Thu, 5 Dec 2019 09:22:19 +0000 (12:22 +0300)
committerMaksim Pechnikov <parallel588@gmail.com>
Thu, 5 Dec 2019 09:22:19 +0000 (12:22 +0300)
1  2 
CHANGELOG.md
docs/API/differences_in_mastoapi_responses.md
lib/pleroma/notification.ex
test/notification_test.exs

diff --combined CHANGELOG.md
index b86c9fad8ac0af5b3b993c4549cfde53cc3fc778,a06ea211eb9dcf6b401c799c5357d3b8d04a1093..f021fefc3b23ff4969c00090ecbb9dbc3056673d
@@@ -10,36 -10,46 +10,47 @@@ The format is based on [Keep a Changelo
  
  ### Changed
  - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
+ - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default
  - Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings)
  - Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler
  - Enabled `:instance, extended_nickname_format` in the default config
  - Add `rel="ugc"` to all links in statuses, to prevent SEO spam
  - Extract RSS functionality from OStatus
  - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
+ - OStatus: Extract RSS functionality
+ - Deprecated `User.Info` embedded schema (fields moved to `User`)
+ - Store status data inside Flag activity
  <details>
    <summary>API Changes</summary>
  
+ - **Breaking** Admin API: `PATCH /api/pleroma/admin/users/:nickname/force_password_reset` is now `PATCH /api/pleroma/admin/users/force_password_reset` (accepts `nicknames` array in the request body)
  - **Breaking:** Admin API: Return link alongside with token on password reset
+ - **Breaking:** Admin API: `PUT /api/pleroma/admin/reports/:id` is now `PATCH /api/pleroma/admin/reports`, see admin_api.md for details
  - **Breaking:** `/api/pleroma/admin/users/invite_token` now uses `POST`, changed accepted params and returns full invite in json instead of only token string.
  - Admin API: Return `total` when querying for reports
  - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
  - Admin API: Return link alongside with token on password reset
+ - Admin API: Support authentication via `x-admin-token` HTTP header
  - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
  - Mastodon API: `pleroma.thread_muted` to the Status entity
  - Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
  - Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
 +- Mastodon API: Add `pleroma.unread_count` to the Marker entity
  </details>
  
  ### Added
  - Refreshing poll results for remote polls
  - Authentication: Added rate limit for password-authorized actions / login existence checks
+ - Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.
  - Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
+ - Mix task to list all users (`mix pleroma.user list`)
  - Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
+ - MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
  <details>
    <summary>API Changes</summary>
  
  - Job queue stats to the healthcheck page
+ - Admin API: Add ability to fetch reports, grouped by status `GET /api/pleroma/admin/grouped_reports`
  - Admin API: Add ability to require password reset
  - Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
  - Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items
  - Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
  - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
  - Metadata Link: Atom syndication Feed
+ - Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
  - Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
  - Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
- - Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
+ - Admin API: Multiple endpoints now require `nicknames` array, instead of singe `nickname`:
+   - `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group`
+   - `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
  - Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
  - Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
+ - ActivityPub: Support `Move` activities
  - Mastodon API: Add `/api/v1/markers` for managing timeline read markers
- ### Changed
- - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
- - **Breaking:** Admin API: Return link alongside with token on password reset
- - Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings)
- - Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler
- - Admin API: Return `total` when querying for reports
- - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
- - Admin API: Return link alongside with token on password reset
- - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
- - OStatus: Extract RSS functionality
- - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
- - Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
- - Deprecated `User.Info` embedded schema (fields moved to `User`)
- - Store status data inside Flag activity
+ - Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
+ - Configuration: `feed` option for user atom feed.
+ - Pleroma API: Add Emoji reactions
+ - Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
+ - Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
  </details>
  
  ### Fixed
  - Report emails now include functional links to profiles of remote user accounts
+ - Not being able to log in to some third-party apps when logged in to MastoFE
  <details>
    <summary>API Changes</summary>
  
  - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
  - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
+ - Admin API: Error when trying to update reports in the "old" format
  </details>
  
+ ## [1.1.6] - 2019-11-19
+ ### Fixed
+ - Not being able to log into to third party apps when the browser is logged into mastofe
+ - Email confirmation not being required even when enabled
+ - Mastodon API: conversations API crashing when one status is malformed
+ ### Bundled Pleroma-FE Changes
+ #### Added
+ - About page
+ - Meme arrows
+ #### Fixed
+ - Image modal not closing unless clicked outside of image
+ - Attachment upload spinner not being centered
+ - Showing follow counters being 0 when they are actually hidden
+ ## [1.1.5] - 2019-11-09
+ ### Fixed
+ - Polls having different numbers in timelines/notifications/poll api endpoints due to cache desyncronization
+ - Pleroma API: OAuth token endpoint not being found when ".json" suffix is appended
+ ### Changed
+ - Frontend bundle updated to [044c9ad0](https://git.pleroma.social/pleroma/pleroma-fe/commit/044c9ad0562af059dd961d50961a3880fca9c642)
+ ## [1.1.4] - 2019-11-01
+ ### Fixed
+ - Added a migration that fills up empty user.info fields to prevent breakage after previous unsafe migrations.
+ - Failure to migrate from pre-1.0.0 versions
+ - Mastodon API: Notification stream not including follow notifications
+ ## [1.1.3] - 2019-10-25
+ ### Fixed
+ - Blocked users showing up in notifications collapsed as if they were muted
+ - `pleroma_ctl` not working on Debian's default shell
  ## [1.1.2] - 2019-10-18
  ### Fixed
  - `pleroma_ctl` trying to connect to a running instance when generating the config, which of course doesn't exist.
index d18b976b66f10a9239e1c826ada52e7d978710de,006d17c1bc28abd63ae6331f0cf9e9ab99dbac9a..eea7f07070e15e1a1aa7147d88e09324c6768b73
@@@ -57,6 -57,7 +57,7 @@@ Has these additional fields under the `
  - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
  - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
  - `deactivated`: boolean, true when the user is deactivated
+ - `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
  - `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
  
  ### Source
@@@ -72,6 -73,12 +73,12 @@@ Has an additional field under the `pler
  
  - `recipients`: The list of the recipients of this Conversation. These will be addressed when replying to this conversation.
  
+ ## GET `/api/v1/conversations`
+ Accepts additional parameters:
+ - `recipients`: Only return conversations with the given recipients (a list of user ids). Usage example: `GET /api/v1/conversations?recipients[]=1&recipients[]=2`
  ## Account Search
  
  Behavior has changed:
@@@ -85,6 -92,12 +92,12 @@@ Has these additional fields under the `
  
  - `is_seen`: true if the notification was read by the user
  
+ ### Move Notification
+ The `type` value is `move`. Has an additional field:
+ - `target`: new account
  ## GET `/api/v1/notifications`
  
  Accepts additional parameters:
@@@ -130,6 -143,7 +143,7 @@@ Additional parameters can be added to t
  - `default_scope` - the scope returned under `privacy` key in Source subentity
  - `pleroma_settings_store` - Opaque user settings to be saved on the backend.
  - `skip_thread_containment` - if true, skip filtering out broken threads
+ - `allow_following_move` - if true, allows automatically follow moved following accounts
  - `pleroma_background_image` - sets the background image of the user.
  
  ### Pleroma Settings Store
@@@ -155,9 -169,3 +169,9 @@@ Has theses additionnal parameters (whic
      * `captcha_solution`: optional, contains provider-specific captcha solution,
      * `captcha_token`: optional, contains provider-specific captcha token
      * `token`: invite token required when the registerations aren't public.
 +
 +## Markers
 +
 +Has these additional fields under the `pleroma` object:
 +
 +- `unread_count`: contains number unread notifications
index 1cc6a4735c094f3ed5df2306934680bf4cb68ac1,f37e7ec671ee88c39848cc92a7f715621766999e..d6149cd0da2bf8500bab549e7b3fd0efc063fa6e
@@@ -5,9 -5,7 +5,9 @@@
  defmodule Pleroma.Notification do
    use Ecto.Schema
  
 +  alias Ecto.Multi
    alias Pleroma.Activity
 +  alias Pleroma.Marker
    alias Pleroma.Notification
    alias Pleroma.Object
    alias Pleroma.Pagination
      |> cast(attrs, [:seen])
    end
  
 +  @spec unread_count_query(User.t()) :: Ecto.Queryable.t()
 +  def unread_count_query(user) do
 +    from(q in Pleroma.Notification,
 +      where: q.user_id == ^user.id,
 +      where: q.seen == false
 +    )
 +  end
 +
 +  @spec last_read_query(User.t()) :: Ecto.Queryable.t()
 +  def last_read_query(user) do
 +    from(q in Pleroma.Notification,
 +      where: q.user_id == ^user.id,
 +      where: q.seen == true,
 +      select: type(q.id, :string),
 +      limit: 1,
 +      order_by: [desc: :id]
 +    )
 +  end
 +
    def for_user_query(user, opts \\ []) do
      Notification
      |> where(user_id: ^user.id)
      |> Repo.all()
    end
  
 -  def set_read_up_to(%{id: user_id} = _user, id) do
 +  def set_read_up_to(%{id: user_id} = user, id) do
      query =
        from(
          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
        )
  
 -    {_, notification_ids} = Repo.update_all(query, [])
 +    {:ok, %{ids: {_, notification_ids}}} =
 +      Multi.new()
 +      |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()])
 +      |> Marker.multi_set_unread_count(user, "notifications")
 +      |> Repo.transaction()
  
      Notification
      |> where([n], n.id in ^notification_ids)
      |> Repo.all()
    end
  
 +  @spec read_one(User.t(), String.t()) ::
 +          {:ok, Notification.t()} | {:error, Ecto.Changeset.t()} | nil
    def read_one(%User{} = user, notification_id) do
      with {:ok, %Notification{} = notification} <- get(user, notification_id) do
 -      notification
 -      |> changeset(%{seen: true})
 -      |> Repo.update()
 +      Multi.new()
 +      |> Multi.update(:update, changeset(notification, %{seen: true}))
 +      |> Marker.multi_set_unread_count(user, "notifications")
 +      |> Repo.transaction()
 +      |> case do
 +        {:ok, %{update: notification}} -> {:ok, notification}
 +        {:error, :update, changeset, _} -> {:error, changeset}
 +      end
      end
    end
  
      object = Object.normalize(activity)
  
      unless object && object.data["type"] == "Answer" do
 -      users = get_notified_from_activity(activity)
 -      notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
 +      notifications =
 +        activity
 +        |> get_notified_from_activity()
 +        |> Enum.map(&create_notification(activity, &1))
 +
        {:ok, notifications}
      else
        {:ok, []}
      end
    end
  
-   def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
-       when type in ["Like", "Announce", "Follow"] do
+   def create_notifications(%Activity{data: %{"type" => type}} = activity)
+       when type in ["Like", "Announce", "Follow", "Move"] do
      notifications =
        activity
-       |> get_notified_from_activity
+       |> get_notified_from_activity()
        |> Enum.map(&create_notification(activity, &1))
  
      {:ok, notifications}
    # TODO move to sql, too.
    def create_notification(%Activity{} = activity, %User{} = user) do
      unless skip?(activity, user) do
 -      notification = %Notification{user_id: user.id, activity: activity}
 -      {:ok, notification} = Repo.insert(notification)
 +      {:ok, %{notification: notification}} =
 +        Multi.new()
 +        |> Multi.insert(:notification, %Notification{user_id: user.id, activity: activity})
 +        |> Marker.multi_set_unread_count(user, "notifications")
 +        |> Repo.transaction()
  
        ["user", "user:notification"]
        |> Streamer.stream(notification)
  
    def get_notified_from_activity(activity, local_only \\ true)
  
-   def get_notified_from_activity(
-         %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
-         local_only
-       )
-       when type in ["Create", "Like", "Announce", "Follow"] do
-     recipients =
-       []
-       |> Utils.maybe_notify_to_recipients(activity)
-       |> Utils.maybe_notify_mentioned_recipients(activity)
-       |> Utils.maybe_notify_subscribers(activity)
-       |> Enum.uniq()
-     User.get_users_from_set(recipients, local_only)
+   def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
+       when type in ["Create", "Like", "Announce", "Follow", "Move"] do
+     []
+     |> Utils.maybe_notify_to_recipients(activity)
+     |> Utils.maybe_notify_mentioned_recipients(activity)
+     |> Utils.maybe_notify_subscribers(activity)
+     |> Utils.maybe_notify_followers(activity)
+     |> Enum.uniq()
+     |> User.get_users_from_set(local_only)
    end
  
    def get_notified_from_activity(_, _local_only), do: []
index d358e433fabb788ea6f862a17168782b2e14a8da,dcbffeafe39b16c1afdf7505fc4744cf2119ce25..8db3495262a0e44e0c3cf2deef6f462d8130d650
@@@ -31,9 -31,6 +31,9 @@@ defmodule Pleroma.NotificationTest d
        assert notified_ids == [other_user.id, third_user.id]
        assert notification.activity_id == activity.id
        assert other_notification.activity_id == activity.id
 +
 +      assert [%Pleroma.Marker{unread_count: 2}] =
 +               Pleroma.Marker.get_markers(other_user, ["notifications"])
      end
  
      test "it creates a notification for subscribed users" do
        assert n1.seen == true
        assert n2.seen == true
        assert n3.seen == false
 +
 +      assert %Pleroma.Marker{unread_count: 1} =
 +               Pleroma.Repo.get_by(
 +                 Pleroma.Marker,
 +                 user_id: other_user.id,
 +                 timeline: "notifications"
 +               )
      end
    end
  
  
        assert Enum.empty?(Notification.for_user(local_user))
      end
+     test "move activity generates a notification" do
+       %{ap_id: old_ap_id} = old_user = insert(:user)
+       %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
+       follower = insert(:user)
+       other_follower = insert(:user, %{allow_following_move: false})
+       User.follow(follower, old_user)
+       User.follow(other_follower, old_user)
+       Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
+       ObanHelpers.perform_all()
+       assert [
+                %{
+                  activity: %{
+                    data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
+                  }
+                }
+              ] = Notification.for_user(follower)
+       assert [
+                %{
+                  activity: %{
+                    data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
+                  }
+                }
+              ] = Notification.for_user(other_follower)
+     end
    end
  
    describe "for_user" do