utils.ex 5.15 KB
Newer Older
1
defmodule Pleroma.Web.CommonAPI.Utils do
feld's avatar
feld committed
2
  alias Pleroma.{Repo, Object, Formatter, Activity}
lain's avatar
lain committed
3
  alias Pleroma.Web.ActivityPub.Utils
4
  alias Pleroma.User
dtluna's avatar
dtluna committed
5
  alias Calendar.Strftime
6
  alias Comeonin.Pbkdf2
lain's avatar
lain committed
7

8 9 10
  # This is a hack for twidere.
  def get_by_id_or_ap_id(id) do
    activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
lain's avatar
lain committed
11

12 13 14 15 16 17 18 19 20 21
    if activity.data["type"] == "Create" do
      activity
    else
      Activity.get_create_activity_by_object_ap_id(activity.data["object"])
    end
  end

  def get_replied_to_activity(id) when not is_nil(id) do
    Repo.get(Activity, id)
  end
lain's avatar
lain committed
22

23 24
  def get_replied_to_activity(_), do: nil

lain's avatar
lain committed
25
  def attachments_from_ids(ids) do
lain's avatar
lain committed
26
    Enum.map(ids || [], fn media_id ->
lain's avatar
lain committed
27 28 29 30
      Repo.get(Object, media_id).data
    end)
  end

31 32
  def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do
    to = ["https://www.w3.org/ns/activitystreams#Public"]
33

lain's avatar
lain committed
34
    mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
35
    cc = [user.follower_address | mentioned_users]
lain's avatar
lain committed
36

37
    if inReplyTo do
38
      {to, Enum.uniq([inReplyTo.data["actor"] | cc])}
lain's avatar
lain committed
39
    else
40 41 42 43 44 45 46 47 48 49 50 51 52 53
      {to, cc}
    end
  end

  def to_for_user_and_mentions(user, mentions, inReplyTo, "unlisted") do
    {to, cc} = to_for_user_and_mentions(user, mentions, inReplyTo, "public")
    {cc, to}
  end

  def to_for_user_and_mentions(user, mentions, inReplyTo, "private") do
    {to, cc} = to_for_user_and_mentions(user, mentions, inReplyTo, "direct")
    {[user.follower_address | to], cc}
  end

feld's avatar
feld committed
54
  def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do
lain's avatar
lain committed
55 56
    mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)

57 58 59 60
    if inReplyTo do
      {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
    else
      {mentioned_users, []}
lain's avatar
lain committed
61 62 63
    end
  end

eal's avatar
eal committed
64
  def make_content_html(status, mentions, attachments, tags, no_attachment_links \\ false) do
65
    status
66
    |> String.replace("\r", "")
lain's avatar
lain committed
67
    |> format_input(mentions, tags)
eal's avatar
eal committed
68
    |> maybe_add_attachments(attachments, no_attachment_links)
69 70 71
  end

  def make_context(%Activity{data: %{"context" => context}}), do: context
lain's avatar
lain committed
72
  def make_context(_), do: Utils.generate_context_id()
73

feld's avatar
feld committed
74
  def maybe_add_attachments(text, _attachments, _no_links = true), do: text
lain's avatar
lain committed
75

eal's avatar
eal committed
76 77 78
  def maybe_add_attachments(text, attachments, _no_links) do
    add_attachments(text, attachments)
  end
lain's avatar
lain committed
79

lain's avatar
lain committed
80
  def add_attachments(text, attachments) do
lain's avatar
lain committed
81 82 83 84 85 86 87 88 89 90
    attachment_text =
      Enum.map(attachments, fn
        %{"url" => [%{"href" => href} | _]} ->
          name = URI.decode(Path.basename(href))
          "<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"

        _ ->
          ""
      end)

eal's avatar
eal committed
91
    Enum.join([text | attachment_text], "<br>")
lain's avatar
lain committed
92 93
  end

lain's avatar
lain committed
94
  def format_input(text, mentions, tags) do
eal's avatar
eal committed
95
    text
lain's avatar
lain committed
96
    |> Formatter.html_escape()
eal's avatar
eal committed
97
    |> String.replace("\n", "<br>")
lain's avatar
lain committed
98 99
    |> (&{[], &1}).()
    |> Formatter.add_links()
lain's avatar
lain committed
100 101
    |> Formatter.add_user_links(mentions)
    |> Formatter.add_hashtag_links(tags)
lain's avatar
lain committed
102
    |> Formatter.finalize()
lain's avatar
lain committed
103 104 105
  end

  def add_tag_links(text, tags) do
lain's avatar
lain committed
106 107 108
    tags =
      tags
      |> Enum.sort_by(fn {tag, _} -> -String.length(tag) end)
lain's avatar
lain committed
109

lain's avatar
lain committed
110 111
    Enum.reduce(tags, text, fn {full, tag}, text ->
      url = "#<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag}</a>"
lain's avatar
lain committed
112 113
      String.replace(text, full, url)
    end)
lain's avatar
lain committed
114 115
  end

lain's avatar
lain committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  def make_note_data(
        actor,
        to,
        context,
        content_html,
        attachments,
        inReplyTo,
        tags,
        cw \\ nil,
        cc \\ []
      ) do
    object = %{
      "type" => "Note",
      "to" => to,
      "cc" => cc,
      "content" => content_html,
      "summary" => cw,
      "context" => context,
      "attachment" => attachments,
      "actor" => actor,
      "tag" => tags |> Enum.map(fn {_, tag} -> tag end)
    }
lain's avatar
lain committed
138 139 140 141 142 143 144 145 146

    if inReplyTo do
      object
      |> Map.put("inReplyTo", inReplyTo.data["object"]["id"])
      |> Map.put("inReplyToStatusId", inReplyTo.id)
    else
      object
    end
  end
dtluna's avatar
dtluna committed
147 148 149 150 151 152 153 154 155 156

  def format_naive_asctime(date) do
    date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
  end

  def format_asctime(date) do
    Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
  end

  def date_to_asctime(date) do
lain's avatar
lain committed
157
    with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
dtluna's avatar
dtluna committed
158
      format_asctime(date)
lain's avatar
lain committed
159 160
    else
      _e ->
dtluna's avatar
dtluna committed
161 162 163
        ""
    end
  end
164

165 166
  def to_masto_date(%NaiveDateTime{} = date) do
    date
lain's avatar
lain committed
167
    |> NaiveDateTime.to_iso8601()
168 169 170 171 172 173
    |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
  end

  def to_masto_date(date) do
    try do
      date
lain's avatar
lain committed
174 175
      |> NaiveDateTime.from_iso8601!()
      |> NaiveDateTime.to_iso8601()
176 177 178 179 180 181
      |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
    rescue
      _e -> ""
    end
  end

182 183 184 185 186 187 188
  defp shortname(name) do
    if String.length(name) < 30 do
      name
    else
      String.slice(name, 0..30) <> "…"
    end
  end
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

  def confirm_current_password(user, params) do
    case user do
      nil ->
        {:error, "Invalid credentials."}

      _ ->
        with %User{local: true} = db_user <- Repo.get(User, user.id),
             true <- Pbkdf2.checkpw(params["password"], db_user.password_hash) do
          {:ok, db_user}
        else
          _ -> {:error, "Invalid password."}
        end
    end
  end
lain's avatar
lain committed
204
end