diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/mix/tasks/pleroma/user.ex | 83 | ||||
| -rw-r--r-- | lib/pleroma/notification.ex | 64 | ||||
| -rw-r--r-- | lib/pleroma/user.ex | 59 | ||||
| -rw-r--r-- | lib/pleroma/user/info.ex | 34 | ||||
| -rw-r--r-- | lib/pleroma/user_invite_token.ex | 113 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 16 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/admin_api_controller.ex | 27 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/views/account_view.ex | 18 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 19 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 28 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/views/account_view.ex | 23 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 31 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/controllers/util_controller.ex | 9 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 61 | 
15 files changed, 519 insertions, 72 deletions
| diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 0d0bea8c0..441168df2 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -7,6 +7,7 @@ defmodule Mix.Tasks.Pleroma.User do    import Ecto.Changeset    alias Mix.Tasks.Pleroma.Common    alias Pleroma.User +  alias Pleroma.UserInviteToken    @shortdoc "Manages Pleroma users"    @moduledoc """ @@ -26,7 +27,19 @@ defmodule Mix.Tasks.Pleroma.User do    ## Generate an invite link. -      mix pleroma.user invite +      mix pleroma.user invite [OPTION...] + +    Options: +    - `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05") +    - `--max_use NUMBER` - maximum numbers of token uses + +  ## List generated invites + +      mix pleroma.user invites + +  ## Revoke invite + +      mix pleroma.user revoke_invite TOKEN OR TOKEN_ID    ## Delete the user's account. @@ -287,23 +300,79 @@ defmodule Mix.Tasks.Pleroma.User do      end    end -  def run(["invite"]) do +  def run(["invite" | rest]) do +    {options, [], []} = +      OptionParser.parse(rest, +        strict: [ +          expires_at: :string, +          max_use: :integer +        ] +      ) + +    options = +      options +      |> Keyword.update(:expires_at, {:ok, nil}, fn +        nil -> {:ok, nil} +        val -> Date.from_iso8601(val) +      end) +      |> Enum.into(%{}) +      Common.start_pleroma() -    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do -      Mix.shell().info("Generated user invite token") +    with {:ok, val} <- options[:expires_at], +         options = Map.put(options, :expires_at, val), +         {:ok, invite} <- UserInviteToken.create_invite(options) do +      Mix.shell().info( +        "Generated user invite token " <> String.replace(invite.invite_type, "_", " ") +      )        url =          Pleroma.Web.Router.Helpers.redirect_url(            Pleroma.Web.Endpoint,            :registration_page, -          token.token +          invite.token          )        IO.puts(url)      else -      _ -> -        Mix.shell().error("Could not create invite token.") +      error -> +        Mix.shell().error("Could not create invite token: #{inspect(error)}") +    end +  end + +  def run(["invites"]) do +    Common.start_pleroma() + +    Mix.shell().info("Invites list:") + +    UserInviteToken.list_invites() +    |> Enum.each(fn invite -> +      expire_info = +        with expires_at when not is_nil(expires_at) <- invite.expires_at do +          " | Expires at: #{Date.to_string(expires_at)}" +        end + +      using_info = +        with max_use when not is_nil(max_use) <- invite.max_use do +          " | Max use: #{max_use}    Left use: #{max_use - invite.uses}" +        end + +      Mix.shell().info( +        "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{ +          invite.used +        }#{expire_info}#{using_info}" +      ) +    end) +  end + +  def run(["revoke_invite", token]) do +    Common.start_pleroma() + +    with {:ok, invite} <- UserInviteToken.find_by_token(token), +         {:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do +      Mix.shell().info("Invite for token #{token} was revoked.") +    else +      _ -> Mix.shell().error("No invite found with token #{token}")      end    end diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index cac10f24a..15789907a 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -122,13 +122,7 @@ defmodule Pleroma.Notification do    # TODO move to sql, too.    def create_notification(%Activity{} = activity, %User{} = user) do -    unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or -             CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or -             (activity.data["type"] == "Follow" and -                Enum.any?(Notification.for_user(user), fn notif -> -                  notif.activity.data["type"] == "Follow" and -                    notif.activity.data["actor"] == activity.data["actor"] -                end)) do +    unless skip?(activity, user) do        notification = %Notification{user_id: user.id, activity: activity}        {:ok, notification} = Repo.insert(notification)        Pleroma.Web.Streamer.stream("user", notification) @@ -148,10 +142,66 @@ defmodule Pleroma.Notification do        []        |> Utils.maybe_notify_to_recipients(activity)        |> Utils.maybe_notify_mentioned_recipients(activity) +      |> Utils.maybe_notify_subscribers(activity)        |> Enum.uniq()      User.get_users_from_set(recipients, local_only)    end    def get_notified_from_activity(_, _local_only), do: [] + +  def skip?(activity, user) do +    [:self, :blocked, :local, :muted, :followers, :follows, :recently_followed] +    |> Enum.any?(&skip?(&1, activity, user)) +  end + +  def skip?(:self, activity, user) do +    activity.data["actor"] == user.ap_id +  end + +  def skip?(:blocked, activity, user) do +    actor = activity.data["actor"] +    User.blocks?(user, %{ap_id: actor}) +  end + +  def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}), +    do: true + +  def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}), +    do: true + +  def skip?(:muted, activity, user) do +    actor = activity.data["actor"] + +    User.mutes?(user, %{ap_id: actor}) or +      CommonAPI.thread_muted?(user, activity) +  end + +  def skip?( +        :followers, +        activity, +        %{info: %{notification_settings: %{"followers" => false}}} = user +      ) do +    actor = activity.data["actor"] +    follower = User.get_cached_by_ap_id(actor) +    User.following?(follower, user) +  end + +  def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do +    actor = activity.data["actor"] +    followed = User.get_by_ap_id(actor) +    User.following?(user, followed) +  end + +  def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do +    actor = activity.data["actor"] + +    Notification.for_user(user) +    |> Enum.any?(fn +      %{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true +      _ -> false +    end) +  end + +  def skip?(_, _, _), do: false  end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 05f56c01e..6e2269aff 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -931,6 +931,38 @@ defmodule Pleroma.User do      update_and_set_cache(cng)    end +  def subscribe(subscriber, %{ap_id: ap_id}) do +    deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) + +    with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do +      blocked = blocks?(subscribed, subscriber) and deny_follow_blocked + +      if blocked do +        {:error, "Could not subscribe: #{subscribed.nickname} is blocking you"} +      else +        info_cng = +          subscribed.info +          |> User.Info.add_to_subscribers(subscriber.ap_id) + +        change(subscribed) +        |> put_embed(:info, info_cng) +        |> update_and_set_cache() +      end +    end +  end + +  def unsubscribe(unsubscriber, %{ap_id: ap_id}) do +    with %User{} = user <- get_cached_by_ap_id(ap_id) do +      info_cng = +        user.info +        |> User.Info.remove_from_subscribers(unsubscriber.ap_id) + +      change(user) +      |> put_embed(:info, info_cng) +      |> update_and_set_cache() +    end +  end +    def block(blocker, %User{ap_id: ap_id} = blocked) do      # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)      blocker = @@ -941,10 +973,20 @@ defmodule Pleroma.User do          blocker        end +    blocker = +      if subscribed_to?(blocked, blocker) do +        {:ok, blocker} = unsubscribe(blocked, blocker) +        blocker +      else +        blocker +      end +      if following?(blocked, blocker) do        unfollow(blocked, blocker)      end +    {:ok, blocker} = update_follower_count(blocker) +      info_cng =        blocker.info        |> User.Info.add_to_block(ap_id) @@ -987,12 +1029,21 @@ defmodule Pleroma.User do        end)    end +  def subscribed_to?(user, %{ap_id: ap_id}) do +    with %User{} = target <- User.get_by_ap_id(ap_id) do +      Enum.member?(target.info.subscribers, user.ap_id) +    end +  end +    def muted_users(user),      do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes))    def blocked_users(user),      do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks)) +  def subscribers(user), +    do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers)) +    def block_domain(user, domain) do      info_cng =        user.info @@ -1090,6 +1141,14 @@ defmodule Pleroma.User do      update_and_set_cache(cng)    end +  def update_notification_settings(%User{} = user, settings \\ %{}) do +    info_changeset = User.Info.update_notification_settings(user.info, settings) + +    change(user) +    |> put_embed(:info, info_changeset) +    |> update_and_set_cache() +  end +    def delete(%User{} = user) do      {:ok, user} = User.deactivate(user) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 740a46727..5afa7988c 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -22,6 +22,7 @@ defmodule Pleroma.User.Info do      field(:domain_blocks, {:array, :string}, default: [])      field(:mutes, {:array, :string}, default: [])      field(:muted_reblogs, {:array, :string}, default: []) +    field(:subscribers, {:array, :string}, default: [])      field(:deactivated, :boolean, default: false)      field(:no_rich_text, :boolean, default: false)      field(:ap_enabled, :boolean, default: false) @@ -40,6 +41,10 @@ defmodule Pleroma.User.Info do      field(:pinned_activities, {:array, :string}, default: [])      field(:flavour, :string, default: nil) +    field(:notification_settings, :map, +      default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true} +    ) +      # Found in the wild      # ap_id -> Where is this used?      # bio -> Where is this used? @@ -57,6 +62,19 @@ defmodule Pleroma.User.Info do      |> validate_required([:deactivated])    end +  def update_notification_settings(info, settings) do +    notification_settings = +      info.notification_settings +      |> Map.merge(settings) +      |> Map.take(["remote", "local", "followers", "follows"]) + +    params = %{notification_settings: notification_settings} + +    info +    |> cast(params, [:notification_settings]) +    |> validate_required([:notification_settings]) +  end +    def add_to_note_count(info, number) do      set_note_count(info, info.note_count + number)    end @@ -93,6 +111,14 @@ defmodule Pleroma.User.Info do      |> validate_required([:blocks])    end +  def set_subscribers(info, subscribers) do +    params = %{subscribers: subscribers} + +    info +    |> cast(params, [:subscribers]) +    |> validate_required([:subscribers]) +  end +    def add_to_mutes(info, muted) do      set_mutes(info, Enum.uniq([muted | info.mutes]))    end @@ -109,6 +135,14 @@ defmodule Pleroma.User.Info do      set_blocks(info, List.delete(info.blocks, blocked))    end +  def add_to_subscribers(info, subscribed) do +    set_subscribers(info, Enum.uniq([subscribed | info.subscribers])) +  end + +  def remove_from_subscribers(info, subscribed) do +    set_subscribers(info, List.delete(info.subscribers, subscribed)) +  end +    def set_domain_blocks(info, domain_blocks) do      params = %{domain_blocks: domain_blocks} diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex index 9c5579934..86f0a5486 100644 --- a/lib/pleroma/user_invite_token.ex +++ b/lib/pleroma/user_invite_token.ex @@ -6,40 +6,119 @@ defmodule Pleroma.UserInviteToken do    use Ecto.Schema    import Ecto.Changeset - +  import Ecto.Query    alias Pleroma.Repo    alias Pleroma.UserInviteToken +  @type t :: %__MODULE__{} +  @type token :: String.t() +    schema "user_invite_tokens" do      field(:token, :string)      field(:used, :boolean, default: false) +    field(:max_use, :integer) +    field(:expires_at, :date) +    field(:uses, :integer, default: 0) +    field(:invite_type, :string)      timestamps()    end -  def create_token do +  @spec create_invite(map()) :: UserInviteToken.t() +  def create_invite(params \\ %{}) do +    %UserInviteToken{} +    |> cast(params, [:max_use, :expires_at]) +    |> add_token() +    |> assign_type() +    |> Repo.insert() +  end + +  defp add_token(changeset) do      token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() +    put_change(changeset, :token, token) +  end -    token = %UserInviteToken{ -      used: false, -      token: token -    } +  defp assign_type(%{changes: %{max_use: _max_use, expires_at: _expires_at}} = changeset) do +    put_change(changeset, :invite_type, "reusable_date_limited") +  end + +  defp assign_type(%{changes: %{expires_at: _expires_at}} = changeset) do +    put_change(changeset, :invite_type, "date_limited") +  end + +  defp assign_type(%{changes: %{max_use: _max_use}} = changeset) do +    put_change(changeset, :invite_type, "reusable") +  end + +  defp assign_type(changeset), do: put_change(changeset, :invite_type, "one_time") -    Repo.insert(token) +  @spec list_invites() :: [UserInviteToken.t()] +  def list_invites do +    query = from(u in UserInviteToken, order_by: u.id) +    Repo.all(query)    end -  def used_changeset(struct) do -    struct -    |> cast(%{}, []) -    |> put_change(:used, true) +  @spec update_invite!(UserInviteToken.t(), map()) :: UserInviteToken.t() | no_return() +  def update_invite!(invite, changes) do +    change(invite, changes) |> Repo.update!()    end -  def mark_as_used(token) do -    with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}), -         {:ok, token} <- Repo.update(used_changeset(token)) do -      {:ok, token} -    else -      _e -> {:error, token} +  @spec update_invite(UserInviteToken.t(), map()) :: +          {:ok, UserInviteToken.t()} | {:error, Changeset.t()} +  def update_invite(invite, changes) do +    change(invite, changes) |> Repo.update() +  end + +  @spec find_by_token!(token()) :: UserInviteToken.t() | no_return() +  def find_by_token!(token), do: Repo.get_by!(UserInviteToken, token: token) + +  @spec find_by_token(token()) :: {:ok, UserInviteToken.t()} | nil +  def find_by_token(token) do +    with invite <- Repo.get_by(UserInviteToken, token: token) do +      {:ok, invite}      end    end + +  @spec valid_invite?(UserInviteToken.t()) :: boolean() +  def valid_invite?(%{invite_type: "one_time"} = invite) do +    not invite.used +  end + +  def valid_invite?(%{invite_type: "date_limited"} = invite) do +    not_overdue_date?(invite) and not invite.used +  end + +  def valid_invite?(%{invite_type: "reusable"} = invite) do +    invite.uses < invite.max_use and not invite.used +  end + +  def valid_invite?(%{invite_type: "reusable_date_limited"} = invite) do +    not_overdue_date?(invite) and invite.uses < invite.max_use and not invite.used +  end + +  defp not_overdue_date?(%{expires_at: expires_at}) do +    Date.compare(Date.utc_today(), expires_at) in [:lt, :eq] +  end + +  @spec update_usage!(UserInviteToken.t()) :: nil | UserInviteToken.t() | no_return() +  def update_usage!(%{invite_type: "date_limited"}), do: nil + +  def update_usage!(%{invite_type: "one_time"} = invite), +    do: update_invite!(invite, %{used: true}) + +  def update_usage!(%{invite_type: invite_type} = invite) +      when invite_type == "reusable" or invite_type == "reusable_date_limited" do +    changes = %{ +      uses: invite.uses + 1 +    } + +    changes = +      if changes.uses >= invite.max_use do +        Map.put(changes, :used, true) +      else +        changes +      end + +    update_invite!(invite, changes) +  end  end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 593ae3188..49ea73204 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -83,6 +83,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      |> fix_content_map      |> fix_likes      |> fix_addressing +    |> fix_summary +  end + +  def fix_summary(%{"summary" => nil} = object) do +    object +    |> Map.put("summary", "") +  end + +  def fix_summary(%{"summary" => _} = object) do +    # summary is present, nothing to do +    object +  end + +  def fix_summary(object) do +    object +    |> Map.put("summary", "")    end    def fix_addressing_list(map, field) do diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 78bf31893..70a5b5c5d 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -5,6 +5,7 @@  defmodule Pleroma.Web.AdminAPI.AdminAPIController do    use Pleroma.Web, :controller    alias Pleroma.User +  alias Pleroma.UserInviteToken    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.AdminAPI.AccountView    alias Pleroma.Web.AdminAPI.Search @@ -235,7 +236,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      with true <-             Pleroma.Config.get([:instance, :invites_enabled]) &&               !Pleroma.Config.get([:instance, :registrations_open]), -         {:ok, invite_token} <- Pleroma.UserInviteToken.create_token(), +         {:ok, invite_token} <- UserInviteToken.create_invite(),           email <-             Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),           {:ok, _} <- Pleroma.Mailer.deliver(email) do @@ -244,11 +245,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    end    @doc "Get a account registeration invite token (base64 string)" -  def get_invite_token(conn, _params) do -    {:ok, token} = Pleroma.UserInviteToken.create_token() +  def get_invite_token(conn, params) do +    options = params["invite"] || %{} +    {:ok, invite} = UserInviteToken.create_invite(options)      conn -    |> json(token.token) +    |> json(invite.token) +  end + +  @doc "Get list of created invites" +  def invites(conn, _params) do +    invites = UserInviteToken.list_invites() + +    conn +    |> json(AccountView.render("invites.json", %{invites: invites})) +  end + +  @doc "Revokes invite by token" +  def revoke_invite(conn, %{"token" => token}) do +    invite = UserInviteToken.find_by_token!(token) +    {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) + +    conn +    |> json(AccountView.render("invite.json", %{invite: updated_invite}))    end    @doc "Get a password reset token (base64 string) for given nickname" diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index 4d6f921ef..28bb667d8 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -26,4 +26,22 @@ defmodule Pleroma.Web.AdminAPI.AccountView do        "tags" => user.tags || []      }    end + +  def render("invite.json", %{invite: invite}) do +    %{ +      "id" => invite.id, +      "token" => invite.token, +      "used" => invite.used, +      "expires_at" => invite.expires_at, +      "uses" => invite.uses, +      "max_use" => invite.max_use, +      "invite_type" => invite.invite_type +    } +  end + +  def render("invites.json", %{invites: invites}) do +    %{ +      invites: render_many(invites, AccountView, "invite.json", as: :invite) +    } +  end  end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 051db6c79..7b9f0ea06 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web.ActivityPub.Utils +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.Endpoint    alias Pleroma.Web.MediaProxy @@ -335,6 +336,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do    def maybe_notify_mentioned_recipients(recipients, _), do: recipients +  def maybe_notify_subscribers( +        recipients, +        %Activity{data: %{"actor" => actor, "type" => type}} = activity +      ) +      when type == "Create" do +    with %User{} = user <- User.get_cached_by_ap_id(actor) do +      subscriber_ids = +        user +        |> User.subscribers() +        |> Enum.filter(&Visibility.visible_for_user?(activity, &1)) +        |> Enum.map(& &1.ap_id) + +      recipients ++ subscriber_ids +    end +  end + +  def maybe_notify_subscribers(recipients, _), do: recipients +    def maybe_extract_mentions(%{"tag" => tag}) do      tag      |> Enum.filter(fn x -> is_map(x) end) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 5462ce8be..ed082abdf 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -931,6 +931,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      json(conn, %{})    end +  def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do +    with %User{} = subscription_target <- User.get_cached_by_id(id), +         {:ok, subscription_target} = User.subscribe(user, subscription_target) do +      conn +      |> put_view(AccountView) +      |> render("relationship.json", %{user: user, target: subscription_target}) +    else +      {:error, message} -> +        conn +        |> put_resp_content_type("application/json") +        |> send_resp(403, Jason.encode!(%{"error" => message})) +    end +  end + +  def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do +    with %User{} = subscription_target <- User.get_cached_by_id(id), +         {:ok, subscription_target} = User.unsubscribe(user, subscription_target) do +      conn +      |> put_view(AccountView) +      |> render("relationship.json", %{user: user, target: subscription_target}) +    else +      {:error, message} -> +        conn +        |> put_resp_content_type("application/json") +        |> send_resp(403, Jason.encode!(%{"error" => message})) +    end +  end +    def status_search(user, query) do      fetched =        if Regex.match?(~r/https?:/, query) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b5f3bbb9d..af56c4149 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -53,6 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do        blocking: User.blocks?(user, target),        muting: User.mutes?(user, target),        muting_notifications: false, +      subscribing: User.subscribed_to?(user, target),        requested: requested,        domain_blocking: false,        showing_reblogs: User.showing_reblogs?(user, target), @@ -117,13 +118,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do        },        # Pleroma extension -      pleroma: %{ -        confirmation_pending: user_info.confirmation_pending, -        tags: user.tags, -        is_moderator: user.info.is_moderator, -        is_admin: user.info.is_admin, -        relationship: relationship -      } +      pleroma: +        %{ +          confirmation_pending: user_info.confirmation_pending, +          tags: user.tags, +          is_moderator: user.info.is_moderator, +          is_admin: user.info.is_admin, +          relationship: relationship +        } +        |> with_notification_settings(user, opts[:for])      }    end @@ -132,4 +135,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do    end    defp username_from_nickname(_), do: nil + +  defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do +    Map.put(data, :notification_settings, user.info.notification_settings) +  end + +  defp with_notification_settings(data, _, _), do: data  end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 4c0b53bdd..d4a8e4fff 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -147,20 +147,39 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      content =        object        |> render_content() + +    content_html = +      content        |> HTML.get_cached_scrubbed_html_for_activity(          User.html_filter_policy(opts[:for]),          activity,          "mastoapi:content"        ) -    summary = -      (object["summary"] || "") +    content_plaintext = +      content +      |> HTML.get_cached_stripped_html_for_activity( +        activity, +        "mastoapi:content" +      ) + +    summary = object["summary"] || "" + +    summary_html = +      summary        |> HTML.get_cached_scrubbed_html_for_activity(          User.html_filter_policy(opts[:for]),          activity,          "mastoapi:summary"        ) +    summary_plaintext = +      summary +      |> HTML.get_cached_stripped_html_for_activity( +        activity, +        "mastoapi:summary" +      ) +      card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))      url = @@ -179,7 +198,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),        reblog: nil,        card: card, -      content: content, +      content: content_html,        created_at: created_at,        reblogs_count: announcement_count,        replies_count: object["repliesCount"] || 0, @@ -190,7 +209,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),        pinned: pinned?(activity, user),        sensitive: sensitive, -      spoiler_text: summary, +      spoiler_text: summary_html,        visibility: get_visibility(object),        media_attachments: attachments,        mentions: mentions, @@ -203,7 +222,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        emojis: build_emojis(activity.data["object"]["emoji"]),        pleroma: %{          local: activity.local, -        conversation_id: get_context_id(activity) +        conversation_id: get_context_id(activity), +        content: %{"text/plain" => content_plaintext}, +        spoiler_text: %{"text/plain" => summary_plaintext}        }      }    end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ef38fc34d..172f337db 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -168,6 +168,8 @@ defmodule Pleroma.Web.Router do      delete("/relay", AdminAPIController, :relay_unfollow)      get("/invite_token", AdminAPIController, :get_invite_token) +    get("/invites", AdminAPIController, :invites) +    post("/revoke_invite", AdminAPIController, :revoke_invite)      post("/email_invite", AdminAPIController, :email_invite)      get("/password_reset", AdminAPIController, :get_password_reset) @@ -193,6 +195,7 @@ defmodule Pleroma.Web.Router do        post("/change_password", UtilController, :change_password)        post("/delete_account", UtilController, :delete_account) +      put("/notification_settings", UtilController, :update_notificaton_settings)      end      scope [] do @@ -336,6 +339,9 @@ defmodule Pleroma.Web.Router do        post("/domain_blocks", MastodonAPIController, :block_domain)        delete("/domain_blocks", MastodonAPIController, :unblock_domain) + +      post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe) +      post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)      end      scope [] do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 26407aebd..d066d35f5 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -286,12 +286,19 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do      emoji =        Emoji.get_all()        |> Enum.map(fn {short_code, path, tags} -> -        %{short_code => %{image_url: path, tags: String.split(tags, ",")}} +        {short_code, %{image_url: path, tags: String.split(tags, ",")}}        end) +      |> Enum.into(%{})      json(conn, emoji)    end +  def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do +    with {:ok, _} <- User.update_notification_settings(user, params) do +      json(conn, %{status: "success"}) +    end +  end +    def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do      follow_import(conn, %{"list" => File.read!(listfile.path)})    end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 9b081a316..9e9a46cf1 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -129,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    end    def register_user(params) do -    token_string = params["token"] +    token = params["token"]      params = %{        nickname: params["nickname"], @@ -163,36 +163,49 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do        {:error, %{error: Jason.encode!(%{captcha: [error]})}}      else        registrations_open = Pleroma.Config.get([:instance, :registrations_open]) +      registration_process(registrations_open, params, token) +    end +  end -      # no need to query DB if registration is open -      token = -        unless registrations_open || is_nil(token_string) do -          Repo.get_by(UserInviteToken, %{token: token_string}) -        end +  defp registration_process(registration_open, params, token) +       when registration_open == false or is_nil(registration_open) do +    invite = +      unless is_nil(token) do +        Repo.get_by(UserInviteToken, %{token: token}) +      end -      cond do -        registrations_open || (!is_nil(token) && !token.used) -> -          changeset = User.register_changeset(%User{}, params) +    valid_invite? = invite && UserInviteToken.valid_invite?(invite) -          with {:ok, user} <- User.register(changeset) do -            !registrations_open && UserInviteToken.mark_as_used(token.token) +    case invite do +      nil -> +        {:error, "Invalid token"} -            {:ok, user} -          else -            {:error, changeset} -> -              errors = -                Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) -                |> Jason.encode!() +      invite when valid_invite? -> +        UserInviteToken.update_usage!(invite) +        create_user(params) -              {:error, %{error: errors}} -          end +      _ -> +        {:error, "Expired token"} +    end +  end -        !registrations_open && is_nil(token) -> -          {:error, "Invalid token"} +  defp registration_process(true, params, _token) do +    create_user(params) +  end -        !registrations_open && token.used -> -          {:error, "Expired token"} -      end +  defp create_user(params) do +    changeset = User.register_changeset(%User{}, params) + +    case User.register(changeset) do +      {:ok, user} -> +        {:ok, user} + +      {:error, changeset} -> +        errors = +          Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) +          |> Jason.encode!() + +        {:error, %{error: errors}}      end    end | 
