diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/formatter.ex | 14 | ||||
| -rw-r--r-- | lib/pleroma/user.ex | 158 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 16 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/relay.ex | 2 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/utils.ex | 8 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 14 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 2 | 
9 files changed, 168 insertions, 55 deletions
| diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 4149265a2..37737853a 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -120,7 +120,7 @@ defmodule Pleroma.Formatter do    end    @doc "Adds the links to mentioned users" -  def add_user_links({subs, text}, mentions) do +  def add_user_links({subs, text}, mentions, options \\ []) do      mentions =        mentions        |> Enum.sort_by(fn {name, _} -> -String.length(name) end) @@ -142,12 +142,16 @@ defmodule Pleroma.Formatter do                ap_id              end -          short_match = String.split(match, "@") |> tl() |> hd() +          nickname = +            if options[:format] == :full do +              User.full_nickname(match) +            else +              User.local_nickname(match) +            end            {uuid, -           "<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{ -             short_match -           }</span></a></span>"} +           "<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>" <> +             "@<span>#{nickname}</span></a></span>"}          end)      {subs, uuid_text} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c91f2d31a..06084b117 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -35,7 +35,7 @@ defmodule Pleroma.User do      field(:avatar, :map)      field(:local, :boolean, default: true)      field(:follower_address, :string) -    field(:search_distance, :float, virtual: true) +    field(:search_rank, :float, virtual: true)      field(:tags, {:array, :string}, default: [])      field(:last_refreshed_at, :naive_datetime)      has_many(:notifications, Notification) @@ -447,8 +447,7 @@ defmodule Pleroma.User do    def get_by_nickname(nickname) do      Repo.get_by(User, nickname: nickname) ||        if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do -        [local_nickname, _] = String.split(nickname, "@") -        Repo.get_by(User, nickname: local_nickname) +        Repo.get_by(User, nickname: local_nickname(nickname))        end    end @@ -511,6 +510,12 @@ defmodule Pleroma.User do      {:ok, Repo.all(q)}    end +  def get_followers_ids(user, page \\ nil) do +    q = get_followers_query(user, page) + +    Repo.all(from(u in q, select: u.id)) +  end +    def get_friends_query(%User{id: id, following: following}, nil) do      from(        u in User, @@ -535,6 +540,12 @@ defmodule Pleroma.User do      {:ok, Repo.all(q)}    end +  def get_friends_ids(user, page \\ nil) do +    q = get_friends_query(user, page) + +    Repo.all(from(u in q, select: u.id)) +  end +    def get_follow_requests_query(%User{} = user) do      from(        a in Activity, @@ -666,37 +677,120 @@ defmodule Pleroma.User do      Repo.all(query)    end -  def search(query, resolve \\ false) do -    # strip the beginning @ off if there is a query +  def search(query, resolve \\ false, for_user \\ nil) do +    # Strip the beginning @ off if there is a query      query = String.trim_leading(query, "@") -    if resolve do -      User.get_or_fetch_by_nickname(query) -    end +    if resolve, do: User.get_or_fetch_by_nickname(query) -    inner = -      from( -        u in User, -        select_merge: %{ -          search_distance: -            fragment( -              "? <-> (? || coalesce(?, ''))", -              ^query, -              u.nickname, -              u.name -            ) -        }, -        where: not is_nil(u.nickname) -      ) +    fts_results = do_search(fts_search_subquery(query), for_user) + +    {:ok, trigram_results} = +      Repo.transaction(fn -> +        Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", []) +        do_search(trigram_search_subquery(query), for_user) +      end) +    Enum.uniq_by(fts_results ++ trigram_results, & &1.id) +  end + +  defp do_search(subquery, for_user, options \\ []) do      q =        from( -        s in subquery(inner), -        order_by: s.search_distance, -        limit: 20 +        s in subquery(subquery), +        order_by: [desc: s.search_rank], +        limit: ^(options[:limit] || 20)        ) -    Repo.all(q) +    results = +      q +      |> Repo.all() +      |> Enum.filter(&(&1.search_rank > 0)) + +    boost_search_results(results, for_user) +  end + +  defp fts_search_subquery(query) do +    processed_query = +      query +      |> String.replace(~r/\W+/, " ") +      |> String.trim() +      |> String.split() +      |> Enum.map(&(&1 <> ":*")) +      |> Enum.join(" | ") + +    from( +      u in User, +      select_merge: %{ +        search_rank: +          fragment( +            """ +            ts_rank_cd( +              setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || +              setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'), +              to_tsquery('simple', ?), +              32 +            ) +            """, +            u.nickname, +            u.name, +            ^processed_query +          ) +      }, +      where: +        fragment( +          """ +            (setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || +            setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?) +          """, +          u.nickname, +          u.name, +          ^processed_query +        ) +    ) +  end + +  defp trigram_search_subquery(query) do +    from( +      u in User, +      select_merge: %{ +        search_rank: +          fragment( +            "similarity(?, trim(? || ' ' || coalesce(?, '')))", +            ^query, +            u.nickname, +            u.name +          ) +      }, +      where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query) +    ) +  end + +  defp boost_search_results(results, nil), do: results + +  defp boost_search_results(results, for_user) do +    friends_ids = get_friends_ids(for_user) +    followers_ids = get_followers_ids(for_user) + +    Enum.map( +      results, +      fn u -> +        search_rank_coef = +          cond do +            u.id in friends_ids -> +              1.2 + +            u.id in followers_ids -> +              1.1 + +            true -> +              1 +          end + +        Map.put(u, :search_rank, u.search_rank * search_rank_coef) +      end +    ) +    |> Enum.sort_by(&(-&1.search_rank))    end    def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do @@ -997,7 +1091,7 @@ defmodule Pleroma.User do        end)      bio -    |> CommonUtils.format_input(mentions, tags, "text/plain") +    |> CommonUtils.format_input(mentions, tags, "text/plain", user_links: [format: :full])      |> Formatter.emojify(emoji)    end @@ -1048,6 +1142,16 @@ defmodule Pleroma.User do      end    end +  def local_nickname(nickname_or_mention) do +    nickname_or_mention +    |> full_nickname() +    |> String.split("@") +    |> hd() +  end + +  def full_nickname(nickname_or_mention), +    do: String.trim_leading(nickname_or_mention, "@") +    def error_user(ap_id) do      %User{        name: ap_id, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5b87f7462..130c06028 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -224,10 +224,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do          %User{ap_id: _} = user,          %Object{data: %{"id" => _}} = object,          activity_id \\ nil, -        local \\ true +        local \\ true, +        public \\ true        ) do      with true <- is_public?(object), -         announce_data <- make_announce_data(user, object, activity_id), +         announce_data <- make_announce_data(user, object, activity_id, public),           {:ok, activity} <- insert(announce_data, local),           {:ok, object} <- add_announce_to_object(activity, object),           :ok <- maybe_federate(activity) do @@ -796,13 +797,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      end    end -  def is_public?(%Object{data: %{"type" => "Tombstone"}}) do -    false -  end +  def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false +  def is_public?(%Object{data: data}), do: is_public?(data) +  def is_public?(%Activity{data: data}), do: is_public?(data) -  def is_public?(activity) do -    "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++ -                                                         (activity.data["cc"] || [])) +  def is_public?(data) do +    "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))    end    def visible_for_user?(activity, nil) do diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index abddbc790..c0a52e349 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do    def publish(%Activity{data: %{"type" => "Create"}} = activity) do      with %User{} = user <- get_actor(),           %Object{} = object <- Object.normalize(activity.data["object"]["id"]) do -      ActivityPub.announce(user, object) +      ActivityPub.announce(user, object, nil, true, false)      else        e -> Logger.error("error: #{inspect(e)}")      end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 86d11c874..fa3abe3d8 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -451,7 +451,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      with actor <- get_actor(data),           %User{} = actor <- User.get_or_fetch_by_ap_id(actor),           {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), -         {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do +         public <- ActivityPub.is_public?(data), +         {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do        {:ok, activity}      else        _e -> :error diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 6ecab773c..d2e457a68 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -386,9 +386,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do    """    # for relayed messages, we only want to send to subscribers    def make_announce_data( -        %User{ap_id: ap_id, nickname: nil} = user, +        %User{ap_id: ap_id} = user,          %Object{data: %{"id" => id}} = object, -        activity_id +        activity_id, +        false        ) do      data = %{        "type" => "Announce", @@ -405,7 +406,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do    def make_announce_data(          %User{ap_id: ap_id} = user,          %Object{data: %{"id" => id}} = object, -        activity_id +        activity_id, +        true        ) do      data = %{        "type" => "Announce", diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 7e30d224c..a36ab5c15 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -116,16 +116,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do      Enum.join([text | attachment_text], "<br>")    end +  def format_input(text, mentions, tags, format, options \\ []) +    @doc """    Formatting text to plain text.    """ -  def format_input(text, mentions, tags, "text/plain") do +  def format_input(text, mentions, tags, "text/plain", options) do      text      |> Formatter.html_escape("text/plain")      |> String.replace(~r/\r?\n/, "<br>")      |> (&{[], &1}).()      |> Formatter.add_links() -    |> Formatter.add_user_links(mentions) +    |> Formatter.add_user_links(mentions, options[:user_links] || [])      |> Formatter.add_hashtag_links(tags)      |> Formatter.finalize()    end @@ -133,24 +135,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do    @doc """    Formatting text to html.    """ -  def format_input(text, mentions, _tags, "text/html") do +  def format_input(text, mentions, _tags, "text/html", options) do      text      |> Formatter.html_escape("text/html")      |> (&{[], &1}).() -    |> Formatter.add_user_links(mentions) +    |> Formatter.add_user_links(mentions, options[:user_links] || [])      |> Formatter.finalize()    end    @doc """    Formatting text to markdown.    """ -  def format_input(text, mentions, tags, "text/markdown") do +  def format_input(text, mentions, tags, "text/markdown", options) do      text      |> Formatter.mentions_escape(mentions)      |> Earmark.as_html!()      |> Formatter.html_escape("text/html")      |> (&{[], &1}).() -    |> Formatter.add_user_links(mentions) +    |> Formatter.add_user_links(mentions, options[:user_links] || [])      |> Formatter.add_hashtag_links(tags)      |> Formatter.finalize()    end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index daad89185..882d336be 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -771,7 +771,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      statuses = status_search(user, query) @@ -795,7 +795,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      statuses = status_search(user, query) @@ -816,7 +816,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 1c728166c..ede079963 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -675,7 +675,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    end    def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do -    users = User.search(query, true) +    users = User.search(query, true, user)      conn      |> put_view(UserView) | 
