diff options
| author | Ivan Tashkinov <ivantashkinov@gmail.com> | 2020-02-05 07:56:36 +0300 | 
|---|---|---|
| committer | Ivan Tashkinov <ivantashkinov@gmail.com> | 2020-02-05 07:56:36 +0300 | 
| commit | e84fee5b8624c8909ddd8a7e0d99c6beea4f54d0 (patch) | |
| tree | d0d2bfc3d847f9bb4fa03f08d9d60f49042b2688 /lib | |
| parent | 86e4d23acb640efea8cbc879ddbeadfa0e04f9c8 (diff) | |
| parent | a56db789359c7c7d57b45e6c68f791eeadc171e4 (diff) | |
| download | pleroma-e84fee5b8624c8909ddd8a7e0d99c6beea4f54d0.tar.gz pleroma-e84fee5b8624c8909ddd8a7e0d99c6beea4f54d0.zip | |
Merge remote-tracking branch 'remotes/origin/develop' into 1505-threads-federation
Diffstat (limited to 'lib')
43 files changed, 543 insertions, 195 deletions
| diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 861832451..3e76d2c97 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -52,6 +52,9 @@ defmodule Mix.Tasks.Pleroma.Config do    defp do_migrate_to_db(config_file) do      if File.exists?(config_file) do +      Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;") +      Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;") +        custom_config =          config_file          |> read_file() diff --git a/lib/mix/tasks/pleroma/email.ex b/lib/mix/tasks/pleroma/email.ex new file mode 100644 index 000000000..2c3801429 --- /dev/null +++ b/lib/mix/tasks/pleroma/email.ex @@ -0,0 +1,25 @@ +defmodule Mix.Tasks.Pleroma.Email do +  use Mix.Task + +  @shortdoc "Simple Email test" +  @moduledoc File.read!("docs/administration/CLI_tasks/email.md") + +  def run(["test" | args]) do +    Mix.Pleroma.start_pleroma() + +    {options, [], []} = +      OptionParser.parse( +        args, +        strict: [ +          to: :string +        ] +      ) + +    email = Pleroma.Emails.AdminEmail.test_email(options[:to]) +    {:ok, _} = Pleroma.Emails.Mailer.deliver(email) + +    Mix.shell().info( +      "Test email has been sent to #{inspect(email.to)} from #{inspect(email.from)}" +    ) +  end +end diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 35669af27..24d999707 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -9,6 +9,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do    @moduledoc File.read!("docs/administration/CLI_tasks/emoji.md")    def run(["ls-packs" | args]) do +    Mix.Pleroma.start_pleroma()      Application.ensure_all_started(:hackney)      {options, [], []} = parse_global_opts(args) @@ -35,6 +36,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do    end    def run(["get-packs" | args]) do +    Mix.Pleroma.start_pleroma()      Application.ensure_all_started(:hackney)      {options, pack_names, []} = parse_global_opts(args) diff --git a/lib/mix/tasks/pleroma/robotstxt.ex b/lib/mix/tasks/pleroma/robotstxt.ex index 2128e1cd6..e99dd8502 100644 --- a/lib/mix/tasks/pleroma/robotstxt.ex +++ b/lib/mix/tasks/pleroma/robotstxt.ex @@ -18,6 +18,7 @@ defmodule Mix.Tasks.Pleroma.RobotsTxt do    """    def run(["disallow_all"]) do +    Mix.Pleroma.start_pleroma()      static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")      if !File.exists?(static_dir) do diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index b7be7a800..c9294a716 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -30,7 +30,8 @@ defmodule Pleroma.Activity do      "Follow" => "follow",      "Announce" => "reblog",      "Like" => "favourite", -    "Move" => "move" +    "Move" => "move", +    "EmojiReaction" => "pleroma:emoji_reaction"    }    @mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types, diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index e17068876..2c8889ce5 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -33,6 +33,7 @@ defmodule Pleroma.Application do    def start(_type, _args) do      Pleroma.HTML.compile_scrubbers()      Pleroma.Config.DeprecationWarnings.warn() +    Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()      Pleroma.Repo.check_migrations_applied!()      setup_instrumenters()      load_custom_modules() diff --git a/lib/pleroma/config/config_db.ex b/lib/pleroma/config/config_db.ex index 91a1aa0cc..119251bee 100644 --- a/lib/pleroma/config/config_db.ex +++ b/lib/pleroma/config/config_db.ex @@ -236,15 +236,7 @@ defmodule Pleroma.ConfigDB do    end    @spec from_string(String.t()) :: atom() | no_return() -  def from_string(":" <> entity), do: String.to_existing_atom(entity) - -  def from_string(entity) when is_binary(entity) do -    if is_module_name?(entity) do -      String.to_existing_atom("Elixir.#{entity}") -    else -      entity -    end -  end +  def from_string(string), do: do_transform_string(string)    @spec convert(any()) :: any()    def convert(entity), do: do_convert(entity) @@ -416,7 +408,7 @@ defmodule Pleroma.ConfigDB do    @spec is_module_name?(String.t()) :: boolean()    def is_module_name?(string) do -    Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth)\./, string) or +    Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or        string in ["Oban", "Ueberauth", "ExSyslogger"]    end  end diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index b15e4041b..5f23345f7 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Emails.AdminEmail do    import Swoosh.Email +  alias Pleroma.Config    alias Pleroma.Web.Router.Helpers    defp instance_config, do: Pleroma.Config.get(:instance) @@ -17,7 +18,20 @@ defmodule Pleroma.Emails.AdminEmail do    end    defp user_url(user) do -    Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) +    Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) +  end + +  def test_email(mail_to \\ nil) do +    html_body = """ +    <h3>Instance Test Email</h3> +    <p>A test email was requested. Hello. :)</p> +    """ + +    new() +    |> to(mail_to || Config.get([:instance, :email])) +    |> from({instance_name(), instance_notify_email()}) +    |> subject("Instance Test Email") +    |> html_body(html_body)    end    def report(to, reporter, account, statuses, comment) do diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 8f3e46af9..d04a65a1e 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -294,7 +294,7 @@ defmodule Pleroma.Notification do    end    def create_notifications(%Activity{data: %{"type" => type}} = activity) -      when type in ["Like", "Announce", "Follow", "Move"] do +      when type in ["Like", "Announce", "Follow", "Move", "EmojiReaction"] do      notifications =        activity        |> get_notified_from_activity() @@ -322,7 +322,7 @@ defmodule Pleroma.Notification do    def get_notified_from_activity(activity, local_only \\ true)    def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) -      when type in ["Create", "Like", "Announce", "Follow", "Move"] do +      when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReaction"] do      []      |> Utils.maybe_notify_to_recipients(activity)      |> Utils.maybe_notify_mentioned_recipients(activity) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 38e372f6d..52556bf31 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -184,11 +184,14 @@ defmodule Pleroma.Object do      with {:ok, _obj} = swap_object_with_tombstone(object),           deleted_activity = Activity.delete_all_by_object_ap_id(id),           {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), -         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path), -         {:ok, _} <- -           Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ -             "object" => object -           }) do +         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do +      with true <- Pleroma.Config.get([:instance, :cleanup_attachments]) do +        {:ok, _} = +          Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ +            "object" => object +          }) +      end +        {:ok, object, deleted_activity}      end    end diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index a7cc22831..b04273979 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do    alias Pleroma.Config    import Plug.Conn +  require Logger +    def init(opts), do: opts    def call(conn, _options) do @@ -90,6 +92,51 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do      |> Enum.join("; ")    end +  def warn_if_disabled do +    unless Config.get([:http_security, :enabled]) do +      Logger.warn(" +                                 .i;;;;i. +                               iYcviii;vXY: +                             .YXi       .i1c. +                            .YC.     .    in7. +                           .vc.   ......   ;1c. +                           i7,   ..        .;1; +                          i7,   .. ...      .Y1i +                         ,7v     .6MMM@;     .YX, +                        .7;.   ..IMMMMMM1     :t7. +                       .;Y.     ;$MMMMMM9.     :tc. +                       vY.   .. .nMMM@MMU.      ;1v. +                      i7i   ...  .#MM@M@C. .....:71i +                     it:   ....   $MMM@9;.,i;;;i,;tti +                    :t7.  .....   0MMMWv.,iii:::,,;St. +                   .nC.   .....   IMMMQ..,::::::,.,czX. +                  .ct:   ....... .ZMMMI..,:::::::,,:76Y. +                  c2:   ......,i..Y$M@t..:::::::,,..inZY +                 vov   ......:ii..c$MBc..,,,,,,,,,,..iI9i +                i9Y   ......iii:..7@MA,..,,,,,,,,,....;AA: +               iIS.  ......:ii::..;@MI....,............;Ez. +              .I9.  ......:i::::...8M1..................C0z. +             .z9;  ......:i::::,.. .i:...................zWX. +             vbv  ......,i::::,,.      ................. :AQY +            c6Y.  .,...,::::,,..:t0@@QY. ................ :8bi +           :6S. ..,,...,:::,,,..EMMMMMMI. ............... .;bZ, +          :6o,  .,,,,..:::,,,..i#MMMMMM#v.................  YW2. +         .n8i ..,,,,,,,::,,,,.. tMMMMM@C:.................. .1Wn +         7Uc. .:::,,,,,::,,,,..   i1t;,..................... .UEi +         7C...::::::::::::,,,,..        ....................  vSi. +         ;1;...,,::::::,.........       ..................    Yz: +          v97,.........                                     .voC. +           izAotX7777777777777777777777777777777777777777Y7n92: +             .;CoIIIIIUAA666666699999ZZZZZZZZZZZZZZZZZZZZ6ov. + +HTTP Security is disabled. Please re-enable it to prevent users from attacking +your instance and your users via malicious posts: + +      config :pleroma, :http_security, enabled: true +      ") +    end +  end +    defp maybe_send_sts_header(conn, true) do      max_age_sts = Config.get([:http_security, :sts_max_age])      max_age_ct = Config.get([:http_security, :ct_max_age]) diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex index d720508c8..7fb92489c 100644 --- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex +++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex @@ -67,6 +67,8 @@ defmodule Pleroma.Plugs.RateLimiter do    alias Pleroma.Plugs.RateLimiter.LimiterSupervisor    alias Pleroma.User +  require Logger +    def init(opts) do      limiter_name = Keyword.get(opts, :name) @@ -89,18 +91,39 @@ defmodule Pleroma.Plugs.RateLimiter do    def call(conn, nil), do: conn    def call(conn, settings) do -    settings -    |> incorporate_conn_info(conn) -    |> check_rate() -    |> case do -      {:ok, _count} -> +    case disabled?() do +      true -> +        if Pleroma.Config.get(:env) == :prod, +          do: Logger.warn("Rate limiter is disabled for localhost/socket") +          conn -      {:error, _count} -> -        render_throttled_error(conn) +      false -> +        settings +        |> incorporate_conn_info(conn) +        |> check_rate() +        |> case do +          {:ok, _count} -> +            conn + +          {:error, _count} -> +            render_throttled_error(conn) +        end      end    end +  def disabled? do +    localhost_or_socket = +      Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip]) +      |> Tuple.to_list() +      |> Enum.join(".") +      |> String.match?(~r/^local|^127.0.0.1/) + +    remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled]) + +    localhost_or_socket and remote_ip_disabled +  end +    def inspect_bucket(conn, name_root, settings) do      settings =        settings diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex index fdedc27ee..1cd5af48a 100644 --- a/lib/pleroma/plugs/remote_ip.ex +++ b/lib/pleroma/plugs/remote_ip.ex @@ -10,10 +10,7 @@ defmodule Pleroma.Plugs.RemoteIp do    @behaviour Plug    @headers ~w[ -    forwarded      x-forwarded-for -    x-client-ip -    x-real-ip    ]    # https://en.wikipedia.org/wiki/Localhost diff --git a/lib/pleroma/plugs/user_enabled_plug.ex b/lib/pleroma/plugs/user_enabled_plug.ex index 8d102ee5b..7b304eebc 100644 --- a/lib/pleroma/plugs/user_enabled_plug.ex +++ b/lib/pleroma/plugs/user_enabled_plug.ex @@ -11,11 +11,9 @@ defmodule Pleroma.Plugs.UserEnabledPlug do    end    def call(%{assigns: %{user: %User{} = user}} = conn, _) do -    if User.auth_active?(user) do -      conn -    else -      conn -      |> assign(:user, nil) +    case User.account_status(user) do +      :active -> conn +      _ -> assign(conn, :user, nil)      end    end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 430f04ae9..3c86cdb38 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -12,6 +12,7 @@ defmodule Pleroma.User do    alias Comeonin.Pbkdf2    alias Ecto.Multi    alias Pleroma.Activity +  alias Pleroma.Config    alias Pleroma.Conversation.Participation    alias Pleroma.Delivery    alias Pleroma.FollowingRelationship @@ -35,7 +36,7 @@ defmodule Pleroma.User do    require Logger    @type t :: %__MODULE__{} - +  @type account_status :: :active | :deactivated | :password_reset_pending | :confirmation_pending    @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}    # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength @@ -216,14 +217,21 @@ defmodule Pleroma.User do      end    end -  @doc "Returns if the user should be allowed to authenticate" -  def auth_active?(%User{deactivated: true}), do: false +  @doc "Returns status account" +  @spec account_status(User.t()) :: account_status() +  def account_status(%User{deactivated: true}), do: :deactivated +  def account_status(%User{password_reset_pending: true}), do: :password_reset_pending -  def auth_active?(%User{confirmation_pending: true}), -    do: !Pleroma.Config.get([:instance, :account_activation_required]) +  def account_status(%User{confirmation_pending: true}) do +    case Config.get([:instance, :account_activation_required]) do +      true -> :confirmation_pending +      _ -> :active +    end +  end -  def auth_active?(%User{}), do: true +  def account_status(%User{}), do: :active +  @spec visible_for?(User.t(), User.t() | nil) :: boolean()    def visible_for?(user, for_user \\ nil)    def visible_for?(%User{invisible: true}, _), do: false @@ -231,15 +239,17 @@ defmodule Pleroma.User do    def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true    def visible_for?(%User{} = user, for_user) do -    auth_active?(user) || superuser?(for_user) +    account_status(user) == :active || superuser?(for_user)    end    def visible_for?(_, _), do: false +  @spec superuser?(User.t()) :: boolean()    def superuser?(%User{local: true, is_admin: true}), do: true    def superuser?(%User{local: true, is_moderator: true}), do: true    def superuser?(_), do: false +  @spec invisible?(User.t()) :: boolean()    def invisible?(%User{invisible: true}), do: true    def invisible?(_), do: false @@ -1502,7 +1512,7 @@ defmodule Pleroma.User do      data      |> Map.put(:name, blank?(data[:name]) || data[:nickname])      |> remote_user_creation() -    |> Repo.insert(on_conflict: :replace_all_except_primary_key, conflict_target: :nickname) +    |> Repo.insert(on_conflict: {:replace_all_except, [:id]}, conflict_target: :nickname)      |> set_cache()    end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 24c724549..3149e10e9 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -58,7 +58,7 @@ defmodule Pleroma.UserRelationship do        target_id: target.id      })      |> Repo.insert( -      on_conflict: :replace_all_except_primary_key, +      on_conflict: {:replace_all_except, [:id]},        conflict_target: [:source_id, :relationship_type, :target_id]      )    end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 2e9d56ee5..5c436941a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -325,12 +325,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    def react_with_emoji(user, object, emoji, options \\ []) do      with local <- Keyword.get(options, :local, true),           activity_id <- Keyword.get(options, :activity_id, nil), -         Pleroma.Emoji.is_unicode_emoji?(emoji), +         true <- Pleroma.Emoji.is_unicode_emoji?(emoji),           reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),           {:ok, activity} <- insert(reaction_data, local),           {:ok, object} <- add_emoji_reaction_to_object(activity, object),           :ok <- maybe_federate(activity) do        {:ok, activity, object} +    else +      e -> {:error, e}      end    end @@ -345,6 +347,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do           {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),           :ok <- maybe_federate(activity) do        {:ok, activity, object} +    else +      e -> {:error, e}      end    end @@ -728,7 +732,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do        params        |> Map.put("user", reading_user)        |> Map.put("actor_id", user.ap_id) -      |> Map.put("whole_db", true)      recipients =        user_activities_recipients(%{ @@ -746,7 +749,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do        |> Map.put("type", ["Create", "Announce"])        |> Map.put("user", reading_user)        |> Map.put("actor_id", user.ap_id) -      |> Map.put("whole_db", true)        |> Map.put("pinned_activity_ids", user.pinned_activities)      params = @@ -773,7 +775,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do        params        |> Map.put("type", ["Create", "Announce"])        |> Map.put("instance", params["instance"]) -      |> Map.put("whole_db", true)      fetch_activities([Pleroma.Constants.as_public()], params, :offset)      |> Enum.reverse() diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 4def431f1..4f7fdaf38 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -337,7 +337,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do          %Activity{data: %{"content" => emoji, "actor" => actor}},          object        ) do -    reactions = object.data["reactions"] || [] +    reactions = get_cached_emoji_reactions(object)      new_reactions =        case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do @@ -365,7 +365,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do          %Activity{data: %{"content" => emoji, "actor" => actor}},          object        ) do -    reactions = object.data["reactions"] || [] +    reactions = get_cached_emoji_reactions(object)      new_reactions =        case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do @@ -385,6 +385,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do      update_element_in_object("reaction", new_reactions, object, count)    end +  def get_cached_emoji_reactions(object) do +    if is_list(object.data["reactions"]) do +      object.data["reactions"] +    else +      [] +    end +  end +    @spec add_like_to_object(Activity.t(), Object.t()) ::            {:ok, Object.t()} | {:error, Ecto.Changeset.t()}    def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 9a4e322c9..e3d7a465b 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -76,8 +76,7 @@ defmodule Pleroma.Web.ControllerHelper do      end    end -  def try_render(conn, target, params) -      when is_binary(target) do +  def try_render(conn, target, params) when is_binary(target) do      case render(conn, target, params) do        nil -> render_error(conn, :not_implemented, "Can't display this activity")        res -> res @@ -87,4 +86,8 @@ defmodule Pleroma.Web.ControllerHelper do    def try_render(conn, _, _) do      render_error(conn, :not_implemented, "Can't display this activity")    end + +  @spec put_in_if_exist(map(), atom() | String.t(), any) :: map() +  def put_in_if_exist(map, _key, nil), do: map +  def put_in_if_exist(map, key, value), do: put_in(map, key, value)  end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index bb1332fd3..334802e0a 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -13,21 +13,53 @@ defmodule Pleroma.Web.Feed.FeedView do    require Pleroma.Constants -  def prepare_activity(activity) do +  @spec pub_date(String.t() | DateTime.t()) :: String.t() +  def pub_date(date) when is_binary(date) do +    date +    |> Timex.parse!("{ISO:Extended}") +    |> pub_date +  end + +  def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") + +  def prepare_activity(activity, opts \\ []) do      object = activity_object(activity) +    actor = +      if opts[:actor] do +        Pleroma.User.get_cached_by_ap_id(activity.actor) +      end +      %{        activity: activity,        data: Map.get(object, :data), -      object: object +      object: object, +      actor: actor      }    end +  def most_recent_update(activities) do +    with %{updated_at: updated_at} <- List.first(activities) do +      NaiveDateTime.to_iso8601(updated_at) +    end +  end +    def most_recent_update(activities, user) do      (List.first(activities) || user).updated_at      |> NaiveDateTime.to_iso8601()    end +  def feed_logo do +    case Pleroma.Config.get([:feed, :logo]) do +      nil -> +        "#{Pleroma.Web.base_url()}/static/logo.png" + +      logo -> +        "#{Pleroma.Web.base_url()}#{logo}" +    end +    |> MediaProxy.url() +  end +    def logo(user) do      user      |> User.avatar_url() @@ -40,6 +72,8 @@ defmodule Pleroma.Web.Feed.FeedView do    def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do      content +    |> Pleroma.Web.Metadata.Utils.scrub_html() +    |> Pleroma.Emoji.Formatter.demojify()      |> Formatter.truncate(opts[:max_length], opts[:omission])      |> escape()    end @@ -50,6 +84,8 @@ defmodule Pleroma.Web.Feed.FeedView do      |> escape()    end +  def activity_content(_), do: "" +    def activity_context(activity), do: activity.data["context"]    def attachment_href(attachment) do diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex new file mode 100644 index 000000000..9accd0872 --- /dev/null +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.TagController do +  use Pleroma.Web, :controller + +  alias Pleroma.Config +  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.Feed.FeedView + +  import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3] + +  def feed(conn, %{"tag" => raw_tag} = params) do +    {format, tag} = parse_tag(raw_tag) + +    activities = +      %{"type" => ["Create"], "tag" => tag} +      |> put_in_if_exist("max_id", params["max_id"]) +      |> ActivityPub.fetch_public_activities() + +    conn +    |> put_resp_content_type("application/atom+xml") +    |> put_view(FeedView) +    |> render("tag.#{format}", +      activities: activities, +      tag: tag, +      feed_config: Config.get([:feed]) +    ) +  end + +  @spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()} +  defp parse_tag(raw_tag) when is_binary(raw_tag) do +    case Enum.reverse(String.split(raw_tag, ".")) do +      [format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")} +      _ -> {"rss", raw_tag} +    end +  end + +  defp parse_tag(raw_tag), do: {"rss", raw_tag} +end diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/user_controller.ex index d0e23007d..f5096834b 100644 --- a/lib/pleroma/web/feed/feed_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -2,13 +2,16 @@  # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.Feed.FeedController do +defmodule Pleroma.Web.Feed.UserController do    use Pleroma.Web, :controller    alias Fallback.RedirectController    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.ActivityPubController +  alias Pleroma.Web.Feed.FeedView + +  import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3]    plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect]) @@ -27,7 +30,7 @@ defmodule Pleroma.Web.Feed.FeedController do    def feed_redirect(conn, %{"nickname" => nickname}) do      with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do -      redirect(conn, external: "#{feed_url(conn, :feed, user.nickname)}.atom") +      redirect(conn, external: "#{user_feed_url(conn, :feed, user.nickname)}.atom")      end    end @@ -36,15 +39,15 @@ defmodule Pleroma.Web.Feed.FeedController do        activities =          %{            "type" => ["Create"], -          "whole_db" => true,            "actor_id" => user.ap_id          } -        |> Map.merge(Map.take(params, ["max_id"])) +        |> put_in_if_exist("max_id", params["max_id"])          |> ActivityPub.fetch_public_activities()        conn        |> put_resp_content_type("application/atom+xml") -      |> render("feed.xml", +      |> put_view(FeedView) +      |> render("user.xml",          user: user,          activities: activities,          feed_config: Pleroma.Config.get([:feed]) diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index fe71c36af..b9cc8f104 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -7,62 +7,8 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do    require Logger -  alias Pleroma.Config -  alias Pleroma.Plugs.OAuthScopesPlug -  alias Pleroma.User -  alias Pleroma.Web.MediaProxy - -  action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - -  plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) - -  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) -    @doc "GET /api/v1/suggestions" -  def index(%{assigns: %{user: user}} = conn, _) do -    if Config.get([:suggestions, :enabled], false) do -      with {:ok, data} <- fetch_suggestions(user) do -        limit = Config.get([:suggestions, :limit], 23) - -        data = -          data -          |> Enum.slice(0, limit) -          |> Enum.map(fn x -> -            x -            |> Map.put("id", fetch_suggestion_id(x)) -            |> Map.put("avatar", MediaProxy.url(x["avatar"])) -            |> Map.put("avatar_static", MediaProxy.url(x["avatar_static"])) -          end) - -        json(conn, data) -      end -    else -      json(conn, []) -    end -  end - -  defp fetch_suggestions(user) do -    api = Config.get([:suggestions, :third_party_engine], "") -    timeout = Config.get([:suggestions, :timeout], 5000) -    host = Config.get([Pleroma.Web.Endpoint, :url, :host]) - -    url = -      api -      |> String.replace("{{host}}", host) -      |> String.replace("{{user}}", user.nickname) - -    with {:ok, %{status: 200, body: body}} <- -           Pleroma.HTTP.get(url, [], adapter: [recv_timeout: timeout, pool: :default]) do -      Jason.decode(body) -    else -      e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}") -    end -  end - -  defp fetch_suggestion_id(attrs) do -    case User.get_or_fetch(attrs["acct"]) do -      {:ok, %User{id: id}} -> id -      _ -> 0 -    end +  def index(conn, _) do +    json(conn, [])    end  end diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index f52b693a6..beba89edb 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -7,10 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.AppView do    alias Pleroma.Web.OAuth.App -  @vapid_key :web_push_encryption -             |> Application.get_env(:vapid_details, []) -             |> Keyword.get(:public_key) -    def render("show.json", %{app: %App{} = app}) do      %{        id: app.id |> to_string, @@ -32,8 +28,10 @@ defmodule Pleroma.Web.MastodonAPI.AppView do    end    defp with_vapid_key(data) do -    if @vapid_key do -      Map.put(data, "vapid_key", @vapid_key) +    vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key] + +    if vapid_key do +      Map.put(data, "vapid_key", vapid_key)      else        data      end diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index ddd7f5318..360ec10f0 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -37,18 +37,37 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do        }        case mastodon_type do -        "mention" -> put_status(response, activity, user) -        "favourite" -> put_status(response, parent_activity, user) -        "reblog" -> put_status(response, parent_activity, user) -        "move" -> put_target(response, activity, user) -        "follow" -> response -        _ -> nil +        "mention" -> +          put_status(response, activity, user) + +        "favourite" -> +          put_status(response, parent_activity, user) + +        "reblog" -> +          put_status(response, parent_activity, user) + +        "move" -> +          put_target(response, activity, user) + +        "follow" -> +          response + +        "pleroma:emoji_reaction" -> +          put_status(response, parent_activity, user) |> put_emoji(activity) + +        _ -> +          nil        end      else        _ -> nil      end    end +  defp put_emoji(response, activity) do +    response +    |> Map.put(:emoji, activity.data["content"]) +  end +    defp put_status(response, activity, user) do      Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))    end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 64a97896a..5df29d93f 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -256,7 +256,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      emoji_reactions =        with %{data: %{"reactions" => emoji_reactions}} <- object do          Enum.map(emoji_reactions, fn [emoji, users] -> -          [emoji, length(users)] +          %{ +            emoji: emoji, +            count: length(users), +            reacted: !!(opts[:for] && opts[:for].ap_id in users) +          }          end)        else          _ -> [] diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex index 8043e6c54..ee48913a7 100644 --- a/lib/pleroma/web/metadata/feed.ex +++ b/lib/pleroma/web/metadata/feed.ex @@ -16,7 +16,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do         [           rel: "alternate",           type: "application/atom+xml", -         href: Helpers.feed_path(Endpoint, :feed, user.nickname) <> ".atom" +         href: Helpers.user_feed_path(Endpoint, :feed, user.nickname) <> ".atom"         ], []}      ]    end diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 589d11901..000bd9f66 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -21,15 +21,22 @@ defmodule Pleroma.Web.Metadata.Utils do    def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do      content +    |> scrub_html +    |> Emoji.Formatter.demojify() +    |> HtmlEntities.decode() +    |> Formatter.truncate(max_length) +  end + +  def scrub_html(content) when is_binary(content) do +    content      # html content comes from DB already encoded, decode first and scrub after      |> HtmlEntities.decode()      |> String.replace(~r/<br\s?\/?>/, " ")      |> HTML.strip_tags() -    |> Emoji.Formatter.demojify() -    |> HtmlEntities.decode() -    |> Formatter.truncate(max_length)    end +  def scrub_html(content), do: content +    def attachment_url(url) do      MediaProxy.url(url)    end diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index abcf46034..03c35cc2a 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -69,9 +69,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do          if Config.get([:chat, :enabled]) do            "chat"          end, -        if Config.get([:suggestions, :enabled]) do -          "suggestions" -        end,          if Config.get([:instance, :allow_relay]) do            "relay"          end, @@ -104,11 +101,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do          nodeDescription: Config.get([:instance, :description]),          private: !Config.get([:instance, :public], true),          suggestions: %{ -          enabled: Config.get([:suggestions, :enabled], false), -          thirdPartyEngine: Config.get([:suggestions, :third_party_engine], ""), -          timeout: Config.get([:suggestions, :timeout], 5000), -          limit: Config.get([:suggestions, :limit], 23), -          web: Config.get([:suggestions, :web], "") +          enabled: false          },          staffAccounts: staff_accounts,          federation: federation_response, diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 5292aedf2..528f08574 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -167,17 +167,37 @@ defmodule Pleroma.Web.OAuth.OAuthController do    defp handle_create_authorization_error(           %Plug.Conn{} = conn, -         {:auth_active, false}, +         {:account_status, :confirmation_pending},           %{"authorization" => _} = params         ) do -    # Per https://github.com/tootsuite/mastodon/blob/ -    #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76      conn      |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))      |> put_status(:forbidden)      |> authorize(params)    end +  defp handle_create_authorization_error( +         %Plug.Conn{} = conn, +         {:account_status, :password_reset_pending}, +         %{"authorization" => _} = params +       ) do +    conn +    |> put_flash(:error, dgettext("errors", "Password reset is required")) +    |> put_status(:forbidden) +    |> authorize(params) +  end + +  defp handle_create_authorization_error( +         %Plug.Conn{} = conn, +         {:account_status, :deactivated}, +         %{"authorization" => _} = params +       ) do +    conn +    |> put_flash(:error, dgettext("errors", "Your account is currently disabled")) +    |> put_status(:forbidden) +    |> authorize(params) +  end +    defp handle_create_authorization_error(%Plug.Conn{} = conn, error, %{"authorization" => _}) do      Authenticator.handle_error(conn, error)    end @@ -218,46 +238,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do        ) do      with {:ok, %User{} = user} <- Authenticator.get_user(conn),           {:ok, app} <- Token.Utils.fetch_app(conn), -         {:auth_active, true} <- {:auth_active, User.auth_active?(user)}, -         {:user_active, true} <- {:user_active, !user.deactivated}, -         {:password_reset_pending, false} <- -           {:password_reset_pending, user.password_reset_pending}, +         {:account_status, :active} <- {:account_status, User.account_status(user)},           {:ok, scopes} <- validate_scopes(app, params),           {:ok, auth} <- Authorization.create_authorization(app, user, scopes),           {:ok, token} <- Token.exchange_token(app, auth) do        json(conn, Token.Response.build(user, token))      else -      {:auth_active, false} -> -        # Per https://github.com/tootsuite/mastodon/blob/ -        #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76 -        render_error( -          conn, -          :forbidden, -          "Your login is missing a confirmed e-mail address", -          %{}, -          "missing_confirmed_email" -        ) - -      {:user_active, false} -> -        render_error( -          conn, -          :forbidden, -          "Your account is currently disabled", -          %{}, -          "account_is_disabled" -        ) - -      {:password_reset_pending, true} -> -        render_error( -          conn, -          :forbidden, -          "Password reset is required", -          %{}, -          "password_reset_required" -        ) - -      _error -> -        render_invalid_credentials_error(conn) +      error -> +        handle_token_exchange_error(conn, error)      end    end @@ -286,6 +274,43 @@ defmodule Pleroma.Web.OAuth.OAuthController do    # Bad request    def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params) +  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :deactivated}) do +    render_error( +      conn, +      :forbidden, +      "Your account is currently disabled", +      %{}, +      "account_is_disabled" +    ) +  end + +  defp handle_token_exchange_error( +         %Plug.Conn{} = conn, +         {:account_status, :password_reset_pending} +       ) do +    render_error( +      conn, +      :forbidden, +      "Password reset is required", +      %{}, +      "password_reset_required" +    ) +  end + +  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :confirmation_pending}) do +    render_error( +      conn, +      :forbidden, +      "Your login is missing a confirmed e-mail address", +      %{}, +      "missing_confirmed_email" +    ) +  end + +  defp handle_token_exchange_error(%Plug.Conn{} = conn, _error) do +    render_invalid_credentials_error(conn) +  end +    def token_revoke(%Plug.Conn{} = conn, %{"token" => _token} = params) do      with {:ok, app} <- Token.Utils.fetch_app(conn),           {:ok, _token} <- RevokeToken.revoke(app, params) do @@ -472,7 +497,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do           %App{} = app <- Repo.get_by(App, client_id: client_id),           true <- redirect_uri in String.split(app.redirect_uris),           {:ok, scopes} <- validate_scopes(app, auth_attrs), -         {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do +         {:account_status, :active} <- {:account_status, User.account_status(user)} do        Authorization.create_authorization(app, user, scopes)      end    end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 0bbf84fd3..a2f6d2287 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -573,11 +573,14 @@ keeping it in cache for #{div(cache_ms, 1000)}s")    assumed to be emojis and stored in the new `pack.json` file.    """    def import_from_fs(conn, _params) do -    with {:ok, results} <- File.ls(emoji_dir_path()) do +    emoji_path = emoji_dir_path() + +    with {:ok, %{access: :read_write}} <- File.stat(emoji_path), +         {:ok, results} <- File.ls(emoji_path) do        imported_pack_names =          results          |> Enum.filter(fn file -> -          dir_path = Path.join(emoji_dir_path(), file) +          dir_path = Path.join(emoji_path, file)            # Find the directories that do NOT have pack.json            File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json"))          end) @@ -585,6 +588,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s")        json(conn, imported_pack_names)      else +      {:ok, %{access: _}} -> +        conn +        |> put_status(:internal_server_error) +        |> json(%{error: "Error: emoji pack directory must be writable"}) +        {:error, _} ->          conn          |> put_status(:internal_server_error) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index bb19836ae..d76e39795 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -47,9 +47,17 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do             Object.normalize(activity) do        reactions =          emoji_reactions -        |> Enum.map(fn [emoji, users] -> -          users = Enum.map(users, &User.get_cached_by_ap_id/1) -          {emoji, AccountView.render("index.json", %{users: users, for: user, as: :user})} +        |> Enum.map(fn [emoji, user_ap_ids] -> +          users = +            Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) +            |> Enum.filter(& &1) + +          %{ +            emoji: emoji, +            count: length(users), +            accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), +            reacted: !!(user && user.ap_id in user_ap_ids) +          }          end)        conn diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index 913975616..fae3c462e 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -48,6 +48,6 @@ defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do    defp maybe_put_title(meta, _), do: meta    defp get_page_title(html) do -    Floki.find(html, "title") |> Floki.text() +    Floki.find(html, "html head title") |> List.first() |> Floki.text()    end  end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ef6e5a565..b5c1d85c7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -527,8 +527,10 @@ defmodule Pleroma.Web.Router do      get("/notice/:id", OStatus.OStatusController, :notice)      get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) -    get("/users/:nickname/feed", Feed.FeedController, :feed) -    get("/users/:nickname", Feed.FeedController, :feed_redirect) +    get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed) +    get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed) + +    get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)    end    scope "/", Pleroma.Web do diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index a1b445f2f..5392c1ec3 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -138,7 +138,8 @@ defmodule Pleroma.Web.Streamer.Worker do      with parent <- Object.normalize(item) || item,           true <- -           Enum.all?([blocked_ap_ids, muted_ap_ids, reblog_muted_ap_ids], &(item.actor not in &1)), +           Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), +         true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,           true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),           true <- MapSet.disjoint?(recipients, recipient_blocks),           %{host: item_host} <- URI.parse(item.actor), diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex index 514eacaed..ac8a75009 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -9,7 +9,7 @@    <ostatus:conversation ref="<%= activity_context(@activity) %>">      <%= activity_context(@activity) %>    </ostatus:conversation> -  <link ref="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> +  <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>    <%= if @data["summary"] do %>      <summary><%= @data["summary"] %></summary> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex new file mode 100644 index 000000000..da4fa6d6c --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -0,0 +1,51 @@ +<entry> +    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> +    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> +     +    <%= render @view_module, "_tag_author.atom", assigns %> +     +    <id><%= @data["id"] %></id> +    <title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> +    <content type="html"><%= activity_content(@object) %></content> + +  <%= if @activity.local do %> +    <link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/> +    <link type="text/html" href='<%= @data["id"] %>' rel="alternate"/> +  <% else %> +    <link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/> +  <% end %> + +    <published><%= @data["published"] %></published> +    <updated><%= @data["published"] %></updated> + +    <ostatus:conversation ref="<%= activity_context(@activity) %>"> +      <%= activity_context(@activity) %> +    </ostatus:conversation> +    <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> + +   <%= if @data["summary"] do %> +    <summary><%= @data["summary"] %></summary> +   <% end %> +   +    <%= for id <- @activity.recipients do %> +      <%= if id == Pleroma.Constants.as_public() do %> +        <link rel="mentioned" +          ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" +          href="http://activityschema.org/collection/public"/> +      <% else %> +        <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %> +          <link rel="mentioned" +            ostatus:object-type="http://activitystrea.ms/schema/1.0/person" +            href="<%= id %>" /> +        <% end %> +      <% end %> +    <% end %> +   +    <%= for tag <- @data["tag"] || [] do %> +      <category term="<%= tag %>"></category> +    <% end %> + +    <%= for {emoji, file} <- @data["emoji"] || %{} do %> +      <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/> +    <% end %> +</entry> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex new file mode 100644 index 000000000..295574df1 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -0,0 +1,15 @@ +<item> +  <title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> +   +   +  <guid isPermalink="true"><%= activity_context(@activity) %></guid> +  <link><%= activity_context(@activity) %></link> +  <pubDate><%= pub_date(@data["published"]) %></pubDate> +   +  <description><%= activity_content(@object) %></description> +  <%= for attachment <- @data["attachment"] || [] do %> +    <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/> +  <% end %> +   +</item> + diff --git a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex new file mode 100644 index 000000000..997c4936e --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex @@ -0,0 +1,18 @@ +<author> +    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> +    <id><%= @actor.ap_id %></id> +    <uri><%= @actor.ap_id %></uri> +    <name><%= @actor.nickname %></name> +    <summary><%= escape(@actor.bio) %></summary> +    <link rel="avatar" href="<%= User.avatar_url(@actor) %>"/> +    <%= if User.banner_url(@actor) do %> +      <link rel="header" href="<%= User.banner_url(@actor) %>"/> +    <% end %> +    <%= if @actor.local do %> +      <ap_enabled>true</ap_enabled> +    <% end %> +   +    <poco:preferredUsername><%= @actor.nickname %></poco:preferredUsername>         +    <poco:displayName><%= @actor.name %></poco:displayName> +    <poco:note><%= escape(@actor.bio) %></poco:note>     +</author> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex new file mode 100644 index 000000000..a288539ed --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" +      xmlns:thr="http://purl.org/syndication/thread/1.0" +      xmlns:georss="http://www.georss.org/georss" +      xmlns:activity="http://activitystrea.ms/spec/1.0/" +      xmlns:media="http://purl.org/syndication/atommedia" +      xmlns:poco="http://portablecontacts.net/spec/1.0" +      xmlns:ostatus="http://ostatus.org/schema/1.0" +      xmlns:statusnet="http://status.net/schema/api/1/"> + +    <id><%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %></id> +    <title>#<%= @tag %></title> + +    <subtitle>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</subtitle> +    <logo><%= feed_logo() %></logo> +    <updated><%= most_recent_update(@activities) %></updated> +    <link rel="self" href="<%= '#{tag_feed_url(@conn, :feed, @tag)}.atom'  %>" type="application/atom+xml"/> +    <%= for activity <- @activities do %> +    <%= render @view_module, "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> +    <% end %> +</feed> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex new file mode 100644 index 000000000..eeda01a04 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<rss version="2.0" xmlns:webfeeds="http://webfeeds.org/rss/1.0"> +  <channel> + + +    <title>#<%= @tag %></title> +    <description>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</description> +    <link><%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %></link> +    <webfeeds:logo><%= feed_logo() %></webfeeds:logo> +    <webfeeds:accentColor>2b90d9</webfeeds:accentColor> +    <%= for activity <- @activities do %> +    <%= render @view_module, "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> +    <% end %> +  </channel> +</rss> diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/user.xml.eex index 5ae36d345..d274c08ae 100644 --- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/user.xml.eex @@ -6,16 +6,16 @@    xmlns:poco="http://portablecontacts.net/spec/1.0"    xmlns:ostatus="http://ostatus.org/schema/1.0"> -  <id><%= feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id> +  <id><%= user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id>    <title><%= @user.nickname <> "'s timeline" %></title>    <updated><%= most_recent_update(@activities, @user) %></updated>    <logo><%= logo(@user) %></logo> -  <link rel="self" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/> +  <link rel="self" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>    <%= render @view_module, "_author.xml", assigns %>    <%= if last_activity(@activities) do %> -    <link rel="next" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/> +    <link rel="next" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/>    <% end %>    <%= for activity <- @activities do %> diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index 3f421db40..2cbc6b64d 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -12,7 +12,10 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do    @impl Oban.Worker    def perform( -        %{"object" => %{"data" => %{"attachment" => [_ | _] = attachments, "actor" => actor}}}, +        %{ +          "op" => "cleanup_attachments", +          "object" => %{"data" => %{"attachment" => [_ | _] = attachments, "actor" => actor}} +        },          _job        ) do      hrefs = @@ -37,7 +40,7 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do        )        # The query above can be time consumptive on large instances until we        # refactor how uploads are stored -      |> Repo.all(timout: :infinity) +      |> Repo.all(timeout: :infinity)        # we should delete 1 object for any given attachment, but don't delete        # files if there are more than 1 object for it        |> Enum.reduce(%{}, fn %{ @@ -70,7 +73,11 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do                _ -> ""              end -          base_url = Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) +          base_url = +            String.trim_trailing( +              Pleroma.Config.get([Pleroma.Upload, :base_url], Pleroma.Web.base_url()), +              "/" +            )            file_path = String.trim_leading(href, "#{base_url}/#{prefix}") @@ -84,5 +91,5 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do      |> Repo.delete_all()    end -  def perform(%{"object" => _object}, _job), do: :ok +  def perform(%{"op" => "cleanup_attachments", "object" => _object}, _job), do: :ok  end | 
