# Conflicts: # CHANGELOG.mdtags/v1.1.4
@@ -23,9 +23,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Not being able to pin unlisted posts | |||
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised. | |||
- Metadata rendering errors resulting in the entire page being inaccessible | |||
- `federation_incoming_replies_max_depth` option being ignored in certain cases | |||
- Federation/MediaProxy not working with instances that have wrong certificate order | |||
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`) | |||
- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity | |||
- Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set | |||
- Mastodon API: `muted` in the Status entity, using author's account to determine if the tread was muted | |||
- Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`) | |||
- Mastodon API, streaming: Fix filtering of notifications based on blocks/mutes/thread mutes | |||
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set | |||
@@ -34,16 +37,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Rich Media: The crawled URL is now spliced into the rich media data. | |||
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification. | |||
- ActivityPub S2S: remote user deletions now work the same as local user deletions. | |||
- ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header. | |||
- Not being able to access the Mastodon FE login page on private instances | |||
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag | |||
- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected. | |||
- Report email not being sent to admins when the reporter is a remote user | |||
- MRF: ensure that subdomain_match calls are case-insensitive | |||
- MRF: fix use of unserializable keyword lists in describe() implementations | |||
- ActivityPub: Deactivated user deletion | |||
### Added | |||
- **Breaking:** MRF describe API, which adds support for exposing configuration information about MRF policies to NodeInfo. | |||
Custom modules will need to be updated by adding, at the very least, `def describe, do: {:ok, %{}}` to the MRF policy modules. | |||
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`) | |||
- MRF: Support for excluding specific domains from Transparency. | |||
- MRF: Support for filtering posts based on who they mention (`Pleroma.Web.ActivityPub.MRF.MentionPolicy`) | |||
- MRF: Support for filtering posts based on ActivityStreams vocabulary (`Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`) | |||
- MRF (Simple Policy): Support for wildcard domains. | |||
- Support for wildcard domains in user domain blocks setting. | |||
- Configuration: `quarantined_instances` support wildcard domains. | |||
@@ -66,6 +75,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Added synchronization of following/followers counters for external users | |||
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`. | |||
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options. | |||
- Configuration: `user_bio_length` and `user_name_length` options. | |||
- Addressable lists | |||
- Twitter API: added rate limit for `/api/account/password_reset` endpoint. | |||
- ActivityPub: Add an internal service actor for fetching ActivityPub objects. | |||
@@ -73,6 +83,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Admin API: Endpoint for fetching latest user's statuses | |||
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation. | |||
- Relays: Added a task to list relay subscriptions. | |||
- Mix Tasks: `mix pleroma.database fix_likes_collections` | |||
- Federation: Remove `likes` from objects. | |||
### Changed | |||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text | |||
@@ -83,6 +95,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
### Removed | |||
- Emoji: Remove longfox emojis. | |||
- Remove `Reply-To` header from report emails for admins. | |||
- ActivityPub: The `accept_blocks` configuration setting. | |||
## [1.0.1] - 2019-07-14 | |||
### Security | |||
@@ -253,6 +253,8 @@ config :pleroma, :instance, | |||
skip_thread_containment: true, | |||
limit_to_local_content: :unauthenticated, | |||
dynamic_configuration: false, | |||
user_bio_length: 5000, | |||
user_name_length: 100, | |||
external_user_synchronization: true | |||
config :pleroma, :markup, | |||
@@ -302,7 +304,6 @@ config :pleroma, :assets, | |||
default_mascot: :pleroma_fox_tan | |||
config :pleroma, :activitypub, | |||
accept_blocks: true, | |||
unfollow_blocked: true, | |||
outgoing_blocks: true, | |||
follow_handshake_timeout: 500, | |||
@@ -337,6 +338,10 @@ config :pleroma, :mrf_keyword, | |||
config :pleroma, :mrf_subchain, match_actor: %{} | |||
config :pleroma, :mrf_vocabulary, | |||
accept: [], | |||
reject: [] | |||
config :pleroma, :rich_media, | |||
enabled: true, | |||
ignore_hosts: [], | |||
@@ -18,6 +18,7 @@ Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. | |||
## Pleroma.Uploaders.S3 | |||
* `bucket`: S3 bucket name | |||
* `bucket_namespace`: S3 bucket namespace | |||
* `public_endpoint`: S3 endpoint that the user finally accesses(ex. "https://s3.dualstack.ap-northeast-1.amazonaws.com") | |||
* `truncated_namespace`: If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or "" etc. | |||
For example, when using CDN to S3 virtual host format, set "". | |||
@@ -102,6 +103,7 @@ config :pleroma, Pleroma.Emails.Mailer, | |||
* `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. | |||
* `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (see `:mrf_mention` section) | |||
* `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (see `:mrf_vocabulary` section) | |||
* `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. | |||
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json`` | |||
@@ -125,6 +127,8 @@ config :pleroma, Pleroma.Emails.Mailer, | |||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`. | |||
* `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``. | |||
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database. | |||
* `user_bio_length`: A user bio maximum length (default: `5000`) | |||
* `user_name_length`: A user name maximum length (default: `100`) | |||
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`. | |||
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`. | |||
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api. | |||
@@ -275,6 +279,10 @@ config :pleroma, :mrf_subchain, | |||
## :mrf_mention | |||
* `actors`: A list of actors, for which to drop any posts mentioning. | |||
## :mrf_vocabulary | |||
* `accept`: A list of ActivityStreams terms to accept. If empty, all supported messages are accepted. | |||
* `reject`: A list of ActivityStreams terms to reject. If empty, no messages are rejected. | |||
## :media_proxy | |||
* `enabled`: Enables proxying of remote media to the instance’s proxy | |||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts. | |||
@@ -328,7 +336,6 @@ config :pleroma, Pleroma.Web.Endpoint, | |||
This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls starting with `https://example.com:2020` | |||
## :activitypub | |||
* ``accept_blocks``: Whether to accept incoming block activities from other instances | |||
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed | |||
* ``outgoing_blocks``: Whether to federate blocks to other instances | |||
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question | |||
@@ -36,6 +36,10 @@ defmodule Mix.Tasks.Pleroma.Database do | |||
## Remove duplicated items from following and update followers count for all users | |||
mix pleroma.database update_users_following_followers_counts | |||
## Fix the pre-existing "likes" collections for all objects | |||
mix pleroma.database fix_likes_collections | |||
""" | |||
def run(["remove_embedded_objects" | args]) do | |||
{options, [], []} = | |||
@@ -125,4 +129,36 @@ defmodule Mix.Tasks.Pleroma.Database do | |||
) | |||
end | |||
end | |||
def run(["fix_likes_collections"]) do | |||
import Ecto.Query | |||
start_pleroma() | |||
from(object in Object, | |||
where: fragment("(?)->>'likes' is not null", object.data), | |||
select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)} | |||
) | |||
|> Pleroma.RepoStreamer.chunk_stream(100) | |||
|> Stream.each(fn objects -> | |||
ids = | |||
objects | |||
|> Enum.filter(fn object -> object.likes |> Jason.decode!() |> is_map() end) | |||
|> Enum.map(& &1.id) | |||
Object | |||
|> where([object], object.id in ^ids) | |||
|> update([object], | |||
set: [ | |||
data: | |||
fragment( | |||
"jsonb_set(?, '{likes}', '[]'::jsonb, true)", | |||
object.data | |||
) | |||
] | |||
) | |||
|> Repo.update_all([], timeout: :infinity) | |||
end) | |||
|> Stream.run() | |||
end | |||
end |
@@ -224,6 +224,29 @@ defmodule Pleroma.Activity do | |||
def get_create_by_object_ap_id(_), do: nil | |||
def create_by_object_ap_id_with_object(ap_ids) when is_list(ap_ids) do | |||
from( | |||
activity in Activity, | |||
where: | |||
fragment( | |||
"coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", | |||
activity.data, | |||
activity.data, | |||
^ap_ids | |||
), | |||
where: fragment("(?)->>'type' = 'Create'", activity.data), | |||
inner_join: o in Object, | |||
on: | |||
fragment( | |||
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", | |||
o.data, | |||
activity.data, | |||
activity.data | |||
), | |||
preload: [object: o] | |||
) | |||
end | |||
def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do | |||
from( | |||
activity in Activity, | |||
@@ -263,8 +286,8 @@ defmodule Pleroma.Activity do | |||
defp get_in_reply_to_activity_from_object(_), do: nil | |||
def get_in_reply_to_activity(%Activity{data: %{"object" => object}}) do | |||
get_in_reply_to_activity_from_object(Object.normalize(object)) | |||
def get_in_reply_to_activity(%Activity{} = activity) do | |||
get_in_reply_to_activity_from_object(Object.normalize(activity)) | |||
end | |||
def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"]) | |||
@@ -3,11 +3,14 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Application do | |||
import Cachex.Spec | |||
use Application | |||
@name Mix.Project.config()[:name] | |||
@version Mix.Project.config()[:version] | |||
@repository Mix.Project.config()[:source_url] | |||
@env Mix.env() | |||
def name, do: @name | |||
def version, do: @version | |||
def named_version, do: @name <> " " <> @version | |||
@@ -21,116 +24,25 @@ defmodule Pleroma.Application do | |||
# See http://elixir-lang.org/docs/stable/elixir/Application.html | |||
# for more information on OTP Applications | |||
def start(_type, _args) do | |||
import Cachex.Spec | |||
Pleroma.Config.DeprecationWarnings.warn() | |||
setup_instrumenters() | |||
# Define workers and child supervisors to be supervised | |||
children = | |||
[ | |||
# Start the Ecto repository | |||
%{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor}, | |||
%{id: Pleroma.Config.TransferTask, start: {Pleroma.Config.TransferTask, :start_link, []}}, | |||
%{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}}, | |||
%{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}}, | |||
%{ | |||
id: :cachex_used_captcha_cache, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:used_captcha_cache, | |||
[ | |||
ttl_interval: | |||
:timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])) | |||
] | |||
]} | |||
}, | |||
%{ | |||
id: :cachex_user, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:user_cache, | |||
[ | |||
default_ttl: 25_000, | |||
ttl_interval: 1000, | |||
limit: 2500 | |||
] | |||
]} | |||
}, | |||
%{ | |||
id: :cachex_object, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:object_cache, | |||
[ | |||
default_ttl: 25_000, | |||
ttl_interval: 1000, | |||
limit: 2500 | |||
] | |||
]} | |||
}, | |||
%{ | |||
id: :cachex_rich_media, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:rich_media_cache, | |||
[ | |||
default_ttl: :timer.minutes(120), | |||
limit: 5000 | |||
] | |||
]} | |||
}, | |||
%{ | |||
id: :cachex_scrubber, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:scrubber_cache, | |||
[ | |||
limit: 2500 | |||
] | |||
]} | |||
}, | |||
%{ | |||
id: :cachex_idem, | |||
start: | |||
{Cachex, :start_link, | |||
[ | |||
:idempotency_cache, | |||
[ | |||
expiration: | |||
expiration( | |||
default: :timer.seconds(6 * 60 * 60), | |||
interval: :timer.seconds(60) | |||
), | |||
limit: 2500 | |||
] | |||
]} | |||
}, | |||
%{id: Pleroma.FlakeId, start: {Pleroma.FlakeId, :start_link, []}}, | |||
%{ | |||
id: Pleroma.ScheduledActivityWorker, | |||
start: {Pleroma.ScheduledActivityWorker, :start_link, []} | |||
} | |||
Pleroma.Repo, | |||
Pleroma.Config.TransferTask, | |||
Pleroma.Emoji, | |||
Pleroma.Captcha, | |||
Pleroma.FlakeId, | |||
Pleroma.ScheduledActivityWorker | |||
] ++ | |||
cachex_children() ++ | |||
hackney_pool_children() ++ | |||
[ | |||
%{ | |||
id: Pleroma.Web.Federator.RetryQueue, | |||
start: {Pleroma.Web.Federator.RetryQueue, :start_link, []} | |||
}, | |||
%{ | |||
id: Pleroma.Web.OAuth.Token.CleanWorker, | |||
start: {Pleroma.Web.OAuth.Token.CleanWorker, :start_link, []} | |||
}, | |||
%{ | |||
id: Pleroma.Stats, | |||
start: {Pleroma.Stats, :start_link, []} | |||
}, | |||
Pleroma.Web.Federator.RetryQueue, | |||
Pleroma.Web.OAuth.Token.CleanWorker, | |||
Pleroma.Stats, | |||
%{ | |||
id: :web_push_init, | |||
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]}, | |||
@@ -147,16 +59,12 @@ defmodule Pleroma.Application do | |||
restart: :temporary | |||
} | |||
] ++ | |||
streamer_child() ++ | |||
chat_child() ++ | |||
oauth_cleanup_child(oauth_cleanup_enabled?()) ++ | |||
streamer_child(@env) ++ | |||
chat_child(@env, chat_enabled?()) ++ | |||
[ | |||
# Start the endpoint when the application starts | |||
%{ | |||
id: Pleroma.Web.Endpoint, | |||
start: {Pleroma.Web.Endpoint, :start_link, []}, | |||
type: :supervisor | |||
}, | |||
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}} | |||
Pleroma.Web.Endpoint, | |||
Pleroma.Gopher.Server | |||
] | |||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html | |||
@@ -201,28 +109,54 @@ defmodule Pleroma.Application do | |||
end | |||
end | |||
if Pleroma.Config.get(:env) == :test do | |||
defp streamer_child, do: [] | |||
defp chat_child, do: [] | |||
else | |||
defp streamer_child do | |||
[%{id: Pleroma.Web.Streamer, start: {Pleroma.Web.Streamer, :start_link, []}}] | |||
end | |||
defp cachex_children do | |||
[ | |||
build_cachex("used_captcha", ttl_interval: seconds_valid_interval()), | |||
build_cachex("user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), | |||
build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), | |||
build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000), | |||
build_cachex("scrubber", limit: 2500), | |||
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500) | |||
] | |||
end | |||
defp chat_child do | |||
if Pleroma.Config.get([:chat, :enabled]) do | |||
[ | |||
%{ | |||
id: Pleroma.Web.ChatChannel.ChatChannelState, | |||
start: {Pleroma.Web.ChatChannel.ChatChannelState, :start_link, []} | |||
} | |||
] | |||
else | |||
[] | |||
end | |||
end | |||
defp idempotency_expiration, | |||
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60)) | |||
defp seconds_valid_interval, | |||
do: :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])) | |||
defp build_cachex(type, opts), | |||
do: %{ | |||
id: String.to_atom("cachex_" <> type), | |||
start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]}, | |||
type: :worker | |||
} | |||
defp chat_enabled?, do: Pleroma.Config.get([:chat, :enabled]) | |||
defp oauth_cleanup_enabled?, | |||
do: Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) | |||
defp streamer_child(:test), do: [] | |||
defp streamer_child(_) do | |||
[Pleroma.Web.Streamer] | |||
end | |||
defp oauth_cleanup_child(true), | |||
do: [Pleroma.Web.OAuth.Token.CleanWorker] | |||
defp oauth_cleanup_child(_), do: [] | |||
defp chat_child(:test, _), do: [] | |||
defp chat_child(_env, true) do | |||
[Pleroma.Web.ChatChannel.ChatChannelState] | |||
end | |||
defp chat_child(_, _), do: [] | |||
defp hackney_pool_children do | |||
for pool <- enabled_hackney_pools() do | |||
options = Pleroma.Config.get([:hackney_pools, pool]) | |||
@@ -12,7 +12,7 @@ defmodule Pleroma.Captcha do | |||
use GenServer | |||
@doc false | |||
def start_link do | |||
def start_link(_) do | |||
GenServer.start_link(__MODULE__, [], name: __MODULE__) | |||
end | |||
@@ -6,7 +6,7 @@ defmodule Pleroma.Config.TransferTask do | |||
use Task | |||
alias Pleroma.Web.AdminAPI.Config | |||
def start_link do | |||
def start_link(_) do | |||
load_and_update_env() | |||
if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Pleroma.Repo) | |||
:ignore | |||
@@ -24,7 +24,7 @@ defmodule Pleroma.Emoji do | |||
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}] | |||
@doc false | |||
def start_link do | |||
def start_link(_) do | |||
GenServer.start_link(__MODULE__, [], name: __MODULE__) | |||
end | |||
@@ -98,7 +98,7 @@ defmodule Pleroma.FlakeId do | |||
def autogenerate, do: get() | |||
# -- GenServer API | |||
def start_link do | |||
def start_link(_) do | |||
:gen_server.start_link({:local, :flake}, __MODULE__, [], []) | |||
end | |||
@@ -6,7 +6,7 @@ defmodule Pleroma.Gopher.Server do | |||
use GenServer | |||
require Logger | |||
def start_link do | |||
def start_link(_) do | |||
config = Pleroma.Config.get(:gopher, []) | |||
ip = Keyword.get(config, :ip, {0, 0, 0, 0}) | |||
port = Keyword.get(config, :port, 1234) | |||
@@ -16,7 +16,7 @@ defmodule Pleroma.ScheduledActivityWorker do | |||
@schedule_interval :timer.minutes(1) | |||
def start_link do | |||
def start_link(_) do | |||
GenServer.start_link(__MODULE__, nil) | |||
end | |||
@@ -7,31 +7,56 @@ defmodule Pleroma.Stats do | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
def start_link do | |||
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__) | |||
spawn(fn -> schedule_update() end) | |||
agent | |||
use GenServer | |||
@interval 1000 * 60 * 60 | |||
def start_link(_) do | |||
GenServer.start_link(__MODULE__, initial_data(), name: __MODULE__) | |||
end | |||
def force_update do | |||
GenServer.call(__MODULE__, :force_update) | |||
end | |||
def get_stats do | |||
Agent.get(__MODULE__, fn {_, stats} -> stats end) | |||
%{stats: stats} = GenServer.call(__MODULE__, :get_state) | |||
stats | |||
end | |||
def get_peers do | |||
Agent.get(__MODULE__, fn {peers, _} -> peers end) | |||
%{peers: peers} = GenServer.call(__MODULE__, :get_state) | |||
peers | |||
end | |||
def init(args) do | |||
Process.send(self(), :run_update, []) | |||
{:ok, args} | |||
end | |||
def handle_call(:force_update, _from, _state) do | |||
new_stats = get_stat_data() | |||
{:reply, new_stats, new_stats} | |||
end | |||
def schedule_update do | |||
spawn(fn -> | |||
# 1 hour | |||
Process.sleep(1000 * 60 * 60) | |||
schedule_update() | |||
end) | |||
def handle_call(:get_state, _from, state) do | |||
{:reply, state, state} | |||
end | |||
def handle_info(:run_update, _state) do | |||
new_stats = get_stat_data() | |||
Process.send_after(self(), :run_update, @interval) | |||
{:noreply, new_stats} | |||
end | |||
update_stats() | |||
defp initial_data do | |||
%{peers: [], stats: %{}} | |||
end | |||
def update_stats do | |||
defp get_stat_data do | |||
peers = | |||
from( | |||
u in User, | |||
@@ -52,8 +77,9 @@ defmodule Pleroma.Stats do | |||
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) | |||
Agent.update(__MODULE__, fn _ -> | |||
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} | |||
end) | |||
%{ | |||
peers: peers, | |||
stats: %{domain_count: domain_count, status_count: status_count, user_count: user_count} | |||
} | |||
end | |||
end |
@@ -11,7 +11,7 @@ defmodule Pleroma.Uploaders.Local do | |||
def put_file(upload) do | |||
{local_path, file} = | |||
case Enum.reverse(String.split(upload.path, "/", trim: true)) do | |||
case Enum.reverse(Path.split(upload.path)) do | |||
[file] -> | |||
{upload_path(), file} | |||
@@ -23,7 +23,7 @@ defmodule Pleroma.Uploaders.Local do | |||
result_file = Path.join(local_path, file) | |||
unless File.exists?(result_file) do | |||
if not File.exists?(result_file) do | |||
File.cp!(upload.tempfile, result_file) | |||
end | |||
@@ -3,6 +3,8 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Uploaders.MDII do | |||
@moduledoc "Represents uploader for https://github.com/hakaba-hitoyo/minimal-digital-image-infrastructure" | |||
alias Pleroma.Config | |||
alias Pleroma.HTTP | |||
@@ -6,10 +6,12 @@ defmodule Pleroma.Uploaders.S3 do | |||
@behaviour Pleroma.Uploaders.Uploader | |||
require Logger | |||
alias Pleroma.Config | |||
# The file name is re-encoded with S3's constraints here to comply with previous | |||
# links with less strict filenames | |||
def get_file(file) do | |||
config = Pleroma.Config.get([__MODULE__]) | |||
config = Config.get([__MODULE__]) | |||
bucket = Keyword.fetch!(config, :bucket) | |||
bucket_with_namespace = | |||
@@ -34,15 +36,15 @@ defmodule Pleroma.Uploaders.S3 do | |||
end | |||
def put_file(%Pleroma.Upload{} = upload) do | |||
config = Pleroma.Config.get([__MODULE__]) | |||
config = Config.get([__MODULE__]) | |||
bucket = Keyword.get(config, :bucket) | |||
{:ok, file_data} = File.read(upload.tempfile) | |||
s3_name = strict_encode(upload.path) | |||
op = | |||
ExAws.S3.put_object(bucket, s3_name, file_data, [ | |||
upload.tempfile | |||
|> ExAws.S3.Upload.stream_file() | |||
|> ExAws.S3.upload(bucket, s3_name, [ | |||
{:acl, :public_read}, | |||
{:content_type, upload.content_type} | |||
]) | |||
@@ -152,10 +152,10 @@ defmodule Pleroma.User do | |||
end | |||
def remote_user_creation(params) do | |||
params = | |||
params | |||
|> Map.put(:info, params[:info] || %{}) | |||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) | |||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) | |||
params = Map.put(params, :info, params[:info] || %{}) | |||
info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info]) | |||
changes = | |||
@@ -164,8 +164,8 @@ defmodule Pleroma.User do | |||
|> validate_required([:name, :ap_id]) | |||
|> unique_constraint(:nickname) | |||
|> validate_format(:nickname, @email_regex) | |||
|> validate_length(:bio, max: 5000) | |||
|> validate_length(:name, max: 100) | |||
|> validate_length(:bio, max: bio_limit) | |||
|> validate_length(:name, max: name_limit) | |||
|> put_change(:local, false) | |||
|> put_embed(:info, info_cng) | |||
@@ -188,22 +188,23 @@ defmodule Pleroma.User do | |||
end | |||
def update_changeset(struct, params \\ %{}) do | |||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) | |||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) | |||
struct | |||
|> cast(params, [:bio, :name, :avatar, :following]) | |||
|> unique_constraint(:nickname) | |||
|> validate_format(:nickname, local_nickname_regex()) | |||
|> validate_length(:bio, max: 5000) | |||
|> validate_length(:name, min: 1, max: 100) | |||
|> validate_length(:bio, max: bio_limit) | |||
|> validate_length(:name, min: 1, max: name_limit) | |||
end | |||
def upgrade_changeset(struct, params \\ %{}) do | |||
params = | |||
params | |||
|> Map.put(:last_refreshed_at, NaiveDateTime.utc_now()) | |||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) | |||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) | |||
info_cng = | |||
struct.info | |||
|> User.Info.user_upgrade(params[:info]) | |||
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now()) | |||
info_cng = User.Info.user_upgrade(struct.info, params[:info]) | |||
struct | |||
|> cast(params, [ | |||
@@ -216,8 +217,8 @@ defmodule Pleroma.User do | |||
]) | |||
|> unique_constraint(:nickname) | |||
|> validate_format(:nickname, local_nickname_regex()) | |||
|> validate_length(:bio, max: 5000) | |||
|> validate_length(:name, max: 100) | |||
|> validate_length(:bio, max: bio_limit) | |||
|> validate_length(:name, max: name_limit) | |||
|> put_embed(:info, info_cng) | |||
end | |||
@@ -244,6 +245,9 @@ defmodule Pleroma.User do | |||
end | |||
def register_changeset(struct, params \\ %{}, opts \\ []) do | |||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) | |||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) | |||
need_confirmation? = | |||
if is_nil(opts[:need_confirmation]) do | |||
Pleroma.Config.get([:instance, :account_activation_required]) | |||
@@ -264,8 +268,8 @@ defmodule Pleroma.User do | |||
|> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames])) | |||
|> validate_format(:nickname, local_nickname_regex()) | |||
|> validate_format(:email, @email_regex) | |||
|> validate_length(:bio, max: 1000) | |||
|> validate_length(:name, min: 1, max: 100) | |||
|> validate_length(:bio, max: bio_limit) | |||
|> validate_length(:name, min: 1, max: name_limit) | |||
|> put_change(:info, info_change) | |||
changeset = | |||
@@ -520,6 +520,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
from(activity in Activity) | |||
|> maybe_preload_objects(opts) | |||
|> maybe_preload_bookmarks(opts) | |||
|> maybe_set_thread_muted_field(opts) | |||
|> restrict_blocked(opts) | |||
|> restrict_recipients(recipients, opts["user"]) | |||
|> where( | |||
@@ -533,6 +535,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
) | |||
) | |||
|> exclude_poll_votes(opts) | |||
|> exclude_id(opts) | |||
|> order_by([activity], desc: activity.id) | |||
end | |||
@@ -625,6 +628,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
params = | |||
params | |||
|> Map.put("type", ["Create", "Announce"]) | |||
|> Map.put("user", reading_user) | |||
|> Map.put("actor_id", user.ap_id) | |||
|> Map.put("whole_db", true) | |||
|> Map.put("pinned_activity_ids", user.info.pinned_activities) | |||
@@ -872,6 +876,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
end | |||
end | |||
defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do | |||
from(activity in query, where: activity.id != ^id) | |||
end | |||
defp exclude_id(query, _), do: query | |||
defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query | |||
defp maybe_preload_objects(query, _) do | |||
@@ -28,11 +28,43 @@ defmodule Pleroma.Web.ActivityPub.MRF do | |||
@spec subdomains_regex([String.t()]) :: [Regex.t()] | |||
def subdomains_regex(domains) when is_list(domains) do | |||
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$) | |||
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)i | |||
end | |||
@spec subdomain_match?([Regex.t()], String.t()) :: boolean() | |||
def subdomain_match?(domains, host) do | |||
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end) | |||
end | |||
@callback describe() :: {:ok | :error, Map.t()} | |||
def describe(policies) do | |||
{:ok, policy_configs} = | |||
policies | |||
|> Enum.reduce({:ok, %{}}, fn | |||
policy, {:ok, data} -> | |||
{:ok, policy_data} = policy.describe() | |||
{:ok, Map.merge(data, policy_data)} | |||
_, error -> | |||
error | |||
end) | |||
mrf_policies = | |||
get_policies() | |||
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end) | |||
exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions]) | |||
base = | |||
%{ | |||
mrf_policies: mrf_policies, | |||
exclusions: length(exclusions) > 0 | |||
} | |||
|> Map.merge(policy_configs) | |||
{:ok, base} | |||
end | |||
def describe, do: get_policies() |> describe() | |||
end |
@@ -62,4 +62,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -5,6 +5,8 @@ | |||
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do | |||
alias Pleroma.User | |||
@behaviour Pleroma.Web.ActivityPub.MRF | |||
require Logger | |||
# has the user successfully posted before? | |||
@@ -22,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do | |||
defp contains_links?(_), do: false | |||
@impl true | |||
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do | |||
with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor), | |||
{:contains_links, true} <- {:contains_links, contains_links?(object)}, | |||
@@ -45,4 +48,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do | |||
# in all other cases, pass through | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -12,4 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do | |||
Logger.info("REJECTING #{inspect(object)}") | |||
{:reject, object} | |||
end | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -39,4 +39,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do | |||
end | |||
def filter(object), do: {:ok, object} | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -90,4 +90,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, | |||
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}} | |||
end |
@@ -96,4 +96,36 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe do | |||
# This horror is needed to convert regex sigils to strings | |||
mrf_keyword = | |||
Pleroma.Config.get(:mrf_keyword, []) | |||
|> Enum.map(fn {key, value} -> | |||
{key, | |||
Enum.map(value, fn | |||
{pattern, replacement} -> | |||
%{ | |||
"pattern" => | |||
if not is_binary(pattern) do | |||
inspect(pattern) | |||
else | |||
pattern | |||
end, | |||
"replacement" => replacement | |||
} | |||
pattern -> | |||
if not is_binary(pattern) do | |||
inspect(pattern) | |||
else | |||
pattern | |||
end | |||
end)} | |||
end) | |||
|> Enum.into(%{}) | |||
{:ok, %{mrf_keyword: mrf_keyword}} | |||
end | |||
end |
@@ -53,4 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -21,4 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -19,4 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do | |||
@impl true | |||
def filter(object), do: {:ok, object} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -10,4 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do | |||
def filter(object) do | |||
{:ok, object} | |||
end | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -21,4 +21,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do | |||
end | |||
def filter(object), do: {:ok, object} | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -44,4 +44,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do | |||
@impl true | |||
def filter(object), do: {:ok, object} | |||
@impl true | |||
def describe, | |||
do: {:ok, %{mrf_rejectnonpublic: Pleroma.Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}} | |||
end |
@@ -177,4 +177,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do | |||
end | |||
def filter(object), do: {:ok, object} | |||
@impl true | |||
def describe do | |||
exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions]) | |||
mrf_simple = | |||
Pleroma.Config.get(:mrf_simple) | |||
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end) | |||
|> Enum.into(%{}) | |||
{:ok, %{mrf_simple: mrf_simple}} | |||
end | |||
end |
@@ -37,4 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -165,4 +165,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{}} | |||
end |
@@ -32,4 +32,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do | |||
end | |||
def filter(object), do: {:ok, object} | |||
@impl true | |||
def describe do | |||
mrf_user_allowlist = | |||
Config.get([:mrf_user_allowlist], []) | |||
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end) | |||
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}} | |||
end | |||
end |
@@ -0,0 +1,37 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do | |||
@moduledoc "Filter messages which belong to certain activity vocabularies" | |||
@behaviour Pleroma.Web.ActivityPub.MRF | |||
def filter(%{"type" => "Undo", "object" => child_message} = message) do | |||
with {:ok, _} <- filter(child_message) do | |||
{:ok, message} | |||
else | |||
{:reject, nil} -> | |||
{:reject, nil} | |||
end | |||
end | |||
def filter(%{"type" => message_type} = message) do | |||
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]), | |||
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]), | |||
true <- | |||
length(accepted_vocabulary) == 0 || Enum.member?(accepted_vocabulary, message_type), | |||
false <- | |||
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type), | |||
{:ok, _} <- filter(message["object"]) do | |||
{:ok, message} | |||
else | |||
_ -> {:reject, nil} | |||
end | |||
end | |||
def filter(message), do: {:ok, message} | |||
def describe, | |||
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}} | |||
end |
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do | |||
""" | |||
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do | |||
Logger.info("Federating #{id} to #{inbox}") | |||
host = URI.parse(inbox).host | |||
%{host: host, path: path} = URI.parse(inbox) | |||
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) | |||
@@ -56,6 +56,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do | |||
signature = | |||
Pleroma.Signature.sign(actor, %{ | |||
"(request-target)": "post #{path}", | |||
host: host, | |||
"content-length": byte_size(json), | |||
digest: digest, | |||
@@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do | |||
|> User.get_or_create_service_actor_by_ap_id() | |||
end | |||
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()} | |||
def follow(target_instance) do | |||
with %User{} = local_user <- get_actor(), | |||
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), | |||
@@ -21,12 +22,17 @@ defmodule Pleroma.Web.ActivityPub.Relay do | |||
Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}") | |||
{:ok, activity} | |||
else | |||
{:error, _} = error -> | |||
Logger.error("error: #{inspect(error)}") | |||
error | |||
e -> | |||
Logger.error("error: #{inspect(e)}") | |||
{:error, e} | |||
end | |||
end | |||
@spec unfollow(String.t()) :: {:ok, Activity.t()} | {:error, any()} | |||
def unfollow(target_instance) do | |||
with %User{} = local_user <- get_actor(), | |||
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), | |||
@@ -34,20 +40,27 @@ defmodule Pleroma.Web.ActivityPub.Relay do | |||
Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}") | |||
{:ok, activity} | |||
else | |||
{:error, _} = error -> | |||
Logger.error("error: #{inspect(error)}") | |||
error | |||
e -> | |||
Logger.error("error: #{inspect(e)}") | |||
{:error, e} | |||
end | |||
end | |||
@spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()} | |||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do | |||
with %User{} = user <- get_actor(), | |||
%Object{} = object <- Object.normalize(activity) do | |||
ActivityPub.announce(user, object, nil, true, false) | |||
else | |||
e -> Logger.error("error: #{inspect(e)}") | |||
e -> | |||
Logger.error("error: #{inspect(e)}") | |||
{:error, inspect(e)} | |||
end | |||
end | |||
def publish(_), do: nil | |||
def publish(_), do: {:error, "Not implemented"} | |||
end |
@@ -26,6 +26,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
""" | |||
def fix_object(object, options \\ []) do | |||
object | |||
|> strip_internal_fields | |||
|> fix_actor | |||
|> fix_url | |||
|> fix_attachments | |||
@@ -34,7 +35,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
|> fix_emoji | |||
|> fix_tag | |||
|> fix_content_map | |||
|> fix_likes | |||
|> fix_addressing | |||
|> fix_summary | |||
|> fix_type(options) | |||
@@ -151,20 +151,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
|> Map.put("actor", Containment.get_actor(%{"actor" => actor})) | |||
end | |||
# Check for standardisation | |||
# This is what Peertube does | |||
# curl -H 'Accept: application/activity+json' $likes | jq .totalItems | |||
# Prismo returns only an integer (count) as "likes" | |||
def fix_likes(%{"likes" => likes} = object) when not is_map(likes) do | |||
object | |||
|> Map.put("likes", []) | |||
|> Map.put("like_count", 0) | |||
end | |||
def fix_likes(object) do | |||
object | |||
end | |||
def fix_in_reply_to(object, options \\ []) | |||
def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options) | |||
@@ -347,13 +333,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
def fix_type(object, options \\ []) | |||
def fix_type(%{"inReplyTo" => reply_id} = object, options) when is_binary(reply_id) do | |||
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options) | |||
when is_binary(reply_id) do | |||
reply = | |||
if Federator.allowed_incoming_reply_depth?(options[:depth]) do | |||
Object.normalize(reply_id, true) | |||
with true <- Federator.allowed_incoming_reply_depth?(options[:depth]), | |||
{:ok, object} <- get_obj_helper(reply_id, options) do | |||
object | |||
end | |||
if reply && (reply.data["type"] == "Question" and object["name"]) do | |||
if reply && reply.data["type"] == "Question" do | |||
Map.put(object, "type", "Answer") | |||
else | |||
object | |||
@@ -713,8 +701,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
} = _data, | |||
_options | |||
) do | |||
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), | |||
%User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), | |||
with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), | |||
{:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker), | |||
{:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do | |||
User.unblock(blocker, blocked) | |||
@@ -728,8 +715,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
%{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data, | |||
_options | |||
) do | |||
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), | |||
%User{local: true} = blocked = User.get_cached_by_ap_id(blocked), | |||
with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), | |||
{:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker), | |||
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do | |||
User.unfollow(blocker, blocked) | |||
@@ -784,7 +770,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
|> add_mention_tags | |||
|> add_emoji_tags | |||
|> add_attributed_to | |||
|> add_likes | |||
|> prepare_attachments | |||
|> set_conversation | |||
|> set_reply_to_uri | |||
@@ -971,22 +956,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
|> Map.put("attributedTo", attributed_to) | |||
end | |||
def add_likes(%{"id" => id, "like_count" => likes} = object) do | |||
likes = %{ | |||
"id" => "#{id}/likes", | |||
"first" => "#{id}/likes?page=1", | |||
"type" => "OrderedCollection", | |||
"totalItems" => likes | |||
} | |||
object | |||
|> Map.put("likes", likes) | |||
end | |||
def add_likes(object) do | |||
object | |||
end | |||
def prepare_attachments(object) do | |||
attachments = | |||
(object["attachment"] || []) | |||
@@ -1002,6 +971,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
defp strip_internal_fields(object) do | |||
object | |||
|> Map.drop([ | |||
"likes", | |||
"like_count", | |||
"announcements", | |||
"announcement_count", | |||
@@ -33,9 +33,11 @@ defmodule Pleroma.Web.ChatChannel do | |||
end | |||
defmodule Pleroma.Web.ChatChannel.ChatChannelState do | |||
use Agent | |||
@max_messages 20 | |||
def start_link do | |||
def start_link(_) do | |||
Agent.start_link(fn -> %{max_id: 1, messages: []} end, name: __MODULE__) | |||
end | |||
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do | |||
{:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}} | |||
end | |||
def start_link do | |||
def start_link(_) do | |||
enabled = | |||
if Pleroma.Config.get(:env) == :test, | |||
do: true, | |||
@@ -13,10 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
@spec follow(User.t(), User.t(), map) :: {:ok, User.t()} | {:error, String.t()} | |||
def follow(follower, followed, params \\ %{}) do | |||
options = cast_params(params) | |||
reblogs = options[:reblogs] | |||
result = | |||
if not User.following?(follower, followed) do | |||
CommonAPI.follow(follower, followed) | |||
@@ -24,19 +22,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
{:ok, follower, followed, nil} | |||
end | |||
with {:ok, follower, followed, _} <- result do | |||
reblogs | |||
|> case do | |||
false -> CommonAPI.hide_reblogs(follower, followed) | |||
_ -> CommonAPI.show_reblogs(follower, followed) | |||
end | |||
|> case do | |||
with {:ok, follower, _followed, _} <- result do | |||
options = cast_params(params) | |||
case reblogs_visibility(options[:reblogs], result) do | |||
{:ok, follower} -> {:ok, follower} | |||
_ -> {:ok, follower} | |||
end | |||
end | |||
end | |||
defp reblogs_visibility(false, {:ok, follower, followed, _}) do | |||
CommonAPI.hide_reblogs(follower, followed) | |||
end | |||
defp reblogs_visibility(_, {:ok, follower, followed, _}) do | |||
CommonAPI.show_reblogs(follower, followed) | |||
end | |||
@spec get_followers(User.t(), map()) :: list(User.t()) | |||
def get_followers(user, params \\ %{}) do | |||
user | |||
|> User.get_followers_query() | |||
@@ -435,6 +435,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> Map.put("local_only", local_only) | |||
|> Map.put("blocking_user", user) | |||
|> Map.put("muting_user", user) | |||
|> Map.put("user", user) | |||
|> ActivityPub.fetch_public_activities() | |||
|> Enum.reverse() | |||
@@ -496,12 +497,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
activities <- | |||
ActivityPub.fetch_activities_for_context(activity.data["context"], %{ | |||
"blocking_user" => user, | |||
"user" => user | |||
"user" => user, | |||
"exclude_id" => activity.id | |||
}), | |||
activities <- | |||
activities |> Enum.filter(fn %{id: aid} -> to_string(aid) != to_string(id) end), | |||
activities <- | |||
activities |> Enum.filter(fn %{data: %{"type" => type}} -> type == "Create" end), | |||
grouped_activities <- Enum.group_by(activities, fn %{id: id} -> id < activity.id end) do | |||
result = %{ | |||
ancestors: | |||
@@ -536,8 +534,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> put_view(StatusView) | |||
|> try_render("poll.json", %{object: object, for: user}) | |||
else | |||
nil -> render_error(conn, :not_found, "Record not found") | |||
false -> render_error(conn, :not_found, "Record not found") | |||
error when is_nil(error) or error == false -> | |||
render_error(conn, :not_found, "Record not found") | |||
end | |||
end | |||
@@ -885,8 +883,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id), | |||
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do | |||
with %Activity{} = activity <- Activity.get_by_id_with_object(id), | |||
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do | |||
q = from(u in User, where: u.ap_id in ^likes) | |||
users = | |||
@@ -902,8 +900,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id), | |||
%Object{data: %{"announcements" => announces}} <- Object.normalize(object) do | |||
with %Activity{} = activity <- Activity.get_by_id_with_object(id), | |||
%Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do | |||
q = from(u in User, where: u.ap_id in ^announces) | |||
users = | |||
@@ -944,6 +942,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> Map.put("local_only", local_only) | |||
|> Map.put("blocking_user", user) | |||
|> Map.put("muting_user", user) | |||
|> Map.put("user", user) | |||
|> Map.put("tag", tags) | |||
|> Map.put("tag_all", tag_all) | |||
|> Map.put("tag_reject", tag_reject) | |||
@@ -1350,6 +1349,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
params | |||
|> Map.put("type", "Create") | |||
|> Map.put("blocking_user", user) | |||
|> Map.put("user", user) | |||
|> Map.put("muting_user", user) | |||
# we must filter the following list for the user to avoid leaking statuses the user | |||
@@ -1690,45 +1690,35 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> String.replace("{{user}}", user) | |||
with {:ok, %{status: 200, body: body}} <- | |||
HTTP.get( | |||
url, | |||
[], | |||
adapter: [ | |||
recv_timeout: timeout, | |||
pool: :default | |||
] | |||
), | |||
HTTP.get(url, [], adapter: [recv_timeout: timeout, pool: :default]), | |||
{:ok, data} <- Jason.decode(body) do | |||
data = | |||
data | |||
|> Enum.slice(0, limit) | |||
|> Enum.map(fn x -> | |||
Map.put( | |||
x, | |||
"id", | |||
case User.get_or_fetch(x["acct"]) do | |||
{:ok, %User{id: id}} -> id | |||
_ -> 0 | |||
end | |||
) | |||
end) | |||
|> Enum.map(fn x -> | |||
Map.put(x, "avatar", MediaProxy.url(x["avatar"])) | |||
end) | |||
|> Enum.map(fn x -> | |||
Map.put(x, "avatar_static", MediaProxy.url(x["avatar_static"])) | |||
x | |||
|> Map.put("id", fetch_suggestion_id(x)) | |||
|> Map.put("avatar", MediaProxy.url(x["avatar"])) | |||
|> Map.put("avatar_static", MediaProxy.url(x["avatar_static"])) | |||
end) | |||
conn | |||
|> json(data) | |||
json(conn, data) | |||
else | |||
e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}") | |||
e -> | |||
Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}") | |||
end | |||
else | |||
json(conn, []) | |||
end | |||
end | |||
defp fetch_suggestion_id(attrs) do | |||
case User.get_or_fetch(attrs["acct"]) do | |||
{:ok, %User{id: id}} -> id | |||
_ -> 0 | |||
end | |||
end | |||
def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do | |||
with %Activity{} = activity <- Activity.get_by_id(status_id), | |||
true <- Visibility.visible_for_user?(activity, user) do | |||
@@ -72,6 +72,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
image = User.avatar_url(user) |> MediaProxy.url() | |||
header = User.banner_url(user) |> MediaProxy.url() | |||
user_info = User.get_cached_user_info(user) | |||
following_count = | |||
((!user.info.hide_follows or opts[:for] == user) && user_info.following_count) || 0 | |||
followers_count = | |||
((!user.info.hide_followers or opts[:for] == user) && user_info.follower_count) || 0 | |||
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"] | |||
emojis = | |||
@@ -102,8 +109,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
display_name: display_name, | |||
locked: user_info.locked, | |||
created_at: Utils.to_masto_date(user.inserted_at), | |||
followers_count: user_info.follower_count, | |||
following_count: user_info.following_count, | |||
followers_count: followers_count, | |||
following_count: following_count, | |||
statuses_count: user_info.note_count, | |||
note: bio || "", | |||
url: User.profile_url(user), | |||
@@ -5,6 +5,8 @@ | |||
defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
use Pleroma.Web, :view | |||
require Pleroma.Constants | |||
alias Pleroma.Activity | |||
alias Pleroma.HTML | |||
alias Pleroma.Object | |||
@@ -24,19 +26,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
defp get_replied_to_activities(activities) do | |||
activities | |||
|> Enum.map(fn | |||
%{data: %{"type" => "Create", "object" => object}} -> | |||
object = Object.normalize(object) | |||
object.data["inReplyTo"] != "" && object.data["inReplyTo"] | |||
%{data: %{"type" => "Create"}} = activity -> | |||
object = Object.normalize(activity) | |||
object && object.data["inReplyTo"] != "" && object.data["inReplyTo"] | |||
_ -> | |||
nil | |||
end) | |||
|> Enum.filter(& &1) | |||
|> Activity.create_by_object_ap_id() | |||
|> Activity.create_by_object_ap_id_with_object() | |||
|> Repo.all() | |||
|> Enum.reduce(%{}, fn activity, acc -> | |||
object = Object.normalize(activity) | |||
Map.put(acc, object.data["id"], activity) | |||
if object, do: Map.put(acc, object.data["id"], activity), else: acc | |||
end) | |||
end | |||
@@ -88,6 +90,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
reblogged_activity = | |||
Activity.create_by_object_ap_id(activity_object.data["id"]) | |||
|> Activity.with_preloaded_bookmark(opts[:for]) | |||
|> Activity.with_set_thread_muted_field(opts[:for]) | |||
|> Repo.one() | |||
reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity)) | |||
@@ -142,6 +145,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
object = Object.normalize(activity) | |||
user = get_user(activity.data["actor"]) | |||
user_follower_address = user.follower_address | |||
like_count = object.data["like_count"] || 0 | |||
announcement_count = object.data["announcement_count"] || 0 | |||
@@ -157,7 +161,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
mentions = | |||
(object.data["to"] ++ tag_mentions) | |||
|> Enum.uniq() | |||
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) | |||
|> Enum.map(fn | |||
Pleroma.Constants.as_public() -> nil | |||
^user_follower_address -> nil | |||
ap_id -> User.get_cached_by_ap_id(ap_id) | |||
end) | |||
|> Enum.filter(& &1) | |||
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end) | |||
@@ -168,7 +176,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
thread_muted? = | |||
case activity.thread_muted? do | |||
thread_muted? when is_boolean(thread_muted?) -> thread_muted? | |||
nil -> CommonAPI.thread_muted?(user, activity) | |||
nil -> (opts[:for] && CommonAPI.thread_muted?(opts[:for], activity)) || false | |||
end | |||
attachment_data = object.data["attachment"] || [] | |||
@@ -34,64 +34,18 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do | |||
def raw_nodeinfo do | |||
stats = Stats.get_stats() | |||
exclusions = Config.get([:instance, :mrf_transparency_exclusions]) | |||
mrf_simple = | |||
Config.get(:mrf_simple) | |||
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end) | |||
|> Enum.into(%{}) | |||
# This horror is needed to convert regex sigils to strings | |||
mrf_keyword = | |||
Config.get(:mrf_keyword, []) | |||
|> Enum.map(fn {key, value} -> | |||
{key, | |||
Enum.map(value, fn | |||
{pattern, replacement} -> | |||
%{ | |||
"pattern" => | |||
if not is_binary(pattern) do | |||
inspect(pattern) | |||
else | |||
pattern | |||
end, | |||
"replacement" => replacement | |||
} | |||
pattern -> | |||
if not is_binary(pattern) do | |||
inspect(pattern) | |||
else | |||
pattern | |||
end | |||
end)} | |||
end) | |||
|> Enum.into(%{}) | |||
mrf_policies = | |||
MRF.get_policies() | |||
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end) | |||
quarantined = Config.get([:instance, :quarantined_instances], []) | |||
staff_accounts = | |||
User.all_superusers() | |||
|> Enum.map(fn u -> u.ap_id end) | |||
mrf_user_allowlist = | |||
Config.get([:mrf_user_allowlist], []) | |||
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end) | |||
federation_response = | |||
if Config.get([:instance, :mrf_transparency]) do | |||
%{ | |||
mrf_policies: mrf_policies, | |||
mrf_simple: mrf_simple, | |||
mrf_keyword: mrf_keyword, | |||
mrf_user_allowlist: mrf_user_allowlist, | |||
quarantined_instances: quarantined, | |||
exclusions: length(exclusions) > 0 | |||
} | |||
{:ok, data} = MRF.describe() | |||
data | |||
|> Map.merge(%{quarantined_instances: quarantined}) | |||
else | |||
%{} | |||
end | |||
@@ -6,36 +6,30 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do | |||
@moduledoc """ | |||
The module represents functions to clean an expired oauth tokens. | |||
""" | |||
use GenServer | |||
@ten_seconds 10_000 | |||
@one_day 86_400_000 | |||
# 10 seconds | |||
@start_interval 10_000 | |||
@interval Pleroma.Config.get( | |||
# 24 hours | |||
[:oauth2, :clean_expired_tokens_interval], | |||
86_400_000 | |||
@one_day | |||
) | |||
@queue :background | |||
alias Pleroma.Web.OAuth.Token | |||
def start_link, do: GenServer.start_link(__MODULE__, nil) | |||
def start_link(_), do: GenServer.start_link(__MODULE__, %{}) | |||
def init(_) do | |||
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do | |||
Process.send_after(self(), :perform, @start_interval) | |||
{:ok, nil} | |||
else | |||
:ignore | |||
end | |||
Process.send_after(self(), :perform, @ten_seconds) | |||
{:ok, nil} | |||
end | |||
@doc false | |||
def handle_info(:perform, state) do | |||
Token.delete_expired_tokens() | |||
Process.send_after(self(), :perform, @interval) | |||
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean]) | |||
{:noreply, state} | |||
end | |||
# Job Worker Callbacks | |||
def perform(:clean), do: Token.delete_expired_tokens() | |||
end |
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.Streamer do | |||
@keepalive_interval :timer.seconds(30) | |||
def start_link do | |||
def start_link(_) do | |||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__) | |||
end | |||
@@ -35,28 +35,21 @@ defmodule Pleroma.Web.Streamer do | |||
end | |||
def init(args) do | |||
spawn(fn -> | |||
# 30 seconds | |||
Process.sleep(@keepalive_interval) | |||
GenServer.cast(__MODULE__, %{action: :ping}) | |||
end) | |||
Process.send_after(self(), %{action: :ping}, @keepalive_interval) | |||
{:ok, args} | |||
end | |||
def handle_cast(%{action: :ping}, topics) do | |||
Map.values(topics) | |||
def handle_info(%{action: :ping}, topics) do | |||
topics | |||
|> Map.values() | |||
|> List.flatten() | |||
|> Enum.each(fn socket -> | |||
Logger.debug("Sending keepalive ping") | |||
send(socket.transport_pid, {:text, ""}) | |||
end) | |||
spawn(fn -> | |||
# 30 seconds | |||
Process.sleep(@keepalive_interval) | |||
GenServer.cast(__MODULE__, %{action: :ping}) | |||
end) | |||
Process.send_after(self(), %{action: :ping}, @keepalive_interval) | |||
{:noreply, topics} | |||
end | |||
@@ -58,10 +58,10 @@ defmodule Pleroma.Web do | |||
rescue | |||
error -> | |||
Logger.error( | |||
"#{__MODULE__} failed to render #{inspect({view, template})}: #{inspect(error)}" | |||
"#{__MODULE__} failed to render #{inspect({view, template})}\n" <> | |||
Exception.format(:error, error, __STACKTRACE__) | |||
) | |||
Logger.error(inspect(__STACKTRACE__)) | |||
nil | |||
end | |||
@@ -95,7 +95,7 @@ defmodule Pleroma.Mixfile do | |||
defp deps do | |||
[ | |||
{:phoenix, "~> 1.4.8"}, | |||
{:tzdata, "~> 1.0"}, | |||
{:tzdata, "~> 0.5.21"}, | |||
{:plug_cowboy, "~> 2.0"}, | |||
{:phoenix_pubsub, "~> 1.1"}, | |||
{:phoenix_ecto, "~> 4.0"}, | |||
@@ -114,8 +114,9 @@ defmodule Pleroma.Mixfile do | |||
{:tesla, "~> 1.2"}, | |||
{:jason, "~> 1.0"}, | |||
{:mogrify, "~> 0.6.1"}, | |||
{:ex_aws, "~> 2.0"}, | |||
{:ex_aws, "~> 2.1"}, | |||
{:ex_aws_s3, "~> 2.0"}, | |||
{:sweet_xml, "~> 0.6.6"}, | |||
{:earmark, "~> 1.3"}, | |||
{:bbcode, "~> 0.1.1"}, | |||
{:ex_machina, "~> 2.3", only: :test}, | |||
@@ -80,13 +80,14 @@ | |||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, | |||
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]}, | |||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, | |||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"}, | |||
"swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]}, | |||
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, | |||
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"tzdata": {:hex, :tzdata, "0.5.21", "8cbf3607fcce69636c672d5be2bbb08687fe26639a62bdcc283d267277db7cf0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, | |||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"}, | |||
@@ -0,0 +1 @@ | |||
header[data-v-71c7ded0]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;margin:22px 0;padding-left:15px}header h1[data-v-71c7ded0]{margin:0 0 0 10px}table[data-v-71c7ded0]{margin:10px 0 0 15px}table .name-col[data-v-71c7ded0]{width:150px}.el-table--border[data-v-71c7ded0]:after,.el-table--group[data-v-71c7ded0]:after,.el-table[data-v-71c7ded0]:before{background-color:transparent}.poll ul[data-v-71c7ded0]{list-style-type:none;padding:0;width:30%}.image[data-v-71c7ded0]{width:20%}.image img[data-v-71c7ded0]{width:100%}.statuses[data-v-71c7ded0]{padding-right:20px}.show-private[data-v-71c7ded0]{text-align:right;line-height:67px;padding-right:20px} |
@@ -1 +1 @@ | |||
.status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-avatar-img{width:15px;height:15px;margin-right:5px}.status-account-name{margin:0;height:22px}.status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-content{font-size:15px}.status-card{margin-bottom:15px}.status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.account{text-decoration:underline}.avatar-img{vertical-align:bottom;width:15px;height:15px;margin-left:5px}.el-card__body{padding:17px}.el-card__header{background-color:#fafafa;padding:10px 20px}.el-collapse{border-bottom:none}.el-collapse-item__header{height:46px;font-size:14px}.el-collapse-item__content{padding-bottom:7px}.el-icon-arrow-right{margin-right:6px}.el-icon-close{padding:10px 5px 10px 10px;cursor:pointer}h4{margin:0;height:17px}.header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline;height:40px}.id{color:grey;margin-top:6px}.line{width:100%;height:0;border:.5px solid #ebeef5;margin:15px 0}.new-note p{font-size:14px;font-weight:500;height:17px;margin:13px 0 7px}.note{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1);box-shadow:0 2px 5px 0 rgba(0,0,0,.1);margin-bottom:10px}.no-notes{font-style:italic;color:grey}.report-row-key{font-weight:500;font-size:14px}.report-title{margin:0}.statuses{margin-top:15px}.submit-button{display:block;margin:7px 0 17px auto}.timestamp{margin:0;font-style:italic;color:grey}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.timeline-item-container .header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:80px}.timeline-item-container .id{margin:6px 0 0}}.select-field[data-v-bb4390da]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-bb4390da]{width:100%;margin-bottom:5px}}.reports-container .el-timeline[data-v-e32c7dc6]{margin:45px 45px 45px 19px;padding:0}.reports-container .filter-container[data-v-e32c7dc6]{margin:22px 15px;padding-bottom:0}.reports-container h1[data-v-e32c7dc6]{margin:22px 0 0 15px}.reports-container .no-reports-message[data-v-e32c7dc6]{color:grey;margin-left:19px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.reports-container h1[data-v-e32c7dc6]{margin:7px 10px 15px}.reports-container .filter-container[data-v-e32c7dc6]{margin:0 10px}.reports-container .timeline[data-v-e32c7dc6]{margin:20px 20px 20px 18px}} | |||
.status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-avatar-img{width:15px;height:15px;margin-right:5px}.status-account-name{margin:0;height:22px}.status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-content{font-size:15px}.status-card{margin-bottom:15px}.status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.account{text-decoration:underline}.avatar-img{vertical-align:bottom;width:15px;height:15px;margin-left:5px}.el-card__body{padding:17px}.el-card__header{background-color:#fafafa;padding:10px 20px}.el-collapse{border-bottom:none}.el-collapse-item__header{height:46px;font-size:14px}.el-collapse-item__content{padding-bottom:7px}.el-icon-arrow-right{margin-right:6px}.el-icon-close{padding:10px 5px 10px 10px;cursor:pointer}h4{margin:0;height:17px}.header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline;height:40px}.id{color:grey;margin-top:6px}.line{width:100%;height:0;border:.5px solid #ebeef5;margin:15px 0}.new-note p{font-size:14px;font-weight:500;height:17px;margin:13px 0 7px}.note{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1);box-shadow:0 2px 5px 0 rgba(0,0,0,.1);margin-bottom:10px}.no-notes{font-style:italic;color:grey}.report-row-key{font-weight:500;font-size:14px}.report-title{margin:0}.statuses{margin-top:15px}.submit-button{display:block;margin:7px 0 17px auto}.timestamp{margin:0;font-style:italic;color:grey}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.timeline-item-container .header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:80px}.timeline-item-container .id{margin:6px 0 0}}.select-field[data-v-07695bc4]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-07695bc4]{width:100%;margin-bottom:5px}}.reports-container .el-timeline[data-v-e32c7dc6]{margin:45px 45px 45px 19px;padding:0}.reports-container .filter-container[data-v-e32c7dc6]{margin:22px 15px;padding-bottom:0}.reports-container h1[data-v-e32c7dc6]{margin:22px 0 0 15px}.reports-container .no-reports-message[data-v-e32c7dc6]{color:grey;margin-left:19px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.reports-container h1[data-v-e32c7dc6]{margin:7px 10px 15px}.reports-container .filter-container[data-v-e32c7dc6]{margin:0 10px}.reports-container .timeline[data-v-e32c7dc6]{margin:20px 20px 20px 18px}} |
@@ -0,0 +1 @@ | |||
a{text-decoration:underline}.code{background-color:rgba(173,190,214,.48);border-radius:3px;font-family:monospace;padding:0 3px}.el-form-item{margin-right:30px}.el-select{width:100%}.esshd-list{margin:0}.expl{color:#666;font-size:13px;line-height:22px;margin:5px 0 0;overflow-wrap:break-word}.highlight{background-color:#e6e6e6}.limit-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline}.limit-expl{margin-left:10px}.limit-input{width:48%;margin:0 0 5px 8px}.line{width:100%;height:0;border:1px solid #eee;margin-bottom:22px}.mascot-container{margin-bottom:15px}.mascot-input{margin-bottom:7px}.mascot-name-container{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:7px}.mascot-name-input{margin-right:10px}.name-input{width:30%;margin-right:8px}.options-paragraph{font-size:14px;color:#606266;font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei;font-weight:700;line-height:20px;margin:0 0 14px}.options-paragraph-container{overflow-wrap:break-word;margin-bottom:0}.pattern-input{width:20%;margin-right:8px}.setting-input{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:10px}.single-input{margin-right:10px}.scale-input{width:48%;margin:0 8px 5px 0}.replacement-input{width:80%;margin-left:8px;margin-right:10px}.text{line-height:20px;margin-right:15px}.upload-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline}.value-input{width:70%;margin-left:8px;margin-right:10px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-form-item{margin-right:15px}.el-input__inner{padding:0 5px}.el-form-item__label:not(.no-top-margin){padding-left:3px;padding-right:10px;line-height:22px;margin-top:7px}.el-message{min-width:80%}.el-select__tags{overflow:hidden}.name-input{width:40%;margin-right:5px}p.expl{line-height:20px}.pattern-input{width:40%;margin-right:4px}.replacement-input{width:60%;margin-left:4px;margin-right:5px}.top-margin{position:absolute;top:25%}.value-input{width:60%;margin-left:5px;margin-right:8px}}.settings-container .el-tabs[data-v-729534ce]{margin-top:20px}.settings-container h1[data-v-729534ce]{margin:22px 0 0 15px} |
@@ -1 +1 @@ | |||
.select-field[data-v-71bc6b38]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-71bc6b38]{width:100%;margin-bottom:5px}}.actions-button[data-v-94227b1e]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-94227b1e]{float:right}.el-icon-edit[data-v-94227b1e]{margin-right:5px}.tag-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-94227b1e]{padding-right:20px}.no-hover[data-v-94227b1e]:hover{color:#606266;background-color:#fff;cursor:auto}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.create-user-dialog{width:80%}.create-account-form-item{margin-bottom:30px}.el-dialog__body{padding:20px 20px 0}}.actions-button[data-v-3ffddd00]{text-align:left;width:350px;padding:10px}.actions-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.active-tag[data-v-3ffddd00]{color:#409eff;font-weight:700}.active-tag .el-icon-check[data-v-3ffddd00]{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link[data-v-3ffddd00]:hover{cursor:pointer;color:#409eff}.el-icon-plus[data-v-3ffddd00]{margin-right:5px}.users-container h1[data-v-3ffddd00]{margin:22px 0 0 15px}.users-container .pagination[data-v-3ffddd00]{margin:25px 0;text-align:center}.users-container .search[data-v-3ffddd00]{width:350px;float:right}.users-container .filter-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 15px 15px}.users-container .user-count[data-v-3ffddd00]{color:grey;font-size:28px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.users-container h1[data-v-3ffddd00]{margin:7px 10px 15px}.users-container .actions-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .create-account[data-v-3ffddd00]{width:100%}.users-container .el-icon-arrow-down[data-v-3ffddd00]{font-size:12px}.users-container .search[data-v-3ffddd00]{width:100%}.users-container .filter-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag[data-v-3ffddd00]{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger[data-v-3ffddd00],.users-container .el-tag.el-tag--success[data-v-3ffddd00]{padding-left:8px}} | |||
.select-field[data-v-71bc6b38]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-71bc6b38]{width:100%;margin-bottom:5px}}.actions-button[data-v-94227b1e]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-94227b1e]{float:right}.el-icon-edit[data-v-94227b1e]{margin-right:5px}.tag-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-94227b1e]{padding-right:20px}.no-hover[data-v-94227b1e]:hover{color:#606266;background-color:#fff;cursor:auto}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.create-user-dialog{width:80%}.create-account-form-item{margin-bottom:30px}.el-dialog__body{padding:20px 20px 0}}.actions-button[data-v-c51cd8ee]{text-align:left;width:350px;padding:10px}.actions-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.active-tag[data-v-c51cd8ee]{color:#409eff;font-weight:700}.active-tag .el-icon-check[data-v-c51cd8ee]{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link[data-v-c51cd8ee]:hover{cursor:pointer;color:#409eff}.el-icon-plus[data-v-c51cd8ee]{margin-right:5px}.users-container h1[data-v-c51cd8ee]{margin:22px 0 0 15px}.users-container .pagination[data-v-c51cd8ee]{margin:25px 0;text-align:center}.users-container .search[data-v-c51cd8ee]{width:350px;float:right}.users-container .filter-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 15px 15px}.users-container .user-count[data-v-c51cd8ee]{color:grey;font-size:28px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.users-container h1[data-v-c51cd8ee]{margin:7px 10px 15px}.users-container .actions-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .create-account[data-v-c51cd8ee]{width:100%}.users-container .el-icon-arrow-down[data-v-c51cd8ee]{font-size:12px}.users-container .search[data-v-c51cd8ee]{width:100%}.users-container .filter-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag[data-v-c51cd8ee]{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger[data-v-c51cd8ee],.users-container .el-tag.el-tag--success[data-v-c51cd8ee]{padding-left:8px}} |
@@ -1 +1 @@ | |||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.f74c256b.css rel=stylesheet><link href=chunk-libs.4e8c4664.css rel=stylesheet><link href=app.34fc670f.css rel=stylesheet></head><body><script src=/pleroma/admin/static/tinymce4.7.5/tinymce.min.js></script><div id=app></div><script type=text/javascript src=static/js/runtime.d8d12c12.js></script><script type=text/javascript src=static/js/chunk-elementUI.1fa5434b.js></script><script type=text/javascript src=static/js/chunk-libs.d5609760.js></script><script type=text/javascript src=static/js/app.4137ad8f.js></script></body></html> | |||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.e5cd8da6.css rel=stylesheet><link href=chunk-libs.4e8c4664.css rel=stylesheet><link href=app.34fc670f.css rel=stylesheet></head><body><script src=/pleroma/admin/static/tinymce4.7.5/tinymce.min.js></script><div id=app></div><script type=text/javascript src=static/js/runtime.f40c8ec4.js></script><script type=text/javascript src=static/js/chunk-elementUI.1911151b.js></script><script type=text/javascript src=static/js/chunk-libs.fb0b7f4a.js></script><script type=text/javascript src=static/js/app.8e186193.js></script></body></html> |
@@ -0,0 +1 @@ | |||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-0e18"],{"4bFr":function(t,e,s){"use strict";s.r(e);var a={name:"UsersShow",data:function(){return{showPrivate:!1}},computed:{loading:function(){return this.$store.state.userProfile.loading},user:function(){return this.$store.state.userProfile.user},statuses:function(){return this.$store.state.userProfile.statuses}},mounted:function(){this.$store.dispatch("FetchData",{id:this.$route.params.id,godmode:!1})},methods:{optionPercent:function(t,e){var s=t.options.reduce(function(t,e){return t+e.votes_count},0);return 0===s?0:+(e.votes_count/s*100).toFixed(1)},createdAtLocaleString:function(t){var e=new Date(t);return"".concat(e.toLocaleDateString()," ").concat(e.toLocaleTimeString())},onTogglePrivate:function(){console.log(this.showPrivate),this.$store.dispatch("FetchData",{id:this.$route.params.id,godmode:this.showPrivate})}}},r=(s("QG2t"),s("KHd+")),l=Object(r.a)(a,function(){var t=this,e=t.$createElement,s=t._self._c||e;return t.loading?t._e():s("main",[s("header",[s("el-avatar",{attrs:{src:t.user.avatar,size:"large"}}),t._v(" "),s("h1",[t._v(t._s(t.user.display_name))])],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("div",{staticClass:"el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table--medium"},[s("table",{staticClass:"el-table__body"},[s("tbody",[s("tr",{staticClass:"el-table__row"},[s("td",{staticClass:"name-col"},[t._v("ID")]),t._v(" "),s("td",{staticClass:"value-col"},[t._v("\n "+t._s(t.user.id)+"\n ")])]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.tags")))]),t._v(" "),s("td",[t._l(t.user.tags,function(e){return s("el-tag",{key:e},[t._v(t._s(e))])}),t._v(" "),0===t.user.tags.length?s("span",[t._v("None")]):t._e()],2)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.moderator")))]),t._v(" "),s("td",[t.user.roles.moderator?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.roles.moderator?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.admin")))]),t._v(" "),s("td",[t.user.roles.admin?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.roles.admin?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.local")))]),t._v(" "),s("td",[t.user.local?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.local?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.deactivated")))]),t._v(" "),s("td",[t.user.deactivated?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.deactivated?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.nickname")))]),t._v(" "),s("td",[t._v("\n "+t._s(t.user.nickname)+"\n ")])])])])])]),t._v(" "),s("el-row",{staticClass:"row-bg",attrs:{type:"flex",justify:"space-between"}},[s("el-col",{attrs:{span:18}},[s("h2",[t._v(t._s(t.$t("userProfile.recentStatuses")))])]),t._v(" "),s("el-col",{staticClass:"show-private",attrs:{span:6}},[s("el-checkbox",{on:{change:t.onTogglePrivate},model:{value:t.showPrivate,callback:function(e){t.showPrivate=e},expression:"showPrivate"}},[t._v("\n "+t._s(t.$t("userProfile.showPrivateStatuses"))+"\n ")])],1)],1),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-timeline",{staticClass:"statuses"},t._l(t.statuses,function(e){return s("el-timeline-item",{key:e.id,attrs:{timestamp:t.createdAtLocaleString(e.created_at)}},[s("el-card",[e.spoiler_text?s("strong",[t._v(t._s(e.spoiler_text))]):t._e(),t._v(" "),e.content?s("p",{domProps:{innerHTML:t._s(e.content)}}):t._e(),t._v(" "),e.poll?s("div",{staticClass:"poll"},[s("ul",t._l(e.poll.options,function(a,r){return s("li",{key:r},[t._v("\n "+t._s(a.title)+"\n "),s("el-progress",{attrs:{percentage:t.optionPercent(e.poll,a)}})],1)}),0)]):t._e(),t._v(" "),t._l(e.media_attachments,function(t,e){return s("div",{key:e,staticClass:"image"},[s("img",{attrs:{src:t.preview_url}})])})],2)],1)}),1)],1)],1)],1)},[],!1,null,"71c7ded0",null);l.options.__file="show.vue";e.default=l.exports},QG2t:function(t,e,s){"use strict";var a=s("R7Mx");s.n(a).a},R7Mx:function(t,e,s){}}]); |
@@ -1 +0,0 @@ | |||
!function(e){function t(t){for(var r,o,a=t[0],i=t[1],f=t[2],l=0,h=[];l<a.length;l++)o=a[l],u[o]&&h.push(u[o][0]),u[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);for(s&&s(t);h.length;)h.shift()();return c.push.apply(c,f||[]),n()}function n(){for(var e,t=0;t<c.length;t++){for(var n=c[t],r=!0,o=1;o<n.length;o++){var i=n[o];0!==u[i]&&(r=!1)}r&&(c.splice(t--,1),e=a(a.s=n[0]))}return e}var r={},o={runtime:0},u={runtime:0},c=[];function a(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,a),n.l=!0,n.exports}a.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{"chunk-56c9":1,"chunk-5eaf":1,"chunk-18e1":1,"chunk-8b70":1,"chunk-f018":1}[e]&&t.push(o[e]=new Promise(function(t,n){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-02a0":"31d6cfe0","chunk-56c9":"c27dac5e","chunk-0620":"31d6cfe0","chunk-5eaf":"1a04e979","chunk-18e1":"6aaab273","chunk-8b70":"9ba0945c","chunk-f018":"0d22684d"}[e]+".css",o=a.p+r,u=document.getElementsByTagName("link"),c=0;c<u.length;c++){var i=(l=u[c]).getAttribute("data-href")||l.getAttribute("href");if("stylesheet"===l.rel&&(i===r||i===o))return t()}var f=document.getElementsByTagName("style");for(c=0;c<f.length;c++){var l;if((i=(l=f[c]).getAttribute("data-href"))===r||i===o)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||o,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,n(u)},s.href=o,document.getElementsByTagName("head")[0].appendChild(s)}).then(function(){o[e]=0}));var n=u[e];if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=u[e]=[t,r]});t.push(n[2]=r);var c,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-02a0":"db6ec114","chunk-56c9":"28e35fc3","chunk-0620":"c765c190","chunk-5eaf":"5b76e416","chunk-18e1":"7f9c377c","chunk-8b70":"46525646","chunk-f018":"e1a7a454"}[e]+".js"}(e),c=function(t){i.onerror=i.onload=null,clearTimeout(f);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,c=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");c.type=r,c.request=o,n[1](c)}u[e]=void 0}};var f=setTimeout(function(){c({type:"timeout",target:i})},12e4);i.onerror=i.onload=c,document.head.appendChild(i)}return Promise.all(t)},a.m=e,a.c=r,a.d=function(e,t,n){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(a.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)a.d(n,r,function(t){return e[t]}.bind(null,r));return n},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],f=i.push.bind(i);i.push=t,i=i.slice();for(var l=0;l<i.length;l++)t(i[l]);var s=f;n()}([]); |
@@ -0,0 +1 @@ | |||
!function(e){function n(n){for(var r,u,a=n[0],f=n[1],i=n[2],l=0,s=[];l<a.length;l++)u=a[l],c[u]&&s.push(c[u][0]),c[u]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(h&&h(n);s.length;)s.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,u=1;u<t.length;u++){var f=t[u];0!==c[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},u={runtime:0},c={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];u[e]?n.push(u[e]):0!==u[e]&&{"chunk-1fbf":1,"chunk-e547":1,"chunk-0e18":1,"chunk-18e1":1,"chunk-2325":1,"chunk-5e57":1,"chunk-8b70":1}[e]&&n.push(u[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-02a0":"31d6cfe0","chunk-1fbf":"d7a1893c","chunk-0620":"31d6cfe0","chunk-e547":"e4b6230b","chunk-0e18":"e12401fb","chunk-18e1":"6aaab273","chunk-2325":"0d22684d","chunk-7fe2":"31d6cfe0","chunk-5e57":"ac97b15a","chunk-8b70":"9ba0945c"}[e]+".css",u=a.p+r,c=document.getElementsByTagName("link"),o=0;o<c.length;o++){var f=(l=c[o]).getAttribute("data-href")||l.getAttribute("href");if("stylesheet"===l.rel&&(f===r||f===u))return n()}var i=document.getElementsByTagName("style");for(o=0;o<i.length;o++){var l;if((f=(l=i[o]).getAttribute("data-href"))===r||f===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,c=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");c.request=r,t(c)},h.href=u,document.getElementsByTagName("head")[0].appendChild(h)}).then(function(){u[e]=0}));var t=c[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=c[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-02a0":"db6ec114","chunk-1fbf":"616fb309","chunk-0620":"c765c190","chunk-e547":"d57d1b91","chunk-0e18":"208cd826","chunk-18e1":"7f9c377c","chunk-2325":"154a537b","chunk-7fe2":"458f9da5","chunk-5e57":"7313703a","chunk-8b70":"46525646"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(i);var t=c[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),u=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+u+")");o.type=r,o.request=u,t[1](o)}c[e]=void 0}};var i=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=n,f=f.slice();for(var l=0;l<f.length;l++)n(f[l]);var h=i;t()}([]); |
@@ -31,7 +31,7 @@ defmodule Pleroma.Config.TransferTaskTest do | |||
value: [live: 15, com: 35] | |||
}) | |||
Pleroma.Config.TransferTask.start_link() | |||
Pleroma.Config.TransferTask.start_link([]) | |||
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] | |||
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] | |||
@@ -50,7 +50,7 @@ defmodule Pleroma.Config.TransferTaskTest do | |||
}) | |||
assert ExUnit.CaptureLog.capture_log(fn -> | |||
Pleroma.Config.TransferTask.start_link() | |||
Pleroma.Config.TransferTask.start_link([]) | |||
end) =~ | |||
"updating env causes error, key: \"undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}" | |||
end | |||
@@ -0,0 +1,13 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule MRFModuleMock do | |||
@behaviour Pleroma.Web.ActivityPub.MRF | |||
@impl true | |||
def filter(message), do: {:ok, message} | |||
@impl true | |||
def describe, do: {:ok, %{mrf_module_mock: "some config data"}} | |||
end |
@@ -3,8 +3,11 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Mix.Tasks.Pleroma.DatabaseTest do | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
use Pleroma.DataCase | |||
import Pleroma.Factory | |||
@@ -46,4 +49,37 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do | |||
assert user.info.follower_count == 0 | |||
end | |||
end | |||
describe "running fix_likes_collections" do | |||
test "it turns OrderedCollection likes into empty arrays" do | |||
[user, user2] = insert_pair(:user) | |||
{:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"}) | |||
{:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"}) | |||
CommonAPI.favorite(id, user2) | |||
likes = %{ | |||
"first" => | |||
"http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", | |||
"id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", | |||
"totalItems" => 3, | |||
"type" => "OrderedCollection" | |||
} | |||
new_data = Map.put(object2.data, "likes", likes) | |||
object2 | |||
|> Ecto.Changeset.change(%{data: new_data}) | |||
|> Repo.update() | |||
assert length(Object.get_by_id(object.id).data["likes"]) == 1 | |||
assert is_map(Object.get_by_id(object2.id).data["likes"]) | |||
assert :ok == Mix.Tasks.Pleroma.Database.run(["fix_likes_collections"]) | |||
assert length(Object.get_by_id(object.id).data["likes"]) == 1 | |||
assert Enum.empty?(Object.get_by_id(object2.id).data["likes"]) | |||
end | |||
end | |||
end |
@@ -0,0 +1,32 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Uploaders.LocalTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Uploaders.Local | |||
describe "get_file/1" do | |||
test "it returns path to local folder for files" do | |||
assert Local.get_file("") == {:ok, {:static_dir, "test/uploads"}} | |||
end | |||
end | |||
describe "put_file/1" do | |||
test "put file to local folder" do | |||
file_path = "local_upload/files/image.jpg" | |||
file = %Pleroma.Upload{ | |||
name: "image.jpg", | |||
content_type: "image/jpg", | |||
path: file_path, | |||
tempfile: Path.absname("test/fixtures/image_tmp.jpg") | |||
} | |||
assert Local.put_file(file) == :ok | |||
assert Path.join([Local.upload_path(), file_path]) | |||
|> File.exists?() | |||
end | |||
end | |||
end |
@@ -0,0 +1,50 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Uploaders.MDIITest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Uploaders.MDII | |||
import Tesla.Mock | |||
describe "get_file/1" do | |||
test "it returns path to local folder for files" do | |||
assert MDII.get_file("") == {:ok, {:static_dir, "test/uploads"}} | |||
end | |||
end | |||
describe "put_file/1" do | |||
setup do | |||
file_upload = %Pleroma.Upload{ | |||
name: "mdii-image.jpg", | |||
content_type: "image/jpg", | |||
path: "test_folder/mdii-image.jpg", | |||
tempfile: Path.absname("test/fixtures/image_tmp.jpg") | |||
} | |||
[file_upload: file_upload] | |||
end | |||
test "save file", %{file_upload: file_upload} do | |||
mock(fn | |||
%{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} -> | |||
%Tesla.Env{status: 200, body: "mdii-image"} | |||
end) | |||
assert MDII.put_file(file_upload) == | |||
{:ok, {:url, "https://mdii.sakura.ne.jp/mdii-image.jpg"}} | |||
end | |||
test "save file to local if MDII isn`t available", %{file_upload: file_upload} do | |||
mock(fn | |||
%{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} -> | |||
%Tesla.Env{status: 500} | |||
end) | |||
assert MDII.put_file(file_upload) == :ok | |||
assert Path.join([Pleroma.Uploaders.Local.upload_path(), file_upload.path]) | |||
|> File.exists?() | |||
end | |||
end | |||
end |
@@ -0,0 +1,90 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Uploaders.S3Test do | |||
use Pleroma.DataCase | |||
alias Pleroma.Config | |||
alias Pleroma.Uploaders.S3 | |||
import Mock | |||
import ExUnit.CaptureLog | |||
setup do | |||
config = Config.get([Pleroma.Uploaders.S3]) | |||
Config.put([Pleroma.Uploaders.S3], | |||
bucket: "test_bucket", | |||
public_endpoint: "https://s3.amazonaws.com" | |||
) | |||
on_exit(fn -> | |||
Config.put([Pleroma.Uploaders.S3], config) | |||
end) | |||
:ok | |||
end | |||
describe "get_file/1" do | |||
test "it returns path to local folder for files" do | |||
assert S3.get_file("test_image.jpg") == { | |||
:ok, | |||
{:url, "https://s3.amazonaws.com/test_bucket/test_image.jpg"} | |||
} | |||
end | |||
test "it returns path without bucket when truncated_namespace set to ''" do | |||
Config.put([Pleroma.Uploaders.S3], | |||
bucket: "test_bucket", | |||
public_endpoint: "https://s3.amazonaws.com", | |||
truncated_namespace: "" | |||
) | |||
assert S3.get_file("test_image.jpg") == { | |||
:ok, | |||
{:url, "https://s3.amazonaws.com/test_image.jpg"} | |||
} | |||
end | |||
test "it returns path with bucket namespace when namespace is set" do | |||
Config.put([Pleroma.Uploaders.S3], | |||
bucket: "test_bucket", | |||
public_endpoint: "https://s3.amazonaws.com", | |||
bucket_namespace: "family" | |||
) | |||
assert S3.get_file("test_image.jpg") == { | |||
:ok, | |||
{:url, "https://s3.amazonaws.com/family:test_bucket/test_image.jpg"} | |||
} | |||
end | |||
end | |||
describe "put_file/1" do | |||
setup do | |||
file_upload = %Pleroma.Upload{ | |||
name: "image-tet.jpg", | |||
content_type: "image/jpg", | |||
path: "test_folder/image-tet.jpg", | |||
tempfile: Path.absname("test/fixtures/image_tmp.jpg") | |||
} | |||
[file_upload: file_upload] | |||
end | |||
test "save file", %{file_upload: file_upload} do | |||
with_mock ExAws, request: fn _ -> {:ok, :ok} end do | |||
assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}} | |||
end | |||
end | |||
test "returns error", %{file_upload: file_upload} do | |||
with_mock ExAws, request: fn _ -> {:error, "S3 Upload failed"} end do | |||
assert capture_log(fn -> | |||
assert S3.put_file(file_upload) == {:error, "S3 Upload failed"} | |||
end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}" | |||
end | |||
end | |||
end | |||
end |
@@ -525,7 +525,10 @@ defmodule Pleroma.UserTest do | |||
end | |||
test "it restricts some sizes" do | |||
[bio: 5000, name: 100] | |||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) | |||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) | |||
[bio: bio_limit, name: name_limit] | |||
|> Enum.each(fn {field, size} -> | |||
string = String.pad_leading(".", size) | |||
cs = User.remote_user_creation(Map.put(@valid_remote, field, string)) | |||
@@ -4,8 +4,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do | |||
test "subdomains_regex/1" do | |||
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [ | |||
~r/^unsafe.tld$/, | |||
~r/^(.*\.)*unsafe.tld$/ | |||
~r/^unsafe.tld$/i, | |||
~r/^(.*\.)*unsafe.tld$/i | |||
] | |||
end | |||
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do | |||
test "common domains" do | |||
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) | |||
assert regexes == [~r/^unsafe.tld$/, ~r/^unsafe2.tld$/] | |||
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i] | |||
assert MRF.subdomain_match?(regexes, "unsafe.tld") | |||
assert MRF.subdomain_match?(regexes, "unsafe2.tld") | |||
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do | |||
test "wildcard domains with one subdomain" do | |||
regexes = MRF.subdomains_regex(["*.unsafe.tld"]) | |||
assert regexes == [~r/^(.*\.)*unsafe.tld$/] | |||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i] | |||
assert MRF.subdomain_match?(regexes, "unsafe.tld") | |||
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") | |||
@@ -35,12 +35,52 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do | |||
test "wildcard domains with two subdomains" do | |||
regexes = MRF.subdomains_regex(["*.unsafe.tld"]) | |||
assert regexes == [~r/^(.*\.)*unsafe.tld$/] | |||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i] | |||
assert MRF.subdomain_match?(regexes, "unsafe.tld") | |||
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") | |||
refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld") | |||
refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother") | |||
end | |||
test "matches are case-insensitive" do | |||
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) | |||
assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i] | |||
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") | |||
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") | |||
assert MRF.subdomain_match?(regexes, "unsafe.tld") | |||
assert MRF.subdomain_match?(regexes, "unsafe2.tld") | |||
refute MRF.subdomain_match?(regexes, "EXAMPLE.COM") | |||
refute MRF.subdomain_match?(regexes, "example.com") | |||
end | |||
end | |||
describe "describe/0" do | |||
test "it works as expected with noop policy" do | |||
expected = %{ | |||
mrf_policies: ["NoOpPolicy"], | |||
exclusions: false | |||
} | |||
{:ok, ^expected} = MRF.describe() | |||
end | |||
test "it works as expected with mock policy" do | |||
config = Pleroma.Config.get([:instance, :rewrite_policy]) | |||
Pleroma.Config.put([:instance, :rewrite_policy], [MRFModuleMock]) | |||
expected = %{ | |||
mrf_policies: ["MRFModuleMock"], | |||
mrf_module_mock: "some config data", | |||
exclusions: false | |||
} | |||
{:ok, ^expected} = MRF.describe() | |||
Pleroma.Config.put([:instance, :rewrite_policy], config) | |||
end | |||
end | |||
end |
@@ -0,0 +1,123 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy | |||
describe "accept" do | |||
test "it accepts based on parent activity type" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :accept]) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"]) | |||
message = %{ | |||
"type" => "Like", | |||
"object" => "whatever" | |||
} | |||
{:ok, ^message} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], config) | |||
end | |||
test "it accepts based on child object type" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :accept]) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) | |||
message = %{ | |||
"type" => "Create", | |||
"object" => %{ | |||
"type" => "Note", | |||
"content" => "whatever" | |||
} | |||
} | |||
{:ok, ^message} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], config) | |||
end | |||
test "it does not accept disallowed child objects" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :accept]) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) | |||
message = %{ | |||
"type" => "Create", | |||
"object" => %{ | |||
"type" => "Article", | |||
"content" => "whatever" | |||
} | |||
} | |||
{:reject, nil} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], config) | |||
end | |||
test "it does not accept disallowed parent types" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :accept]) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"]) | |||
message = %{ | |||
"type" => "Create", | |||
"object" => %{ | |||
"type" => "Note", | |||
"content" => "whatever" | |||
} | |||
} | |||
{:reject, nil} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :accept], config) | |||
end | |||
end | |||
describe "reject" do | |||
test "it rejects based on parent activity type" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :reject]) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) | |||
message = %{ | |||
"type" => "Like", | |||
"object" => "whatever" | |||
} | |||
{:reject, nil} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], config) | |||
end | |||
test "it rejects based on child object type" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :reject]) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"]) | |||
message = %{ | |||
"type" => "Create", | |||
"object" => %{ | |||
"type" => "Note", | |||
"content" => "whatever" | |||
} | |||
} | |||
{:reject, nil} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], config) | |||
end | |||
test "it passes through objects that aren't disallowed" do | |||
config = Pleroma.Config.get([:mrf_vocabulary, :reject]) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) | |||
message = %{ | |||
"type" => "Announce", | |||
"object" => "whatever" | |||
} | |||
{:ok, ^message} = VocabularyPolicy.filter(message) | |||
Pleroma.Config.put([:mrf_vocabulary, :reject], config) | |||
end | |||
end | |||
end |
@@ -5,11 +5,71 @@ | |||
defmodule Pleroma.Web.ActivityPub.RelayTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Activity | |||
alias Pleroma.Object | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.ActivityPub.Relay | |||
import Pleroma.Factory | |||
test "gets an actor for the relay" do | |||
user = Relay.get_actor() | |||
assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay" | |||
end | |||
describe "follow/1" do | |||
test "returns errors when user not found" do | |||
assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"} | |||
end | |||
test "returns activity" do | |||
user = insert(:user) | |||
service_actor = Relay.get_actor() | |||
assert {:ok, %Activity{} = activity} = Relay.follow(user.ap_id) | |||
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" | |||
assert user.ap_id in activity.recipients | |||
assert activity.data["type"] == "Follow" | |||
assert activity.data["actor"] == service_actor.ap_id | |||
assert activity.data["object"] == user.ap_id | |||
end | |||
end | |||
describe "unfollow/1" do | |||
test "returns errors when user not found" do | |||
assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"} | |||
end | |||
test "returns activity" do | |||
user = insert(:user) | |||
service_actor = Relay.get_actor() | |||
ActivityPub.follow(service_actor, user) | |||
assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) | |||
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" | |||
assert user.ap_id in activity.recipients | |||
assert activity.data["type"] == "Undo" | |||
assert activity.data["actor"] == service_actor.ap_id | |||
assert activity.data["to"] == [user.ap_id] | |||
end | |||
end | |||
describe "publish/1" do | |||
test "returns error when activity not `Create` type" do | |||
activity = insert(:like_activity) | |||
assert Relay.publish(activity) == {:error, "Not implemented"} | |||
end | |||
test "returns error when activity not public" do | |||
activity = insert(:direct_note_activity) | |||
assert Relay.publish(activity) == {:error, false} | |||
end | |||
assert user.ap_id =~ "/relay" | |||
test "returns announce activity" do | |||
service_actor = Relay.get_actor() | |||
note = insert(:note_activity) | |||
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note) | |||
assert activity.data["type"] == "Announce" | |||
assert activity.data["actor"] == service_actor.ap_id | |||
assert activity.data["object"] == obj.data["id"] | |||
end | |||
end | |||
end |
@@ -450,6 +450,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do | |||
assert !is_nil(data["cc"]) | |||
end | |||
test "it strips internal likes" do | |||
data = | |||
File.read!("test/fixtures/mastodon-post-activity.json") | |||
|> Poison.decode!() | |||
likes = %{ | |||
"first" => | |||
"http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", | |||
"id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", | |||
"totalItems" => 3, | |||
"type" => "OrderedCollection" | |||
} | |||
object = Map.put(data["object"], "likes", likes) | |||
data = Map.put(data, "object", object) | |||
{:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data) | |||
refute Map.has_key?(object.data, "likes") | |||
end | |||
test "it works for incoming update activities" do | |||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() | |||
@@ -1061,14 +1082,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do | |||
assert is_nil(modified["object"]["announcements"]) | |||
assert is_nil(modified["object"]["announcement_count"]) | |||
assert is_nil(modified["object"]["context_id"]) | |||
end | |||
test "it adds like collection to object" do | |||
activity = insert(:note_activity) | |||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) | |||
assert modified["object"]["likes"]["type"] == "OrderedCollection" | |||
assert modified["object"]["likes"]["totalItems"] == 0 | |||
assert is_nil(modified["object"]["likes"]) | |||
end | |||
test "the directMessage flag is present" do | |||
@@ -356,4 +356,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
result = AccountView.render("account.json", %{user: user}) | |||
refute result.display_name == "<marquee> username </marquee>" | |||
end | |||
describe "hiding follows/following" do | |||
test "shows when follows/following are hidden and sets follower/following count to 0" do | |||
user = insert(:user, info: %{hide_followers: true, hide_follows: true}) | |||
other_user = insert(:user) | |||
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) | |||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{ | |||
followers_count: 0, | |||
following_count: 0, | |||
pleroma: %{hide_follows: true, hide_followers: true} | |||
} = AccountView.render("account.json", %{user: user}) | |||
end | |||
test "shows actual follower/following count to the account owner" do | |||
user = insert(:user, info: %{hide_followers: true, hide_follows: true}) | |||
other_user = insert(:user) | |||
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) | |||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{ | |||
followers_count: 1, | |||
following_count: 1 | |||
} = AccountView.render("account.json", %{user: user, for: user}) | |||
end | |||
end | |||
end |
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
alias Ecto.Changeset | |||
alias Pleroma.Activity | |||
alias Pleroma.Config | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
@@ -85,11 +86,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
end | |||
test "the public timeline when public is set to false", %{conn: conn} do | |||
public = Pleroma.Config.get([:instance, :public]) | |||
Pleroma.Config.put([:instance, :public], false) | |||
public = Config.get([:instance, :public]) | |||
Config.put([:instance, :public], false) | |||
on_exit(fn -> | |||
Pleroma.Config.put([:instance, :public], public) | |||
Config.put([:instance, :public], public) | |||
end) | |||
assert conn | |||
@@ -250,7 +251,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
end | |||
test "posting a status with OGP link preview", %{conn: conn} do | |||
Pleroma.Config.put([:rich_media, :enabled], true) | |||
Config.put([:rich_media, :enabled], true) | |||
conn = | |||
conn | |||
@@ -260,7 +261,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200) | |||
assert Activity.get_by_id(id) | |||
Pleroma.Config.put([:rich_media, :enabled], false) | |||
Config.put([:rich_media, :enabled], false) | |||
end | |||
test "posting a direct status", %{conn: conn} do | |||
@@ -304,7 +305,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
test "option limit is enforced", %{conn: conn} do | |||
user = insert(:user) | |||
limit = Pleroma.Config.get([:instance, :poll_limits, :max_options]) | |||
limit = Config.get([:instance, :poll_limits, :max_options]) | |||
conn = | |||
conn | |||
@@ -320,7 +321,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
test "option character limit is enforced", %{conn: conn} do | |||
user = insert(:user) | |||
limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars]) | |||
limit = Config.get([:instance, :poll_limits, :max_option_chars]) | |||
conn = | |||
conn | |||
@@ -339,7 +340,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
test "minimal date limit is enforced", %{conn: conn} do | |||
user = insert(:user) | |||
limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration]) | |||
limit = Config.get([:instance, :poll_limits, :min_expiration]) | |||
conn = | |||
conn | |||
@@ -358,7 +359,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
test "maximum date limit is enforced", %{conn: conn} do | |||
user = insert(:user) | |||
limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration]) | |||
limit = Config.get([:instance, :poll_limits, :max_expiration]) | |||
conn = | |||
conn | |||
@@ -1633,12 +1634,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
describe "media upload" do | |||
setup do | |||
upload_config = Pleroma.Config.get([Pleroma.Upload]) | |||
proxy_config = Pleroma.Config.get([:media_proxy]) | |||
upload_config = Config.get([Pleroma.Upload]) | |||
proxy_config = Config.get([:media_proxy]) | |||
on_exit(fn -> | |||
Pleroma.Config.put([Pleroma.Upload], upload_config) | |||
Pleroma.Config.put([:media_proxy], proxy_config) | |||
Config.put([Pleroma.Upload], upload_config) | |||
Config.put([:media_proxy], proxy_config) | |||
end) | |||
user = insert(:user) | |||
@@ -2581,7 +2582,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
conn = get(conn, "/api/v1/instance") | |||
assert result = json_response(conn, 200) | |||
email = Pleroma.Config.get([:instance, :email]) | |||
email = Config.get([:instance, :email]) | |||
# Note: not checking for "max_toot_chars" since it's optional | |||
assert %{ | |||
"uri" => _, | |||
@@ -2623,7 +2624,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
|> Changeset.put_embed(:info, info_change) | |||
|> User.update_and_set_cache() | |||
Pleroma.Stats.update_stats() | |||
Pleroma.Stats.force_update() | |||
conn = get(conn, "/api/v1/instance") | |||
@@ -2641,7 +2642,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
insert(:user, %{local: false, nickname: "u@peer1.com"}) | |||
insert(:user, %{local: false, nickname: "u@peer2.com"}) | |||
Pleroma.Stats.update_stats() | |||
Pleroma.Stats.force_update() | |||
conn = get(conn, "/api/v1/instance/peers") | |||
@@ -2666,7 +2667,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
describe "pinned statuses" do | |||
setup do | |||
Pleroma.Config.put([:instance, :max_pinned_statuses], 1) | |||
Config.put([:instance, :max_pinned_statuses], 1) | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) | |||
@@ -2766,10 +2767,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
describe "cards" do | |||
setup do | |||
Pleroma.Config.put([:rich_media, :enabled], true) | |||
Config.put([:rich_media, :enabled], true) | |||
on_exit(fn -> | |||
Pleroma.Config.put([:rich_media, :enabled], false) | |||
Config.put([:rich_media, :enabled], false) | |||
end) | |||
user = insert(:user) | |||
@@ -2901,8 +2902,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
describe "conversation muting" do | |||
setup do | |||
post_user = insert(:user) | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"}) | |||
{:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"}) | |||
[user: user, activity: activity] | |||
end | |||
@@ -2995,7 +2998,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
reporter: reporter, | |||
target_user: target_user | |||
} do | |||
max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000) | |||
max_size = Config.get([:instance, :max_report_comment_size], 1000) | |||
comment = String.pad_trailing("a", max_size + 1, "a") | |||
error = %{"error" => "Comment must be up to #{max_size} characters"} | |||
@@ -3124,15 +3127,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
conn: conn, | |||
path: path | |||
} do | |||
is_public = Pleroma.Config.get([:instance, :public]) | |||
Pleroma.Config.put([:instance, :public], false) | |||
is_public = Config.get([:instance, :public]) | |||
Config.put([:instance, :public], false) | |||
conn = get(conn, path) | |||
assert conn.status == 302 | |||
assert redirected_to(conn) == "/web/login" | |||
Pleroma.Config.put([:instance, :public], is_public) | |||
Config.put([:instance, :public], is_public) | |||
end | |||
test "does not redirect logged in users to the login page", %{conn: conn, path: path} do | |||
@@ -3874,8 +3877,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) | |||
email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) | |||
notify_email = Pleroma.Config.get([:instance, :notify_email]) | |||
instance_name = Pleroma.Config.get([:instance, :name]) | |||
notify_email = Config.get([:instance, :notify_email]) | |||
instance_name = Config.get([:instance, :name]) | |||
assert_email_sent( | |||
from: {instance_name, notify_email}, | |||
@@ -3907,11 +3910,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
describe "POST /api/v1/pleroma/accounts/confirmation_resend" do | |||
setup do | |||
setting = Pleroma.Config.get([:instance, :account_activation_required]) | |||
setting = Config.get([:instance, :account_activation_required]) | |||
unless setting do | |||
Pleroma.Config.put([:instance, :account_activation_required], true) | |||
on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) | |||
Config.put([:instance, :account_activation_required], true) | |||
on_exit(fn -> Config.put([:instance, :account_activation_required], setting) end) | |||
end | |||
user = insert(:user) | |||
@@ -3935,8 +3938,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
|> json_response(:no_content) | |||
email = Pleroma.Emails.UserEmail.account_confirmation_email(user) | |||
notify_email = Pleroma.Config.get([:instance, :notify_email]) | |||
instance_name = Pleroma.Config.get([:instance, :name]) | |||
notify_email = Config.get([:instance, :notify_email]) | |||
instance_name = Config.get([:instance, :name]) | |||
assert_email_sent( | |||
from: {instance_name, notify_email}, | |||
@@ -3945,4 +3948,84 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
) | |||
end | |||
end | |||
describe "GET /api/v1/suggestions" do | |||
setup do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
config = Config.get(:suggestions) | |||
on_exit(fn -> Config.put(:suggestions, config) end) | |||
host = Config.get([Pleroma.Web.Endpoint, :url, :host]) | |||
url500 = "http://test500?#{host}&#{user.nickname}" | |||
url200 = "http://test200?#{host}&#{user.nickname}" | |||
mock(fn | |||
%{method: :get, url: ^url500} -> | |||
%Tesla.Env{status: 500, body: "bad request"} | |||
%{method: :get, url: ^url200} -> | |||
%Tesla.Env{ | |||
status: 200, | |||
body: | |||
~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{ | |||
other_user.ap_id | |||
}","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}]) | |||
} | |||
end) | |||
[user: user, other_user: other_user] | |||
end | |||
test "returns empty result when suggestions disabled", %{conn: conn, user: user} do | |||
Config.put([:suggestions, :enabled], false) | |||
res = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/suggestions") | |||
|> json_response(200) | |||
assert res == [] | |||
end | |||
test "returns error", %{conn: conn, user: user} do | |||
Config.put([:suggestions, :enabled], true) | |||
Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}") | |||
res = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/suggestions") | |||
|> json_response(500) | |||
assert res == "Something went wrong" | |||
end | |||
test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do | |||
Config.put([:suggestions, :enabled], true) | |||
Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}") | |||
res = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/suggestions") | |||
|> json_response(200) | |||
assert res == [ | |||
%{ | |||
"acct" => "yj455", | |||
"avatar" => "https://social.heldscal.la/avatar/201.jpeg", | |||
"avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg", | |||
"id" => 0 | |||
}, | |||
%{ | |||
"acct" => other_user.ap_id, | |||
"avatar" => "https://social.heldscal.la/avatar/202.jpeg", | |||
"avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg", | |||
"id" => other_user.id | |||
} | |||
] | |||
end | |||
end | |||
end |
@@ -0,0 +1,103 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.Notification | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
import Pleroma.Factory | |||
describe "follow/3" do | |||
test "returns error when user deactivated" do | |||
follower = insert(:user) | |||
user = insert(:user, local: true, info: %{deactivated: true}) | |||
{:error, error} = MastodonAPI.follow(follower, user) | |||
assert error == "Could not follow user: You are deactivated." | |||
end | |||
test "following for user" do | |||
follower = insert(:user) | |||
user = insert(:user) | |||
{:ok, follower} = MastodonAPI.follow(follower, user) | |||
assert User.following?(follower, user) | |||
end | |||
test "returns ok if user already followed" do | |||
follower = insert(:user) | |||
user = insert(:user) | |||
{:ok, follower} = User.follow(follower, user) | |||
{:ok, follower} = MastodonAPI.follow(follower, refresh_record(user)) | |||
assert User.following?(follower, user) | |||
end | |||
end | |||
describe "get_followers/2" do | |||
test "returns user followers" do | |||
follower1_user = insert(:user) | |||
follower2_user = insert(:user) | |||
user = insert(:user) | |||
{:ok, _follower1_user} = User.follow(follower1_user, user) | |||
{:ok, follower2_user} = User.follow(follower2_user, user) | |||
assert MastodonAPI.get_followers(user, %{"limit" => 1}) == [follower2_user] | |||
end | |||
end | |||
describe "get_friends/2" do | |||
test "returns user friends" do | |||
user = insert(:user) | |||
followed_one = insert(:user) | |||
followed_two = insert(:user) | |||
followed_three = insert(:user) | |||
{:ok, user} = User.follow(user, followed_one) | |||
{:ok, user} = User.follow(user, followed_two) | |||
{:ok, user} = User.follow(user, followed_three) | |||
res = MastodonAPI.get_friends(user) | |||
assert length(res) == 3 | |||
assert Enum.member?(res, refresh_record(followed_three)) | |||
assert Enum.member?(res, refresh_record(followed_two)) | |||
assert Enum.member?(res, refresh_record(followed_one)) | |||
end | |||
end | |||
describe "get_notifications/2" do | |||
test "returns notifications for user" do | |||
user = insert(:user) | |||
subscriber = insert(:user) | |||
User.subscribe(subscriber, user) | |||
{:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) | |||
{:ok, status1} = TwitterAPI.create_status(user, %{"status" => "Magi"}) | |||
{:ok, [notification]} = Notification.create_notifications(status) | |||
{:ok, [notification1]} = Notification.create_notifications(status1) | |||
res = MastodonAPI.get_notifications(subscriber) | |||
assert Enum.member?(Enum.map(res, & &1.id), notification.id) | |||
assert Enum.member?(Enum.map(res, & &1.id), notification1.id) | |||
end | |||
end | |||
describe "get_scheduled_activities/2" do | |||
test "returns user scheduled activities" do | |||
user = insert(:user) | |||
today = | |||
NaiveDateTime.utc_now() | |||
|> NaiveDateTime.add(:timer.minutes(6), :millisecond) | |||
|> NaiveDateTime.to_iso8601() | |||
attrs = %{params: %{}, scheduled_at: today} | |||
{:ok, schedule} = ScheduledActivity.create(user, attrs) | |||
assert MastodonAPI.get_scheduled_activities(user) == [schedule] | |||
end | |||
end | |||
end |
@@ -85,6 +85,9 @@ defmodule Pleroma.Web.NodeInfoTest do | |||
end | |||
test "it shows MRF transparency data if enabled", %{conn: conn} do | |||
config = Pleroma.Config.get([:instance, :rewrite_policy]) | |||
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) | |||
option = Pleroma.Config.get([:instance, :mrf_transparency]) | |||
Pleroma.Config.put([:instance, :mrf_transparency], true) | |||
@@ -98,11 +101,15 @@ defmodule Pleroma.Web.NodeInfoTest do | |||
assert response["metadata"]["federation"]["mrf_simple"] == simple_config | |||
Pleroma.Config.put([:instance, :rewrite_policy], config) | |||
Pleroma.Config.put([:instance, :mrf_transparency], option) | |||
Pleroma.Config.put(:mrf_simple, %{}) | |||
end | |||
test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do | |||
config = Pleroma.Config.get([:instance, :rewrite_policy]) | |||
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) | |||
option = Pleroma.Config.get([:instance, :mrf_transparency]) | |||
Pleroma.Config.put([:instance, :mrf_transparency], true) | |||
@@ -122,6 +129,7 @@ defmodule Pleroma.Web.NodeInfoTest do | |||
assert response["metadata"]["federation"]["mrf_simple"] == expected_config | |||
assert response["metadata"]["federation"]["exclusions"] == true | |||
Pleroma.Config.put([:instance, :rewrite_policy], config) | |||
Pleroma.Config.put([:instance, :mrf_transparency], option) | |||
Pleroma.Config.put([:instance, :mrf_transparency_exclusions], exclusions) | |||
Pleroma.Config.put(:mrf_simple, %{}) | |||