Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop

This commit is contained in:
sadposter 2019-07-06 18:41:06 +01:00
commit ed709aec2c
106 changed files with 842 additions and 400 deletions

View file

@ -35,6 +35,7 @@ docs-build:
- develop@pleroma/pleroma - develop@pleroma/pleroma
variables: variables:
MIX_ENV: dev MIX_ENV: dev
PLEROMA_BUILD_ENV: prod
script: script:
- mix deps.get - mix deps.get
- mix compile - mix compile

View file

@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
### Fixed
- Not being able to pin unlisted posts
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
### Changed
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
## [1.0.0] - 2019-06-29 ## [1.0.0] - 2019-06-29
### Security ### Security
- Mastodon API: Fix display names not being sanitized - Mastodon API: Fix display names not being sanitized

View file

@ -38,7 +38,9 @@ Authentication is required and the user must be an admin.
"moderator": bool "moderator": bool
}, },
"local": bool, "local": bool,
"tags": array "tags": array,
"avatar": string,
"display_name": string
}, },
... ...
] ]
@ -331,6 +333,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
"pleroma": {}, "pleroma": {},
"sensitive": false "sensitive": false
}, },
"tags": ["force_unlisted"],
"statuses_count": 3, "statuses_count": 3,
"url": "https://pleroma.example.org/users/user", "url": "https://pleroma.example.org/users/user",
"username": "user" "username": "user"
@ -366,6 +369,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
"pleroma": {}, "pleroma": {},
"sensitive": false "sensitive": false
}, },
"tags": ["force_unlisted"],
"statuses_count": 1, "statuses_count": 1,
"url": "https://pleroma.example.org/users/lain", "url": "https://pleroma.example.org/users/lain",
"username": "lain" "username": "lain"

View file

@ -36,7 +36,7 @@ No specific configuration.
This filter replaces the filename (not the path) of an upload. For complete obfuscation, add This filter replaces the filename (not the path) of an upload. For complete obfuscation, add
`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename. `Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename.
* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. * `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. You can get the original filename extension by using `{extension}`, for example `custom-file-name.{extension}`.
## Pleroma.Emails.Mailer ## Pleroma.Emails.Mailer
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox. * `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
@ -98,6 +98,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section) * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:. * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
* `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links. * `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. * `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send. * `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json`` * `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
@ -279,7 +280,7 @@ config :pleroma, :mrf_subchain,
## Pleroma.Web.Endpoint ## Pleroma.Web.Endpoint
`Phoenix` endpoint configuration, all configuration options can be viewed [here](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-dynamic-configuration), only common options are listed here `Phoenix` endpoint configuration, all configuration options can be viewed [here](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-dynamic-configuration), only common options are listed here
* `http` - a list containing http protocol configuration, all configuration options can be viewed [here](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-options), only common options are listed here * `http` - a list containing http protocol configuration, all configuration options can be viewed [here](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-options), only common options are listed here. For deployment using docker, you need to set this to `[ip: {0,0,0,0}, port: 4000]` to make pleroma accessible from other containers (such as your nginx server).
- `ip` - a tuple consisting of 4 integers - `ip` - a tuple consisting of 4 integers
- `port` - `port`
* `url` - a list containing the configuration for generating urls, accepts * `url` - a list containing the configuration for generating urls, accepts

View file

@ -203,12 +203,12 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
#### Further reading #### Further reading
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](Backup-your-instance) * [Backup your instance](backup.html)
* [Configuration tips](General tips for customizing pleroma fe) * [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](Hardening-your-instance) * [Hardening your instance](hardening.html)
* [How to activate mediaproxy](How-to-activate-mediaproxy) * [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](Small customizations) * [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](Updating-your-instance) * [Updating your instance](updating.html)
## Questions ## Questions

View file

@ -201,12 +201,12 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
#### Further reading #### Further reading
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](Backup-your-instance) * [Backup your instance](backup.html)
* [Configuration tips](General tips for customizing pleroma fe) * [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](Hardening-your-instance) * [Hardening your instance](hardening.html)
* [How to activate mediaproxy](How-to-activate-mediaproxy) * [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](Small customizations) * [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](Updating-your-instance) * [Updating your instance](updating.html)
## Questions ## Questions

View file

@ -265,12 +265,12 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
#### Further reading #### Further reading
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](Backup-your-instance) * [Backup your instance](backup.html)
* [Configuration tips](General tips for customizing pleroma fe) * [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](Hardening-your-instance) * [Hardening your instance](hardening.html)
* [How to activate mediaproxy](How-to-activate-mediaproxy) * [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](Small customizations) * [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](Updating-your-instance) * [Updating your instance](updating.html)
## Questions ## Questions

View file

@ -191,12 +191,12 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
#### Further reading #### Further reading
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](Backup-your-instance) * [Backup your instance](backup.html)
* [Configuration tips](General tips for customizing pleroma fe) * [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](Hardening-your-instance) * [Hardening your instance](hardening.html)
* [How to activate mediaproxy](How-to-activate-mediaproxy) * [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](Small customizations) * [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](Updating-your-instance) * [Updating your instance](updating.html)
## Questions ## Questions

View file

@ -180,9 +180,13 @@ mix set_moderator username [true|false]
#### コンフィギュレーションとカスタマイズ #### コンフィギュレーションとカスタマイズ
* [Configuration tips](General tips for customizing pleroma fe)
* [Small Pleroma-FE customizations](Small customizations)
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](backup.html)
* [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](hardening.html)
* [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](updating.html)
## 質問ある? ## 質問ある?

View file

@ -284,12 +284,12 @@ If you opted to allow sudo for the `pleroma` user but would like to remove the a
#### Further reading #### Further reading
* [Admin tasks](Admin tasks) * [Admin tasks](Admin tasks)
* [Backup your instance](Backup-your-instance) * [Backup your instance](backup.html)
* [Configuration tips](General tips for customizing pleroma fe) * [Configuration tips](general-tips-for-customizing-pleroma-fe.html)
* [Hardening your instance](Hardening-your-instance) * [Hardening your instance](hardening.html)
* [How to activate mediaproxy](How-to-activate-mediaproxy) * [How to activate mediaproxy](howto_mediaproxy.html)
* [Small Pleroma-FE customizations](Small customizations) * [Small Pleroma-FE customizations](small_customizations.html)
* [Updating your instance](Updating-your-instance) * [Updating your instance](updating.html)
## Questions ## Questions

View file

@ -49,7 +49,7 @@ mkdir -p /var/lib/pleroma/static
chown -R pleroma /var/lib/pleroma chown -R pleroma /var/lib/pleroma
# If you use the local uploader with default settings your uploads should be located in `~pleroma/uploads` # If you use the local uploader with default settings your uploads should be located in `~pleroma/uploads`
mv ~pleroma/uploads /var/lib/pleroma/uploads mv ~pleroma/uploads/* /var/lib/pleroma/uploads
# If you have created the custom public files directory with default settings it should be located in `~pleroma/instance/static` # If you have created the custom public files directory with default settings it should be located in `~pleroma/instance/static`
mv ~pleroma/instance/static /var/lib/pleroma/static mv ~pleroma/instance/static /var/lib/pleroma/static
@ -122,13 +122,15 @@ su pleroma -s $SHELL -lc "./bin/pleroma stop"
## Setting up a system service ## Setting up a system service
OTP releases have different service files than from-source installs so they need to be copied over again. OTP releases have different service files than from-source installs so they need to be copied over again.
**Warning:** The service files assume pleroma user's home directory is `/opt/pleroma`, please make sure all paths fit your installation.
Debian/Ubuntu: Debian/Ubuntu:
```sh ```sh
# Copy the service into a proper directory # Copy the service into a proper directory
cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
# Reload service files # Reload service files
systemctl reload-daemon systemctl daemon-reload
# Reenable pleroma to start on boot # Reenable pleroma to start on boot
systemctl reenable pleroma systemctl reenable pleroma

View file

@ -207,7 +207,7 @@ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --
# Add it to the daily cron # Add it to the daily cron
echo '#!/bin/sh echo '#!/bin/sh
certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook "systemctl reload nginx" certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx"
' > /etc/cron.daily/renew-pleroma-cert ' > /etc/cron.daily/renew-pleroma-cert
chmod +x /etc/cron.daily/renew-pleroma-cert chmod +x /etc/cron.daily/renew-pleroma-cert
@ -228,7 +228,7 @@ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --
# Add it to the daily cron # Add it to the daily cron
echo '#!/bin/sh echo '#!/bin/sh
certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook "rc-service nginx reload" certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "rc-service nginx reload"
' > /etc/periodic/daily/renew-pleroma-cert ' > /etc/periodic/daily/renew-pleroma-cert
chmod +x /etc/periodic/daily/renew-pleroma-cert chmod +x /etc/periodic/daily/renew-pleroma-cert

View file

@ -149,7 +149,7 @@ def run(["gen" | rest]) do
uploads_dir = uploads_dir =
get_option( get_option(
options, options,
:upload_dir, :uploads_dir,
"What directory should media uploads go in (when using the local uploader)?", "What directory should media uploads go in (when using the local uploader)?",
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads]) Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
) )

View file

@ -38,7 +38,7 @@ def put([key], value), do: put(key, value)
def put([parent_key | keys], value) do def put([parent_key | keys], value) do
parent = parent =
Application.get_env(:pleroma, parent_key) Application.get_env(:pleroma, parent_key, [])
|> put_in(keys, value) |> put_in(keys, value)
Application.put_env(:pleroma, parent_key, parent) Application.put_env(:pleroma, parent_key, parent)

View file

@ -10,10 +10,19 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilename do
""" """
@behaviour Pleroma.Upload.Filter @behaviour Pleroma.Upload.Filter
def filter(upload) do alias Pleroma.Config
extension = List.last(String.split(upload.name, ".")) alias Pleroma.Upload
name = Pleroma.Config.get([__MODULE__, :text], random(extension))
{:ok, %Pleroma.Upload{upload | name: name}} def filter(%Upload{name: name} = upload) do
extension = List.last(String.split(name, "."))
name = predefined_name(extension) || random(extension)
{:ok, %Upload{upload | name: name}}
end
@spec predefined_name(String.t()) :: String.t() | nil
defp predefined_name(extension) do
with name when not is_nil(name) <- Config.get([__MODULE__, :text]),
do: String.replace(name, "{extension}", extension)
end end
defp random(extension) do defp random(extension) do

View file

@ -836,15 +836,12 @@ def unblock(blocker, %{ap_id: ap_id}) do
def mutes?(nil, _), do: false def mutes?(nil, _), do: false
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id) def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
def blocks?(user, %{ap_id: ap_id}) do def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
blocks = user.info.blocks blocks = info.blocks
domain_blocks = user.info.domain_blocks domain_blocks = info.domain_blocks
%{host: host} = URI.parse(ap_id) %{host: host} = URI.parse(ap_id)
Enum.member?(blocks, ap_id) || Enum.member?(blocks, ap_id) || Enum.any?(domain_blocks, &(&1 == host))
Enum.any?(domain_blocks, fn domain ->
host == domain
end)
end end
def subscribed_to?(user, %{ap_id: ap_id}) do def subscribed_to?(user, %{ap_id: ap_id}) do

View file

@ -43,6 +43,8 @@ def search(query_string, opts \\ []) do
defp search_query(query_string, for_user, following) do defp search_query(query_string, for_user, following) do
for_user for_user
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user)
|> filter_blocked_domains(for_user)
|> search_subqueries(query_string) |> search_subqueries(query_string)
|> union_subqueries |> union_subqueries
|> distinct_query() |> distinct_query()
@ -55,6 +57,25 @@ defp search_query(query_string, for_user, following) do
defp base_query(_user, false), do: User defp base_query(_user, false), do: User
defp base_query(user, true), do: User.get_followers_query(user) defp base_query(user, true), do: User.get_followers_query(user)
defp filter_blocked_user(query, %User{info: %{blocks: blocks}})
when length(blocks) > 0 do
from(q in query, where: not (q.ap_id in ^blocks))
end
defp filter_blocked_user(query, _), do: query
defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}})
when length(domain_blocks) > 0 do
domains = Enum.join(domain_blocks, ",")
from(
q in query,
where: fragment("substring(ap_id from '.*://([^/]*)') NOT IN (?)", ^domains)
)
end
defp filter_blocked_domains(query, _), do: query
defp paginate(query, limit, offset) do defp paginate(query, limit, offset) do
from(q in query, limit: ^limit, offset: ^offset) from(q in query, limit: ^limit, offset: ^offset)
end end

View file

@ -0,0 +1,56 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
@behaviour Pleroma.Web.ActivityPub.MRF
alias Pleroma.HTTP
alias Pleroma.Web.MediaProxy
require Logger
@hackney_options [
pool: :media,
recv_timeout: 10_000
]
def perform(:prefetch, url) do
Logger.info("Prefetching #{inspect(url)}")
url
|> MediaProxy.url()
|> HTTP.get([], adapter: @hackney_options)
end
def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do
Enum.each(attachments, fn
%{"url" => url} when is_list(url) ->
url
|> Enum.each(fn
%{"href" => href} ->
PleromaJobQueue.enqueue(:background, __MODULE__, [:prefetch, href])
x ->
Logger.debug("Unhandled attachment URL object #{inspect(x)}")
end)
x ->
Logger.debug("Unhandled attachment #{inspect(x)}")
end)
end
@impl true
def filter(
%{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message
)
when is_list(attachments) and length(attachments) > 0 do
PleromaJobQueue.enqueue(:background, __MODULE__, [:preload, message])
{:ok, message}
end
@impl true
def filter(message), do: {:ok, message}
end

View file

@ -5,8 +5,11 @@
defmodule Pleroma.Web.AdminAPI.AccountView do defmodule Pleroma.Web.AdminAPI.AccountView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.User.Info alias Pleroma.User.Info
alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.MediaProxy
def render("index.json", %{users: users, count: count, page_size: page_size}) do def render("index.json", %{users: users, count: count, page_size: page_size}) do
%{ %{
@ -17,9 +20,14 @@ def render("index.json", %{users: users, count: count, page_size: page_size}) do
end end
def render("show.json", %{user: user}) do def render("show.json", %{user: user}) do
avatar = User.avatar_url(user) |> MediaProxy.url()
display_name = HTML.strip_tags(user.name || user.nickname)
%{ %{
"id" => user.id, "id" => user.id,
"avatar" => avatar,
"nickname" => user.nickname, "nickname" => user.nickname,
"display_name" => display_name,
"deactivated" => user.info.deactivated, "deactivated" => user.info.deactivated,
"local" => user.local, "local" => user.local,
"roles" => Info.roles(user.info), "roles" => Info.roles(user.info),

View file

@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
alias Pleroma.HTML alias Pleroma.HTML
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
def render("index.json", %{reports: reports}) do def render("index.json", %{reports: reports}) do
@ -38,12 +37,19 @@ def render("show.json", %{report: report}) do
%{ %{
id: report.id, id: report.id,
account: AccountView.render("account.json", %{user: account}), account: merge_account_views(account),
actor: AccountView.render("account.json", %{user: user}), actor: merge_account_views(user),
content: content, content: content,
created_at: created_at, created_at: created_at,
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}), statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
state: report.data["state"] state: report.data["state"]
} }
end end
defp merge_account_views(%User{} = user) do
Pleroma.Web.MastodonAPI.AccountView.render("account.json", %{user: user})
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
end
defp merge_account_views(_), do: %{}
end end

View file

@ -11,6 +11,7 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
import Pleroma.Web.CommonAPI.Utils import Pleroma.Web.CommonAPI.Utils
@ -284,12 +285,11 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
}, },
object: %Object{ object: %Object{
data: %{ data: %{
"to" => object_to,
"type" => "Note" "type" => "Note"
} }
} }
} = activity <- get_by_id_or_ap_id(id_or_ap_id), } = activity <- get_by_id_or_ap_id(id_or_ap_id),
true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"), true <- Visibility.is_public?(activity),
%{valid?: true} = info_changeset <- %{valid?: true} = info_changeset <-
User.Info.add_pinnned_activity(user.info, activity), User.Info.add_pinnned_activity(user.info, activity),
changeset <- changeset <-

View file

@ -356,6 +356,10 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
with %User{} = user <- User.get_cached_by_id(params["id"]) do with %User{} = user <- User.get_cached_by_id(params["id"]) do
params =
params
|> Map.put("tag", params["tagged"])
activities = ActivityPub.fetch_user_activities(user, reading_user, params) activities = ActivityPub.fetch_user_activities(user, reading_user, params)
conn conn

View file

@ -17,8 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search]) plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user)) accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = Activity.search(user, query) statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags_path = Web.base_url() <> "/tag/" tags_path = Web.base_url() <> "/tag/"
tags = tags =
@ -40,8 +40,8 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
end end
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user)) accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = Activity.search(user, query) statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags = tags =
query query
@ -76,4 +76,14 @@ defp search_options(params, user) do
for_user: user for_user: user
] ]
end end
defp with_fallback(f, fallback) do
try do
f.()
rescue
error ->
Logger.error("#{__MODULE__} search error: #{inspect(error)}")
fallback
end
end
end end

View file

@ -162,7 +162,8 @@ def raw_nodeinfo do
accountActivationRequired: Config.get([:instance, :account_activation_required], false), accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Config.get([:instance, :invites_enabled], false), invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features, features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]) restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
} }
} }
end end

55
mix.exs
View file

@ -174,10 +174,14 @@ defp aliases do
# Builds a version string made of: # Builds a version string made of:
# * the application version # * the application version
# * a pre-release if ahead of the tag: the describe string (-count-commithash) # * a pre-release if ahead of the tag: the describe string (-count-commithash)
# * build info: # * branch name
# * build metadata:
# * a build name if `PLEROMA_BUILD_NAME` or `:pleroma, :build_name` is defined # * a build name if `PLEROMA_BUILD_NAME` or `:pleroma, :build_name` is defined
# * the mix environment if different than prod # * the mix environment if different than prod
defp version(version) do defp version(version) do
identifier_filter = ~r/[^0-9a-z\-]+/i
# Pre-release version, denoted from patch version with a hyphen
{git_tag, git_pre_release} = {git_tag, git_pre_release} =
with {tag, 0} <- with {tag, 0} <-
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true), System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
@ -198,6 +202,19 @@ defp version(version) do
) )
end end
# Branch name as pre-release version component, denoted with a dot
branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
true <- branch_name != "master" do
branch_name =
branch_name
|> String.trim()
|> String.replace(identifier_filter, "-")
"." <> branch_name
end
build_name = build_name =
cond do cond do
name = Application.get_env(:pleroma, :build_name) -> name name = Application.get_env(:pleroma, :build_name) -> name
@ -206,28 +223,26 @@ defp version(version) do
end end
env_name = if Mix.env() != :prod, do: to_string(Mix.env()) env_name = if Mix.env() != :prod, do: to_string(Mix.env())
env_override = System.get_env("PLEROMA_BUILD_ENV")
build = env_name =
[build_name, env_name] case env_override do
|> Enum.filter(fn string -> string && string != "" end) nil -> env_name
|> Enum.join("-") env_override when env_override in ["", "prod"] -> nil
|> (fn env_override -> env_override
"" -> nil
string -> "+" <> string
end).()
branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
true <- branch_name != "master" do
branch_name =
String.trim(branch_name)
|> String.replace(~r/[^0-9a-z\-\.]+/i, "-")
"-" <> branch_name
end end
[version, git_pre_release, branch_name, build] # Build metadata, denoted with a plus sign
build_metadata =
[build_name, env_name]
|> Enum.filter(fn string -> string && string != "" end)
|> Enum.join(".")
|> (fn
"" -> nil
string -> "+" <> String.replace(string, identifier_filter, "-")
end).()
[version, git_pre_release, branch_name, build_metadata]
|> Enum.filter(fn string -> string && string != "" end) |> Enum.filter(fn string -> string && string != "" end)
|> Enum.join() |> Enum.join()
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.User do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:users) do create_if_not_exists table(:users) do
add :email, :string add :email, :string
add :password_hash, :string add :password_hash, :string
add :name, :string add :name, :string

View file

@ -2,13 +2,13 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Activity do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:activities) do create_if_not_exists table(:activities) do
add :data, :map add :data, :map
timestamps() timestamps()
end end
create index(:activities, [:data], using: :gin) create_if_not_exists index(:activities, [:data], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Object do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:objects) do create_if_not_exists table(:objects) do
add :data, :map add :data, :map
timestamps() timestamps()

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexToObjects do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:objects, [:data], using: :gin) create_if_not_exists index(:objects, [:data], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToEmailAndNickname do
use Ecto.Migration use Ecto.Migration
def change do def change do
create unique_index(:users, [:email]) create_if_not_exists unique_index(:users, [:email])
create unique_index(:users, [:nickname]) create_if_not_exists unique_index(:users, [:nickname])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubServerSubscription do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:websub_server_subscriptions) do create_if_not_exists table(:websub_server_subscriptions) do
add :topic, :string add :topic, :string
add :callback, :string add :callback, :string
add :secret, :string add :secret, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubClientSubscription do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:websub_client_subscriptions) do create_if_not_exists table(:websub_client_subscriptions) do
add :topic, :string add :topic, :string
add :secret, :string add :secret, :string
add :valid_until, :naive_datetime_usec add :valid_until, :naive_datetime_usec

View file

@ -1,10 +1,12 @@
defmodule Pleroma.Repo.Migrations.AddIdContraintsToActivitiesAndObjectsPartTwo do defmodule Pleroma.Repo.Migrations.AddIdContraintsToActivitiesAndObjectsPartTwo do
use Ecto.Migration use Ecto.Migration
def change do def up do
drop_if_exists index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index) drop_if_exists index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index)
drop_if_exists index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index) drop_if_exists index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index)
create unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index) create_if_not_exists unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index)
create unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index) create_if_not_exists unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index)
end end
def down, do: :ok
end end

View file

@ -6,6 +6,6 @@ def change do
add :local, :boolean, default: true add :local, :boolean, default: true
end end
create index(:activities, [:local]) create_if_not_exists index(:activities, [:local])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAPID do
use Ecto.Migration use Ecto.Migration
def change do def change do
create unique_index(:users, [:ap_id]) create_if_not_exists unique_index(:users, [:ap_id])
end end
end end

View file

@ -1,19 +1,31 @@
defmodule Pleroma.Repo.Migrations.CaseInsensivtivity do defmodule Pleroma.Repo.Migrations.CaseInsensivtivity do
use Ecto.Migration use Ecto.Migration
# Two-steps alters are intentional.
# When alter of 2 columns is done in a single operation,
# inconsistent failures happen because of index on `email` column.
def up do def up do
execute ("create extension if not exists citext") execute("create extension if not exists citext")
alter table(:users) do alter table(:users) do
modify :email, :citext modify(:email, :citext)
modify :nickname, :citext end
alter table(:users) do
modify(:nickname, :citext)
end end
end end
def down do def down do
alter table(:users) do alter table(:users) do
modify :email, :string modify(:email, :string)
modify :nickname, :string
end end
execute ("drop extension if exists citext")
alter table(:users) do
modify(:nickname, :string)
end
execute("drop extension if exists citext")
end end
end end

View file

@ -1,9 +1,16 @@
defmodule Pleroma.Repo.Migrations.LongerBios do defmodule Pleroma.Repo.Migrations.LongerBios do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:users) do alter table(:users) do
modify :bio, :text modify :bio, :text
end end
end end
def down do
alter table(:users) do
modify :bio, :string
end
end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.RemoveActivitiesIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
drop index(:activities, [:data]) drop_if_exists index(:activities, [:data])
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddObjectActivityIndex do
def change do def change do
# This was wrong, now a noop # This was wrong, now a noop
# create index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) # create_if_not_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddObjectActivityIndexPartTwo do
def change do def change do
drop_if_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) drop_if_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
create index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) create_if_not_exists index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddActorIndexToActivity do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index) create_if_not_exists index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddMastodonApps do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:apps) do create_if_not_exists table(:apps) do
add :client_name, :string add :client_name, :string
add :redirect_uris, :string add :redirect_uris, :string
add :scopes, :string add :scopes, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthAuthorizations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:oauth_authorizations) do create_if_not_exists table(:oauth_authorizations) do
add :app_id, references(:apps) add :app_id, references(:apps)
add :user_id, references(:users) add :user_id, references(:users)
add :token, :string add :token, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthToken do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:oauth_tokens) do create_if_not_exists table(:oauth_tokens) do
add :app_id, references(:apps) add :app_id, references(:apps)
add :user_id, references(:users) add :user_id, references(:users)
add :token, :string add :token, :string

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateNotifications do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:notifications) do create_if_not_exists table(:notifications) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :activity_id, references(:activities, on_delete: :delete_all) add :activity_id, references(:activities, on_delete: :delete_all)
add :seen, :boolean, default: false add :seen, :boolean, default: false
@ -10,6 +10,6 @@ def change do
timestamps() timestamps()
end end
create index(:notifications, [:user_id]) create_if_not_exists index(:notifications, [:user_id])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePasswordResetTokens do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:password_reset_tokens) do create_if_not_exists table(:password_reset_tokens) do
add :token, :string add :token, :string
add :user_id, references(:users) add :user_id, references(:users)
add :used, :boolean, default: false add :used, :boolean, default: false

View file

@ -12,7 +12,7 @@ def up do
end end
def down do def down do
drop index(:activities, [:actor, "id DESC NULLS LAST"]) drop_if_exists index(:activities, [:actor, "id DESC NULLS LAST"])
alter table(:activities) do alter table(:activities) do
remove :actor remove :actor
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddLocalIndexToUser do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, [:local]) create_if_not_exists index(:users, [:local])
end end
end end

View file

@ -6,6 +6,6 @@ def change do
add :recipients, {:array, :string} add :recipients, {:array, :string}
end end
create index(:activities, [:recipients], using: :gin) create_if_not_exists index(:activities, [:recipients], using: :gin)
end end
end end

View file

@ -18,4 +18,6 @@ def up do
end) end)
end end
end end
def down, do: :ok
end end

View file

@ -1,7 +1,7 @@
defmodule Pleroma.Repo.Migrations.MakeFollowingPostgresArray do defmodule Pleroma.Repo.Migrations.MakeFollowingPostgresArray do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:users) do alter table(:users) do
add :following_temp, {:array, :string} add :following_temp, {:array, :string}
end end
@ -15,4 +15,6 @@ def change do
end end
rename table(:users), :following_temp, to: :following rename table(:users), :following_temp, to: :following
end end
def down, do: :ok
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.DropLocalIndexOnActivities do
use Ecto.Migration use Ecto.Migration
def change do def change do
drop index(:users, [:local]) drop_if_exists index(:users, [:local])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.ActuallyDropLocalIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, [:local]) create_if_not_exists index(:users, [:local])
drop_if_exists index("activities", :local) drop_if_exists index("activities", :local)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateLists do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:lists) do create_if_not_exists table(:lists) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :title, :string add :title, :string
add :following, {:array, :string} add :following, {:array, :string}
@ -10,6 +10,6 @@ def change do
timestamps() timestamps()
end end
create index(:lists, [:user_id]) create_if_not_exists index(:lists, [:user_id])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.CreateUserTrigramIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) create_if_not_exists index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddListFollowIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:lists, [:following]) create_if_not_exists index(:lists, [:following])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateUserInviteTokens do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:user_invite_tokens) do create_if_not_exists table(:user_invite_tokens) do
add :token, :string add :token, :string
add :used, :boolean, default: false add :used, :boolean, default: false

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateFilters do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:filters) do create_if_not_exists table(:filters) do
add :user_id, references(:users, on_delete: :delete_all) add :user_id, references(:users, on_delete: :delete_all)
add :filter_id, :integer add :filter_id, :integer
add :hide, :boolean add :hide, :boolean
@ -14,7 +14,7 @@ def change do
timestamps() timestamps()
end end
create index(:filters, [:user_id]) create_if_not_exists index(:filters, [:user_id])
create index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index) create_if_not_exists index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index)
end end
end end

View file

@ -7,7 +7,7 @@ def change do
add :recipients_cc, {:array, :string} add :recipients_cc, {:array, :string}
end end
create index(:activities, [:recipients_to], using: :gin) create_if_not_exists index(:activities, [:recipients_to], using: :gin)
create index(:activities, [:recipients_cc], using: :gin) create_if_not_exists index(:activities, [:recipients_cc], using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.ActivitiesAddToCcIndices do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin) create_if_not_exists index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin)
create index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin) create_if_not_exists index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin)
end end
end end

View file

@ -1,10 +1,17 @@
defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:activities) do alter table(:activities) do
remove :recipients_to remove :recipients_to
remove :recipients_cc remove :recipients_cc
end end
end end
def down do
alter table(:activities) do
add :recipients_to, {:array, :string}
add :recipients_cc, {:array, :string}
end
end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsModeratorIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin) create_if_not_exists index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table("push_subscriptions") do create_if_not_exists table("push_subscriptions") do
add :user_id, references("users", on_delete: :delete_all) add :user_id, references("users", on_delete: :delete_all)
add :token_id, references("oauth_tokens", on_delete: :delete_all) add :token_id, references("oauth_tokens", on_delete: :delete_all)
add :endpoint, :string add :endpoint, :string
@ -13,6 +13,6 @@ def change do
timestamps() timestamps()
end end
create index("push_subscriptions", [:user_id, :token_id], unique: true) create_if_not_exists index("push_subscriptions", [:user_id, :token_id], unique: true)
end end
end end

View file

@ -1,7 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddUUIDExtension do defmodule Pleroma.Repo.Migrations.AddUUIDExtension do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute("create extension if not exists \"uuid-ossp\"") execute("create extension if not exists \"uuid-ossp\"")
end end
def down, do: :ok
end end

View file

@ -1,7 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddUUIDsToUserInfo do defmodule Pleroma.Repo.Migrations.AddUUIDsToUserInfo do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute("update users set info = jsonb_set(info, '{\"id\"}', to_jsonb(uuid_generate_v4()))") execute("update users set info = jsonb_set(info, '{\"id\"}', to_jsonb(uuid_generate_v4()))")
end end
def down, do: :ok
end end

View file

@ -6,6 +6,6 @@ def change do
add :tags, {:array, :string} add :tags, {:array, :string}
end end
create index(:users, [:tags], using: :gin) create_if_not_exists index(:users, [:tags], using: :gin)
end end
end end

View file

@ -12,7 +12,7 @@ defmodule Pleroma.Repo.Migrations.UsersAndActivitiesFlakeId do
# 4- update relation pkeys with the new ids # 4- update relation pkeys with the new ids
# 5- rename the temporary column to id # 5- rename the temporary column to id
# 6- re-create the constraints # 6- re-create the constraints
def change do def up do
# Old serial int ids are transformed to 128bits with extra padding. # Old serial int ids are transformed to 128bits with extra padding.
# The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility # The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility
# with previously issued ids. # with previously issued ids.
@ -75,6 +75,8 @@ def change do
stop_clippy_heartbeats(clippy) stop_clippy_heartbeats(clippy)
end end
def down, do: :ok
defp start_clippy_heartbeats() do defp start_clippy_heartbeats() do
count = from(a in "activities", select: count(a.id)) |> Repo.one! count = from(a in "activities", select: count(a.id)) |> Repo.one!

View file

@ -37,12 +37,12 @@ def up do
end end
def down do def down do
drop( drop_if_exists(
index(:activities, ["activity_visibility(actor, recipients, data)"], index(:activities, ["activity_visibility(actor, recipients, data)"],
name: :activities_visibility_index name: :activities_visibility_index
) )
) )
execute("drop function activity_visibility(actor varchar, recipients varchar[], data jsonb)") execute("drop function if exists activity_visibility(actor varchar, recipients varchar[], data jsonb)")
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateUserFtsIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index( create_if_not_exists index(
:users, :users,
[ [
""" """

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Repo.Migrations.FixUserTrigramIndex do
def up do def up do
drop_if_exists(index(:users, [], name: :users_trigram_index)) drop_if_exists(index(:users, [], name: :users_trigram_index))
create( create_if_not_exists(
index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"], index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"],
name: :users_trigram_index, name: :users_trigram_index,
using: :gist using: :gist
@ -15,7 +15,7 @@ def up do
def down do def down do
drop_if_exists(index(:users, [], name: :users_trigram_index)) drop_if_exists(index(:users, [], name: :users_trigram_index))
create( create_if_not_exists(
index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
) )
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsAdminIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin)) create_if_not_exists(index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin))
end end
end end

View file

@ -2,14 +2,14 @@ defmodule Pleroma.Repo.Migrations.CreateInstances do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:instances) do create_if_not_exists table(:instances) do
add :host, :string add :host, :string
add :unreachable_since, :naive_datetime_usec add :unreachable_since, :naive_datetime_usec
timestamps() timestamps()
end end
create unique_index(:instances, [:host]) create_if_not_exists unique_index(:instances, [:host])
create index(:instances, [:unreachable_since]) create_if_not_exists index(:instances, [:unreachable_since])
end end
end end

View file

@ -1,9 +1,11 @@
defmodule Pleroma.Repo.Migrations.FixInfoIds do defmodule Pleroma.Repo.Migrations.FixInfoIds do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute( execute(
"update users set info = jsonb_set(info, '{id}', to_jsonb(uuid_generate_v4())) where info->'id' is null;" "update users set info = jsonb_set(info, '{id}', to_jsonb(uuid_generate_v4())) where info->'id' is null;"
) )
end end
def down, do: :ok
end end

View file

@ -1,9 +1,15 @@
defmodule Pleroma.Repo.Migrations.ChangePushSubscriptionsVarchar do defmodule Pleroma.Repo.Migrations.ChangePushSubscriptionsVarchar do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:push_subscriptions) do alter table(:push_subscriptions) do
modify(:endpoint, :varchar) modify(:endpoint, :varchar)
end end
end end
def down do
alter table(:push_subscriptions) do
modify(:endpoint, :string)
end
end
end end

View file

@ -19,7 +19,7 @@ def up do
end end
def down do def down do
drop( drop_if_exists(
index(:activities, ["activity_visibility(actor, recipients, data)", "id DESC"], index(:activities, ["activity_visibility(actor, recipients, data)", "id DESC"],
name: :activities_visibility_index, name: :activities_visibility_index,
concurrently: true, concurrently: true,

View file

@ -2,11 +2,11 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:thread_mutes) do create_if_not_exists table(:thread_mutes) do
add :user_id, references(:users, type: :uuid, on_delete: :delete_all) add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
add :context, :string add :context, :string
end end
create unique_index(:thread_mutes, [:user_id, :context], name: :unique_index) create_if_not_exists unique_index(:thread_mutes, [:user_id, :context], name: :unique_index)
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateRegistrations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:registrations, primary_key: false) do create_if_not_exists table(:registrations, primary_key: false) do
add :id, :uuid, primary_key: true add :id, :uuid, primary_key: true
add :user_id, references(:users, type: :uuid, on_delete: :delete_all) add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
add :provider, :string add :provider, :string
@ -12,7 +12,7 @@ def change do
timestamps() timestamps()
end end
create unique_index(:registrations, [:provider, :uid]) create_if_not_exists unique_index(:registrations, [:provider, :uid])
create unique_index(:registrations, [:user_id, :provider, :uid]) create_if_not_exists unique_index(:registrations, [:user_id, :provider, :uid])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.CreateNotificationIdIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:notifications, ["id desc nulls last"]) create_if_not_exists index(:notifications, ["id desc nulls last"])
end end
end end

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.CreateScheduledActivities do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:scheduled_activities) do create_if_not_exists table(:scheduled_activities) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:scheduled_at, :naive_datetime, null: false) add(:scheduled_at, :naive_datetime, null: false)
add(:params, :map, null: false) add(:params, :map, null: false)
@ -10,7 +10,7 @@ def change do
timestamps() timestamps()
end end
create(index(:scheduled_activities, [:scheduled_at])) create_if_not_exists(index(:scheduled_activities, [:scheduled_at]))
create(index(:scheduled_activities, [:user_id])) create_if_not_exists(index(:scheduled_activities, [:user_id]))
end end
end end

View file

@ -2,8 +2,8 @@ defmodule Pleroma.Repo.Migrations.AddOauthTokenIndexes do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(unique_index(:oauth_tokens, [:token])) create_if_not_exists(unique_index(:oauth_tokens, [:token]))
create(index(:oauth_tokens, [:app_id])) create_if_not_exists(index(:oauth_tokens, [:app_id]))
create(index(:oauth_tokens, [:user_id])) create_if_not_exists(index(:oauth_tokens, [:user_id]))
end end
end end

View file

@ -6,12 +6,12 @@ defmodule Pleroma.Repo.Migrations.CreateConversations do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:conversations) do create_if_not_exists table(:conversations) do
add(:ap_id, :string, null: false) add(:ap_id, :string, null: false)
timestamps() timestamps()
end end
create table(:conversation_participations) do create_if_not_exists table(:conversation_participations) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:conversation_id, references(:conversations, on_delete: :delete_all)) add(:conversation_id, references(:conversations, on_delete: :delete_all))
add(:read, :boolean, default: false) add(:read, :boolean, default: false)
@ -19,8 +19,8 @@ def change do
timestamps() timestamps()
end end
create index(:conversation_participations, [:conversation_id]) create_if_not_exists index(:conversation_participations, [:conversation_id])
create unique_index(:conversation_participations, [:user_id, :conversation_id]) create_if_not_exists unique_index(:conversation_participations, [:user_id, :conversation_id])
create unique_index(:conversations, [:ap_id]) create_if_not_exists unique_index(:conversations, [:ap_id])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddParticipationUpdatedAtIndex do
use Ecto.Migration use Ecto.Migration
def change do def change do
create index(:conversation_participations, ["updated_at desc"]) create_if_not_exists index(:conversation_participations, ["updated_at desc"])
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnUserInfoDeactivated do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin)) create_if_not_exists(index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin))
end end
end end

View file

@ -2,13 +2,13 @@ defmodule Pleroma.Repo.Migrations.CreateBookmarks do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:bookmarks) do create_if_not_exists table(:bookmarks) do
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all)) add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all))
timestamps() timestamps()
end end
create(unique_index(:bookmarks, [:user_id, :activity_id])) create_if_not_exists(unique_index(:bookmarks, [:user_id, :activity_id]))
end end
end end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Repo alias Pleroma.Repo
def change do def up do
query = query =
from(u in User, from(u in User,
where: u.local == true, where: u.local == true,
@ -18,7 +18,7 @@ def change do
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} -> |> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
Enum.each(bookmarks, fn ap_id -> Enum.each(bookmarks, fn ap_id ->
activity = Activity.get_create_by_object_ap_id(ap_id) activity = Activity.get_create_by_object_ap_id(ap_id)
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id) unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
end) end)
end) end)
@ -26,4 +26,10 @@ def change do
remove(:bookmarks) remove(:bookmarks)
end end
end end
def down do
alter table(:users) do
add :bookmarks, {:array, :string}, null: false, default: []
end
end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddFTSIndexToObjects do
def change do def change do
drop_if_exists index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], using: :gin, name: :activities_fts) drop_if_exists index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], using: :gin, name: :activities_fts)
create index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts)
end end
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddRefreshTokenIndexToToken do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(unique_index(:oauth_tokens, [:refresh_token])) create_if_not_exists(unique_index(:oauth_tokens, [:refresh_token]))
end end
end end

View file

@ -1,9 +1,15 @@
defmodule Pleroma.Repo.Migrations.ChangeHideColumnInFilterTable do defmodule Pleroma.Repo.Migrations.ChangeHideColumnInFilterTable do
use Ecto.Migration use Ecto.Migration
def change do def up do
alter table(:filters) do alter table(:filters) do
modify :hide, :boolean, default: false modify :hide, :boolean, default: false
end end
end end
def down do
alter table(:filters) do
modify :hide, :boolean
end
end
end end

View file

@ -68,6 +68,6 @@ def up do
end end
def down do def down do
execute("drop function thread_visibility(actor varchar, activity_id varchar)") execute("drop function if exists thread_visibility(actor varchar, activity_id varchar)")
end end
end end

View file

@ -2,12 +2,12 @@ defmodule Pleroma.Repo.Migrations.CreateConfig do
use Ecto.Migration use Ecto.Migration
def change do def change do
create table(:config) do create_if_not_exists table(:config) do
add(:key, :string) add(:key, :string)
add(:value, :binary) add(:value, :binary)
timestamps() timestamps()
end end
create(unique_index(:config, :key)) create_if_not_exists(unique_index(:config, :key))
end end
end end

View file

@ -1,10 +1,12 @@
defmodule Pleroma.Repo.Migrations.AddNonFollowsAndNonFollowersFieldsToNotificationSettings do defmodule Pleroma.Repo.Migrations.AddNonFollowsAndNonFollowersFieldsToNotificationSettings do
use Ecto.Migration use Ecto.Migration
def change do def up do
execute(""" execute("""
update users set info = jsonb_set(info, '{notification_settings}', '{"local": true, "remote": true, "follows": true, "followers": true, "non_follows": true, "non_followers": true}') update users set info = jsonb_set(info, '{notification_settings}', '{"local": true, "remote": true, "follows": true, "followers": true, "non_follows": true, "non_followers": true}')
where local=true where local=true
""") """)
end end
def down, do: :ok
end end

View file

@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnActivitiesLocal do
use Ecto.Migration use Ecto.Migration
def change do def change do
create(index("activities", [:local])) create_if_not_exists(index("activities", [:local]))
end end
end end

View file

@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddTagIndexToObjects do
def change do def change do
drop_if_exists index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags) drop_if_exists index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags)
create index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags) create_if_not_exists index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags)
end end
end end

View file

@ -6,7 +6,7 @@ def change do
add(:group, :string) add(:group, :string)
end end
drop(unique_index("config", :key)) drop_if_exists(unique_index("config", :key))
create(unique_index("config", [:group, :key])) create_if_not_exists(unique_index("config", [:group, :key]))
end end
end end

View file

@ -14,7 +14,7 @@ def up do
return new; return new;
end end
$$ LANGUAGE plpgsql") $$ LANGUAGE plpgsql")
execute("create index objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');") execute("create index if not exists objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');")
execute("CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON objects execute("CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON objects
FOR EACH ROW EXECUTE PROCEDURE objects_fts_update()") FOR EACH ROW EXECUTE PROCEDURE objects_fts_update()")
@ -23,12 +23,12 @@ def up do
end end
def down do def down do
execute "drop index objects_fts" execute "drop index if exists objects_fts"
execute "drop trigger tsvectorupdate on objects" execute "drop trigger if exists tsvectorupdate on objects"
execute "drop function objects_fts_update()" execute "drop function if exists objects_fts_update()"
alter table(:objects) do alter table(:objects) do
remove(:fts_content, :tsvector) remove(:fts_content, :tsvector)
end end
create index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts)
end end
end end

View file

@ -30,12 +30,15 @@ detect_flavour() {
detect_branch() { detect_branch() {
version="$(cut -d' ' -f2 <"$RELEASE_ROOT"/releases/start_erl.data)" version="$(cut -d' ' -f2 <"$RELEASE_ROOT"/releases/start_erl.data)"
branch="$(echo "$version" | cut -d'-' -f 4)" # Expected format: major.minor.patch_version(-number_of_commits_ahead_of_tag-gcommit_hash).branch
branch="$(echo "$version" | cut -d'.' -f 4)"
if [ "$branch" = "develop" ]; then if [ "$branch" = "develop" ]; then
echo "develop" echo "develop"
elif [ "$branch" = "" ]; then elif [ "$branch" = "" ]; then
echo "master" echo "master"
else else
# Note: branch name in version is of SemVer format and may only contain [0-9a-zA-Z-] symbols —
# if supporting releases for more branches, need to ensure they contain only these symbols.
echo "Releases are built only for master and develop branches" >&2 echo "Releases are built only for master and develop branches" >&2
exit 1 exit 1
fi fi

View file

@ -9,6 +9,12 @@ defmodule Pleroma.Tests.Helpers do
defmacro __using__(_opts) do defmacro __using__(_opts) do
quote do quote do
def collect_ids(collection) do
collection
|> Enum.map(& &1.id)
|> Enum.sort()
end
def refresh_record(%{id: id, __struct__: model} = _), def refresh_record(%{id: id, __struct__: model} = _),
do: refresh_record(model, %{id: id}) do: refresh_record(model, %{id: id})

View file

@ -0,0 +1,40 @@
defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
use Pleroma.DataCase
alias Pleroma.Config
alias Pleroma.Upload
setup do
custom_filename = Config.get([Upload.Filter.AnonymizeFilename, :text])
on_exit(fn ->
Config.put([Upload.Filter.AnonymizeFilename, :text], custom_filename)
end)
upload_file = %Upload{
name: "an… image.jpg",
content_type: "image/jpg",
path: Path.absname("test/fixtures/image_tmp.jpg")
}
%{upload_file: upload_file}
end
test "it replaces filename on pre-defined text", %{upload_file: upload_file} do
Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
{:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert name == "custom-file.png"
end
test "it replaces filename on pre-defined text expression", %{upload_file: upload_file} do
Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}")
{:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert name == "custom-file.jpg"
end
test "it replaces filename on random text", %{upload_file: upload_file} do
{:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert <<_::bytes-size(14)>> <> ".jpg" = name
refute name == "an… image.jpg"
end
end

221
test/user_search_test.exs Normal file
View file

@ -0,0 +1,221 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserSearchTest do
alias Pleroma.Repo
alias Pleroma.User
use Pleroma.DataCase
import Pleroma.Factory
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
describe "User.search" do
test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john")) == 5
end
test "accepts offset parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john", limit: 3, offset: 3)) == 2
end
test "finds a user by full or partial nickname" do
user = insert(:user, %{nickname: "john"})
Enum.each(["john", "jo", "j"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds a user by full or partial name" do
user = insert(:user, %{name: "John Doe"})
Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds users, preferring nickname matches over name matches" do
u1 = insert(:user, %{name: "lain", nickname: "nick1"})
u2 = insert(:user, %{nickname: "lain", name: "nick1"})
assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
end
test "finds users, considering density of matched tokens" do
u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
end
test "finds users, ranking by similarity" do
u1 = insert(:user, %{name: "lain"})
_u2 = insert(:user, %{name: "ean"})
u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
end
test "finds users, handling misspelled requests" do
u1 = insert(:user, %{name: "lain"})
assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
end
test "finds users, boosting ranks of friends and followers" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Doe"})
follower = insert(:user, %{name: "Doe"})
friend = insert(:user, %{name: "Doe"})
{:ok, follower} = User.follow(follower, u1)
{:ok, u1} = User.follow(u1, friend)
assert [friend.id, follower.id, u2.id] --
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
end
test "finds followers of user by partial name" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Jimi"})
follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
follower_lizz = insert(:user, %{name: "Lizz Wright"})
friend = insert(:user, %{name: "Jimi"})
{:ok, follower_jimi} = User.follow(follower_jimi, u1)
{:ok, _follower_lizz} = User.follow(follower_lizz, u2)
{:ok, u1} = User.follow(u1, friend)
assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
follower_jimi.id
]
assert User.search("lizz", following: true, for_user: u1) == []
end
test "find local and remote users for authenticated users" do
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search(for_user: u1)
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
end
test "find only local users for unauthenticated users" do
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
end
test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
Pleroma.Config.put([:instance, :limit_to_local_content], false)
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search()
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "finds a user whose name is nil" do
_user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
assert user_two ==
User.search("lain@pleroma.soykaf.com")
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end
test "does not yield false-positive matches" do
insert(:user, %{name: "John Doe"})
Enum.each(["mary", "a", ""], fn query ->
assert [] == User.search(query)
end)
end
test "works with URIs" do
user = insert(:user)
results =
User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
result = results |> List.first()
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
assert length(results) == 1
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
end
test "excludes a blocked users from search result" do
user = insert(:user, %{nickname: "Bill"})
[blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"}))
blocked_user2 =
insert(
:user,
%{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"}
)
User.block_domain(user, "awful-and-rude-instance.com")
User.block(user, blocked_user)
account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids
assert account_ids == collect_ids(users)
refute Enum.member?(account_ids, blocked_user.id)
refute Enum.member?(account_ids, blocked_user2.id)
assert length(account_ids) == 3
end
end
end

View file

@ -1012,189 +1012,6 @@ test "User.delete() plugs any possible zombie objects" do
end end
end end
describe "User.search" do
test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john")) == 5
end
test "accepts offset parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
assert length(User.search("john", limit: 3, offset: 3)) == 2
end
test "finds a user by full or partial nickname" do
user = insert(:user, %{nickname: "john"})
Enum.each(["john", "jo", "j"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds a user by full or partial name" do
user = insert(:user, %{name: "John Doe"})
Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
assert user ==
User.search(query)
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end)
end
test "finds users, preferring nickname matches over name matches" do
u1 = insert(:user, %{name: "lain", nickname: "nick1"})
u2 = insert(:user, %{nickname: "lain", name: "nick1"})
assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
end
test "finds users, considering density of matched tokens" do
u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
end
test "finds users, ranking by similarity" do
u1 = insert(:user, %{name: "lain"})
_u2 = insert(:user, %{name: "ean"})
u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
end
test "finds users, handling misspelled requests" do
u1 = insert(:user, %{name: "lain"})
assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
end
test "finds users, boosting ranks of friends and followers" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Doe"})
follower = insert(:user, %{name: "Doe"})
friend = insert(:user, %{name: "Doe"})
{:ok, follower} = User.follow(follower, u1)
{:ok, u1} = User.follow(u1, friend)
assert [friend.id, follower.id, u2.id] --
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
end
test "finds followers of user by partial name" do
u1 = insert(:user)
u2 = insert(:user, %{name: "Jimi"})
follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
follower_lizz = insert(:user, %{name: "Lizz Wright"})
friend = insert(:user, %{name: "Jimi"})
{:ok, follower_jimi} = User.follow(follower_jimi, u1)
{:ok, _follower_lizz} = User.follow(follower_lizz, u2)
{:ok, u1} = User.follow(u1, friend)
assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
follower_jimi.id
]
assert User.search("lizz", following: true, for_user: u1) == []
end
test "find local and remote users for authenticated users" do
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search(for_user: u1)
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
end
test "find only local users for unauthenticated users" do
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
end
test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
%{id: id} = insert(:user, %{name: "lain"})
insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
Pleroma.Config.put([:instance, :limit_to_local_content], false)
u1 = insert(:user, %{name: "lain"})
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
results =
"lain"
|> User.search()
|> Enum.map(& &1.id)
|> Enum.sort()
assert [u1.id, u2.id, u3.id] == results
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end
test "finds a user whose name is nil" do
_user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
assert user_two ==
User.search("lain@pleroma.soykaf.com")
|> List.first()
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
end
test "does not yield false-positive matches" do
insert(:user, %{name: "John Doe"})
Enum.each(["mary", "a", ""], fn query ->
assert [] == User.search(query)
end)
end
test "works with URIs" do
user = insert(:user)
results =
User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
result = results |> List.first()
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
assert length(results) == 1
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
end
end
test "auth_active?/1 works correctly" do test "auth_active?/1 works correctly" do
Pleroma.Config.put([:instance, :account_activation_required], true) Pleroma.Config.put([:instance, :account_activation_required], true)

Some files were not shown because too many files have changed in this diff Show more