DELETE /api/pleroma/admin/users now accepts nicknames array
authorMaxim Filippov <colixer@gmail.com>
Tue, 15 Oct 2019 15:33:29 +0000 (17:33 +0200)
committerMaxim Filippov <colixer@gmail.com>
Tue, 15 Oct 2019 15:33:29 +0000 (17:33 +0200)
34 files changed:
.gitlab-ci.yml
CHANGELOG.md
docs/API/admin_api.md
docs/API/differences_in_mastoapi_responses.md
docs/installation/alpine_linux_en.md
docs/installation/arch_linux_en.md
docs/installation/centos7_en.md
docs/installation/debian_based_en.md
docs/installation/debian_based_jp.md
docs/installation/gentoo_en.md
docs/installation/migrating_from_source_otp_en.md
docs/installation/netbsd_en.md
docs/installation/openbsd_en.md
docs/installation/openbsd_fi.md
docs/installation/otp_en.md
lib/pleroma/moderation_log.ex
lib/pleroma/notification.ex
lib/pleroma/reverse_proxy/reverse_proxy.ex
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/mastodon_api/mastodon_api.ex
lib/pleroma/web/ostatus/handlers/delete_handler.ex
mix.exs
rel/files/bin/pleroma_ctl
test/moderation_log_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/mastodon_api/controllers/account_controller_test.exs
test/web/mastodon_api/controllers/notification_controller_test.exs
test/web/mastodon_api/controllers/search_controller_test.exs
test/web/mastodon_api/controllers/timeline_controller_test.exs

index 748bec74aa1cdb6bf10621b30395fc8371ea6d70..36137b38e09b73e7bd47a0880c59680d230af436 100644 (file)
@@ -70,7 +70,7 @@ docs-deploy:
   stage: deploy
   image: alpine:latest
   only:
-  - master@pleroma/pleroma
+  - stable@pleroma/pleroma
   - develop@pleroma/pleroma
   before_script:
   - apk add curl
@@ -127,9 +127,10 @@ amd64:
   # TODO: Replace with upstream image when 1.9.0 comes out
   image: rinpatch/elixir:1.9.0-rc.0
   only: &release-only
-  - master@pleroma/pleroma
+  - stable@pleroma/pleroma
   - develop@pleroma/pleroma
   - /^maint/.*$/@pleroma/pleroma
+  - /^release/.*$/@pleroma/pleroma
   artifacts: &release-artifacts
     name: "pleroma-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
     paths:
index 30b765251cc7b0ede0bd723b836d0a858a8a7cdf..24876d3f2b08fc2909c09bc03b8295b72acd0371 100644 (file)
@@ -17,8 +17,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Authentication: Added rate limit for password-authorized actions / login existence checks
 - 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 /api/pleroma/admin/users/:nickname/permission_group/:permission_group` / `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST /api/pleroma/admin/users/permission_group/:permission_group` / `DELETE /api/pleroma/admin/users/permission_group/:permission_group` (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).
 
 ### Changed
 - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
@@ -39,6 +40,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Report emails now include functional links to profiles of remote user accounts
 
 ## [1.1.0] - 2019-??-??
+**Breaking:** The stable branch has been changed from `master` to `stable`, `master` now points to `release/1.0`
 ### Security
 - Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
 
index 2c8237b57f3bbf1c78964cd437d9171f45fc56b7..60755e40a755053773683483289c1463588fd5b1 100644 (file)
@@ -47,7 +47,7 @@ Authentication is required and the user must be an admin.
 }
 ```
 
-## `/api/pleroma/admin/users`
+## DEPRECATED `DELETE /api/pleroma/admin/users`
 
 ### Remove a user
 
@@ -56,6 +56,15 @@ Authentication is required and the user must be an admin.
   - `nickname`
 - Response: User’s nickname
 
+## `DELETE /api/pleroma/admin/users`
+
+### Remove a user
+
+- Method `DELETE`
+- Params:
+  - `nicknames`
+- Response: Array of user nicknames
+
 ### Create a user
 
 - Method: `POST`
index 21b29752914e5d877c88336ba25cb5ec156ad45c..aca0f5e0e9d01c1ec43398778a0feaedb1fea72f 100644 (file)
@@ -13,6 +13,7 @@ Some apps operate under the assumption that no more than 4 attachments can be re
 ## Timelines
 
 Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
+Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
 
 ## Statuses
 
@@ -84,6 +85,12 @@ Has these additional fields under the `pleroma` object:
 
 - `is_seen`: true if the notification was read by the user
 
+## GET `/api/v1/notifications`
+
+Accepts additional parameters:
+
+- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
+
 ## POST `/api/v1/statuses`
 
 Additional parameters can be added to the JSON body/Form data:
index f5d1fade14aa9d80261ef35fdc6230beac2fb837..2a9b8f6ff292856903a113dad0b13151e5051db4 100644 (file)
@@ -91,7 +91,7 @@ sudo adduser -S -s /bin/false -h /opt/pleroma -H -G pleroma pleroma
 ```shell
 sudo mkdir -p /opt/pleroma
 sudo chown -R pleroma:pleroma /opt/pleroma
-sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
 ```
 
 * Change to the new directory:
index 58a8d023ff08211098b92117606a4c5960e2a584..8370986ada37cf3b16089eaf5e9c9260e2d2e31c 100644 (file)
@@ -66,7 +66,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
 ```shell
 sudo mkdir -p /opt/pleroma
 sudo chown -R pleroma:pleroma /opt/pleroma
-sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
 ```
 
 * Change to the new directory:
index fe26339b5c1b5c562ae01255189c7f70027b4193..ad4f58dc18ace0bef7247380463aa33aad02c373 100644 (file)
@@ -143,7 +143,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
 ```shell
 sudo mkdir -p /opt/pleroma
 sudo chown -R pleroma:pleroma /opt/pleroma
-sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
 ```
 
 * Change to the new directory:
index e5f8dd6709c9425bc23e0685a26136a4ac28c206..fe2dbb92d0948dac840a9c49937e6076cd8e73a3 100644 (file)
@@ -68,7 +68,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
 ```shell
 sudo mkdir -p /opt/pleroma
 sudo chown -R pleroma:pleroma /opt/pleroma
-sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
 ```
 
 * Change to the new directory:
index e7d834b1730924077f0a1bbe9bd2dda5bbbb95e8..7aa0bcc247a54a1a67608eca5004da127794e1cb 100644 (file)
@@ -68,7 +68,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
 ```
 sudo mkdir -p /opt/pleroma
 sudo chown -R pleroma:pleroma /opt/pleroma
-sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
 ```
 
 *  新しいディレクトリに移動します。
index 55d677acf99700b3766d9a6d50de635e00d08fd2..1e61373cccff0d942dafd9707fded2c32486fc51 100644 (file)
@@ -106,7 +106,7 @@ It is highly recommended you use your own fork for the `https://path/to/repo` pa
 
 ```shell
  pleroma$ cd ~
- pleroma$ git clone -b master https://path/to/repo
+ pleroma$ git clone -b stable https://path/to/repo
 ```
 
 * Change to the new directory:
index e204cb3ee5f2bc76f47a0e2a017ddc03504d1bfc..87568faade6db158a3da0b24be257a1d0dbaba58 100644 (file)
@@ -96,9 +96,9 @@ rm -r ~pleroma/*
 export FLAVOUR="arm64-musl"
 
 # Clone the release build into a temporary directory and unpack it
-# Replace `master` with `develop` if you want to run the develop branch
+# Replace `stable` with `unstable` if you want to run the unstable branch
 su pleroma -s $SHELL -lc "
-curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/master/download?job=$FLAVOUR' -o /tmp/pleroma.zip
+curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
 unzip /tmp/pleroma.zip -d /tmp/
 "
 
index a096d5354d663aa8f7a829aaac657255d68796ce..6a922a27e4639b0cb71b80d6a7356d51e375745f 100644 (file)
@@ -58,7 +58,7 @@ Clone the repository:
 
 ```
 $ cd /home/pleroma
-$ git clone -b master https://git.pleroma.social/pleroma/pleroma.git
+$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git
 ```
 
 Configure Pleroma. Note that you need a domain name at this point:
index fcba38b2c8800579f06fe38de2ac5ef32014640a..3585a326ba181935f67292f5c588d81f3d5aa6ad 100644 (file)
@@ -29,7 +29,7 @@ This creates a "pleroma" login class and sets higher values than default for dat
 Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
 
 #### Clone pleroma's directory
-Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b master https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
+Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
 
 #### Postgresql
 Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:  
index 39819a8c8e8d0d08d53fdcc3fe0cf409cb9d436e..272273cff1133e17e26124285f304d69b17a7564 100644 (file)
@@ -44,7 +44,7 @@ Vaihda pleroma-käyttäjään ja mene kotihakemistoosi:
 
 Lataa pleroman lähdekoodi:
 
-`$ git clone -b master https://git.pleroma.social/pleroma/pleroma.git`
+`$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`
 
 `$ cd pleroma`
 
index b4e5254cdeba4d94ecaeef6dbf399eb8186859d5..c028f4229983ff82c3e7c9e677732e9a16972925 100644 (file)
@@ -80,7 +80,7 @@ export FLAVOUR="arm64-musl"
 
 # Clone the release build into a temporary directory and unpack it
 su pleroma -s $SHELL -lc "
-curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/master/download?job=$FLAVOUR' -o /tmp/pleroma.zip
+curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
 unzip /tmp/pleroma.zip -d /tmp/
 "
 
index 42649ff02b6a421646dbaf25a8deee15f309f211..e8884e6e81c01bf15a3e0247fb4320831647af1c 100644 (file)
@@ -352,10 +352,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "delete",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => subjects
         }
       }) do
-    "@#{actor_nickname} deleted user @#{subject_nickname}"
+    "@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
index d94ae5971c5a7e8159ed3361d8bdcd402f43ea14..d145f8d5b5a42c8ba3b685196754e45a1eaf682e 100644 (file)
@@ -17,6 +17,7 @@ defmodule Pleroma.Notification do
 
   import Ecto.Query
   import Ecto.Changeset
+  require Logger
 
   @type t :: %__MODULE__{}
 
@@ -34,43 +35,92 @@ defmodule Pleroma.Notification do
   end
 
   def for_user_query(user, opts \\ []) do
-    query =
-      Notification
-      |> where(user_id: ^user.id)
-      |> where(
-        [n, a],
+    Notification
+    |> where(user_id: ^user.id)
+    |> where(
+      [n, a],
+      fragment(
+        "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
+        a.actor
+      )
+    )
+    |> join(:inner, [n], activity in assoc(n, :activity))
+    |> join(:left, [n, a], object in Object,
+      on:
         fragment(
-          "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
-          a.actor
+          "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
+          object.data,
+          a.data
         )
-      )
-      |> 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})
+    )
+    |> preload([n, a, o], activity: {a, object: o})
+    |> exclude_muted(user, opts)
+    |> exclude_visibility(opts)
+  end
+
+  defp exclude_muted(query, _, %{with_muted: true}) do
+    query
+  end
+
+  defp exclude_muted(query, user, _opts) do
+    query
+    |> where([n, a], a.actor not in ^user.info.muted_notifications)
+    |> where([n, a], a.actor not in ^user.info.blocks)
+    |> where(
+      [n, a],
+      fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
+    )
+    |> join(:left, [n, a], tm in Pleroma.ThreadMute,
+      on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
+    )
+    |> where([n, a, o, tm], is_nil(tm.user_id))
+  end
 
-    if opts[:with_muted] do
+  @valid_visibilities ~w[direct unlisted public private]
+
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when is_list(visibility) do
+    if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
       query
-    else
-      where(query, [n, a], a.actor not in ^user.info.muted_notifications)
-      |> where([n, a], a.actor not in ^user.info.blocks)
       |> where(
         [n, a],
-        fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
-      )
-      |> join(:left, [n, a], tm in Pleroma.ThreadMute,
-        on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
+        not fragment(
+          "activity_visibility(?, ?, ?) = ANY (?)",
+          a.actor,
+          a.recipients,
+          a.data,
+          ^visibility
+        )
       )
-      |> where([n, a, o, tm], is_nil(tm.user_id))
+    else
+      Logger.error("Could not exclude visibility to #{visibility}")
+      query
     end
   end
 
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when visibility in @valid_visibilities do
+    query
+    |> where(
+      [n, a],
+      not fragment(
+        "activity_visibility(?, ?, ?) = (?)",
+        a.actor,
+        a.recipients,
+        a.data,
+        ^visibility
+      )
+    )
+  end
+
+  defp exclude_visibility(query, %{exclude_visibilities: visibility})
+       when visibility not in @valid_visibilities do
+    Logger.error("Could not exclude visibility to #{visibility}")
+    query
+  end
+
+  defp exclude_visibility(query, _visibility), do: query
+
   def for_user(user, opts \\ %{}) do
     user
     |> for_user_query(opts)
index 78144cae31400f82969c4bec9df08253570ef5d2..2ed7193150729b906f1f50ee5fffc9c98e7130e5 100644 (file)
@@ -401,11 +401,9 @@ defmodule Pleroma.ReverseProxy do
 
   defp client, do: Pleroma.ReverseProxy.Client
 
-  defp track_failed_url(url, code, opts) do
-    code = to_string(code)
-
+  defp track_failed_url(url, error, opts) do
     ttl =
-      if code in ["403", "404"] or String.starts_with?(code, "5") do
+      unless error in [:body_too_large, 400, 204] do
         Keyword.get(opts, :failed_request_ttl, @failed_request_ttl)
       else
         nil
index a76a5ad702415717d88855deefcb2f1ed9a4aca4..596584062dff3d5f52631726b5441f8b5186c1d2 100644 (file)
@@ -1080,6 +1080,10 @@ defmodule Pleroma.User do
     update_info(user, &User.Info.update_notification_settings(&1, settings))
   end
 
+  def delete(users) when is_list(users) do
+    for user <- users, do: delete(user)
+  end
+
   def delete(%User{} = user) do
     BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
   end
index 9f29087df4438fa26f499c869158f12cedc327eb..1d34c4d7ef8e9f1049f4064458b70ce9246c4e10 100644 (file)
@@ -269,22 +269,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  def accept(%{to: to, actor: actor, object: object} = params) do
-    # only accept false as false value
-    local = !(params[:local] == false)
+  def accept(params) do
+    accept_or_reject("Accept", params)
+  end
 
-    with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
-         {:ok, activity} <- insert(data, local),
-         :ok <- maybe_federate(activity) do
-      {:ok, activity}
-    end
+  def reject(params) do
+    accept_or_reject("Reject", params)
   end
 
-  def reject(%{to: to, actor: actor, object: object} = params) do
-    # only accept false as false value
-    local = !(params[:local] == false)
+  def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
+    local = Map.get(params, :local, true)
+    activity_id = Map.get(params, :activity_id, nil)
 
-    with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
+    with data <-
+           %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
+           |> Utils.maybe_put("id", activity_id),
          {:ok, activity} <- insert(data, local),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
@@ -409,18 +408,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
+  def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
+    local = Keyword.get(options, :local, true)
+    activity_id = Keyword.get(options, :activity_id, nil)
+    actor = Keyword.get(options, :actor, actor)
+
     user = User.get_cached_by_ap_id(actor)
     to = (object.data["to"] || []) ++ (object.data["cc"] || [])
 
     with {:ok, object, activity} <- Object.delete(object),
-         data <- %{
-           "type" => "Delete",
-           "actor" => actor,
-           "object" => id,
-           "to" => to,
-           "deleted_activity_id" => activity && activity.id
-         },
+         data <-
+           %{
+             "type" => "Delete",
+             "actor" => actor,
+             "object" => id,
+             "to" => to,
+             "deleted_activity_id" => activity && activity.id
+           }
+           |> maybe_put("id", activity_id),
          {:ok, activity} <- insert(data, local, false),
          stream_out_participations(object, user),
          _ <- decrease_replies_count_if_reply(object),
@@ -591,6 +596,49 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_visibility(query, _visibility), do: query
 
+  defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
+       when is_list(visibility) do
+    if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
+      from(
+        a in query,
+        where:
+          not fragment(
+            "activity_visibility(?, ?, ?) = ANY (?)",
+            a.actor,
+            a.recipients,
+            a.data,
+            ^visibility
+          )
+      )
+    else
+      Logger.error("Could not exclude visibility to #{visibility}")
+      query
+    end
+  end
+
+  defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
+       when visibility in @valid_visibilities do
+    from(
+      a in query,
+      where:
+        not fragment(
+          "activity_visibility(?, ?, ?) = ?",
+          a.actor,
+          a.recipients,
+          a.data,
+          ^visibility
+        )
+    )
+  end
+
+  defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
+       when visibility not in @valid_visibilities do
+    Logger.error("Could not exclude visibility to #{visibility}")
+    query
+  end
+
+  defp exclude_visibility(query, _visibility), do: query
+
   defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
     do: query
 
@@ -955,6 +1003,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     |> restrict_muted_reblogs(opts)
     |> Activity.restrict_deactivated_users()
     |> exclude_poll_votes(opts)
+    |> exclude_visibility(opts)
   end
 
   def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
index 872ed0eb225f757ca8e38c70100bed7ce818d399..b56343beb619f330a195979a246b59517e807c54 100644 (file)
@@ -514,7 +514,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
+        %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => id} = data,
         _options
       ) do
     with actor <- Containment.get_actor(data),
@@ -528,7 +528,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         type: "Accept",
         actor: followed,
         object: follow_activity.data["id"],
-        local: false
+        local: false,
+        activity_id: id
       })
     else
       _e -> :error
@@ -536,7 +537,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
+        %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => id} = data,
         _options
       ) do
     with actor <- Containment.get_actor(data),
@@ -550,7 +551,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
              type: "Reject",
              actor: followed,
              object: follow_activity.data["id"],
-             local: false
+             local: false,
+             activity_id: id
            }) do
       User.unfollow(follower, followed)
 
@@ -637,7 +639,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   # an error or a tombstone.  This would allow us to verify that a deletion actually took
   # place.
   def handle_incoming(
-        %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
+        %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data,
         _options
       ) do
     object_id = Utils.get_ap_id(object_id)
@@ -646,7 +648,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
          {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
          {:ok, object} <- get_obj_helper(object_id),
          :ok <- Containment.contain_origin(actor.ap_id, object.data),
-         {:ok, activity} <- ActivityPub.delete(object, false) do
+         {:ok, activity} <-
+           ActivityPub.delete(object, local: false, activity_id: id, actor: actor.ap_id) do
       {:ok, activity}
     else
       nil ->
index 33e2180ec05e9ded842e3904d2cc5c8933596b9b..ab0d0fe0a4f976ea6f5bbe64b7e0d9fab6f45009 100644 (file)
@@ -100,7 +100,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
     ModerationLog.insert_log(%{
       actor: admin,
-      subject: user,
+      subject: [user],
       action: "delete"
     })
 
@@ -108,6 +108,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> json(nickname)
   end
 
+  def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+    User.delete(users)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "delete"
+    })
+
+    conn
+    |> json(nicknames)
+  end
+
   def user_follow(%{assigns: %{user: admin}} = conn, %{
         "follower" => follower_nick,
         "followed" => followed_nick
index ac01d1ff39a42639f4b457b780b5893e0429c3e5..d875a578840b4bfa6ec3af391c537aea05f7378c 100644 (file)
@@ -71,6 +71,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
   defp cast_params(params) do
     param_types = %{
       exclude_types: {:array, :string},
+      exclude_visibilities: {:array, :string},
       reblogs: :boolean,
       with_muted: :boolean
     }
index b2f9f39463deab9d9324af6bd9995df55f9a1fcf..ac2dc115c33bb6165af6eaa55b777658e04baf71 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.OStatus.DeleteHandler do
   def handle_delete(entry, _doc \\ nil) do
     with id <- XML.string_from_xpath("//id", entry),
          %Object{} = object <- Object.normalize(id),
-         {:ok, delete} <- ActivityPub.delete(object, false) do
+         {:ok, delete} <- ActivityPub.delete(object, local: false) do
       delete
     end
   end
diff --git a/mix.exs b/mix.exs
index 3a605b4553d41c74ede5cd731007555f28736342..6cf766c521cdbd109387b2b471b58b5c07c17397 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -220,7 +220,10 @@ defmodule Pleroma.Mixfile do
       with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
            branch_name <- String.trim(branch_name),
            branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
-           true <- branch_name not in ["master", "HEAD"] do
+           true <-
+             !Enum.any?(["master", "HEAD", "release/", "stable"], fn name ->
+               String.starts_with?(name, branch_name)
+             end) do
         branch_name =
           branch_name
           |> String.trim()
index f767fe134d9a6d8f9b760b94e0ee6f50dfb0a75c..90f87a9909ed3960976019088645119c8958a917 100755 (executable)
@@ -35,11 +35,11 @@ detect_branch() {
        if [ "$branch" = "develop" ]; then
                echo "develop"
        elif [ "$branch" = "" ]; then
-               echo "master"
+               echo "stable"
        else
                # Note: branch name in version is of SemVer format and may only contain [0-9a-zA-Z-] symbols —
                #   if supporting releases for more branches, need to ensure they contain only these symbols.
-               echo "Releases are built only for master and develop branches" >&2
+               echo "Can't detect the branch automatically, please specify it by using the --branch option." >&2
                exit 1
        fi
 }
index ead97e94886d4c8dd0fe9feaae45fdf696526fa3..81c0fef12766f3d0ef8f55e1dd8fea0206261bd6 100644 (file)
@@ -24,13 +24,13 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "delete"
         })
 
       log = Repo.one(ModerationLog)
 
-      assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}"
+      assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}"
     end
 
     test "logging user creation by moderator", %{
index c9f2a92e78a8298afa58b96a065f9527487355b3..3a5a2f9840a4674ef51560e19c11875c5d656df6 100644 (file)
@@ -87,6 +87,66 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     end
   end
 
+  describe "fetching excluded by visibility" do
+    test "it excludes by the appropriate visibility" do
+      user = insert(:user)
+
+      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+
+      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+      {:ok, unlisted_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+
+      {:ok, private_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+
+      activities =
+        ActivityPub.fetch_activities([], %{
+          "exclude_visibilities" => "direct",
+          "actor_id" => user.ap_id
+        })
+
+      assert public_activity in activities
+      assert unlisted_activity in activities
+      assert private_activity in activities
+      refute direct_activity in activities
+
+      activities =
+        ActivityPub.fetch_activities([], %{
+          "exclude_visibilities" => "unlisted",
+          "actor_id" => user.ap_id
+        })
+
+      assert public_activity in activities
+      refute unlisted_activity in activities
+      assert private_activity in activities
+      assert direct_activity in activities
+
+      activities =
+        ActivityPub.fetch_activities([], %{
+          "exclude_visibilities" => "private",
+          "actor_id" => user.ap_id
+        })
+
+      assert public_activity in activities
+      assert unlisted_activity in activities
+      refute private_activity in activities
+      assert direct_activity in activities
+
+      activities =
+        ActivityPub.fetch_activities([], %{
+          "exclude_visibilities" => "public",
+          "actor_id" => user.ap_id
+        })
+
+      refute public_activity in activities
+      assert unlisted_activity in activities
+      assert private_activity in activities
+      assert direct_activity in activities
+    end
+  end
+
   describe "building a user from his ap id" do
     test "it returns a user" do
       user_id = "http://mastodon.example.org/users/admin"
index 50c0bfb8428e1a29c807af1a417d77009ae6d5e7..6c35a6f4d820f988a32c62f2a6a633836954ea4d 100644 (file)
@@ -682,6 +682,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming deletes" do
       activity = insert(:note_activity)
+      deleting_user = insert(:user)
 
       data =
         File.read!("test/fixtures/mastodon-delete.json")
@@ -694,11 +695,14 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       data =
         data
         |> Map.put("object", object)
-        |> Map.put("actor", activity.data["actor"])
+        |> Map.put("actor", deleting_user.ap_id)
 
-      {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} =
+        Transmogrifier.handle_incoming(data)
 
+      assert id == data["id"]
       refute Activity.get_by_id(activity.id)
+      assert actor == deleting_user.ap_id
     end
 
     test "it fails for incoming deletes with spoofed origin" do
@@ -905,6 +909,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       assert activity.data["object"] == follow_activity.data["id"]
 
+      assert activity.data["id"] == accept_data["id"]
+
       follower = User.get_cached_by_id(follower.id)
 
       assert User.following?(follower, followed) == true
@@ -1009,6 +1015,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
       refute activity.local
+      assert activity.data["id"] == reject_data["id"]
 
       follower = User.get_cached_by_id(follower.id)
 
index 81dc5d101d6216ab39a9ab143c399f683db1d42b..645b79f5776a55be5b665e18c4ef5446f8cd32cc 100644 (file)
@@ -17,8 +17,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   alias Pleroma.Web.MediaProxy
   import Pleroma.Factory
 
-  describe "/api/pleroma/admin/users" do
-    test "Delete" do
+  describe "DELETE /api/pleroma/admin/users" do
+    test "single user" do
       admin = insert(:user, info: %{is_admin: true})
       user = insert(:user)
 
@@ -30,15 +30,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       log_entry = Repo.one(ModerationLog)
 
-      assert log_entry.data["subject"]["nickname"] == user.nickname
-      assert log_entry.data["action"] == "delete"
-
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deleted user @#{user.nickname}"
+               "@#{admin.nickname} deleted users: @#{user.nickname}"
 
       assert json_response(conn, 200) == user.nickname
     end
 
+    test "multiple users" do
+      admin = insert(:user, info: %{is_admin: true})
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
+
+      response = json_response(conn, 200)
+      assert response -- [user_one.nickname, user_two.nickname] == []
+    end
+  end
+
+  describe "/api/pleroma/admin/users" do
     test "Create" do
       admin = insert(:user, info: %{is_admin: true})
 
index 6a59c3d947f45983920fb25455ae3c09359fbf0f..745383757aa19e7ee8dd5ccc2f7e024f9f9186ba 100644 (file)
@@ -237,6 +237,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
       assert [%{"id" => id}] = json_response(conn, 200)
       assert id == to_string(post.id)
     end
+
+    test "the user views their own timelines and excludes direct messages", %{conn: conn} do
+      user = insert(:user)
+      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+      {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_visibilities" => ["direct"]})
+
+      assert [%{"id" => id}] = json_response(conn, 200)
+      assert id == to_string(public_activity.id)
+    end
   end
 
   describe "followers" do
index e4137e92c92fb0d302813ebecb8f6ac0bc47ebc1..fa55a7cf927e33313dd00b074bcf607f1fb8f04b 100644 (file)
@@ -137,6 +137,57 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
     assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
   end
 
+  test "filters notifications using exclude_visibilities", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, public_activity} =
+      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
+
+    {:ok, direct_activity} =
+      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
+
+    {:ok, unlisted_activity} =
+      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
+
+    {:ok, private_activity} =
+      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
+
+    conn = assign(conn, :user, user)
+
+    conn_res =
+      get(conn, "/api/v1/notifications", %{
+        exclude_visibilities: ["public", "unlisted", "private"]
+      })
+
+    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
+    assert id == direct_activity.id
+
+    conn_res =
+      get(conn, "/api/v1/notifications", %{
+        exclude_visibilities: ["public", "unlisted", "direct"]
+      })
+
+    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
+    assert id == private_activity.id
+
+    conn_res =
+      get(conn, "/api/v1/notifications", %{
+        exclude_visibilities: ["public", "private", "direct"]
+      })
+
+    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
+    assert id == unlisted_activity.id
+
+    conn_res =
+      get(conn, "/api/v1/notifications", %{
+        exclude_visibilities: ["unlisted", "private", "direct"]
+      })
+
+    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
+    assert id == public_activity.id
+  end
+
   test "filters notifications using exclude_types", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
index 0ca896e012114403ec0d035793521eac8fca902d..ee413eef7c8219a4f132f7df794db8397f185a29 100644 (file)
@@ -40,9 +40,9 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
     test "search", %{conn: conn} do
       user = insert(:user)
       user_two = insert(:user, %{nickname: "shp@shitposter.club"})
-      user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu 天子"})
+      user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
 
-      {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"})
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private 天子"})
 
       {:ok, _activity} =
         CommonAPI.post(user, %{
@@ -70,8 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
         get(conn, "/api/v2/search", %{"q" => "天子"})
         |> json_response(200)
 
-      [account] == results["accounts"]
-      assert account["id"] == to_string(user_three.id)
+      [status] = results["statuses"]
+      assert status["id"] == to_string(activity.id)
     end
   end
 
index d3652d964b17ee2ddc22096ebc6bd5f811eabf58..fc45c25de98c893a7f31ca67282463348064c43e 100644 (file)
@@ -20,27 +20,52 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
     :ok
   end
 
-  test "the home timeline", %{conn: conn} do
-    user = insert(:user)
-    following = insert(:user)
+  describe "home" do
+    test "the home timeline", %{conn: conn} do
+      user = insert(:user)
+      following = insert(:user)
 
-    {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
+      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
 
-    conn =
-      conn
-      |> assign(:user, user)
-      |> get("/api/v1/timelines/home")
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/timelines/home")
 
-    assert Enum.empty?(json_response(conn, :ok))
+      assert Enum.empty?(json_response(conn, :ok))
 
-    {:ok, user} = User.follow(user, following)
+      {:ok, user} = User.follow(user, following)
 
-    conn =
-      build_conn()
-      |> assign(:user, user)
-      |> get("/api/v1/timelines/home")
+      conn =
+        build_conn()
+        |> assign(:user, user)
+        |> get("/api/v1/timelines/home")
 
-    assert [%{"content" => "test"}] = json_response(conn, :ok)
+      assert [%{"content" => "test"}] = json_response(conn, :ok)
+    end
+
+    test "the home timeline when the direct messages are excluded", %{conn: conn} do
+      user = insert(:user)
+      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+      {:ok, unlisted_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+
+      {:ok, private_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]})
+
+      assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"])
+      assert public_activity.id in status_ids
+      assert unlisted_activity.id in status_ids
+      assert private_activity.id in status_ids
+      refute direct_activity.id in status_ids
+    end
   end
 
   describe "public" do