utils.ex 6.01 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.Web.Endpoint
5
  alias Pleroma.User
6
  alias Calendar.Strftime
7
  alias Comeonin.Pbkdf2
lain's avatar
lain committed
8

9 10 11
  # 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
12

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

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

25 26
  def get_replied_to_activity(_), do: nil

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

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

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

39
    if inReplyTo do
40
      {to, Enum.uniq([inReplyTo.data["actor"] | cc])}
lain's avatar
lain committed
41
    else
42 43 44 45 46 47 48 49 50 51 52 53 54 55
      {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

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

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

66 67 68 69 70 71 72 73
  def make_content_html(
        status,
        mentions,
        attachments,
        tags,
        content_type,
        no_attachment_links \\ false
      ) do
74
    status
75
    |> format_input(mentions, tags, content_type)
eal's avatar
eal committed
76
    |> maybe_add_attachments(attachments, no_attachment_links)
77 78 79
  end

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

82
  def maybe_add_attachments(text, _attachments, _no_links = true), do: text
lain's avatar
lain committed
83

eal's avatar
eal committed
84 85 86
  def maybe_add_attachments(text, attachments, _no_links) do
    add_attachments(text, attachments)
  end
lain's avatar
lain committed
87

lain's avatar
lain committed
88
  def add_attachments(text, attachments) do
lain's avatar
lain committed
89 90 91 92 93 94 95 96 97 98
    attachment_text =
      Enum.map(attachments, fn
        %{"url" => [%{"href" => href} | _]} ->
          name = URI.decode(Path.basename(href))
          "<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"

        _ ->
          ""
      end)

99
    Enum.join([text | attachment_text], "<br>")
lain's avatar
lain committed
100 101
  end

102
  def format_input(text, mentions, tags, "text/plain") do
eal's avatar
eal committed
103
    text
104
    |> Formatter.html_escape("text/plain")
105
    |> String.replace(~r/\r?\n/, "<br>")
lain's avatar
lain committed
106 107
    |> (&{[], &1}).()
    |> Formatter.add_links()
lain's avatar
lain committed
108 109
    |> Formatter.add_user_links(mentions)
    |> Formatter.add_hashtag_links(tags)
lain's avatar
lain committed
110
    |> Formatter.finalize()
lain's avatar
lain committed
111 112
  end

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  def format_input(text, mentions, tags, "text/html") do
    text
    |> Formatter.html_escape("text/html")
    |> String.replace(~r/\r?\n/, "<br>")
    |> (&{[], &1}).()
    |> Formatter.add_user_links(mentions)
    |> Formatter.finalize()
  end

  def format_input(text, mentions, tags, "text/markdown") do
    text
    |> Earmark.as_html!()
    |> Formatter.html_escape("text/html")
    |> String.replace(~r/\r?\n/, "")
    |> (&{[], &1}).()
    |> Formatter.add_user_links(mentions)
    |> Formatter.finalize()
  end

lain's avatar
lain committed
132
  def add_tag_links(text, tags) do
lain's avatar
lain committed
133 134 135
    tags =
      tags
      |> Enum.sort_by(fn {tag, _} -> -String.length(tag) end)
lain's avatar
lain committed
136

lain's avatar
lain committed
137
    Enum.reduce(tags, text, fn {full, tag}, text ->
kaniini's avatar
kaniini committed
138
      url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>##{tag}</a>"
lain's avatar
lain committed
139 140
      String.replace(text, full, url)
    end)
lain's avatar
lain committed
141 142
  end

lain's avatar
lain committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
  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,
163
      "tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
lain's avatar
lain committed
164
    }
lain's avatar
lain committed
165 166 167 168 169 170 171 172 173

    if inReplyTo do
      object
      |> Map.put("inReplyTo", inReplyTo.data["object"]["id"])
      |> Map.put("inReplyToStatusId", inReplyTo.id)
    else
      object
    end
  end
174 175 176 177 178 179 180 181 182 183

  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
184
    with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
185
      format_asctime(date)
lain's avatar
lain committed
186 187
    else
      _e ->
188 189 190
        ""
    end
  end
191

192 193
  def to_masto_date(%NaiveDateTime{} = date) do
    date
lain's avatar
lain committed
194
    |> NaiveDateTime.to_iso8601()
195 196 197 198 199 200
    |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
  end

  def to_masto_date(date) do
    try do
      date
lain's avatar
lain committed
201 202
      |> NaiveDateTime.from_iso8601!()
      |> NaiveDateTime.to_iso8601()
203 204 205 206 207 208
      |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
    rescue
      _e -> ""
    end
  end

209 210 211 212 213 214 215
  defp shortname(name) do
    if String.length(name) < 30 do
      name
    else
      String.slice(name, 0..30) <> "…"
    end
  end
216

217
  def confirm_current_password(user, password) do
218
    with %User{local: true} = db_user <- Repo.get(User, user.id),
219
         true <- Pbkdf2.checkpw(password, db_user.password_hash) do
220 221 222
      {:ok, db_user}
    else
      _ -> {:error, "Invalid password."}
223 224
    end
  end
225 226 227 228 229 230

  def emoji_from_profile(%{info: info} = user) do
    (Formatter.get_emoji(user.bio) ++ Formatter.get_emoji(user.name))
    |> Enum.map(fn {shortcode, url} ->
      %{
        "type" => "Emoji",
eal's avatar
eal committed
231
        "icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{url}"},
232 233 234 235
        "name" => ":#{shortcode}:"
      }
    end)
  end
lain's avatar
lain committed
236
end