utils.ex 5.4 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
dtluna's avatar
dtluna committed
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

feld's avatar
feld committed
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

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

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

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

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

lain's avatar
lain committed
82
  def add_attachments(text, attachments) do
lain's avatar
lain committed
83 84 85 86 87 88 89 90 91 92
    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
93
    Enum.join([text | attachment_text], "<br>")
lain's avatar
lain committed
94 95
  end

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

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

lain's avatar
lain committed
112 113
    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
114 115
      String.replace(text, full, url)
    end)
lain's avatar
lain committed
116 117
  end

lain's avatar
lain committed
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,
138
      "tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
lain's avatar
lain committed
139
    }
lain's avatar
lain committed
140 141 142 143 144 145 146 147 148

    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
149 150 151 152 153 154 155 156 157 158

  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
159
    with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
dtluna's avatar
dtluna committed
160
      format_asctime(date)
lain's avatar
lain committed
161 162
    else
      _e ->
dtluna's avatar
dtluna committed
163 164 165
        ""
    end
  end
166

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

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

184 185 186 187 188 189 190
  defp shortname(name) do
    if String.length(name) < 30 do
      name
    else
      String.slice(name, 0..30) <> "…"
    end
  end
191

192
  def confirm_current_password(user, password) do
193
    with %User{local: true} = db_user <- Repo.get(User, user.id),
194
         true <- Pbkdf2.checkpw(password, db_user.password_hash) do
195 196 197
      {:ok, db_user}
    else
      _ -> {:error, "Invalid password."}
198 199
    end
  end
200 201 202 203 204 205 206 207 208 209 210

  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",
        "icon" => %{"url" => "#{Endpoint.url()}#{url}"},
        "name" => ":#{shortcode}:"
      }
    end)
  end
lain's avatar
lain committed
211
end