diff options
41 files changed, 789 insertions, 127 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c3ca5ee0..04af8c186 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -101,7 +101,7 @@ docs-deploy: stage: deploy image: alpine:latest only: - - master@pleroma/pleroma + - stable@pleroma/pleroma - develop@pleroma/pleroma before_script: - apk add curl @@ -158,9 +158,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: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b24db7f4..e3ccfa4ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/) - 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 ### Changed - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) @@ -27,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - 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`) ### Fixed - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`) @@ -35,6 +38,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` diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 21b297529..aca0f5e0e 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -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: diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md index f5d1fade1..2a9b8f6ff 100644 --- a/docs/installation/alpine_linux_en.md +++ b/docs/installation/alpine_linux_en.md @@ -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: diff --git a/docs/installation/arch_linux_en.md b/docs/installation/arch_linux_en.md index 58a8d023f..8370986ad 100644 --- a/docs/installation/arch_linux_en.md +++ b/docs/installation/arch_linux_en.md @@ -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: diff --git a/docs/installation/centos7_en.md b/docs/installation/centos7_en.md index fe26339b5..ad4f58dc1 100644 --- a/docs/installation/centos7_en.md +++ b/docs/installation/centos7_en.md @@ -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: diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md index e5f8dd670..fe2dbb92d 100644 --- a/docs/installation/debian_based_en.md +++ b/docs/installation/debian_based_en.md @@ -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: diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md index e7d834b17..7aa0bcc24 100644 --- a/docs/installation/debian_based_jp.md +++ b/docs/installation/debian_based_jp.md @@ -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 ``` * 新しいディレクトリに移動します。 diff --git a/docs/installation/gentoo_en.md b/docs/installation/gentoo_en.md index 55d677acf..1e61373cc 100644 --- a/docs/installation/gentoo_en.md +++ b/docs/installation/gentoo_en.md @@ -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: diff --git a/docs/installation/migrating_from_source_otp_en.md b/docs/installation/migrating_from_source_otp_en.md index e204cb3ee..87568faad 100644 --- a/docs/installation/migrating_from_source_otp_en.md +++ b/docs/installation/migrating_from_source_otp_en.md @@ -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/ " diff --git a/docs/installation/netbsd_en.md b/docs/installation/netbsd_en.md index a096d5354..6a922a27e 100644 --- a/docs/installation/netbsd_en.md +++ b/docs/installation/netbsd_en.md @@ -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: diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md index fcba38b2c..3585a326b 100644 --- a/docs/installation/openbsd_en.md +++ b/docs/installation/openbsd_en.md @@ -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: diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md index 39819a8c8..272273cff 100644 --- a/docs/installation/openbsd_fi.md +++ b/docs/installation/openbsd_fi.md @@ -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` diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index b4e5254cd..c028f4229 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -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/ " diff --git a/lib/mix/tasks/pleroma/count_statuses.ex b/lib/mix/tasks/pleroma/count_statuses.ex new file mode 100644 index 000000000..e1e8195dd --- /dev/null +++ b/lib/mix/tasks/pleroma/count_statuses.ex @@ -0,0 +1,22 @@ +defmodule Mix.Tasks.Pleroma.CountStatuses do + @shortdoc "Re-counts statuses for all users" + + use Mix.Task + alias Pleroma.User + import Ecto.Query + + def run([]) do + Mix.Pleroma.start_pleroma() + + stream = + User + |> where(local: true) + |> Pleroma.Repo.stream() + + Pleroma.Repo.transaction(fn -> + Enum.each(stream, &User.update_note_count/1) + end) + + Mix.Pleroma.shell_info("Done") + end +end diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index d94ae5971..d145f8d5b 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -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) diff --git a/lib/pleroma/reverse_proxy/reverse_proxy.ex b/lib/pleroma/reverse_proxy/reverse_proxy.ex index 78144cae3..2ed719315 100644 --- a/lib/pleroma/reverse_proxy/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy/reverse_proxy.ex @@ -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 diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index b1dee010b..091ec2588 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -274,22 +274,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} @@ -414,18 +413,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), @@ -596,6 +601,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 @@ -960,6 +1008,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 diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 872ed0eb2..b56343beb 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -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 -> diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 0c16e9b0f..e5d016f63 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -167,7 +167,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do - try_render(conn, "show.json", activity: activity, for: user) + try_render(conn, "show.json", + activity: activity, + for: user, + with_direct_conversation_id: true + ) end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index ac01d1ff3..d875a5788 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -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 } diff --git a/lib/pleroma/web/ostatus/handlers/delete_handler.ex b/lib/pleroma/web/ostatus/handlers/delete_handler.ex index b2f9f3946..ac2dc115c 100644 --- a/lib/pleroma/web/ostatus/handlers/delete_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/delete_handler.ex @@ -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 @@ -221,7 +221,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() diff --git a/rel/files/bin/pleroma_ctl b/rel/files/bin/pleroma_ctl index e731d20eb..90f87a990 100755 --- a/rel/files/bin/pleroma_ctl +++ b/rel/files/bin/pleroma_ctl @@ -35,31 +35,62 @@ 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 + # 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 "Can't detect the branch automatically, please specify it by using the --branch option." >&2 exit 1 fi } update() { set -e + NO_RM=false + + while echo "$1" | grep "^-" >/dev/null; do + case "$1" in + --zip-url) + FULL_URI="$2" + shift 2 + ;; + --no-rm) + NO_RM=true + shift + ;; + --flavour) + FLAVOUR="$2" + shift 2 + ;; + --branch) + BRANCH="$2" + shift 2 + ;; + --tmp-dir) + TMP_DIR="$2" + shift 2 + ;; + -*) + echo "invalid option: $1" 1>&2 + shift + ;; + esac + done + RELEASE_ROOT=$(dirname "$SCRIPTPATH") - uri="${PLEROMA_CTL_URI:-https://git.pleroma.social}" - project_id="${PLEROMA_CTL_PROJECT_ID:-2}" - project_branch="$(detect_branch)" - flavour="${PLEROMA_CTL_FLAVOUR:-$(detect_flavour)}" - echo "Detected flavour: $flavour" - tmp="${PLEROMA_CTL_TMP_DIR:-/tmp}" + uri="https://git.pleroma.social" + project_id="2" + project_branch="${BRANCH:-$(detect_branch)}" + flavour="${FLAVOUR:-$(detect_flavour)}" + tmp="${TMP_DIR:-/tmp}" artifact="$tmp/pleroma.zip" - full_uri="${uri}/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=${flavour}" + full_uri="${FULL_URI:-${uri}/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=${flavour}}" echo "Downloading the artifact from ${full_uri} to ${artifact}" curl "$full_uri" -o "${artifact}" echo "Unpacking ${artifact} to ${tmp}" unzip -q "$artifact" -d "$tmp" echo "Copying files over to $RELEASE_ROOT" - if [ "$1" != "--no-rm" ]; then + if [ "$NO_RM" = false ]; then + echo "Removing files from the previous release" rm -r "${RELEASE_ROOT:-?}"/* fi cp -rf "$tmp/release"/* "$RELEASE_ROOT" @@ -86,36 +117,41 @@ if [ -z "$1" ] || [ "$1" = "help" ]; then Rollback database migrations (needs to be done before downgrading) update [OPTIONS] - Update the instance using the latest CI artifact for the current branch. - - The only supported option is --no-rm, when set the script won't delete the whole directory, but - just force copy over files from the new release. This wastes more space, but may be useful if - some files are stored inside of the release directories (although you really shouldn't store them - there), or if you want to be able to quickly revert a broken update. - - The script will try to detect your architecture and ABI and set a flavour automatically, - but if it is wrong, you can overwrite it by setting PLEROMA_CTL_FLAVOUR to the desired flavour. - - By default the artifact will be downloaded from https://git.pleroma.social for pleroma/pleroma (project id: 2) - to /tmp/, you can overwrite these settings by setting PLEROMA_CTL_URI, PLEROMA_CTL_PROJECT_ID and PLEROMA_CTL_TMP_DIR - respectively. + Update the instance. + Options: + --branch Update to a specified branch, instead of the latest version of the current one. + --flavour Update to a specified flavour (CPU architecture+libc), instead of the current one. + --zip-url Get the release from a specified url. If set, renders the previous 2 options inactive. + --no-rm Do not erase previous release's files. + --tmp-dir Download the temporary files to a specified directory. and any mix tasks under Pleroma namespace, for example \`mix pleroma.user COMMAND\` is equivalent to \`$(basename "$0") user COMMAND\` By default pleroma_ctl will try calling into a running instance to execute non migration-related commands, - if for some reason this is undesired, set PLEROMA_CTL_RPC_DISABLED environment variable + if for some reason this is undesired, set PLEROMA_CTL_RPC_DISABLED environment variable. + " else SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") - if [ "$1" = "update" ]; then - update "$2" - elif [ "$1" = "migrate" ] || [ "$1" = "rollback" ] || [ "$1" = "create" ] || [ "$1 $2" = "instance gen" ] || [ -n "$PLEROMA_CTL_RPC_DISABLED" ]; then - "$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$*"'")' + FULL_ARGS="$*" + + ACTION="$1" + shift + + if [ "$(echo \"$1\" | grep \"^-\" >/dev/null)" = false ]; then + SUBACTION="$1" + shift + fi + + if [ "$ACTION" = "update" ]; then + update "$@" + elif [ "$ACTION" = "migrate" ] || [ "$ACTION" = "rollback" ] || [ "$ACTION" = "create" ] || [ "$ACTION $SUBACTION" = "instance gen" ] || [ "$PLEROMA_CTL_RPC_DISABLED" = true ]; then + "$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")' else - "$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$*"'")' + "$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")' fi fi diff --git a/test/activity_test.exs b/test/activity_test.exs index 95d9341c4..e7ea2bd5e 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -126,9 +126,25 @@ defmodule Pleroma.ActivityTest do } {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "find me!"}) + {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "更新情報"}) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, remote_activity} = ObanHelpers.perform(job) - %{local_activity: local_activity, remote_activity: remote_activity, user: user} + + %{ + japanese_activity: japanese_activity, + local_activity: local_activity, + remote_activity: remote_activity, + user: user + } + end + + test "finds utf8 text in statuses", %{ + japanese_activity: japanese_activity, + user: user + } do + activities = Activity.search(user, "更新情報") + + assert [^japanese_activity] = activities end test "find local and remote statuses for authenticated users", %{ diff --git a/test/fixtures/tesla_mock/mstdn.jp_host_meta b/test/fixtures/tesla_mock/mstdn.jp_host_meta new file mode 100644 index 000000000..e76ddd47f --- /dev/null +++ b/test/fixtures/tesla_mock/mstdn.jp_host_meta @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Link rel="lrdd" type="application/xrd+xml" template="https://mstdn.jp/.well-known/webfinger?resource={uri}"/> +</XRD> diff --git a/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json b/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json new file mode 100644 index 000000000..3757c0dad --- /dev/null +++ b/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json @@ -0,0 +1,12 @@ +{ + "subject":"acct:pekorino@pawoo.net", + "aliases":["https://pawoo.net/@pekorino","https://pawoo.net/users/pekorino"], + "links":[ + {"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://pawoo.net/@pekorino"}, + {"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://pawoo.net/users/pekorino.atom"}, + {"rel":"self","type":"application/activity+json","href":"https://pawoo.net/users/pekorino"}, + {"rel":"salmon","href":"https://pawoo.net/api/salmon/128378"}, + {"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.1x8XXmBqzyb-QRkfUKxKPd7Ac2KbaFhdKy2FkJY64G-ifga-BppzEb62Q5TdkRdVKdHjh5qI7A1Hk3KfnNQcNWqqak-jxII_txC2grbWpp7v-boceD2pnzdVK5l-RR-9wEwxcoCUeRWS1Ak6DStqE5tFQOAK4IIGQB-thSQGlU75KZ-2080fPA3Xc_ycH3_eB4YqawSxXrh6IeScMevN0YHSF84GAcvhXmwLKZRugiF6nYrknbPEe_niIOmN8hhEXLN9_4kDcH83hkVZd5VXssRrxqDhtokx9emvTHkA7sY1AjYeehTPZErlV74GN-kFYLeI6DluXoSI2sX1QcS08w==.AQAB"}, + {"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://pawoo.net/authorize_follow?acct={uri}"} + ] +} diff --git a/test/fixtures/tesla_mock/sdf.org_host_meta b/test/fixtures/tesla_mock/sdf.org_host_meta new file mode 100644 index 000000000..0ffc4f096 --- /dev/null +++ b/test/fixtures/tesla_mock/sdf.org_host_meta @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Link rel="lrdd" type="application/xrd+xml" template="https://mastodon.sdf.org/.well-known/webfinger?resource={uri}"/> +</XRD> diff --git a/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json b/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json new file mode 100644 index 000000000..273fc3804 --- /dev/null +++ b/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json @@ -0,0 +1,12 @@ +{ + "subject":"acct:snowdusk@mastodon.sdf.org", + "aliases":["https://mastodon.sdf.org/@snowdusk","https://mastodon.sdf.org/users/snowdusk"], + "links":[ + {"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.sdf.org/@snowdusk"}, + {"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.sdf.org/users/snowdusk.atom"}, + {"rel":"self","type":"application/activity+json","href":"https://mastodon.sdf.org/users/snowdusk"}, + {"rel":"salmon","href":"https://mastodon.sdf.org/api/salmon/2"}, + {"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.k4_Hr0WQUHumAD4uwWIz7OybovIKgIuanbXhX5pl7oGyb2TuifBf3nAqEhD6eLSo6-_6160L4BvPPV_l_6rlZEi6_nbeJUgVkayZgcZN3oou3IErSt8L0IbUdWT5s4fWM2zpkndLCkVbeeNQ3DOBccvJw7iA_QNTao8wr3ILvQaKEDnf-H5QBd9Tj3seyo4-7E0e6wCKOH_uBm8pSRgpdMdl2CehiFzaABBkmCeUKH-buU7iNQGi0fsV5VIHn6zffrv6p0EVNkjTDi1vTmmfrp9W0mcKZJ9DtvdehOKSgh3J7Mem-ILbPy6FSL2Oi6Ekj_Wh4M8Ie-YKuxI3N_0Baw==.AQAB"}, + {"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.sdf.org/authorize_interaction?uri={uri}"} + ] +} diff --git a/test/fixtures/tesla_mock/soykaf.com_host_meta b/test/fixtures/tesla_mock/soykaf.com_host_meta new file mode 100644 index 000000000..99d552d32 --- /dev/null +++ b/test/fixtures/tesla_mock/soykaf.com_host_meta @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Link rel="lrdd" template="https://pleroma.soykaf.com/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /> +</XRD> diff --git a/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta b/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta new file mode 100644 index 000000000..481cfec8d --- /dev/null +++ b/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta @@ -0,0 +1,31 @@ +{ + "links":[ + { + "rel":"lrdd", + "type":"application\/jrd+json", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"lrdd", + "type":"application\/json", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"lrdd", + "type":"application\/xrd+xml", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/access_token", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/access_token" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/request_token", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/request_token" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/authorize", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/authorize" + } + ] +} diff --git a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta new file mode 100644 index 000000000..45d260e55 --- /dev/null +++ b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Link rel="lrdd" template="https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /> +</XRD> diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index b825a9307..4feb57f3a 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -344,6 +344,181 @@ defmodule HttpRequestMock do {:error, :nxdomain} end + def get("http://osada.macgirvin.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + + def get("https://osada.macgirvin.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + + def get("http://mastodon.sdf.org/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/sdf.org_host_meta") + }} + end + + def get("https://mastodon.sdf.org/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/sdf.org_host_meta") + }} + end + + def get( + "https://mastodon.sdf.org/.well-known/webfinger?resource=https://mastodon.sdf.org/users/snowdusk", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json") + }} + end + + def get("http://mstdn.jp/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mstdn.jp_host_meta") + }} + end + + def get("https://mstdn.jp/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mstdn.jp_host_meta") + }} + end + + def get("https://mstdn.jp/.well-known/webfinger?resource=kpherox@mstdn.jp", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml") + }} + end + + def get("http://mamot.fr/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mamot.fr_host_meta") + }} + end + + def get("https://mamot.fr/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mamot.fr_host_meta") + }} + end + + def get( + "https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/skruyb@mamot.fr.atom") + }} + end + + def get("http://pawoo.net/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end + + def get("https://pawoo.net/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end + + def get( + "https://pawoo.net/.well-known/webfinger?resource=https://pawoo.net/users/pekorino", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json") + }} + end + + def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") + }} + end + + def get("https://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") + }} + end + + def get("http://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/soykaf.com_host_meta") + }} + end + + def get("https://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/soykaf.com_host_meta") + }} + end + + def get("http://social.stopwatchingus-heidelberg.de/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta") + }} + end + + def get("https://social.stopwatchingus-heidelberg.de/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta") + }} + end + def get( "http://mastodon.example.org/@admin/99541947525187367", _, diff --git a/test/tasks/count_statuses_test.exs b/test/tasks/count_statuses_test.exs new file mode 100644 index 000000000..6035da3c3 --- /dev/null +++ b/test/tasks/count_statuses_test.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.CountStatusesTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureIO, only: [capture_io: 1] + import Pleroma.Factory + + test "counts statuses" do + user = insert(:user) + {:ok, _} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, _} = CommonAPI.post(user, %{"status" => "test2"}) + + user2 = insert(:user) + {:ok, _} = CommonAPI.post(user2, %{"status" => "test3"}) + + user = refresh_record(user) + user2 = refresh_record(user2) + + assert %{info: %{note_count: 2}} = user + assert %{info: %{note_count: 1}} = user2 + + {:ok, user} = User.update_info(user, &User.Info.set_note_count(&1, 0)) + {:ok, user2} = User.update_info(user2, &User.Info.set_note_count(&1, 0)) + + assert %{info: %{note_count: 0}} = user + assert %{info: %{note_count: 0}} = user2 + + assert capture_io(fn -> Mix.Tasks.Pleroma.CountStatuses.run([]) end) == "Done\n" + + assert %{info: %{note_count: 2}} = refresh_record(user) + assert %{info: %{note_count: 1}} = refresh_record(user2) + end +end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index c9f2a92e7..3a5a2f984 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -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" diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 50c0bfb84..6c35a6f4d 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -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) diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 6a59c3d94..745383757 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -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 diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index e4137e92c..fa55a7cf9 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -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) diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 49c79ff0a..ee413eef7 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -42,7 +42,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do user_two = insert(:user, %{nickname: "shp@shitposter.club"}) 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, %{ @@ -52,9 +52,9 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"}) - - assert results = json_response(conn, 200) + results = + get(conn, "/api/v2/search", %{"q" => "2hu #private"}) + |> json_response(200) [account | _] = results["accounts"] assert account["id"] == to_string(user_three.id) @@ -65,6 +65,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do [status] = results["statuses"] assert status["id"] == to_string(activity.id) + + results = + get(conn, "/api/v2/search", %{"q" => "天子"}) + |> json_response(200) + + [status] = results["statuses"] + assert status["id"] == to_string(activity.id) end end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index a4bbfe055..2de2725e0 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do alias Pleroma.Activity alias Pleroma.ActivityExpiration alias Pleroma.Config + alias Pleroma.Conversation.Participation alias Pleroma.Object alias Pleroma.Repo alias Pleroma.ScheduledActivity @@ -465,6 +466,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert id == to_string(activity.id) end + test "get a direct status", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{"status" => "@#{other_user.nickname}", "visibility" => "direct"}) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/statuses/#{activity.id}") + + [participation] = Participation.for_user(user) + + res = json_response(conn, 200) + assert res["pleroma"]["direct_conversation_id"] == participation.id + end + test "get statuses by IDs", %{conn: conn} do %{id: id1} = insert(:note_activity) %{id: id2} = insert(:note_activity) diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index d3652d964..fc45c25de 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -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 |