Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
commit
db3dae189b
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -18,10 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- NodeInfo: Return `mailerEnabled` in `metadata`
|
||||
- Mastodon API: Unsubscribe followers when they unfollow a user
|
||||
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
|
||||
- Improve digest email template
|
||||
|
||||
### Fixed
|
||||
- Following from Osada
|
||||
- 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.
|
||||
- Favorites timeline doing database-intensive queries
|
||||
- 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
|
||||
|
@ -43,9 +46,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- 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
|
||||
- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
|
||||
- MRF: fix use of unserializable keyword lists in describe() implementations
|
||||
- ActivityPub: Deactivated user deletion
|
||||
- MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
|
||||
|
||||
### Added
|
||||
- Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
|
||||
- Mastodon API: in post_status, the expires_in parameter lets you set the number of seconds until an activity expires. It must be at least one hour.
|
||||
- Mastodon API: all status JSON responses contain a `pleroma.expires_at` item which states when an activity will expire. The value is only shown to the user who created the activity. To everyone else it's empty.
|
||||
- Configuration: `ActivityExpiration.enabled` controls whether expired activites will get deleted at the appropriate time. Enabled by default.
|
||||
- Conversations: Add Pleroma-specific conversation endpoints and status posting extensions. Run the `bump_all_conversations` task again to create the necessary data.
|
||||
- **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.
|
||||
|
@ -67,6 +77,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mastodon API: Add `pleroma.deactivated` to the Account entity
|
||||
- Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
|
||||
- Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
|
||||
- Mastodon API: Improve support for the user profile custom fields
|
||||
- Admin API: Return users' tags when querying reports
|
||||
- Admin API: Return avatar and display name when querying users
|
||||
- Admin API: Allow querying user by ID
|
||||
|
@ -85,6 +96,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Relays: Added a task to list relay subscriptions.
|
||||
- Mix Tasks: `mix pleroma.database fix_likes_collections`
|
||||
- Federation: Remove `likes` from objects.
|
||||
- Admin API: Added moderation log
|
||||
|
||||
### Changed
|
||||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
||||
|
@ -186,6 +198,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Configuration: Added `extra_cookie_attrs` for setting non-standard cookie attributes. Defaults to ["SameSite=Lax"] so that remote follows work.
|
||||
- Timelines: Messages involving people you have blocked will be excluded from the timeline in all cases instead of just repeats.
|
||||
- Admin API: Move the user related API to `api/pleroma/admin/users`
|
||||
- Admin API: `POST /api/pleroma/admin/users` will take list of users
|
||||
- Pleroma API: Support for emoji tags in `/api/pleroma/emoji` resulting in a breaking API change
|
||||
- Mastodon API: Support for `exclude_types`, `limit` and `min_id` in `/api/v1/notifications`
|
||||
- Mastodon API: Add `languages` and `registrations` to `/api/v1/instance`
|
||||
|
|
|
@ -255,6 +255,10 @@ config :pleroma, :instance,
|
|||
dynamic_configuration: false,
|
||||
user_bio_length: 5000,
|
||||
user_name_length: 100,
|
||||
max_account_fields: 10,
|
||||
max_remote_account_fields: 20,
|
||||
account_field_name_length: 512,
|
||||
account_field_value_length: 512,
|
||||
external_user_synchronization: true
|
||||
|
||||
config :pleroma, :markup,
|
||||
|
@ -452,6 +456,7 @@ config :pleroma, Pleroma.Web.Federator.RetryQueue,
|
|||
max_retries: 5
|
||||
|
||||
config :pleroma_job_queue, :queues,
|
||||
activity_expiration: 10,
|
||||
federator_incoming: 50,
|
||||
federator_outgoing: 50,
|
||||
web_push: 50,
|
||||
|
@ -512,6 +517,17 @@ config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
|||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
|
||||
|
||||
config :pleroma, Pleroma.Emails.UserEmail,
|
||||
logo: nil,
|
||||
styling: %{
|
||||
link_color: "#d8a070",
|
||||
background_color: "#2C3645",
|
||||
content_background_color: "#1B2635",
|
||||
header_color: "#d8a070",
|
||||
text_color: "#b9b9ba",
|
||||
text_muted_color: "#b9b9ba"
|
||||
}
|
||||
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
|
@ -540,15 +556,9 @@ config :pleroma, :env, Mix.env()
|
|||
config :http_signatures,
|
||||
adapter: Pleroma.Signature
|
||||
|
||||
config :pleroma, :rate_limit,
|
||||
search: [{1000, 10}, {1000, 30}],
|
||||
app_account_creation: {1_800_000, 25},
|
||||
relations_actions: {10_000, 10},
|
||||
relation_id_action: {60_000, 2},
|
||||
statuses_actions: {10_000, 15},
|
||||
status_id_action: {60_000, 3},
|
||||
password_reset: {1_800_000, 5},
|
||||
account_confirmation_resend: {8_640_000, 5}
|
||||
config :pleroma, :rate_limit, nil
|
||||
|
||||
config :pleroma, Pleroma.ActivityExpiration, enabled: true
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
|
|
|
@ -77,7 +77,8 @@ config :pleroma, Pleroma.ScheduledActivity,
|
|||
config :pleroma, :rate_limit,
|
||||
search: [{1000, 30}, {1000, 30}],
|
||||
app_account_creation: {10_000, 5},
|
||||
password_reset: {1000, 30}
|
||||
password_reset: {1000, 30},
|
||||
ap_routes: nil
|
||||
|
||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||
|
||||
|
@ -91,11 +92,10 @@ config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp3
|
|||
|
||||
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
|
||||
|
||||
try do
|
||||
if File.exists?("./config/test.secret.exs") do
|
||||
import_config "test.secret.exs"
|
||||
rescue
|
||||
_ ->
|
||||
IO.puts(
|
||||
"You may want to create test.secret.exs to declare custom database connection parameters."
|
||||
)
|
||||
else
|
||||
IO.puts(
|
||||
"You may want to create test.secret.exs to declare custom database connection parameters."
|
||||
)
|
||||
end
|
||||
|
|
|
@ -694,3 +694,27 @@ Compile time settings (need instance reboot):
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `/api/pleroma/admin/moderation_log`
|
||||
### Get moderation log
|
||||
- Method `GET`
|
||||
- Params:
|
||||
- *optional* `page`: **integer** page number
|
||||
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
||||
- Response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"actor": {
|
||||
"id": 1,
|
||||
"nickname": "lain"
|
||||
},
|
||||
"action": "relay_follow"
|
||||
},
|
||||
"time": 1502812026, // timestamp
|
||||
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -25,6 +25,7 @@ Has these additional fields under the `pleroma` object:
|
|||
- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
|
||||
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
||||
|
||||
## Attachments
|
||||
|
||||
|
@ -86,6 +87,7 @@ Additional parameters can be added to the JSON body/Form data:
|
|||
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
||||
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.
|
||||
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
|
||||
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
|
||||
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
|
||||
|
||||
## PATCH `/api/v1/update_credentials`
|
||||
|
|
|
@ -8,7 +8,7 @@ If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherw
|
|||
* `filters`: List of `Pleroma.Upload.Filter` to use.
|
||||
* `link_name`: When enabled Pleroma will add a `name` parameter to the url of the upload, for example `https://instance.tld/media/corndog.png?name=corndog.png`. This is needed to provide the correct filename in Content-Disposition headers when using filters like `Pleroma.Upload.Filter.Dedupe`
|
||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
||||
* `proxy_remote`: If you\'re using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
||||
|
||||
Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
||||
|
@ -132,6 +132,10 @@ config :pleroma, Pleroma.Emails.Mailer,
|
|||
* `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.
|
||||
* `max_account_fields`: The maximum number of custom fields in the user profile (default: `10`)
|
||||
* `max_remote_account_fields`: The maximum number of custom fields in the remote user profile (default: `20`)
|
||||
* `account_field_name_length`: An account field name maximum length (default: `512`)
|
||||
* `account_field_value_length`: An account field value maximum length (default: `512`)
|
||||
* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
|
||||
|
||||
|
||||
|
@ -491,6 +495,10 @@ config :auto_linker,
|
|||
* `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`)
|
||||
* `enabled`: whether scheduled activities are sent to the job queue to be executed
|
||||
|
||||
## Pleroma.ActivityExpiration
|
||||
|
||||
# `enabled`: whether expired activities will be sent to the job queue to be deleted
|
||||
|
||||
## Pleroma.Web.Auth.Authenticator
|
||||
|
||||
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator
|
||||
|
@ -555,6 +563,11 @@ Email notifications settings.
|
|||
- interval: Minimum interval between digest emails to one user
|
||||
- inactivity_threshold: Minimum user inactivity threshold
|
||||
|
||||
## Pleroma.Emails.UserEmail
|
||||
|
||||
- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
|
||||
- `:styling` - a map with color settings for email templates.
|
||||
|
||||
## OAuth consumer mode
|
||||
|
||||
OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.).
|
||||
|
@ -658,6 +671,8 @@ This will probably take a long time.
|
|||
|
||||
## :rate_limit
|
||||
|
||||
This is an advanced feature and disabled by default.
|
||||
|
||||
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
|
||||
|
||||
* The first element: `scale` (Integer). The time scale in milliseconds.
|
||||
|
|
|
@ -71,26 +71,26 @@ server {
|
|||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||
proxy_pass http://127.0.0.1:4000;
|
||||
|
||||
client_max_body_size 16m;
|
||||
}
|
||||
|
||||
location ~ ^/(media|proxy) {
|
||||
proxy_cache pleroma_media_cache;
|
||||
proxy_cache pleroma_media_cache;
|
||||
slice 1m;
|
||||
proxy_cache_key $host$uri$is_args$args$slice_range;
|
||||
proxy_set_header Range $slice_range;
|
||||
proxy_http_version 1.1;
|
||||
proxy_cache_valid 200 206 301 304 1h;
|
||||
proxy_cache_lock on;
|
||||
proxy_cache_lock on;
|
||||
proxy_ignore_client_abort on;
|
||||
proxy_buffering on;
|
||||
proxy_buffering on;
|
||||
chunked_transfer_encoding on;
|
||||
proxy_ignore_headers Cache-Control;
|
||||
proxy_hide_header Cache-Control;
|
||||
proxy_pass http://localhost:4000;
|
||||
proxy_hide_header Cache-Control;
|
||||
proxy_pass http://127.0.0.1:4000;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,15 @@ defmodule Mix.Tasks.Pleroma.Digest do
|
|||
|
||||
patched_user = %{user | last_digest_emailed_at: last_digest_emailed_at}
|
||||
|
||||
_user = Pleroma.DigestEmailWorker.perform(patched_user)
|
||||
Mix.shell().info("Digest email have been sent to #{nickname} (#{user.email})")
|
||||
with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(patched_user) do
|
||||
{:ok, _} = Pleroma.Emails.Mailer.deliver(email)
|
||||
|
||||
Mix.shell().info("Digest email have been sent to #{nickname} (#{user.email})")
|
||||
else
|
||||
_ ->
|
||||
Mix.shell().info(
|
||||
"Cound't find any mentions for #{nickname} since #{last_digest_emailed_at}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,13 +53,11 @@ defmodule Mix.Tasks.Pleroma.Relay do
|
|||
def run(["list"]) do
|
||||
start_pleroma()
|
||||
|
||||
with %User{} = user <- Relay.get_actor() do
|
||||
user.following
|
||||
|> Enum.each(fn entry ->
|
||||
URI.parse(entry)
|
||||
|> Map.get(:host)
|
||||
|> shell_info()
|
||||
end)
|
||||
with %User{following: following} = _user <- Relay.get_actor() do
|
||||
following
|
||||
|> Enum.map(fn entry -> URI.parse(entry).host end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.each(&shell_info(&1))
|
||||
else
|
||||
e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Activity do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
|
@ -59,6 +60,8 @@ defmodule Pleroma.Activity do
|
|||
# typical case.
|
||||
has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
|
||||
|
||||
has_one(:expiration, ActivityExpiration, on_delete: :delete_all)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.Queries do
|
||||
@moduledoc """
|
||||
Contains queries for Activity.
|
||||
"""
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||
|
||||
alias Pleroma.Activity
|
||||
|
||||
@spec by_actor(query, String.t()) :: query
|
||||
def by_actor(query \\ Activity, actor) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(?)->>'actor' = ?", activity.data, ^actor)
|
||||
)
|
||||
end
|
||||
|
||||
@spec by_object_id(query, String.t()) :: query
|
||||
def by_object_id(query \\ Activity, object_id) do
|
||||
from(activity in query,
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^object_id
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
@spec by_type(query, String.t()) :: query
|
||||
def by_type(query \\ Activity, activity_type) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(?)->>'type' = ?", activity.data, ^activity_type)
|
||||
)
|
||||
end
|
||||
|
||||
@spec limit(query, pos_integer()) :: query
|
||||
def limit(query \\ Activity, limit) do
|
||||
from(activity in query, limit: ^limit)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpiration do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.FlakeId
|
||||
alias Pleroma.Repo
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@min_activity_lifetime :timer.hours(1)
|
||||
|
||||
schema "activity_expirations" do
|
||||
belongs_to(:activity, Activity, type: FlakeId)
|
||||
field(:scheduled_at, :naive_datetime)
|
||||
end
|
||||
|
||||
def changeset(%ActivityExpiration{} = expiration, attrs) do
|
||||
expiration
|
||||
|> cast(attrs, [:scheduled_at])
|
||||
|> validate_required([:scheduled_at])
|
||||
|> validate_scheduled_at()
|
||||
end
|
||||
|
||||
def get_by_activity_id(activity_id) do
|
||||
ActivityExpiration
|
||||
|> where([exp], exp.activity_id == ^activity_id)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def create(%Activity{} = activity, scheduled_at) do
|
||||
%ActivityExpiration{activity_id: activity.id}
|
||||
|> changeset(%{scheduled_at: scheduled_at})
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def due_expirations(offset \\ 0) do
|
||||
naive_datetime =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(offset, :millisecond)
|
||||
|
||||
ActivityExpiration
|
||||
|> where([exp], exp.scheduled_at < ^naive_datetime)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def validate_scheduled_at(changeset) do
|
||||
validate_change(changeset, :scheduled_at, fn _, scheduled_at ->
|
||||
if not expires_late_enough?(scheduled_at) do
|
||||
[scheduled_at: "an ephemeral activity must live for at least one hour"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def expires_late_enough?(scheduled_at) do
|
||||
now = NaiveDateTime.utc_now()
|
||||
diff = NaiveDateTime.diff(scheduled_at, now, :millisecond)
|
||||
diff >= @min_activity_lifetime
|
||||
end
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpirationWorker do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
require Logger
|
||||
use GenServer
|
||||
import Ecto.Query
|
||||
|
||||
@schedule_interval :timer.minutes(1)
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(_) do
|
||||
if Config.get([ActivityExpiration, :enabled]) do
|
||||
schedule_next()
|
||||
{:ok, nil}
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def perform(:execute, expiration_id) do
|
||||
try do
|
||||
expiration =
|
||||
ActivityExpiration
|
||||
|> where([e], e.id == ^expiration_id)
|
||||
|> Repo.one!()
|
||||
|
||||
activity = Activity.get_by_id_with_object(expiration.activity_id)
|
||||
user = User.get_by_ap_id(activity.object.data["actor"])
|
||||
CommonAPI.delete(activity.id, user)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("#{__MODULE__} Couldn't delete expired activity: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:perform, state) do
|
||||
ActivityExpiration.due_expirations(@schedule_interval)
|
||||
|> Enum.each(fn expiration ->
|
||||
PleromaJobQueue.enqueue(:activity_expiration, __MODULE__, [:execute, expiration.id])
|
||||
end)
|
||||
|
||||
schedule_next()
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp schedule_next do
|
||||
Process.send_after(self(), :perform, @schedule_interval)
|
||||
end
|
||||
end
|
|
@ -35,7 +35,8 @@ defmodule Pleroma.Application do
|
|||
Pleroma.Emoji,
|
||||
Pleroma.Captcha,
|
||||
Pleroma.FlakeId,
|
||||
Pleroma.ScheduledActivityWorker
|
||||
Pleroma.ScheduledActivityWorker,
|
||||
Pleroma.ActivityExpirationWorker
|
||||
] ++
|
||||
cachex_children() ++
|
||||
hackney_pool_children() ++
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.DigestEmailWorker do
|
||||
import Ecto.Query
|
||||
|
||||
|
|
|
@ -7,21 +7,21 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
|
||||
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
defp instance_config, do: Pleroma.Config.get(:instance)
|
||||
|
||||
defp instance_name, do: instance_config()[:name]
|
||||
defp instance_name, do: Config.get([:instance, :name])
|
||||
|
||||
defp sender do
|
||||
email = Keyword.get(instance_config(), :notify_email, instance_config()[:email])
|
||||
email = Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
|
||||
{instance_name(), email}
|
||||
end
|
||||
|
||||
defp recipient(email, nil), do: email
|
||||
defp recipient(email, name), do: {name, email}
|
||||
defp recipient(%Pleroma.User{} = user), do: recipient(user.email, user.name)
|
||||
defp recipient(%User{} = user), do: recipient(user.email, user.name)
|
||||
|
||||
def password_reset_email(user, token) when is_binary(token) do
|
||||
password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
|
||||
|
@ -93,67 +93,86 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
Includes Mentions and New Followers data
|
||||
If there are no mentions (even when new followers exist), the function will return nil
|
||||
"""
|
||||
@spec digest_email(Pleroma.User.t()) :: Swoosh.Email.t() | nil
|
||||
@spec digest_email(User.t()) :: Swoosh.Email.t() | nil
|
||||
def digest_email(user) do
|
||||
new_notifications =
|
||||
Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
|
||||
|> Enum.reduce(%{followers: [], mentions: []}, fn
|
||||
%{activity: %{data: %{"type" => "Create"}, actor: actor} = activity} = notification,
|
||||
acc ->
|
||||
new_mention = %{
|
||||
data: notification,
|
||||
object: Pleroma.Object.normalize(activity),
|
||||
from: Pleroma.User.get_by_ap_id(actor)
|
||||
}
|
||||
notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
|
||||
|
||||
%{acc | mentions: [new_mention | acc.mentions]}
|
||||
mentions =
|
||||
notifications
|
||||
|> Enum.filter(&(&1.activity.data["type"] == "Create"))
|
||||
|> Enum.map(fn notification ->
|
||||
object = Pleroma.Object.normalize(notification.activity)
|
||||
object = update_in(object.data["content"], &format_links/1)
|
||||
|
||||
%{activity: %{data: %{"type" => "Follow"}, actor: actor} = activity} = notification,
|
||||
acc ->
|
||||
new_follower = %{
|
||||
data: notification,
|
||||
object: Pleroma.Object.normalize(activity),
|
||||
from: Pleroma.User.get_by_ap_id(actor)
|
||||
}
|
||||
|
||||
%{acc | followers: [new_follower | acc.followers]}
|
||||
|
||||
_, acc ->
|
||||
acc
|
||||
%{
|
||||
data: notification,
|
||||
object: object,
|
||||
from: User.get_by_ap_id(notification.activity.actor)
|
||||
}
|
||||
end)
|
||||
|
||||
with [_ | _] = mentions <- new_notifications.mentions do
|
||||
followers =
|
||||
notifications
|
||||
|> Enum.filter(&(&1.activity.data["type"] == "Follow"))
|
||||
|> Enum.map(fn notification ->
|
||||
%{
|
||||
data: notification,
|
||||
object: Pleroma.Object.normalize(notification.activity),
|
||||
from: User.get_by_ap_id(notification.activity.actor)
|
||||
}
|
||||
end)
|
||||
|
||||
unless Enum.empty?(mentions) do
|
||||
styling = Config.get([__MODULE__, :styling])
|
||||
logo = Config.get([__MODULE__, :logo])
|
||||
|
||||
html_data = %{
|
||||
instance: instance_name(),
|
||||
user: user,
|
||||
mentions: mentions,
|
||||
followers: new_notifications.followers,
|
||||
unsubscribe_link: unsubscribe_url(user, "digest")
|
||||
followers: followers,
|
||||
unsubscribe_link: unsubscribe_url(user, "digest"),
|
||||
styling: styling
|
||||
}
|
||||
|
||||
logo_path =
|
||||
if is_nil(logo) do
|
||||
Path.join(:code.priv_dir(:pleroma), "static/static/logo.png")
|
||||
else
|
||||
Path.join(Config.get([:instance, :static_dir]), logo)
|
||||
end
|
||||
|
||||
new()
|
||||
|> to(recipient(user))
|
||||
|> from(sender())
|
||||
|> subject("Your digest from #{instance_name()}")
|
||||
|> put_layout(false)
|
||||
|> render_body("digest.html", html_data)
|
||||
else
|
||||
_ ->
|
||||
nil
|
||||
|> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline))
|
||||
end
|
||||
end
|
||||
|
||||
defp format_links(str) do
|
||||
re = ~r/<a.+href=['"].*>/iU
|
||||
%{link_color: color} = Config.get([__MODULE__, :styling])
|
||||
|
||||
Regex.replace(re, str, fn link ->
|
||||
String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generate unsubscribe link for given user and notifications type.
|
||||
The link contains JWT token with the data, and subscription can be modified without
|
||||
authorization.
|
||||
"""
|
||||
@spec unsubscribe_url(Pleroma.User.t(), String.t()) :: String.t()
|
||||
@spec unsubscribe_url(User.t(), String.t()) :: String.t()
|
||||
def unsubscribe_url(user, notifications_type) do
|
||||
token =
|
||||
%{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
|
||||
|> Pleroma.JWT.generate_and_sign!()
|
||||
|> Base.encode64()
|
||||
|
||||
Router.Helpers.subscription_url(Pleroma.Web.Endpoint, :unsubscribe, token)
|
||||
Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -282,3 +282,31 @@ defmodule Pleroma.HTML.Transform.MediaProxy do
|
|||
def scrub({_tag, children}), do: children
|
||||
def scrub(text), do: text
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Scrubber.LinksOnly do
|
||||
@moduledoc """
|
||||
An HTML scrubbing policy which limits to links only.
|
||||
"""
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
require HtmlSanitizeEx.Scrubber.Meta
|
||||
alias HtmlSanitizeEx.Scrubber.Meta
|
||||
|
||||
Meta.remove_cdata_sections_before_scrub()
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"me"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
||||
|
|
|
@ -109,15 +109,19 @@ defmodule Pleroma.List do
|
|||
end
|
||||
|
||||
def create(title, %User{} = creator) do
|
||||
list = %Pleroma.List{user_id: creator.id, title: title}
|
||||
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title})
|
||||
|
||||
Repo.transaction(fn ->
|
||||
list = Repo.insert!(list)
|
||||
if changeset.valid? do
|
||||
Repo.transaction(fn ->
|
||||
list = Repo.insert!(changeset)
|
||||
|
||||
list
|
||||
|> change(ap_id: "#{creator.ap_id}/lists/#{list.id}")
|
||||
|> Repo.update!()
|
||||
end)
|
||||
list
|
||||
|> change(ap_id: "#{creator.ap_id}/lists/#{list.id}")
|
||||
|> Repo.update!()
|
||||
end)
|
||||
else
|
||||
{:error, changeset}
|
||||
end
|
||||
end
|
||||
|
||||
def follow(%Pleroma.List{following: following} = list, %User{} = followed) do
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
defmodule Pleroma.ModerationLog do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
schema "moderation_log" do
|
||||
field(:data, :map)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def get_all(page, page_size) do
|
||||
from(q in __MODULE__,
|
||||
order_by: [desc: q.inserted_at],
|
||||
limit: ^page_size,
|
||||
offset: ^((page - 1) * page_size)
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
subject: %User{} = subject,
|
||||
action: action,
|
||||
permission: permission
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
subject: user_to_map(subject),
|
||||
action: action,
|
||||
permission: permission
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_update",
|
||||
subject: %Activity{data: %{"type" => "Flag"}} = subject
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "report_update",
|
||||
subject: report_to_map(subject)
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_response",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "report_response",
|
||||
subject: report_to_map(subject),
|
||||
text: text
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "status_update",
|
||||
subject: %Activity{} = subject,
|
||||
sensitive: sensitive,
|
||||
visibility: visibility
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "status_update",
|
||||
subject: status_to_map(subject),
|
||||
sensitive: sensitive,
|
||||
visibility: visibility
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "status_delete",
|
||||
subject_id: subject_id
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "status_delete",
|
||||
subject_id: subject_id
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: action,
|
||||
subject: user_to_map(subject)
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subjects: [User], action: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, subjects: subjects, action: action}) do
|
||||
subjects = Enum.map(subjects, &user_to_map/1)
|
||||
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: action,
|
||||
subjects: subjects
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
followed: %User{} = followed,
|
||||
follower: %User{} = follower,
|
||||
action: "follow"
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "follow",
|
||||
followed: user_to_map(followed),
|
||||
follower: user_to_map(follower)
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
followed: %User{} = followed,
|
||||
follower: %User{} = follower,
|
||||
action: "unfollow"
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: "unfollow",
|
||||
followed: user_to_map(followed),
|
||||
follower: user_to_map(follower)
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
nicknames: nicknames,
|
||||
tags: tags,
|
||||
action: action
|
||||
}) do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
nicknames: nicknames,
|
||||
tags: tags,
|
||||
action: action
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: action,
|
||||
target: target
|
||||
})
|
||||
when action in ["relay_follow", "relay_unfollow"] do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: user_to_map(actor),
|
||||
action: action,
|
||||
target: target
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defp user_to_map(%User{} = user) do
|
||||
user
|
||||
|> Map.from_struct()
|
||||
|> Map.take([:id, :nickname])
|
||||
|> Map.put(:type, "user")
|
||||
end
|
||||
|
||||
defp report_to_map(%Activity{} = report) do
|
||||
%{
|
||||
type: "report",
|
||||
id: report.id,
|
||||
state: report.data["state"]
|
||||
}
|
||||
end
|
||||
|
||||
defp status_to_map(%Activity{} = status) do
|
||||
%{
|
||||
type: "status",
|
||||
id: status.id
|
||||
}
|
||||
end
|
||||
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => action,
|
||||
"followed" => %{"nickname" => followed_nickname},
|
||||
"follower" => %{"nickname" => follower_nickname}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} made @#{follower_nickname} #{action} @#{followed_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "delete",
|
||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted user @#{subject_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "create",
|
||||
"subjects" => subjects
|
||||
}
|
||||
}) do
|
||||
nicknames =
|
||||
subjects
|
||||
|> Enum.map(&"@#{&1["nickname"]}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
"@#{actor_nickname} created users: #{nicknames}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "activate",
|
||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} activated user @#{subject_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "deactivate",
|
||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deactivated user @#{subject_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"nicknames" => nicknames,
|
||||
"tags" => tags,
|
||||
"action" => "tag"
|
||||
}
|
||||
}) do
|
||||
nicknames_string =
|
||||
nicknames
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags_string = tags |> Enum.join(", ")
|
||||
|
||||
"@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"nicknames" => nicknames,
|
||||
"tags" => tags,
|
||||
"action" => "untag"
|
||||
}
|
||||
}) do
|
||||
nicknames_string =
|
||||
nicknames
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags_string = tags |> Enum.join(", ")
|
||||
|
||||
"@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "grant",
|
||||
"subject" => %{"nickname" => subject_nickname},
|
||||
"permission" => permission
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} made @#{subject_nickname} #{permission}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "revoke",
|
||||
"subject" => %{"nickname" => subject_nickname},
|
||||
"permission" => permission
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "relay_follow",
|
||||
"target" => target
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} followed relay: #{target}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "relay_unfollow",
|
||||
"target" => target
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} unfollowed relay: #{target}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_update",
|
||||
"subject" => %{"id" => subject_id, "state" => state, "type" => "report"}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} updated report ##{subject_id} with '#{state}' state"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_response",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} responded with '#{text}' to report ##{subject_id}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "status_update",
|
||||
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||
"sensitive" => nil,
|
||||
"visibility" => visibility
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} updated status ##{subject_id}, set visibility: '#{visibility}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "status_update",
|
||||
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||
"sensitive" => sensitive,
|
||||
"visibility" => nil
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "status_update",
|
||||
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||
"sensitive" => sensitive,
|
||||
"visibility" => visibility
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{
|
||||
visibility
|
||||
}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "status_delete",
|
||||
"subject_id" => subject_id
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted status ##{subject_id}"
|
||||
end
|
||||
end
|
|
@ -150,8 +150,6 @@ defmodule Pleroma.Object do
|
|||
def update_and_set_cache(changeset) do
|
||||
with {:ok, object} <- Repo.update(changeset) do
|
||||
set_cache(object)
|
||||
else
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -117,9 +117,7 @@ defmodule Pleroma.Object.Fetcher do
|
|||
def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
|
||||
Logger.info("Fetching object #{id} via AP")
|
||||
|
||||
date =
|
||||
NaiveDateTime.utc_now()
|
||||
|> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
|
||||
date = Pleroma.Signature.signed_date()
|
||||
|
||||
headers =
|
||||
[{:Accept, "application/activity+json"}]
|
||||
|
|
|
@ -109,7 +109,11 @@ defmodule Pleroma.ReverseProxy do
|
|||
end
|
||||
|
||||
with {:ok, code, headers, client} <- request(method, url, req_headers, hackney_opts),
|
||||
:ok <- header_length_constraint(headers, Keyword.get(opts, :max_body_length)) do
|
||||
:ok <-
|
||||
header_length_constraint(
|
||||
headers,
|
||||
Keyword.get(opts, :max_body_length, @max_body_length)
|
||||
) do
|
||||
response(conn, client, url, code, headers, opts)
|
||||
else
|
||||
{:ok, code, headers} ->
|
||||
|
@ -200,7 +204,11 @@ defmodule Pleroma.ReverseProxy do
|
|||
{:ok, data} <- client().stream_body(client),
|
||||
{:ok, duration} <- increase_read_duration(duration),
|
||||
sent_so_far = sent_so_far + byte_size(data),
|
||||
:ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
|
||||
:ok <-
|
||||
body_size_constraint(
|
||||
sent_so_far,
|
||||
Keyword.get(opts, :max_body_length, @max_body_length)
|
||||
),
|
||||
{:ok, conn} <- chunk(conn, data) do
|
||||
chunk_reply(conn, client, opts, sent_so_far, duration)
|
||||
else
|
||||
|
|
|
@ -53,4 +53,10 @@ defmodule Pleroma.Signature do
|
|||
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
||||
end
|
||||
end
|
||||
|
||||
def signed_date, do: signed_date(NaiveDateTime.utc_now())
|
||||
|
||||
def signed_date(%NaiveDateTime{} = date) do
|
||||
Timex.format!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -222,12 +222,12 @@ defmodule Pleroma.User do
|
|||
|> validate_length(:name, min: 1, max: name_limit)
|
||||
end
|
||||
|
||||
def upgrade_changeset(struct, params \\ %{}) do
|
||||
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
||||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
||||
|
||||
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
|
||||
info_cng = User.Info.user_upgrade(struct.info, params[:info])
|
||||
info_cng = User.Info.user_upgrade(struct.info, params[:info], remote?)
|
||||
|
||||
struct
|
||||
|> cast(params, [
|
||||
|
@ -330,7 +330,13 @@ defmodule Pleroma.User do
|
|||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||
def register(%Ecto.Changeset{} = changeset) do
|
||||
with {:ok, user} <- Repo.insert(changeset),
|
||||
{:ok, user} <- autofollow_users(user),
|
||||
{:ok, user} <- post_register_action(user) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
def post_register_action(%User{} = user) do
|
||||
with {:ok, user} <- autofollow_users(user),
|
||||
{:ok, user} <- set_cache(user),
|
||||
{:ok, _} <- User.WelcomeMessage.post_welcome_message_to_user(user),
|
||||
{:ok, _} <- try_send_confirmation_email(user) do
|
||||
|
@ -750,6 +756,7 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec maybe_fetch_follow_information(User.t()) :: User.t()
|
||||
def maybe_fetch_follow_information(user) do
|
||||
with {:ok, user} <- fetch_follow_information(user) do
|
||||
user
|
||||
|
@ -807,9 +814,10 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
@spec maybe_update_following_count(User.t()) :: User.t()
|
||||
def maybe_update_following_count(%User{local: false} = user) do
|
||||
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||
{:ok, maybe_fetch_follow_information(user)}
|
||||
maybe_fetch_follow_information(user)
|
||||
else
|
||||
user
|
||||
end
|
||||
|
|
|
@ -49,6 +49,8 @@ defmodule Pleroma.User.Info do
|
|||
field(:mascot, :map, default: nil)
|
||||
field(:emoji, {:array, :map}, default: [])
|
||||
field(:pleroma_settings_store, :map, default: %{})
|
||||
field(:fields, {:array, :map}, default: nil)
|
||||
field(:raw_fields, {:array, :map}, default: [])
|
||||
|
||||
field(:notification_settings, :map,
|
||||
default: %{
|
||||
|
@ -254,11 +256,13 @@ defmodule Pleroma.User.Info do
|
|||
:hide_followers,
|
||||
:hide_follows,
|
||||
:follower_count,
|
||||
:fields,
|
||||
:following_count
|
||||
])
|
||||
|> validate_fields(true)
|
||||
end
|
||||
|
||||
def user_upgrade(info, params) do
|
||||
def user_upgrade(info, params, remote? \\ false) do
|
||||
info
|
||||
|> cast(params, [
|
||||
:ap_enabled,
|
||||
|
@ -269,8 +273,10 @@ defmodule Pleroma.User.Info do
|
|||
:follower_count,
|
||||
:following_count,
|
||||
:hide_follows,
|
||||
:fields,
|
||||
:hide_followers
|
||||
])
|
||||
|> validate_fields(remote?)
|
||||
end
|
||||
|
||||
def profile_update(info, params) do
|
||||
|
@ -286,10 +292,40 @@ defmodule Pleroma.User.Info do
|
|||
:background,
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:fields,
|
||||
:raw_fields,
|
||||
:pleroma_settings_store
|
||||
])
|
||||
|> validate_fields()
|
||||
end
|
||||
|
||||
def validate_fields(changeset, remote? \\ false) do
|
||||
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
||||
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
||||
|
||||
changeset
|
||||
|> validate_length(:fields, max: limit)
|
||||
|> validate_change(:fields, fn :fields, fields ->
|
||||
if Enum.all?(fields, &valid_field?/1) do
|
||||
[]
|
||||
else
|
||||
[fields: "invalid"]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp valid_field?(%{"name" => name, "value" => value}) do
|
||||
name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
|
||||
value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
|
||||
|
||||
is_binary(name) &&
|
||||
is_binary(value) &&
|
||||
String.length(name) <= name_limit &&
|
||||
String.length(value) <= value_limit
|
||||
end
|
||||
|
||||
defp valid_field?(_), do: false
|
||||
|
||||
@spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
|
||||
def confirmation_changeset(info, opts) do
|
||||
need_confirmation? = Keyword.get(opts, :need_confirmation)
|
||||
|
@ -384,6 +420,21 @@ defmodule Pleroma.User.Info do
|
|||
cast(info, params, [:muted_reblogs])
|
||||
end
|
||||
|
||||
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
||||
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
||||
def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
|
||||
limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
|
||||
|
||||
attachment
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|> Enum.take(limit)
|
||||
end
|
||||
|
||||
def fields(%{fields: nil}), do: []
|
||||
|
||||
def fields(%{fields: fields}), do: fields
|
||||
|
||||
def follow_information_update(info, params) do
|
||||
info
|
||||
|> cast(params, [
|
||||
|
|
|
@ -65,12 +65,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
if not is_nil(actor) do
|
||||
with user <- User.get_cached_by_ap_id(actor),
|
||||
false <- user.info.deactivated do
|
||||
:ok
|
||||
true
|
||||
else
|
||||
_e -> :reject
|
||||
_e -> false
|
||||
end
|
||||
else
|
||||
:ok
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -119,10 +119,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
def increase_poll_votes_if_vote(_create_data), do: :noop
|
||||
|
||||
def insert(map, local \\ true, fake \\ false) when is_map(map) do
|
||||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
:ok <- check_actor_is_active(map["actor"]),
|
||||
true <- bypass_actor_check || check_actor_is_active(map["actor"]),
|
||||
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
|
@ -139,7 +139,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
# Splice in the child object if we have one.
|
||||
activity =
|
||||
if !is_nil(object) do
|
||||
if not is_nil(object) do
|
||||
Map.put(activity, :object, object)
|
||||
else
|
||||
activity
|
||||
|
@ -331,12 +331,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def unlike(
|
||||
%User{} = actor,
|
||||
%Object{} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
|
||||
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||
{:ok, unlike_activity} <- insert(unlike_data, local),
|
||||
|
@ -411,7 +406,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
"actor" => ap_id,
|
||||
"object" => %{"type" => "Person", "id" => ap_id}
|
||||
},
|
||||
{:ok, activity} <- insert(data, true, true),
|
||||
{:ok, activity} <- insert(data, true, true, true),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, user}
|
||||
end
|
||||
|
@ -1023,6 +1018,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
"url" => [%{"href" => data["image"]["url"]}]
|
||||
}
|
||||
|
||||
fields =
|
||||
data
|
||||
|> Map.get("attachment", [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
locked = data["manuallyApprovesFollowers"] || false
|
||||
data = Transmogrifier.maybe_fix_user_object(data)
|
||||
|
||||
|
@ -1032,6 +1033,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
ap_enabled: true,
|
||||
source_data: data,
|
||||
banner: banner,
|
||||
fields: fields,
|
||||
locked: locked
|
||||
},
|
||||
avatar: avatar,
|
||||
|
|
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
|
@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
|
||||
{_, true} <- {:public?, Visibility.is_public?(object)} do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ObjectView.render("object.json", %{object: object}))
|
||||
else
|
||||
{:public?, false} ->
|
||||
|
@ -69,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{page, _} = Integer.parse(page)
|
||||
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ObjectView.render("likes.json", ap_id, likes, page))
|
||||
else
|
||||
{:public?, false} ->
|
||||
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{_, true} <- {:public?, Visibility.is_public?(object)},
|
||||
likes <- Utils.get_object_likes(object) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ObjectView.render("likes.json", ap_id, likes))
|
||||
else
|
||||
{:public?, false} ->
|
||||
|
@ -96,7 +96,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
%Activity{} = activity <- Activity.normalize(ap_id),
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ObjectView.render("object.json", %{object: activity}))
|
||||
else
|
||||
{:public?, false} ->
|
||||
|
@ -104,6 +104,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
# GET /relay/following
|
||||
def following(%{assigns: %{relay: true}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("following.json", %{user: Relay.get_actor()}))
|
||||
end
|
||||
|
||||
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
|
||||
|
@ -112,12 +119,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{page, _} = Integer.parse(page)
|
||||
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("following.json", %{user: user, page: page, for: for_user}))
|
||||
else
|
||||
{:show_follows, _} ->
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> send_resp(403, "")
|
||||
end
|
||||
end
|
||||
|
@ -126,11 +133,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("following.json", %{user: user, for: for_user}))
|
||||
end
|
||||
end
|
||||
|
||||
# GET /relay/followers
|
||||
def followers(%{assigns: %{relay: true}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("followers.json", %{user: Relay.get_actor()}))
|
||||
end
|
||||
|
||||
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
|
||||
|
@ -139,12 +153,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{page, _} = Integer.parse(page)
|
||||
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("followers.json", %{user: user, page: page, for: for_user}))
|
||||
else
|
||||
{:show_followers, _} ->
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> send_resp(403, "")
|
||||
end
|
||||
end
|
||||
|
@ -153,7 +167,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("followers.json", %{user: user, for: for_user}))
|
||||
end
|
||||
end
|
||||
|
@ -162,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
|
||||
end
|
||||
end
|
||||
|
@ -210,7 +224,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
defp represent_service_actor(%User{} = user, conn) do
|
||||
with {:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
|
@ -231,7 +245,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def whoami(%{assigns: %{user: %User{} = user}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
end
|
||||
|
||||
|
@ -240,7 +254,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do
|
||||
if nickname == user.nickname do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
|
||||
else
|
||||
err =
|
||||
|
@ -295,42 +309,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
def update_outbox(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
if nickname == user.nickname do
|
||||
actor = user.ap_id()
|
||||
actor = user.ap_id()
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["id"])
|
||||
|> Map.put("actor", actor)
|
||||
|> Transmogrifier.fix_addressing()
|
||||
|
||||
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity.data["id"])
|
||||
|> json(activity.data)
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(message)
|
||||
end
|
||||
else
|
||||
err =
|
||||
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
||||
nickname: nickname,
|
||||
as_nickname: user.nickname
|
||||
)
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["id"])
|
||||
|> Map.put("actor", actor)
|
||||
|> Transmogrifier.fix_addressing()
|
||||
|
||||
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(err)
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity.data["id"])
|
||||
|> json(activity.data)
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(message)
|
||||
end
|
||||
end
|
||||
|
||||
def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do
|
||||
err =
|
||||
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
||||
nickname: nickname,
|
||||
as_nickname: user.nickname
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(err)
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|
|
|
@ -25,11 +25,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
|||
defp score_displayname(_), do: 0.0
|
||||
|
||||
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
||||
# nickname will always be a binary string because it's generated by Pleroma.
|
||||
# nickname will be a binary string except when following a relay
|
||||
nick_score =
|
||||
nickname
|
||||
|> String.downcase()
|
||||
|> score_nickname()
|
||||
if is_binary(nickname) do
|
||||
nickname
|
||||
|> String.downcase()
|
||||
|> score_nickname()
|
||||
else
|
||||
0.0
|
||||
end
|
||||
|
||||
# displayname will either be a binary string or nil, if a displayname isn't set.
|
||||
name_score =
|
||||
|
|
|
@ -50,9 +50,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
|
||||
|
||||
date =
|
||||
NaiveDateTime.utc_now()
|
||||
|> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
|
||||
date = Pleroma.Signature.signed_date()
|
||||
|
||||
signature =
|
||||
Pleroma.Signature.sign(actor, %{
|
||||
|
|
|
@ -22,13 +22,7 @@ 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}
|
||||
error -> format_error(error)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,16 +31,11 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
with %User{} = local_user <- get_actor(),
|
||||
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),
|
||||
{:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do
|
||||
User.unfollow(local_user, target_user)
|
||||
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}
|
||||
error -> format_error(error)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,11 +45,16 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
%Object{} = object <- Object.normalize(activity) do
|
||||
ActivityPub.announce(user, object, nil, true, false)
|
||||
else
|
||||
e ->
|
||||
Logger.error("error: #{inspect(e)}")
|
||||
{:error, inspect(e)}
|
||||
error -> format_error(error)
|
||||
end
|
||||
end
|
||||
|
||||
def publish(_), do: {:error, "Not implemented"}
|
||||
|
||||
defp format_error({:error, error}), do: format_error(error)
|
||||
|
||||
defp format_error(error) do
|
||||
Logger.error("error: #{inspect(error)}")
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -464,8 +464,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
|
||||
_options
|
||||
) do
|
||||
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
|
||||
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
|
||||
with %User{local: true} = followed <-
|
||||
User.get_cached_by_ap_id(Containment.get_actor(%{"actor" => followed})),
|
||||
{:ok, %User{} = follower} <-
|
||||
User.get_or_fetch_by_ap_id(Containment.get_actor(%{"actor" => follower})),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
|
||||
{_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
|
||||
|
@ -598,14 +600,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|
||||
banner = new_user_data[:info][:banner]
|
||||
locked = new_user_data[:info][:locked] || false
|
||||
attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
|
||||
|
||||
fields =
|
||||
attachment
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
update_data =
|
||||
new_user_data
|
||||
|> Map.take([:name, :bio, :avatar])
|
||||
|> Map.put(:info, %{banner: banner, locked: locked})
|
||||
|> Map.put(:info, %{banner: banner, locked: locked, fields: fields})
|
||||
|
||||
actor
|
||||
|> User.upgrade_changeset(update_data)
|
||||
|> User.upgrade_changeset(update_data, true)
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
ActivityPub.update(%{
|
||||
|
|
|
@ -166,6 +166,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Enqueues an activity for federation if it's local
|
||||
"""
|
||||
@spec maybe_federate(any()) :: :ok
|
||||
def maybe_federate(%Activity{local: true} = activity) do
|
||||
if Pleroma.Config.get!([:instance, :federating]) do
|
||||
priority =
|
||||
|
@ -256,46 +257,27 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Returns an existing like if a user already liked an object
|
||||
"""
|
||||
@spec get_existing_like(String.t(), map()) :: Activity.t() | nil
|
||||
def get_existing_like(actor, %{data: %{"id" => id}}) do
|
||||
query =
|
||||
from(
|
||||
activity in Activity,
|
||||
where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^id
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
||||
)
|
||||
|
||||
Repo.one(query)
|
||||
actor
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_object_id(id)
|
||||
|> Activity.Queries.by_type("Like")
|
||||
|> Activity.Queries.limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns like activities targeting an object
|
||||
"""
|
||||
def get_object_likes(%{data: %{"id" => id}}) do
|
||||
query =
|
||||
from(
|
||||
activity in Activity,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^id
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
||||
)
|
||||
|
||||
Repo.all(query)
|
||||
id
|
||||
|> Activity.Queries.by_object_id()
|
||||
|> Activity.Queries.by_type("Like")
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec make_like_data(User.t(), map(), String.t()) :: map()
|
||||
def make_like_data(
|
||||
%User{ap_id: ap_id} = actor,
|
||||
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
||||
|
@ -315,7 +297,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> List.delete(actor.ap_id)
|
||||
|> List.delete(object_actor.follower_address)
|
||||
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Like",
|
||||
"actor" => ap_id,
|
||||
"object" => id,
|
||||
|
@ -323,38 +305,49 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"cc" => cc,
|
||||
"context" => object.data["context"]
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
@spec update_element_in_object(String.t(), list(any), Object.t()) ::
|
||||
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_element_in_object(property, element, object) do
|
||||
with new_data <-
|
||||
object.data
|
||||
|> Map.put("#{property}_count", length(element))
|
||||
|> Map.put("#{property}s", element),
|
||||
changeset <- Changeset.change(object, data: new_data),
|
||||
{:ok, object} <- Object.update_and_set_cache(changeset) do
|
||||
{:ok, object}
|
||||
end
|
||||
data =
|
||||
Map.merge(
|
||||
object.data,
|
||||
%{"#{property}_count" => length(element), "#{property}s" => element}
|
||||
)
|
||||
|
||||
object
|
||||
|> Changeset.change(data: data)
|
||||
|> Object.update_and_set_cache()
|
||||
end
|
||||
|
||||
def update_likes_in_object(likes, object) do
|
||||
@spec add_like_to_object(Activity.t(), Object.t()) ::
|
||||
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
[actor | fetch_likes(object)]
|
||||
|> Enum.uniq()
|
||||
|> update_likes_in_object(object)
|
||||
end
|
||||
|
||||
@spec remove_like_from_object(Activity.t(), Object.t()) ::
|
||||
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
object
|
||||
|> fetch_likes()
|
||||
|> List.delete(actor)
|
||||
|> update_likes_in_object(object)
|
||||
end
|
||||
|
||||
defp update_likes_in_object(likes, object) do
|
||||
update_element_in_object("like", likes, object)
|
||||
end
|
||||
|
||||
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
|
||||
|
||||
with likes <- [actor | likes] |> Enum.uniq() do
|
||||
update_likes_in_object(likes, object)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
|
||||
|
||||
with likes <- likes |> List.delete(actor) do
|
||||
update_likes_in_object(likes, object)
|
||||
defp fetch_likes(object) do
|
||||
if is_list(object.data["likes"]) do
|
||||
object.data["likes"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -405,7 +398,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
%User{ap_id: followed_id} = _followed,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Follow",
|
||||
"actor" => follower_id,
|
||||
"to" => [followed_id],
|
||||
|
@ -413,10 +406,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"object" => followed_id,
|
||||
"state" => "pending"
|
||||
}
|
||||
|
||||
data = if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|
||||
data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||
|
@ -478,7 +468,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
activity_id,
|
||||
false
|
||||
) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Announce",
|
||||
"actor" => ap_id,
|
||||
"object" => id,
|
||||
|
@ -486,8 +476,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"cc" => [],
|
||||
"context" => object.data["context"]
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
def make_announce_data(
|
||||
|
@ -496,7 +485,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
activity_id,
|
||||
true
|
||||
) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Announce",
|
||||
"actor" => ap_id,
|
||||
"object" => id,
|
||||
|
@ -504,8 +493,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => object.data["context"]
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -516,7 +504,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
%Activity{data: %{"context" => context}} = activity,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"actor" => ap_id,
|
||||
"object" => activity.data,
|
||||
|
@ -524,8 +512,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => context
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
def make_unlike_data(
|
||||
|
@ -533,7 +520,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
%Activity{data: %{"context" => context}} = activity,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"actor" => ap_id,
|
||||
"object" => activity.data,
|
||||
|
@ -541,8 +528,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => context
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
def add_announce_to_object(
|
||||
|
@ -573,14 +559,13 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
#### Unfollow-related helpers
|
||||
|
||||
def make_unfollow_data(follower, followed, follow_activity, activity_id) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"actor" => follower.ap_id,
|
||||
"to" => [followed.ap_id],
|
||||
"object" => follow_activity.data
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
#### Block-related helpers
|
||||
|
@ -610,25 +595,23 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
end
|
||||
|
||||
def make_block_data(blocker, blocked, activity_id) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Block",
|
||||
"actor" => blocker.ap_id,
|
||||
"to" => [blocked.ap_id],
|
||||
"object" => blocked.ap_id
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
def make_unblock_data(blocker, blocked, block_activity, activity_id) do
|
||||
data = %{
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"actor" => blocker.ap_id,
|
||||
"to" => [blocked.ap_id],
|
||||
"object" => block_activity.data
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
|> maybe_put("id", activity_id)
|
||||
end
|
||||
|
||||
#### Create-related helpers
|
||||
|
@ -799,4 +782,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
defp maybe_put(map, _key, nil), do: map
|
||||
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||
end
|
||||
|
|
|
@ -80,6 +80,17 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
|> Transmogrifier.add_emoji_tags()
|
||||
|> Map.get("tag", [])
|
||||
|
||||
fields =
|
||||
user.info
|
||||
|> User.Info.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => Pleroma.HTML.strip_tags(name),
|
||||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
}
|
||||
end)
|
||||
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
"type" => "Person",
|
||||
|
@ -98,6 +109,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"publicKeyPem" => public_key
|
||||
},
|
||||
"endpoints" => endpoints,
|
||||
"attachment" => fields,
|
||||
"tag" => (user.info.source_data["tag"] || []) ++ user_tags
|
||||
}
|
||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -12,6 +13,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.AdminAPI.Config
|
||||
alias Pleroma.Web.AdminAPI.ConfigView
|
||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||
alias Pleroma.Web.AdminAPI.ReportView
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
@ -25,52 +27,113 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
action_fallback(:errors)
|
||||
|
||||
def user_delete(conn, %{"nickname" => nickname}) do
|
||||
User.get_cached_by_nickname(nickname)
|
||||
|> User.delete()
|
||||
def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
User.delete(user)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: user,
|
||||
action: "delete"
|
||||
})
|
||||
|
||||
conn
|
||||
|> json(nickname)
|
||||
end
|
||||
|
||||
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.follow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "follow"
|
||||
})
|
||||
end
|
||||
|
||||
conn
|
||||
|> json("ok")
|
||||
end
|
||||
|
||||
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||
"follower" => follower_nick,
|
||||
"followed" => followed_nick
|
||||
}) do
|
||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||
User.unfollow(follower, followed)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: followed,
|
||||
follower: follower,
|
||||
action: "unfollow"
|
||||
})
|
||||
end
|
||||
|
||||
conn
|
||||
|> json("ok")
|
||||
end
|
||||
|
||||
def user_create(
|
||||
conn,
|
||||
%{"nickname" => nickname, "email" => email, "password" => password}
|
||||
) do
|
||||
user_data = %{
|
||||
nickname: nickname,
|
||||
name: nickname,
|
||||
email: email,
|
||||
password: password,
|
||||
password_confirmation: password,
|
||||
bio: "."
|
||||
}
|
||||
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||
changesets =
|
||||
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
||||
user_data = %{
|
||||
nickname: nickname,
|
||||
name: nickname,
|
||||
email: email,
|
||||
password: password,
|
||||
password_confirmation: password,
|
||||
bio: "."
|
||||
}
|
||||
|
||||
changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
{:ok, user} = User.register(changeset)
|
||||
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
end)
|
||||
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
||||
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
||||
end)
|
||||
|
||||
conn
|
||||
|> json(user.nickname)
|
||||
case Pleroma.Repo.transaction(changesets) do
|
||||
{:ok, users} ->
|
||||
res =
|
||||
users
|
||||
|> Map.values()
|
||||
|> Enum.map(fn user ->
|
||||
{:ok, user} = User.post_register_action(user)
|
||||
|
||||
user
|
||||
end)
|
||||
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subjects: Map.values(users),
|
||||
action: "create"
|
||||
})
|
||||
|
||||
conn
|
||||
|> json(res)
|
||||
|
||||
{:error, id, changeset, _} ->
|
||||
res =
|
||||
Enum.map(changesets.operations, fn
|
||||
{current_id, {:changeset, _current_changeset, _}} when current_id == id ->
|
||||
AccountView.render("create-error.json", %{changeset: changeset})
|
||||
|
||||
{_, {:changeset, current_changeset, _}} ->
|
||||
AccountView.render("create-error.json", %{changeset: current_changeset})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> put_status(:conflict)
|
||||
|> json(res)
|
||||
end
|
||||
end
|
||||
|
||||
def user_show(conn, %{"nickname" => nickname}) do
|
||||
|
@ -101,23 +164,47 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def user_toggle_activation(conn, %{"nickname" => nickname}) do
|
||||
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
||||
|
||||
action = if user.info.deactivated, do: "activate", else: "deactivate"
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: user,
|
||||
action: action
|
||||
})
|
||||
|
||||
conn
|
||||
|> json(AccountView.render("show.json", %{user: updated_user}))
|
||||
end
|
||||
|
||||
def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
with {:ok, _} <- User.tag(nicknames, tags),
|
||||
do: json_response(conn, :no_content, "")
|
||||
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
nicknames: nicknames,
|
||||
tags: tags,
|
||||
action: "tag"
|
||||
})
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
end
|
||||
|
||||
def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
with {:ok, _} <- User.untag(nicknames, tags),
|
||||
do: json_response(conn, :no_content, "")
|
||||
def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
with {:ok, _} <- User.untag(nicknames, tags) do
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
nicknames: nicknames,
|
||||
tags: tags,
|
||||
action: "untag"
|
||||
})
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
end
|
||||
|
||||
def list_users(conn, params) do
|
||||
|
@ -158,7 +245,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|> Enum.into(%{}, &{&1, true})
|
||||
end
|
||||
|
||||
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
||||
def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||
"permission_group" => permission_group,
|
||||
"nickname" => nickname
|
||||
})
|
||||
when permission_group in ["moderator", "admin"] do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
|
@ -173,6 +263,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
action: "grant",
|
||||
actor: admin,
|
||||
subject: user,
|
||||
permission: permission_group
|
||||
})
|
||||
|
||||
{:ok, _user} = User.update_and_set_cache(cng)
|
||||
|
||||
json(conn, info)
|
||||
|
@ -193,7 +290,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
|
||||
def right_delete(
|
||||
%{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
|
||||
%{assigns: %{user: %User{:nickname => admin_nickname} = admin}} = conn,
|
||||
%{
|
||||
"permission_group" => permission_group,
|
||||
"nickname" => nickname
|
||||
|
@ -217,6 +314,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
{:ok, _user} = User.update_and_set_cache(cng)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
action: "revoke",
|
||||
actor: admin,
|
||||
subject: user,
|
||||
permission: permission_group
|
||||
})
|
||||
|
||||
json(conn, info)
|
||||
end
|
||||
end
|
||||
|
@ -225,15 +329,33 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
render_error(conn, :not_found, "No such permission_group")
|
||||
end
|
||||
|
||||
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
|
||||
def set_activation_status(%{assigns: %{user: admin}} = conn, %{
|
||||
"nickname" => nickname,
|
||||
"status" => status
|
||||
}) do
|
||||
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
||||
%User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, _} <- User.deactivate(user, !status),
|
||||
do: json_response(conn, :no_content, "")
|
||||
{:ok, _} <- User.deactivate(user, !status) do
|
||||
action = if(user.info.deactivated, do: "activate", else: "deactivate")
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: user,
|
||||
action: action
|
||||
})
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
end
|
||||
end
|
||||
|
||||
def relay_follow(conn, %{"relay_url" => target}) do
|
||||
def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
||||
with {:ok, _message} <- Relay.follow(target) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "relay_follow",
|
||||
actor: admin,
|
||||
target: target
|
||||
})
|
||||
|
||||
json(conn, target)
|
||||
else
|
||||
_ ->
|
||||
|
@ -243,8 +365,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def relay_unfollow(conn, %{"relay_url" => target}) do
|
||||
def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
||||
with {:ok, _message} <- Relay.unfollow(target) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "relay_unfollow",
|
||||
actor: admin,
|
||||
target: target
|
||||
})
|
||||
|
||||
json(conn, target)
|
||||
else
|
||||
_ ->
|
||||
|
@ -335,8 +463,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def report_update_state(conn, %{"id" => id, "state" => state}) do
|
||||
def report_update_state(%{assigns: %{user: admin}} = conn, %{"id" => id, "state" => state}) do
|
||||
with {:ok, report} <- CommonAPI.update_report_state(id, state) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_update",
|
||||
actor: admin,
|
||||
subject: report
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("show.json", %{report: report})
|
||||
|
@ -353,6 +487,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(user, params)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_response",
|
||||
actor: user,
|
||||
subject: activity,
|
||||
text: params["status"]
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
|
@ -365,8 +506,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def status_update(conn, %{"id" => id} = params) do
|
||||
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
action: "status_update",
|
||||
actor: admin,
|
||||
subject: activity,
|
||||
sensitive: sensitive,
|
||||
visibility: params["visibility"]
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
|
@ -375,10 +526,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "status_delete",
|
||||
actor: user,
|
||||
subject_id: id
|
||||
})
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
||||
def list_log(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
log = ModerationLog.get_all(page, page_size)
|
||||
|
||||
conn
|
||||
|> put_view(ModerationLogView)
|
||||
|> render("index.json", %{log: log})
|
||||
end
|
||||
|
||||
def migrate_to_db(conn, _params) do
|
||||
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
|
||||
json(conn, %{})
|
||||
|
|
|
@ -52,4 +52,50 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
invites: render_many(invites, AccountView, "invite.json", as: :invite)
|
||||
}
|
||||
end
|
||||
|
||||
def render("created.json", %{user: user}) do
|
||||
%{
|
||||
type: "success",
|
||||
code: 200,
|
||||
data: %{
|
||||
nickname: user.nickname,
|
||||
email: user.email
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def render("create-error.json", %{changeset: %Ecto.Changeset{changes: changes, errors: errors}}) do
|
||||
%{
|
||||
type: "error",
|
||||
code: 409,
|
||||
error: parse_error(errors),
|
||||
data: %{
|
||||
nickname: Map.get(changes, :nickname),
|
||||
email: Map.get(changes, :email)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp parse_error([]), do: ""
|
||||
|
||||
defp parse_error(errors) do
|
||||
## when nickname is duplicate ap_id constraint error is raised
|
||||
nickname_error = Keyword.get(errors, :nickname) || Keyword.get(errors, :ap_id)
|
||||
email_error = Keyword.get(errors, :email)
|
||||
password_error = Keyword.get(errors, :password)
|
||||
|
||||
cond do
|
||||
nickname_error ->
|
||||
"nickname #{elem(nickname_error, 0)}"
|
||||
|
||||
email_error ->
|
||||
"email #{elem(email_error, 0)}"
|
||||
|
||||
password_error ->
|
||||
"password #{elem(password_error, 0)}"
|
||||
|
||||
true ->
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.ModerationLogView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.ModerationLog
|
||||
|
||||
def render("index.json", %{log: log}) do
|
||||
render_many(log, __MODULE__, "show.json", as: :log_entry)
|
||||
end
|
||||
|
||||
def render("show.json", %{log_entry: log_entry}) do
|
||||
time =
|
||||
log_entry.inserted_at
|
||||
|> DateTime.from_naive!("Etc/UTC")
|
||||
|> DateTime.to_unix()
|
||||
|
||||
%{
|
||||
data: log_entry.data,
|
||||
time: time,
|
||||
message: ModerationLog.get_log_entry_message(log_entry)
|
||||
}
|
||||
end
|
||||
end
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.CommonAPI do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Object
|
||||
|
@ -200,6 +201,23 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_expiry_date({:ok, nil} = res), do: res
|
||||
|
||||
defp check_expiry_date({:ok, in_seconds}) do
|
||||
expiry = NaiveDateTime.utc_now() |> NaiveDateTime.add(in_seconds)
|
||||
|
||||
if ActivityExpiration.expires_late_enough?(expiry) do
|
||||
{:ok, expiry}
|
||||
else
|
||||
{:error, "Expiry date is too soon"}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_expiry_date(expiry_str) do
|
||||
Ecto.Type.cast(:integer, expiry_str)
|
||||
|> check_expiry_date()
|
||||
end
|
||||
|
||||
def post(user, %{"status" => status} = data) do
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
|
@ -226,6 +244,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
context <- make_context(in_reply_to, in_reply_to_conversation),
|
||||
cw <- data["spoiler_text"] || "",
|
||||
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
|
||||
{:ok, expires_at} <- check_expiry_date(data["expires_in"]),
|
||||
full_payload <- String.trim(status <> cw),
|
||||
:ok <- validate_character_limit(full_payload, attachments, limit),
|
||||
object <-
|
||||
|
@ -251,15 +270,24 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||
direct? = visibility == "direct"
|
||||
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => direct?}
|
||||
}
|
||||
|> maybe_add_list_data(user, visibility)
|
||||
|> ActivityPub.create(preview?)
|
||||
result =
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => direct?}
|
||||
}
|
||||
|> maybe_add_list_data(user, visibility)
|
||||
|> ActivityPub.create(preview?)
|
||||
|
||||
if expires_at do
|
||||
with {:ok, activity} <- result do
|
||||
{:ok, _} = ActivityExpiration.create(activity, expires_at)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
else
|
||||
{:private_to_public, true} ->
|
||||
{:error, dgettext("errors", "The message visibility must be direct")}
|
||||
|
|
|
@ -93,8 +93,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
Activity.t() | nil,
|
||||
String.t(),
|
||||
Participation.t() | nil
|
||||
) ::
|
||||
{list(String.t()), list(String.t())}
|
||||
) :: {list(String.t()), list(String.t())}
|
||||
|
||||
def get_to_and_cc(_, _, _, _, %Participation{} = participation) do
|
||||
participation = Repo.preload(participation, :recipients)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# 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.FallbackController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
|
||||
error_message =
|
||||
changeset
|
||||
|> Ecto.Changeset.traverse_errors(fn {message, _opt} -> message end)
|
||||
|> Enum.map_join(", ", fn {_k, v} -> v end)
|
||||
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
|
||||
def call(conn, {:error, :not_found}) do
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
|
||||
def call(conn, {:error, error_message}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(dgettext("errors", "Something went wrong"))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
# 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.ListController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
plug(:list_by_id_and_user when action not in [:index, :create])
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
# GET /api/v1/lists
|
||||
def index(%{assigns: %{user: user}} = conn, opts) do
|
||||
lists = Pleroma.List.for_user(user, opts)
|
||||
render(conn, "index.json", lists: lists)
|
||||
end
|
||||
|
||||
# POST /api/v1/lists
|
||||
def create(%{assigns: %{user: user}} = conn, %{"title" => title}) do
|
||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
end
|
||||
|
||||
# GET /api/v1/lists/:id
|
||||
def show(%{assigns: %{list: list}} = conn, _) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
|
||||
# PUT /api/v1/lists/:id
|
||||
def update(%{assigns: %{list: list}} = conn, %{"title" => title}) do
|
||||
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
||||
render(conn, "show.json", list: list)
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /api/v1/lists/:id
|
||||
def delete(%{assigns: %{list: list}} = conn, _) do
|
||||
with {:ok, _list} <- Pleroma.List.delete(list) do
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
||||
# GET /api/v1/lists/:id/accounts
|
||||
def list_accounts(%{assigns: %{user: user, list: list}} = conn, _) do
|
||||
with {:ok, users} <- Pleroma.List.get_following(list) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", for: user, users: users, as: :user)
|
||||
end
|
||||
end
|
||||
|
||||
# POST /api/v1/lists/:id/accounts
|
||||
def add_to_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
||||
Enum.each(account_ids, fn account_id ->
|
||||
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||
Pleroma.List.follow(list, followed)
|
||||
end
|
||||
end)
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
# DELETE /api/v1/lists/:id/accounts
|
||||
def remove_from_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
||||
Enum.each(account_ids, fn account_id ->
|
||||
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||
Pleroma.List.unfollow(list, followed)
|
||||
end
|
||||
end)
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
defp list_by_id_and_user(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
|
||||
case Pleroma.List.get(id, user) do
|
||||
%Pleroma.List{} = list -> assign(conn, :list, list)
|
||||
nil -> conn |> render_error(:not_found, "List not found") |> halt()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
action_fallback(:errors)
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
def create_app(conn, params) do
|
||||
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||
|
@ -138,7 +138,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
||||
|
||||
user_info_emojis =
|
||||
((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text))
|
||||
user.info
|
||||
|> Map.get(:emoji, [])
|
||||
|> Enum.concat(Formatter.get_emoji_map(emojis_text))
|
||||
|> Enum.dedup()
|
||||
|
||||
info_params =
|
||||
|
@ -157,6 +159,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end)
|
||||
end)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "fields", :fields, fn fields ->
|
||||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
||||
|
||||
{:ok, fields}
|
||||
end)
|
||||
|> add_if_present(params, "fields", :raw_fields)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
||||
{:ok, Map.merge(user.info.pleroma_settings_store, value)}
|
||||
end)
|
||||
|
@ -181,7 +189,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
info_cng = User.Info.profile_update(user.info, info_params)
|
||||
|
||||
with changeset <- User.update_changeset(user, user_params),
|
||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
if original_user != user do
|
||||
CommonAPI.update(user)
|
||||
|
@ -217,7 +225,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||
with new_info <- %{"banner" => %{}},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
CommonAPI.update(user)
|
||||
|
||||
|
@ -229,7 +237,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||
new_info <- %{"banner" => object.data},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
CommonAPI.update(user)
|
||||
%{"url" => [%{"href" => href} | _]} = object.data
|
||||
|
@ -241,7 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||
with new_info <- %{"background" => %{}},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
json(conn, %{url: nil})
|
||||
end
|
||||
|
@ -251,7 +259,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
||||
new_info <- %{"background" => object.data},
|
||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
%{"url" => [%{"href" => href} | _]} = object.data
|
||||
|
||||
|
@ -798,8 +806,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
user_changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_embed(:info, info_changeset)
|
||||
|> Changeset.change()
|
||||
|> Changeset.put_embed(:info, info_changeset)
|
||||
|
||||
{:ok, _user} = User.update_and_set_cache(user_changeset)
|
||||
|
||||
|
@ -1197,88 +1205,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def get_lists(%{assigns: %{user: user}} = conn, opts) do
|
||||
lists = Pleroma.List.for_user(user, opts)
|
||||
res = ListView.render("lists.json", lists: lists)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user) do
|
||||
res = ListView.render("list.json", list: list)
|
||||
json(conn, res)
|
||||
else
|
||||
_e -> render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
end
|
||||
|
||||
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do
|
||||
lists = Pleroma.List.get_lists_account_belongs(user, account_id)
|
||||
res = ListView.render("lists.json", lists: lists)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
{:ok, _list} <- Pleroma.List.delete(list) do
|
||||
json(conn, %{})
|
||||
else
|
||||
_e ->
|
||||
json(conn, dgettext("errors", "error"))
|
||||
end
|
||||
end
|
||||
|
||||
def create_list(%{assigns: %{user: user}} = conn, %{"title" => title}) do
|
||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
||||
res = ListView.render("list.json", list: list)
|
||||
json(conn, res)
|
||||
end
|
||||
end
|
||||
|
||||
def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do
|
||||
accounts
|
||||
|> Enum.each(fn account_id ->
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
%User{} = followed <- User.get_cached_by_id(account_id) do
|
||||
Pleroma.List.follow(list, followed)
|
||||
end
|
||||
end)
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do
|
||||
accounts
|
||||
|> Enum.each(fn account_id ->
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
%User{} = followed <- User.get_cached_by_id(account_id) do
|
||||
Pleroma.List.unfollow(list, followed)
|
||||
end
|
||||
end)
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
{:ok, users} = Pleroma.List.get_following(list) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", %{for: user, users: users, as: :user})
|
||||
end
|
||||
end
|
||||
|
||||
def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title}) do
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
{:ok, list} <- Pleroma.List.rename(list, title) do
|
||||
res = ListView.render("list.json", list: list)
|
||||
json(conn, res)
|
||||
else
|
||||
_e ->
|
||||
json(conn, dgettext("errors", "error"))
|
||||
end
|
||||
end
|
||||
|
||||
def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
||||
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
|
||||
params =
|
||||
|
@ -1412,8 +1344,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
||||
info_cng = User.Info.mastodon_settings_update(user.info, settings)
|
||||
|
||||
with changeset <- Ecto.Changeset.change(user),
|
||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||
with changeset <- Changeset.change(user),
|
||||
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
json(conn, %{})
|
||||
else
|
||||
|
@ -1477,7 +1409,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
{:ok, app}
|
||||
else
|
||||
app
|
||||
|> Ecto.Changeset.change(%{scopes: scopes})
|
||||
|> Changeset.change(%{scopes: scopes})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
|
@ -1579,35 +1511,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, %{})
|
||||
end
|
||||
|
||||
# fallback action
|
||||
#
|
||||
def errors(conn, {:error, %Changeset{} = changeset}) do
|
||||
error_message =
|
||||
changeset
|
||||
|> Changeset.traverse_errors(fn {message, _opt} -> message end)
|
||||
|> Enum.map_join(", ", fn {_k, v} -> v end)
|
||||
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
|
||||
def errors(conn, {:error, error_message}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(dgettext("errors", "Something went wrong"))
|
||||
end
|
||||
|
||||
def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||
suggestions = Config.get(:suggestions)
|
||||
|
|
@ -64,8 +64,6 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
|||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(dgettext("errors", "Something went wrong"))
|
||||
Pleroma.Web.MastodonAPI.FallbackController.call(conn, nil)
|
||||
end
|
||||
end
|
|
@ -94,12 +94,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
end)
|
||||
|
||||
fields =
|
||||
(user.info.source_data["attachment"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
user.info
|
||||
|> User.Info.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => Pleroma.HTML.strip_tags(name),
|
||||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
}
|
||||
end)
|
||||
|
||||
raw_fields = Map.get(user.info, :raw_fields, [])
|
||||
|
||||
bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
|
||||
|
||||
relationship = render("relationship.json", %{user: opts[:for], target: user})
|
||||
|
||||
%{
|
||||
|
@ -124,6 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
source: %{
|
||||
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||
sensitive: false,
|
||||
fields: raw_fields,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ defmodule Pleroma.Web.MastodonAPI.ListView do
|
|||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.MastodonAPI.ListView
|
||||
|
||||
def render("lists.json", %{lists: lists} = opts) do
|
||||
render_many(lists, ListView, "list.json", opts)
|
||||
def render("index.json", %{lists: lists} = opts) do
|
||||
render_many(lists, ListView, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("list.json", %{list: list}) do
|
||||
def render("show.json", %{list: list}) do
|
||||
%{
|
||||
id: to_string(list.id),
|
||||
title: list.title
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.HTML
|
||||
|
@ -177,6 +178,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil
|
||||
|
||||
client_posted_this_activity = opts[:for] && user.id == opts[:for].id
|
||||
|
||||
expires_at =
|
||||
with true <- client_posted_this_activity,
|
||||
expiration when not is_nil(expiration) <-
|
||||
ActivityExpiration.get_by_activity_id(activity.id) do
|
||||
expiration.scheduled_at
|
||||
end
|
||||
|
||||
thread_muted? =
|
||||
case activity.thread_muted? do
|
||||
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
|
||||
|
@ -288,6 +298,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
|
||||
content: %{"text/plain" => content_plaintext},
|
||||
spoiler_text: %{"text/plain" => summary_plaintext},
|
||||
expires_at: expires_at,
|
||||
direct_conversation_id: direct_conversation_id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
alias Pleroma.Web.Router
|
||||
alias Pleroma.Web.XML
|
||||
|
||||
plug(
|
||||
Pleroma.Plugs.RateLimiter,
|
||||
{:ap_routes, params: ["uuid"]} when action in [:object, :activity]
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
||||
|
||||
plug(
|
||||
|
@ -32,8 +37,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
action_fallback(:errors)
|
||||
|
||||
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
|
||||
with {_, %User{} = user} <-
|
||||
{:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
|
||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
|
||||
RedirectController.redirector_with_meta(conn, %{user: user})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -133,6 +133,10 @@ defmodule Pleroma.Web.Router do
|
|||
})
|
||||
end
|
||||
|
||||
pipeline :http_signature do
|
||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
pipe_through(:pleroma_api)
|
||||
|
||||
|
@ -155,7 +159,7 @@ defmodule Pleroma.Web.Router do
|
|||
post("/users/unfollow", AdminAPIController, :user_unfollow)
|
||||
|
||||
delete("/users", AdminAPIController, :user_delete)
|
||||
post("/users", AdminAPIController, :user_create)
|
||||
post("/users", AdminAPIController, :users_create)
|
||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||
put("/users/tag", AdminAPIController, :tag_users)
|
||||
delete("/users/tag", AdminAPIController, :untag_users)
|
||||
|
@ -198,6 +202,8 @@ defmodule Pleroma.Web.Router do
|
|||
post("/config", AdminAPIController, :config_update)
|
||||
get("/config/migrate_to_db", AdminAPIController, :migrate_to_db)
|
||||
get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
|
||||
|
||||
get("/moderation_log", AdminAPIController, :list_log)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.TwitterAPI do
|
||||
|
@ -306,9 +312,9 @@ defmodule Pleroma.Web.Router do
|
|||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
||||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
||||
|
||||
get("/lists", MastodonAPIController, :get_lists)
|
||||
get("/lists/:id", MastodonAPIController, :get_list)
|
||||
get("/lists/:id/accounts", MastodonAPIController, :list_accounts)
|
||||
get("/lists", ListController, :index)
|
||||
get("/lists/:id", ListController, :show)
|
||||
get("/lists/:id/accounts", ListController, :list_accounts)
|
||||
|
||||
get("/domain_blocks", MastodonAPIController, :domain_blocks)
|
||||
|
||||
|
@ -349,12 +355,12 @@ defmodule Pleroma.Web.Router do
|
|||
post("/media", MastodonAPIController, :upload)
|
||||
put("/media/:id", MastodonAPIController, :update_media)
|
||||
|
||||
delete("/lists/:id", MastodonAPIController, :delete_list)
|
||||
post("/lists", MastodonAPIController, :create_list)
|
||||
put("/lists/:id", MastodonAPIController, :rename_list)
|
||||
delete("/lists/:id", ListController, :delete)
|
||||
post("/lists", ListController, :create)
|
||||
put("/lists/:id", ListController, :update)
|
||||
|
||||
post("/lists/:id/accounts", MastodonAPIController, :add_to_list)
|
||||
delete("/lists/:id/accounts", MastodonAPIController, :remove_from_list)
|
||||
post("/lists/:id/accounts", ListController, :add_to_list)
|
||||
delete("/lists/:id/accounts", ListController, :remove_from_list)
|
||||
|
||||
post("/filters", MastodonAPIController, :create_filter)
|
||||
get("/filters/:id", MastodonAPIController, :get_filter)
|
||||
|
@ -686,7 +692,14 @@ defmodule Pleroma.Web.Router do
|
|||
pipe_through(:ap_service_actor)
|
||||
|
||||
get("/", ActivityPubController, :relay)
|
||||
post("/inbox", ActivityPubController, :inbox)
|
||||
|
||||
scope [] do
|
||||
pipe_through(:http_signature)
|
||||
post("/inbox", ActivityPubController, :inbox)
|
||||
end
|
||||
|
||||
get("/following", ActivityPubController, :following, assigns: %{relay: true})
|
||||
get("/followers", ActivityPubController, :followers, assigns: %{relay: true})
|
||||
end
|
||||
|
||||
scope "/internal/fetch", Pleroma.Web.ActivityPub do
|
||||
|
|
|
@ -1,20 +1,568 @@
|
|||
<h1>Hey <%= @user.nickname %>, here is what you've missed!</h1>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<h2>New Mentions:</h2>
|
||||
<ul>
|
||||
<%= for %{data: mention, object: object, from: from} <- @mentions do %>
|
||||
<li><%= link from.nickname, to: mention.activity.actor %>: <%= raw object.data["content"] %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
xmlns:v="urn:schemas-microsoft-com:vml">
|
||||
|
||||
<%= if @followers != [] do %>
|
||||
<h2><%= length(@followers) %> New Followers:</h2>
|
||||
<ul>
|
||||
<%= for %{data: follow, from: from} <- @followers do %>
|
||||
<li><%= link from.nickname, to: follow.activity.actor %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
<head>
|
||||
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
|
||||
<meta content="width=device-width" name="viewport" />
|
||||
<!--[if !mso]><!-->
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||
<!--<![endif]-->
|
||||
<title><%= @email.subject %><</title>
|
||||
<!--[if !mso]><!-->
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
<p>You have received this email because you have signed up to receive digest emails from <b><%= @instance %></b> Pleroma instance.</p>
|
||||
<p>The email address you are subscribed as is <%= @user.email %>. To unsubscribe, please go <%= link "here", to: @unsubscribe_link %>.</p>
|
||||
a {
|
||||
|
||||
color: <%= @styling.link_color %>;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
tr {
|
||||
vertical-align: top;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
* {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a[x-apple-data-detectors=true] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<style id="media-query" type="text/css">
|
||||
@media (max-width: 610px) {
|
||||
|
||||
.block-grid,
|
||||
.col {
|
||||
min-width: 320px !important;
|
||||
max-width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.block-grid {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.col>div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.no-stack .col {
|
||||
min-width: 0 !important;
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
.no-stack.two-up .col {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num8 {
|
||||
width: 66% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num4 {
|
||||
width: 33% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num3 {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num6 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.no-stack .col.num9 {
|
||||
width: 75% !important;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: <%= @styling.background_color %>;">
|
||||
<!--[if IE]><div class="ie-browser"><![endif]-->
|
||||
<table bgcolor="<%= @styling.background_color %>" cellpadding="0" cellspacing="0" class="nl-container" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: <%= @styling.background_color %>; width: 100%;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td style="word-break: break-word; vertical-align: top;" valign="top">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:<%= @styling.background_color %>"><![endif]-->
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<div align="center" class="img-container center"
|
||||
style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img
|
||||
align="center" alt="Image" border="0" class="center" src="cid:logo.png"
|
||||
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;"
|
||||
title="Image" height="80" />
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;">
|
||||
<p style="line-height: 36px; text-align: center; margin: 0;"><span
|
||||
style="font-size: 30px; color: <%= @styling.header_color %>;">Hey <%= @user.nickname %>, here is what you've missed!</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;">
|
||||
<!--<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td class="divider_inner"
|
||||
style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;"
|
||||
valign="top">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content"
|
||||
height="0" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td height="0"
|
||||
style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top"><span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<p
|
||||
style="font-size: 12px; line-height: 24px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
|
||||
<span style="font-size: 20px;">Mentions</span></p>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= for %{data: mention, object: object, from: from} <- @mentions do %>
|
||||
<%# mention START %>
|
||||
<%# user card START %>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid mixed-two-up no-stack"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 20px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num3"
|
||||
style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 20px;">
|
||||
<!--<![endif]-->
|
||||
<div align="left" class="img-container left "
|
||||
style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img
|
||||
alt="<%= from.name %>" border="0" class="left " src="<%= avatar_url(from) %>"
|
||||
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;"
|
||||
title="<%= from.name %>" width="76" />
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num9"
|
||||
style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||
style="font-size: 16px; color: <%= @styling.text_color %>;"><%= from.name %></span></p>
|
||||
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||
style="font-size: 16px;"><%= link "@" <> from.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: mention.activity.actor %></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%# user card END %>
|
||||
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;">
|
||||
<!--<![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||
<span style="font-size: 16px; line-height: 19px;"><%= raw object.data["content"] %></span></div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 15px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_muted_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:15px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_muted_color %>;">
|
||||
<p style="font-size: 14px; line-height: 16px; margin: 0;"><%= format_date object.data["published"] %></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%# mention END %>
|
||||
<% end %>
|
||||
|
||||
<%= if @followers != [] do %>
|
||||
|
||||
<%# new followers header START %>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;">
|
||||
<!--<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td class="divider_inner"
|
||||
style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;"
|
||||
valign="top">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content"
|
||||
height="0" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td height="0"
|
||||
style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top"><span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||
<p style="font-size: 12px; line-height: 24px; text-align: center; margin: 0;"><span
|
||||
style="font-size: 20px;"><%= length(@followers) %> New Followers</span><span
|
||||
style="font-size: 20px; line-height: 24px;"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%# new followers header END %>
|
||||
|
||||
<%= for %{data: follow, from: from} <- @followers do %>
|
||||
<%# user card START %>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid mixed-two-up no-stack"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 20px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num3"
|
||||
style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 20px;">
|
||||
<!--<![endif]-->
|
||||
<div align="left" class="img-container left "
|
||||
style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img
|
||||
alt="<%= from.name %>" border="0" class="left " src="<%= avatar_url(from) %>"
|
||||
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;"
|
||||
title="<%= from.name %>" width="76" />
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num9"
|
||||
style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div
|
||||
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||
style="font-size: 16px; color: <%= @styling.text_color %>;"><%= from.name %></span></p>
|
||||
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||
style="font-size: 16px;"><%= link "@" <> from.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: follow.activity.actor %></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%# user card END %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<% end %>
|
||||
|
||||
<%# divider start %>
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td class="divider_inner"
|
||||
style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;"
|
||||
valign="top">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content"
|
||||
height="0" role="presentation"
|
||||
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;"
|
||||
valign="top" width="100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top;" valign="top">
|
||||
<td height="0"
|
||||
style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||
valign="top"><span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%# divider end %>
|
||||
|
||||
|
||||
<div style="background-color:transparent;">
|
||||
<div class="block-grid"
|
||||
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
<div class="col num12"
|
||||
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||
<div style="width:100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div
|
||||
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div
|
||||
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<p
|
||||
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
|
||||
<span style="font-size: 14px;">You have received this email because you have signed up to receive digest emails from <b><%= @instance %></b> Pleroma instance.</span></p>
|
||||
<p
|
||||
style="font-size: 12px; line-height: 14px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
|
||||
</p>
|
||||
<p
|
||||
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
|
||||
<span style="font-size: 14px;">The email address you are subscribed as is <a href="mailto:<%= @user.email %>" style="color: <%= @styling.link_color %>;text-decoration: none;"><%= @user.email %></a>. </span></p>
|
||||
<p
|
||||
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
|
||||
<span style="font-size: 14px;">To unsubscribe, please go <%= link "here", style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link %>.</span></p>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (IE)]></div><![endif]-->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -74,12 +74,15 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
|> HTML.filter_tags(User.html_filter_policy(for_user))
|
||||
|> Formatter.emojify(emoji)
|
||||
|
||||
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
||||
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
||||
fields =
|
||||
(user.info.source_data["attachment"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
user.info
|
||||
|> User.Info.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => Pleroma.HTML.strip_tags(name),
|
||||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
}
|
||||
end)
|
||||
|
||||
data =
|
||||
%{
|
||||
|
|
|
@ -2,4 +2,14 @@ defmodule Pleroma.Web.EmailView do
|
|||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML
|
||||
import Phoenix.HTML.Link
|
||||
|
||||
def avatar_url(user) do
|
||||
Pleroma.User.avatar_url(user)
|
||||
end
|
||||
|
||||
def format_date(date) when is_binary(date) do
|
||||
date
|
||||
|> Timex.parse!("{ISO:Extended:Z}")
|
||||
|> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddExpirationsTable do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create_if_not_exists table(:activity_expirations) do
|
||||
add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all))
|
||||
add(:scheduled_at, :naive_datetime, null: false)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Pleroma.Repo.Migrations.CreateModerationLog do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:moderation_log) do
|
||||
add(:data, :map)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddLikesIndexToObjects do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create_if_not_exists index(:objects, ["(data->'likes')"], using: :gin, name: :objects_likes)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpirationTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.ActivityExpiration
|
||||
import Pleroma.Factory
|
||||
|
||||
test "finds activities due to be deleted only" do
|
||||
activity = insert(:note_activity)
|
||||
expiration_due = insert(:expiration_in_the_past, %{activity_id: activity.id})
|
||||
activity2 = insert(:note_activity)
|
||||
insert(:expiration_in_the_future, %{activity_id: activity2.id})
|
||||
|
||||
expirations = ActivityExpiration.due_expirations()
|
||||
|
||||
assert length(expirations) == 1
|
||||
assert hd(expirations) == expiration_due
|
||||
end
|
||||
|
||||
test "denies expirations that don't live long enough" do
|
||||
activity = insert(:note_activity)
|
||||
now = NaiveDateTime.utc_now()
|
||||
assert {:error, _} = ActivityExpiration.create(activity, now)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpirationWorkerTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Activity
|
||||
import Pleroma.Factory
|
||||
|
||||
test "deletes an activity" do
|
||||
activity = insert(:note_activity)
|
||||
expiration = insert(:expiration_in_the_past, %{activity_id: activity.id})
|
||||
Pleroma.ActivityExpirationWorker.perform(:execute, expiration.id)
|
||||
|
||||
refute Repo.get(Activity, activity.id)
|
||||
end
|
||||
end
|
|
@ -164,4 +164,13 @@ defmodule Pleroma.ActivityTest do
|
|||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||
end
|
||||
end
|
||||
|
||||
test "add an activity with an expiration" do
|
||||
activity = insert(:note_activity)
|
||||
insert(:expiration_in_the_future, %{activity_id: activity.id})
|
||||
|
||||
Pleroma.ActivityExpiration
|
||||
|> where([a], a.activity_id == ^activity.id)
|
||||
|> Repo.one!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,14 +5,8 @@
|
|||
defmodule Pleroma.Config.TransferTaskTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
setup do
|
||||
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||
|
||||
clear_config([:instance, :dynamic_configuration]) do
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||
end)
|
||||
end
|
||||
|
||||
test "transfer config values from db to env" do
|
||||
|
|
|
@ -11,14 +11,8 @@ defmodule Pleroma.ConversationTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
config_path = [:instance, :federating]
|
||||
initial_setting = Pleroma.Config.get(config_path)
|
||||
|
||||
Pleroma.Config.put(config_path, true)
|
||||
on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
|
||||
|
||||
:ok
|
||||
clear_config_all([:instance, :federating]) do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
end
|
||||
|
||||
test "it goes through old direct conversations" do
|
||||
|
|
|
@ -15,11 +15,7 @@ defmodule Pleroma.Emails.MailerTest do
|
|||
to: [{"Test User", "user1@example.com"}]
|
||||
}
|
||||
|
||||
setup do
|
||||
value = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
|
||||
on_exit(fn -> Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], value) end)
|
||||
:ok
|
||||
end
|
||||
clear_config([Pleroma.Emails.Mailer, :enabled])
|
||||
|
||||
test "not send email when mailer is disabled" do
|
||||
Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"type": "Update",
|
||||
"object": {
|
||||
"url": "http://mastodon.example.org/@gargron",
|
||||
"type": "Person",
|
||||
"summary": "<p>Some bio</p>",
|
||||
"publicKey": {
|
||||
{
|
||||
"type": "Update",
|
||||
"object": {
|
||||
"url": "http://mastodon.example.org/@gargron",
|
||||
"type": "Person",
|
||||
"summary": "<p>Some bio</p>",
|
||||
"publicKey": {
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gs3VnQf6am3R+CeBV4H\nlfI1HZTNRIBHgvFszRZkCERbRgEWMu+P+I6/7GJC5H5jhVQ60z4MmXcyHOGmYMK/\n5XyuHQz7V2Ssu1AxLfRN5Biq1ayb0+DT/E7QxNXDJPqSTnstZ6C7zKH/uAETqg3l\nBonjCQWyds+IYbQYxf5Sp3yhvQ80lMwHML3DaNCMlXWLoOnrOX5/yK5+dedesg2\n/HIvGk+HEt36vm6hoH7bwPuEkgA++ACqwjXRe5Mta7i3eilHxFaF8XIrJFARV0t\nqOu4GID/jG6oA+swIWndGrtR2QRJIt9QIBFfK3HG5M0koZbY1eTqwNFRHFL3xaD\nUQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||
"owner": "http://mastodon.example.org/users/gargron",
|
||||
"id": "http://mastodon.example.org/users/gargron#main-key"
|
||||
|
@ -20,7 +20,27 @@
|
|||
"endpoints": {
|
||||
"sharedInbox": "http://mastodon.example.org/inbox"
|
||||
},
|
||||
"icon":{"type":"Image","mediaType":"image/jpeg","url":"https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"},"image":{"type":"Image","mediaType":"image/png","url":"https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"}
|
||||
"attachment": [{
|
||||
"type": "PropertyValue",
|
||||
"name": "foo",
|
||||
"value": "updated"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "foo1",
|
||||
"value": "updated"
|
||||
}
|
||||
],
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/jpeg",
|
||||
"url": "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
|
||||
},
|
||||
"image": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||
}
|
||||
},
|
||||
"id": "http://mastodon.example.org/users/gargron#updates/1519563538",
|
||||
"actor": "http://mastodon.example.org/users/gargron",
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"@context":[
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://apfed.club/apschema/v1.4"
|
||||
],
|
||||
"id":"https://apfed.club/follow/9",
|
||||
"type":"Follow",
|
||||
"actor":{
|
||||
"type":"Person",
|
||||
"id":"https://apfed.club/channel/indio",
|
||||
"preferredUsername":"indio",
|
||||
"name":"Indio",
|
||||
"updated":"2019-08-20T23:52:34Z",
|
||||
"icon":{
|
||||
"type":"Image",
|
||||
"mediaType":"image/jpeg",
|
||||
"updated":"2019-08-20T23:53:37Z",
|
||||
"url":"https://apfed.club/photo/profile/l/2",
|
||||
"height":300,
|
||||
"width":300
|
||||
},
|
||||
"url":"https://apfed.club/channel/indio",
|
||||
"inbox":"https://apfed.club/inbox/indio",
|
||||
"outbox":"https://apfed.club/outbox/indio",
|
||||
"followers":"https://apfed.club/followers/indio",
|
||||
"following":"https://apfed.club/following/indio",
|
||||
"endpoints":{
|
||||
"sharedInbox":"https://apfed.club/inbox"
|
||||
},
|
||||
"publicKey":{
|
||||
"id":"https://apfed.club/channel/indio",
|
||||
"owner":"https://apfed.club/channel/indio",
|
||||
"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA77TIR1VuSYFnmDRFGHHb\n4vaGdx9ranzRX4bfOKAqa++Ch5L4EqJpPy08RuM+NrYCYiYl4QQFDSSDXAEgb5g9\nC1TgWTfI7q/E0UBX2Vr0mU6X4i1ztv0tuQvegRjcSJ7l1AvoBs8Ip4MEJ3OPEQhB\ngJqAACB3Gnps4zi2I0yavkxUfGVKr6zKT3BxWh5hTpKC7Do+ChIrVZC2EwxND9K6
|
||||
\nsAnQHThcb5EQuvuzUQZKeS7IEOsd0JpZDmJjbfMGrAWE81pLIfEeeA2joCJiBBTO\nglDsW+juvZ+lWqJpMr2hMWpvfrFjJeUawNJCIzsLdVIZR+aKj5yy6yqoS8hkN9Ha\n1MljZpsXl+EmwcwAIqim1YeLwERCEAQ/JWbSt8pQTQbzZ6ibwQ4mchCxacrRbIVR
|
||||
\nnL59fWMBassJcbY0VwrTugm2SBsYbDjESd55UZV03Rwr8qseGTyi+hH8O7w2SIaY\nzjN6AdZiPmsh00YflzlCk8MSLOHMol1vqIUzXxU8CdXn9+KsuQdZGrTz0YKN/db4\naVwUGJatz2Tsvf7R1tJBjJfeQWOWbbn3pycLVH86LjZ83qngp9ZVnAveUnUqz0yS
|
||||
\nhe+buZ6UMsfGzbIYon2bKNlz6gYTH0YPcr+cLe+29drtt0GZiXha1agbpo4RB8zE
|
||||
\naNL2fucF5YT0yNpbd/5WoV0CAwEAAQ==\n-----END PUBLIC KEY-----\n"
|
||||
}
|
||||
},
|
||||
"object":"https://pleroma.site/users/kaniini",
|
||||
"to":[
|
||||
"https://pleroma.site/users/kaniini"
|
||||
],
|
||||
"signature":{
|
||||
"@context":[
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1"
|
||||
],
|
||||
"type":"RsaSignature2017",
|
||||
"nonce":"52c035e0a9e81dce8b486159204e97c22637e91f75cdfad5378de91de68e9117",
|
||||
"creator":"https://apfed.club/channel/indio/public_key_pem",
|
||||
"created":"2019-08-22T03:38:02Z",
|
||||
"signatureValue":"oVliRCIqNIh6yUp851dYrF0y21aHp3Rz6VkIpW1pFMWfXuzExyWSfcELpyLseeRmsw5bUu9zJkH44B4G2LiJQKA9UoEQDjrDMZBmbeUpiQqq3DVUzkrBOI8bHZ7xyJ/CjSZcNHHh0MHhSKxswyxWMGi4zIqzkAZG3vRRgoPVHdjPm00sR3B8jBLw1cjoffv+KKeM/zEUpe13gqX9qHAWHHqZepxgSWmq+EKOkRvHUPBXiEJZfXzc5uW+vZ09F3WBYmaRoy8Y0e1P29fnRLqSy7EEINdrHaGclRqoUZyiawpkgy3lWWlynesV/HiLBR7EXT79eKstxf4wfTDaPKBCfTCsOWuMWHr7Genu37ew2/t7eiBGqCwwW12ylhml/OLHgNK3LOhmRABhtfpaFZSxfDVnlXfaLpY1xekVOj2oC0FpBtnoxVKLpIcyLw6dkfSil5ANd+hl59W/bpPA8KT90ii1fSNCo3+FcwQVx0YsPznJNA60XfFuVsme7zNcOst6393e1WriZxBanFpfB63zVQc9u1fjyfktx/yiUNxIlre+sz9OCc0AACn94iRhBYh4bbzdleUOTnM7lnD4Dj2FP+xeDIP8CA8wXUeq5+9kopSp2kAmlUEyFUdg4no7naIeu1SZnopfUg56PsVCp9JHiUK1SYAyWbdC+FbUECu5CvI="
|
||||
}
|
||||
}
|
|
@ -1 +1,54 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":"as:movedTo","Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji"}],"id":"http://mastodon.example.org/users/admin","type":"Person","following":"http://mastodon.example.org/users/admin/following","followers":"http://mastodon.example.org/users/admin/followers","inbox":"http://mastodon.example.org/users/admin/inbox","outbox":"http://mastodon.example.org/users/admin/outbox","preferredUsername":"admin","name":null,"summary":"\u003cp\u003e\u003c/p\u003e","url":"http://mastodon.example.org/@admin","manuallyApprovesFollowers":false,"publicKey":{"id":"http://mastodon.example.org/users/admin#main-key","owner":"http://mastodon.example.org/users/admin","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"},"endpoints":{"sharedInbox":"http://mastodon.example.org/inbox"},"icon":{"type":"Image","mediaType":"image/jpeg","url":"https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"},"image":{"type":"Image","mediaType":"image/png","url":"https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"}}
|
||||
{
|
||||
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": "as:movedTo",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji"
|
||||
}],
|
||||
"id": "http://mastodon.example.org/users/admin",
|
||||
"type": "Person",
|
||||
"following": "http://mastodon.example.org/users/admin/following",
|
||||
"followers": "http://mastodon.example.org/users/admin/followers",
|
||||
"inbox": "http://mastodon.example.org/users/admin/inbox",
|
||||
"outbox": "http://mastodon.example.org/users/admin/outbox",
|
||||
"preferredUsername": "admin",
|
||||
"name": null,
|
||||
"summary": "\u003cp\u003e\u003c/p\u003e",
|
||||
"url": "http://mastodon.example.org/@admin",
|
||||
"manuallyApprovesFollowers": false,
|
||||
"publicKey": {
|
||||
"id": "http://mastodon.example.org/users/admin#main-key",
|
||||
"owner": "http://mastodon.example.org/users/admin",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
},
|
||||
"attachment": [{
|
||||
"type": "PropertyValue",
|
||||
"name": "foo",
|
||||
"value": "bar"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "foo1",
|
||||
"value": "bar1"
|
||||
}
|
||||
],
|
||||
"endpoints": {
|
||||
"sharedInbox": "http://mastodon.example.org/inbox"
|
||||
},
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/jpeg",
|
||||
"url": "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
|
||||
},
|
||||
"image": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"Person","id":"https://apfed.club/channel/indio","preferredUsername":"indio","name":"Indio","updated":"2019-08-20T23:52:34Z","icon":{"type":"Image","mediaType":"image/jpeg","updated":"2019-08-20T23:53:37Z","url":"https://apfed.club/photo/profile/l/2","height":300,"width":300},"url":"https://apfed.club/channel/indio","inbox":"https://apfed.club/inbox/indio","outbox":"https://apfed.club/outbox/indio","followers":"https://apfed.club/followers/indio","following":"https://apfed.club/following/indio","endpoints":{"sharedInbox":"https://apfed.club/inbox"},"publicKey":{"id":"https://apfed.club/channel/indio","owner":"https://apfed.club/channel/indio","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA77TIR1VuSYFnmDRFGHHb\n4vaGdx9ranzRX4bfOKAqa++Ch5L4EqJpPy08RuM+NrYCYiYl4QQFDSSDXAEgb5g9\nC1TgWTfI7q/E0UBX2Vr0mU6X4i1ztv0tuQvegRjcSJ7l1AvoBs8Ip4MEJ3OPEQhB\ngJqAACB3Gnps4zi2I0yavkxUfGVKr6zKT3BxWh5hTpKC7Do+ChIrVZC2EwxND9K6\nsAnQHThcb5EQuvuzUQZKeS7IEOsd0JpZDmJjbfMGrAWE81pLIfEeeA2joCJiBBTO\nglDsW+juvZ+lWqJpMr2hMWpvfrFjJeUawNJCIzsLdVIZR+aKj5yy6yqoS8hkN9Ha\n1MljZpsXl+EmwcwAIqim1YeLwERCEAQ/JWbSt8pQTQbzZ6ibwQ4mchCxacrRbIVR\nnL59fWMBassJcbY0VwrTugm2SBsYbDjESd55UZV03Rwr8qseGTyi+hH8O7w2SIaY\nzjN6AdZiPmsh00YflzlCk8MSLOHMol1vqIUzXxU8CdXn9+KsuQdZGrTz0YKN/db4\naVwUGJatz2Tsvf7R1tJBjJfeQWOWbbn3pycLVH86LjZ83qngp9ZVnAveUnUqz0yS\nhe+buZ6UMsfGzbIYon2bKNlz6gYTH0YPcr+cLe+29drtt0GZiXha1agbpo4RB8zE\naNL2fucF5YT0yNpbd/5WoV0CAwEAAQ==\n-----END PUBLIC KEY-----\n"},"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"c672a408d2e88b322b36a61bf0c25f586be9245d30293c55b8d653dcc867aaf7","creator":"https://apfed.club/channel/indio/public_key_pem","created":"2019-08-26T07:24:03Z","signatureValue":"MyAv5gnedu6L/DYFaE1TUYvp4LjI9ZUU0axwGYOhgD7qsjivMgwbOrjX/iH32xlcfF8nWOMh/ogu3+Qwr5sqLHkS2AimWmw1+Ubf2KccE58b8vI8zWfyu8QJnMuE92jtBPv8UTQUHw8ZebbExk3L99oXaeyVihKiMBmd63NpVTpGXZTg6m+H+KfWchVajPoyNKZtKMd3nH99x5j54Cqkz0BN5CSTwCSG0wP95G0VtZHtmhX+tsAPM3oAj0d+gtCZSCd8Nu8fvFAwCyTg1oKSfRqKb27EKHlskqK9X57x0jURH77CTAIQSejgGcKJ5GGLtvofubJkafadjagqrtqz6Mz6BZ642ssJ2KGkRAn79Q4F08goI6cfU5lLk2Tooe5A55XERnmE3SkYGyTvLpacZplxJdU0sa+deX9D7+alSGFJZSziaxpCxzrO6lEApe4b9kHXAzn9VaZt9trijkHq/kkq0i3NRcP7n8JG9q+Vv8jY9ddY6HcH89RNCBIA6MKLtAqc+vSc5G24qeZlw2MzlQWBp0KGuVG8DQR00AL6cXLBzF1WY8JZeEg6zqm+DMznbuNzgiS34BP+AehBSHlQ4MZebwDnK3ZPPqGSwioIWMxIFfZDaVDX9Pp1pXAARQMw0c/y4sDcf9FMzsr8jteEa7ZQcoqq5kXQTSCP56TEHnI="}}
|
|
@ -4,21 +4,19 @@
|
|||
|
||||
defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
alias Pleroma.HTTP.RequestBuilder
|
||||
|
||||
describe "headers/2" do
|
||||
clear_config([:http, :send_user_agent])
|
||||
|
||||
test "don't send pleroma user agent" do
|
||||
assert RequestBuilder.headers(%{}, []) == %{headers: []}
|
||||
end
|
||||
|
||||
test "send pleroma user agent" do
|
||||
send = Pleroma.Config.get([:http, :send_user_agent])
|
||||
Pleroma.Config.put([:http, :send_user_agent], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:http, :send_user_agent], send)
|
||||
end)
|
||||
|
||||
assert RequestBuilder.headers(%{}, []) == %{
|
||||
headers: [{"User-Agent", Pleroma.Application.user_agent()}]
|
||||
}
|
||||
|
|
|
@ -15,6 +15,13 @@ defmodule Pleroma.ListTest do
|
|||
assert title == "title"
|
||||
end
|
||||
|
||||
test "validates title" do
|
||||
user = insert(:user)
|
||||
|
||||
assert {:error, changeset} = Pleroma.List.create("", user)
|
||||
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
||||
end
|
||||
|
||||
test "getting a list not belonging to the user" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ModerationLogTest do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ModerationLog
|
||||
|
||||
use Pleroma.DataCase
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "user moderation" do
|
||||
setup do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
moderator = insert(:user, info: %{is_moderator: true})
|
||||
subject1 = insert(:user)
|
||||
subject2 = insert(:user)
|
||||
|
||||
[admin: admin, moderator: moderator, subject1: subject1, subject2: subject2]
|
||||
end
|
||||
|
||||
test "logging user deletion by moderator", %{moderator: moderator, subject1: subject1} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
subject: subject1,
|
||||
action: "delete"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} deleted user @#{subject1.nickname}"
|
||||
end
|
||||
|
||||
test "logging user creation by moderator", %{
|
||||
moderator: moderator,
|
||||
subject1: subject1,
|
||||
subject2: subject2
|
||||
} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
subjects: [subject1, subject2],
|
||||
action: "create"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} created users: @#{subject1.nickname}, @#{subject2.nickname}"
|
||||
end
|
||||
|
||||
test "logging user follow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: subject1,
|
||||
follower: subject2,
|
||||
action: "follow"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{admin.nickname} made @#{subject2.nickname} follow @#{subject1.nickname}"
|
||||
end
|
||||
|
||||
test "logging user unfollow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
followed: subject1,
|
||||
follower: subject2,
|
||||
action: "unfollow"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{admin.nickname} made @#{subject2.nickname} unfollow @#{subject1.nickname}"
|
||||
end
|
||||
|
||||
test "logging user tagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
nicknames: [subject1.nickname, subject2.nickname],
|
||||
tags: ["foo", "bar"],
|
||||
action: "tag"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
users =
|
||||
[subject1.nickname, subject2.nickname]
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags = ["foo", "bar"] |> Enum.join(", ")
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{admin.nickname} added tags: #{tags} to users: #{users}"
|
||||
end
|
||||
|
||||
test "logging user untagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
nicknames: [subject1.nickname, subject2.nickname],
|
||||
tags: ["foo", "bar"],
|
||||
action: "untag"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
users =
|
||||
[subject1.nickname, subject2.nickname]
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags = ["foo", "bar"] |> Enum.join(", ")
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{admin.nickname} removed tags: #{tags} from users: #{users}"
|
||||
end
|
||||
|
||||
test "logging user grant by moderator", %{moderator: moderator, subject1: subject1} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
subject: subject1,
|
||||
action: "grant",
|
||||
permission: "moderator"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} made @#{subject1.nickname} moderator"
|
||||
end
|
||||
|
||||
test "logging user revoke by moderator", %{moderator: moderator, subject1: subject1} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
subject: subject1,
|
||||
action: "revoke",
|
||||
permission: "moderator"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} revoked moderator role from @#{subject1.nickname}"
|
||||
end
|
||||
|
||||
test "logging relay follow", %{moderator: moderator} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "relay_follow",
|
||||
target: "https://example.org/relay"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} followed relay: https://example.org/relay"
|
||||
end
|
||||
|
||||
test "logging relay unfollow", %{moderator: moderator} do
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "relay_unfollow",
|
||||
target: "https://example.org/relay"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} unfollowed relay: https://example.org/relay"
|
||||
end
|
||||
|
||||
test "logging report update", %{moderator: moderator} do
|
||||
report = %Activity{
|
||||
id: "9m9I1F4p8ftrTP6QTI",
|
||||
data: %{
|
||||
"type" => "Flag",
|
||||
"state" => "resolved"
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "report_update",
|
||||
subject: report
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} updated report ##{report.id} with 'resolved' state"
|
||||
end
|
||||
|
||||
test "logging report response", %{moderator: moderator} do
|
||||
report = %Activity{
|
||||
id: "9m9I1F4p8ftrTP6QTI",
|
||||
data: %{
|
||||
"type" => "Note"
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "report_response",
|
||||
subject: report,
|
||||
text: "look at this"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} responded with 'look at this' to report ##{report.id}"
|
||||
end
|
||||
|
||||
test "logging status sensitivity update", %{moderator: moderator} do
|
||||
note = insert(:note_activity)
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "status_update",
|
||||
subject: note,
|
||||
sensitive: "true",
|
||||
visibility: nil
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true'"
|
||||
end
|
||||
|
||||
test "logging status visibility update", %{moderator: moderator} do
|
||||
note = insert(:note_activity)
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "status_update",
|
||||
subject: note,
|
||||
sensitive: nil,
|
||||
visibility: "private"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} updated status ##{note.id}, set visibility: 'private'"
|
||||
end
|
||||
|
||||
test "logging status sensitivity & visibility update", %{moderator: moderator} do
|
||||
note = insert(:note_activity)
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "status_update",
|
||||
subject: note,
|
||||
sensitive: "true",
|
||||
visibility: "private"
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true', visibility: 'private'"
|
||||
end
|
||||
|
||||
test "logging status deletion", %{moderator: moderator} do
|
||||
note = insert(:note_activity)
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "status_delete",
|
||||
subject_id: note.id
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log) ==
|
||||
"@#{moderator.nickname} deleted status ##{note.id}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -159,32 +159,28 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
end
|
||||
|
||||
describe "signed fetches" do
|
||||
clear_config([:activitypub, :sign_object_fetches])
|
||||
|
||||
test_with_mock "it signs fetches when configured to do so",
|
||||
Pleroma.Signature,
|
||||
[:passthrough],
|
||||
[] do
|
||||
option = Pleroma.Config.get([:activitypub, :sign_object_fetches])
|
||||
Pleroma.Config.put([:activitypub, :sign_object_fetches], true)
|
||||
|
||||
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||
|
||||
assert called(Pleroma.Signature.sign(:_, :_))
|
||||
|
||||
Pleroma.Config.put([:activitypub, :sign_object_fetches], option)
|
||||
end
|
||||
|
||||
test_with_mock "it doesn't sign fetches when not configured to do so",
|
||||
Pleroma.Signature,
|
||||
[:passthrough],
|
||||
[] do
|
||||
option = Pleroma.Config.get([:activitypub, :sign_object_fetches])
|
||||
Pleroma.Config.put([:activitypub, :sign_object_fetches], false)
|
||||
|
||||
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||
|
||||
refute called(Pleroma.Signature.sign(:_, :_))
|
||||
|
||||
Pleroma.Config.put([:activitypub, :sign_object_fetches], option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,8 +9,10 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do
|
|||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.User
|
||||
|
||||
clear_config([:instance, :public])
|
||||
|
||||
test "it halts if not public and no user is assigned", %{conn: conn} do
|
||||
set_public_to(false)
|
||||
Config.put([:instance, :public], false)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
@ -21,7 +23,7 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do
|
|||
end
|
||||
|
||||
test "it continues if public", %{conn: conn} do
|
||||
set_public_to(true)
|
||||
Config.put([:instance, :public], true)
|
||||
|
||||
ret_conn =
|
||||
conn
|
||||
|
@ -31,7 +33,7 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do
|
|||
end
|
||||
|
||||
test "it continues if a user is assigned, even if not public", %{conn: conn} do
|
||||
set_public_to(false)
|
||||
Config.put([:instance, :public], false)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
@ -43,13 +45,4 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do
|
|||
|
||||
assert ret_conn == conn
|
||||
end
|
||||
|
||||
defp set_public_to(value) do
|
||||
orig = Config.get!([:instance, :public])
|
||||
Config.put([:instance, :public], value)
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([:instance, :public], orig)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,17 +7,12 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
|||
alias Pleroma.Config
|
||||
alias Plug.Conn
|
||||
|
||||
clear_config([:http_securiy, :enabled])
|
||||
clear_config([:http_security, :sts])
|
||||
|
||||
describe "http security enabled" do
|
||||
setup do
|
||||
enabled = Config.get([:http_securiy, :enabled])
|
||||
|
||||
Config.put([:http_security, :enabled], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([:http_security, :enabled], enabled)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it sends CSP headers when enabled", %{conn: conn} do
|
||||
|
@ -81,14 +76,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
|||
end
|
||||
|
||||
test "it does not send CSP headers when disabled", %{conn: conn} do
|
||||
enabled = Config.get([:http_securiy, :enabled])
|
||||
|
||||
Config.put([:http_security, :enabled], false)
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([:http_security, :enabled], enabled)
|
||||
end)
|
||||
|
||||
conn = get(conn, "/api/v1/instance")
|
||||
|
||||
assert Conn.get_resp_header(conn, "x-xss-protection") == []
|
||||
|
|
|
@ -8,14 +8,12 @@ defmodule Pleroma.Web.RuntimeStaticPlugTest do
|
|||
@dir "test/tmp/instance_static"
|
||||
|
||||
setup do
|
||||
static_dir = Pleroma.Config.get([:instance, :static_dir])
|
||||
Pleroma.Config.put([:instance, :static_dir], @dir)
|
||||
File.mkdir_p!(@dir)
|
||||
on_exit(fn -> File.rm_rf(@dir) end)
|
||||
end
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :static_dir], static_dir)
|
||||
File.rm_rf(@dir)
|
||||
end)
|
||||
clear_config([:instance, :static_dir]) do
|
||||
Pleroma.Config.put([:instance, :static_dir], @dir)
|
||||
end
|
||||
|
||||
test "overrides index" do
|
||||
|
|
|
@ -108,11 +108,11 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "max_body_size returns error if streaming body more than that option", %{conn: conn} do
|
||||
test "max_body_length returns error if streaming body more than that option", %{conn: conn} do
|
||||
stream_mock(3, true)
|
||||
|
||||
assert capture_log(fn ->
|
||||
ReverseProxy.call(conn, "/stream-bytes/50", max_body_size: 30)
|
||||
ReverseProxy.call(conn, "/stream-bytes/50", max_body_length: 30)
|
||||
end) =~
|
||||
"[warn] Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.SignatureTest do
|
|||
import ExUnit.CaptureLog
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
import Mock
|
||||
|
||||
alias Pleroma.Signature
|
||||
|
||||
|
@ -114,4 +115,17 @@ defmodule Pleroma.SignatureTest do
|
|||
"https://example.com/users/1234"
|
||||
end
|
||||
end
|
||||
|
||||
describe "signed_date" do
|
||||
test "it returns formatted current date" do
|
||||
with_mock(NaiveDateTime, utc_now: fn -> ~N[2019-08-23 18:11:24.822233] end) do
|
||||
assert Signature.signed_date() == "Fri, 23 Aug 2019 18:11:24 GMT"
|
||||
end
|
||||
end
|
||||
|
||||
test "it returns formatted date" do
|
||||
assert Signature.signed_date(~N[2019-08-23 08:11:24.822233]) ==
|
||||
"Fri, 23 Aug 2019 08:11:24 GMT"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Factory do
|
||||
|
@ -143,6 +143,25 @@ defmodule Pleroma.Factory do
|
|||
|> Map.merge(attrs)
|
||||
end
|
||||
|
||||
defp expiration_offset_by_minutes(attrs, minutes) do
|
||||
scheduled_at =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(:timer.minutes(minutes), :millisecond)
|
||||
|> NaiveDateTime.truncate(:second)
|
||||
|
||||
%Pleroma.ActivityExpiration{}
|
||||
|> Map.merge(attrs)
|
||||
|> Map.put(:scheduled_at, scheduled_at)
|
||||
end
|
||||
|
||||
def expiration_in_the_past_factory(attrs \\ %{}) do
|
||||
expiration_offset_by_minutes(attrs, -60)
|
||||
end
|
||||
|
||||
def expiration_in_the_future_factory(attrs \\ %{}) do
|
||||
expiration_offset_by_minutes(attrs, 61)
|
||||
end
|
||||
|
||||
def article_activity_factory do
|
||||
article = insert(:article)
|
||||
|
||||
|
@ -188,13 +207,15 @@ defmodule Pleroma.Factory do
|
|||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
data = %{
|
||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||
"actor" => user.ap_id,
|
||||
"type" => "Like",
|
||||
"object" => object.data["id"],
|
||||
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
|
||||
}
|
||||
data =
|
||||
%{
|
||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||
"actor" => user.ap_id,
|
||||
"type" => "Like",
|
||||
"object" => object.data["id"],
|
||||
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
|
||||
}
|
||||
|> Map.merge(attrs[:data_attrs] || %{})
|
||||
|
||||
%Pleroma.Activity{
|
||||
data: data
|
||||
|
|
|
@ -7,8 +7,52 @@ defmodule Pleroma.Tests.Helpers do
|
|||
Helpers for use in tests.
|
||||
"""
|
||||
|
||||
defmacro clear_config(config_path) do
|
||||
quote do
|
||||
clear_config(unquote(config_path)) do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro clear_config(config_path, do: yield) do
|
||||
quote do
|
||||
setup do
|
||||
initial_setting = Pleroma.Config.get(unquote(config_path))
|
||||
unquote(yield)
|
||||
on_exit(fn -> Pleroma.Config.put(unquote(config_path), initial_setting) end)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro clear_config_all(config_path) do
|
||||
quote do
|
||||
clear_config_all(unquote(config_path)) do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro clear_config_all(config_path, do: yield) do
|
||||
quote do
|
||||
setup_all do
|
||||
initial_setting = Pleroma.Config.get(unquote(config_path))
|
||||
unquote(yield)
|
||||
on_exit(fn -> Pleroma.Config.put(unquote(config_path), initial_setting) end)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmacro __using__(_opts) do
|
||||
quote do
|
||||
import Pleroma.Tests.Helpers,
|
||||
only: [
|
||||
clear_config: 1,
|
||||
clear_config: 2,
|
||||
clear_config_all: 1,
|
||||
clear_config_all: 2
|
||||
]
|
||||
|
||||
def collect_ids(collection) do
|
||||
collection
|
||||
|> Enum.map(& &1.id)
|
||||
|
@ -30,6 +74,15 @@ defmodule Pleroma.Tests.Helpers do
|
|||
|> Poison.encode!()
|
||||
|> Poison.decode!()
|
||||
end
|
||||
|
||||
defmacro guards_config(config_path) do
|
||||
quote do
|
||||
initial_setting = Pleroma.Config.get(config_path)
|
||||
|
||||
Pleroma.Config.put(config_path, true)
|
||||
on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,9 +17,12 @@ defmodule HttpRequestMock do
|
|||
with {:ok, res} <- apply(__MODULE__, method, [url, query, body, headers]) do
|
||||
res
|
||||
else
|
||||
{_, _r} = error ->
|
||||
# Logger.warn(r)
|
||||
error
|
||||
error ->
|
||||
with {:error, message} <- error do
|
||||
Logger.warn(message)
|
||||
end
|
||||
|
||||
{_, _r} = error
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -772,6 +775,11 @@ defmodule HttpRequestMock do
|
|||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
|
||||
end
|
||||
|
||||
def get("https://apfed.club/channel/indio", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json")}}
|
||||
end
|
||||
|
||||
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
||||
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
||||
end
|
||||
|
@ -968,9 +976,25 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def get("http://example.com/rel_me/anchor", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor.html")}}
|
||||
end
|
||||
|
||||
def get("http://example.com/rel_me/anchor_nofollow", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor_nofollow.html")}}
|
||||
end
|
||||
|
||||
def get("http://example.com/rel_me/link", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_link.html")}}
|
||||
end
|
||||
|
||||
def get("http://example.com/rel_me/null", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_null.html")}}
|
||||
end
|
||||
|
||||
def get(url, query, body, headers) do
|
||||
{:error,
|
||||
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
|
@ -1032,7 +1056,10 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def post(url, _query, _body, _headers) do
|
||||
{:error, "Not implemented the mock response for post #{inspect(url)}"}
|
||||
def post(url, query, body, headers) do
|
||||
{:error,
|
||||
"Mock response not implemented for POST #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,21 +11,20 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
Mix.shell(Mix.Shell.Process)
|
||||
temp_file = "config/temp.exported_from_db.secret.exs"
|
||||
|
||||
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Mix.shell(Mix.Shell.IO)
|
||||
Application.delete_env(:pleroma, :first_setting)
|
||||
Application.delete_env(:pleroma, :second_setting)
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||
:ok = File.rm(temp_file)
|
||||
end)
|
||||
|
||||
{:ok, temp_file: temp_file}
|
||||
end
|
||||
|
||||
clear_config_all([:instance, :dynamic_configuration]) do
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
end
|
||||
|
||||
test "settings are migrated to db" do
|
||||
assert Repo.all(Config) == []
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.DatabaseTest do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
@ -22,6 +23,52 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
describe "running remove_embedded_objects" do
|
||||
test "it replaces objects with references" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
|
||||
new_data = Map.put(activity.data, "object", activity.object.data)
|
||||
|
||||
{:ok, activity} =
|
||||
activity
|
||||
|> Activity.change(%{data: new_data})
|
||||
|> Repo.update()
|
||||
|
||||
assert is_map(activity.data["object"])
|
||||
|
||||
Mix.Tasks.Pleroma.Database.run(["remove_embedded_objects"])
|
||||
|
||||
activity = Activity.get_by_id_with_object(activity.id)
|
||||
assert is_binary(activity.data["object"])
|
||||
end
|
||||
end
|
||||
|
||||
describe "prune_objects" do
|
||||
test "it prunes old objects from the database" do
|
||||
insert(:note)
|
||||
deadline = Pleroma.Config.get([:instance, :remote_post_retention_days]) + 1
|
||||
|
||||
date =
|
||||
Timex.now()
|
||||
|> Timex.shift(days: -deadline)
|
||||
|> Timex.to_naive_datetime()
|
||||
|> NaiveDateTime.truncate(:second)
|
||||
|
||||
%{id: id} =
|
||||
:note
|
||||
|> insert()
|
||||
|> Ecto.Changeset.change(%{inserted_at: date})
|
||||
|> Repo.update!()
|
||||
|
||||
assert length(Repo.all(Object)) == 2
|
||||
|
||||
Mix.Tasks.Pleroma.Database.run(["prune_objects"])
|
||||
|
||||
assert length(Repo.all(Object)) == 1
|
||||
refute Object.get_by_id(id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "running update_users_following_followers_counts" do
|
||||
test "following and followers count are updated" do
|
||||
[user, user2] = insert_pair(:user)
|
||||
|
|
|
@ -44,7 +44,7 @@ defmodule Mix.Tasks.Pleroma.DigestTest do
|
|||
|
||||
assert_email_sent(
|
||||
to: {user2.name, user2.email},
|
||||
html_body: ~r/new mentions:/i
|
||||
html_body: ~r/here is what you've missed!/i
|
||||
)
|
||||
end
|
||||
end
|
|
@ -50,7 +50,8 @@ defmodule Mix.Tasks.Pleroma.RelayTest do
|
|||
%User{ap_id: follower_id} = local_user = Relay.get_actor()
|
||||
target_user = User.get_cached_by_ap_id(target_instance)
|
||||
follow_activity = Utils.fetch_latest_follow(local_user, target_user)
|
||||
|
||||
User.follow(local_user, target_user)
|
||||
assert "#{target_instance}/followers" in refresh_record(local_user).following
|
||||
Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])
|
||||
|
||||
cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"])
|
||||
|
@ -67,6 +68,30 @@ defmodule Mix.Tasks.Pleroma.RelayTest do
|
|||
assert undo_activity.data["type"] == "Undo"
|
||||
assert undo_activity.data["actor"] == local_user.ap_id
|
||||
assert undo_activity.data["object"] == cancelled_activity.data
|
||||
refute "#{target_instance}/followers" in refresh_record(local_user).following
|
||||
end
|
||||
end
|
||||
|
||||
describe "mix pleroma.relay list" do
|
||||
test "Prints relay subscription list" do
|
||||
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
||||
|
||||
refute_receive {:mix_shell, :info, _}
|
||||
|
||||
Pleroma.Web.ActivityPub.Relay.get_actor()
|
||||
|> Ecto.Changeset.change(
|
||||
following: [
|
||||
"http://test-app.com/user/test1",
|
||||
"http://test-app.com/user/test1",
|
||||
"http://test-app-42.com/user/test1"
|
||||
]
|
||||
)
|
||||
|> Pleroma.User.update_and_set_cache()
|
||||
|
||||
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
||||
|
||||
assert_receive {:mix_shell, :info, ["test-app.com"]}
|
||||
assert_receive {:mix_shell, :info, ["test-app-42.com"]}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
|
||||
defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
|
||||
use ExUnit.Case
|
||||
use Pleroma.Tests.Helpers
|
||||
alias Mix.Tasks.Pleroma.RobotsTxt
|
||||
|
||||
clear_config([:instance, :static_dir])
|
||||
|
||||
test "creates new dir" do
|
||||
path = "test/fixtures/new_dir/"
|
||||
file_path = path <> "robots.txt"
|
||||
|
||||
static_dir = Pleroma.Config.get([:instance, :static_dir])
|
||||
Pleroma.Config.put([:instance, :static_dir], path)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :static_dir], static_dir)
|
||||
{:ok, ["test/fixtures/new_dir/", "test/fixtures/new_dir/robots.txt"]} = File.rm_rf(path)
|
||||
end)
|
||||
|
||||
|
@ -29,11 +29,9 @@ defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
|
|||
test "to existance folder" do
|
||||
path = "test/fixtures/"
|
||||
file_path = path <> "robots.txt"
|
||||
static_dir = Pleroma.Config.get([:instance, :static_dir])
|
||||
Pleroma.Config.put([:instance, :static_dir], path)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :static_dir], static_dir)
|
||||
:ok = File.rm(file_path)
|
||||
end)
|
||||
|
||||
|
|
|
@ -9,12 +9,6 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
|
|||
alias Pleroma.Upload
|
||||
|
||||
setup do
|
||||
custom_filename = Config.get([Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([Upload.Filter.AnonymizeFilename, :text], custom_filename)
|
||||
end)
|
||||
|
||||
upload_file = %Upload{
|
||||
name: "an… image.jpg",
|
||||
content_type: "image/jpg",
|
||||
|
@ -24,6 +18,8 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
|
|||
%{upload_file: upload_file}
|
||||
end
|
||||
|
||||
clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
test "it replaces filename on pre-defined text", %{upload_file: upload_file} do
|
||||
Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
|
||||
{:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
|
||||
|
|
|
@ -10,13 +10,7 @@ defmodule Pleroma.Upload.Filter.MogrifyTest do
|
|||
alias Pleroma.Upload
|
||||
alias Pleroma.Upload.Filter
|
||||
|
||||
setup do
|
||||
filter = Config.get([Filter.Mogrify, :args])
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([Filter.Mogrify, :args], filter)
|
||||
end)
|
||||
end
|
||||
clear_config([Filter.Mogrify, :args])
|
||||
|
||||
test "apply mogrify filter" do
|
||||
Config.put([Filter.Mogrify, :args], [{"tint", "40"}])
|
||||
|
|
|
@ -8,13 +8,7 @@ defmodule Pleroma.Upload.FilterTest do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.Upload.Filter
|
||||
|
||||
setup do
|
||||
custom_filename = Config.get([Pleroma.Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], custom_filename)
|
||||
end)
|
||||
end
|
||||
clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
test "applies filters" do
|
||||
Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
|
||||
|
|
|
@ -250,12 +250,8 @@ defmodule Pleroma.UploadTest do
|
|||
end
|
||||
|
||||
describe "Setting a custom base_url for uploaded media" do
|
||||
setup do
|
||||
clear_config([Pleroma.Upload, :base_url]) do
|
||||
Pleroma.Config.put([Pleroma.Upload, :base_url], "https://cache.pleroma.social")
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([Pleroma.Upload, :base_url], nil)
|
||||
end)
|
||||
end
|
||||
|
||||
test "returns a media url with configured base_url" do
|
||||
|
|
|
@ -11,19 +11,11 @@ defmodule Pleroma.Uploaders.S3Test do
|
|||
import Mock
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
setup do
|
||||
config = Config.get([Pleroma.Uploaders.S3])
|
||||
|
||||
clear_config([Pleroma.Uploaders.S3]) do
|
||||
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
|
||||
|
|
|
@ -21,6 +21,8 @@ defmodule Pleroma.UserTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
clear_config([:instance, :account_activation_required])
|
||||
|
||||
describe "when tags are nil" do
|
||||
test "tagging a user" do
|
||||
user = insert(:user, %{tags: nil})
|
||||
|
@ -203,24 +205,64 @@ defmodule Pleroma.UserTest do
|
|||
# assert websub
|
||||
# end
|
||||
|
||||
test "unfollow takes a user and another user" do
|
||||
followed = insert(:user)
|
||||
user = insert(:user, %{following: [User.ap_followers(followed)]})
|
||||
describe "unfollow/2" do
|
||||
setup do
|
||||
setting = Pleroma.Config.get([:instance, :external_user_synchronization])
|
||||
|
||||
{:ok, user, _activity} = User.unfollow(user, followed)
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], setting)
|
||||
end)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
:ok
|
||||
end
|
||||
|
||||
assert user.following == []
|
||||
end
|
||||
test "unfollow with syncronizes external user" do
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||
|
||||
test "unfollow doesn't unfollow yourself" do
|
||||
user = insert(:user)
|
||||
followed =
|
||||
insert(:user,
|
||||
nickname: "fuser1",
|
||||
follower_address: "http://localhost:4001/users/fuser1/followers",
|
||||
following_address: "http://localhost:4001/users/fuser1/following",
|
||||
ap_id: "http://localhost:4001/users/fuser1"
|
||||
)
|
||||
|
||||
{:error, _} = User.unfollow(user, user)
|
||||
user =
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
nickname: "fuser2",
|
||||
ap_id: "http://localhost:4001/users/fuser2",
|
||||
follower_address: "http://localhost:4001/users/fuser2/followers",
|
||||
following_address: "http://localhost:4001/users/fuser2/following",
|
||||
following: [User.ap_followers(followed)]
|
||||
})
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
assert user.following == [user.ap_id]
|
||||
{:ok, user, _activity} = User.unfollow(user, followed)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
assert user.following == []
|
||||
end
|
||||
|
||||
test "unfollow takes a user and another user" do
|
||||
followed = insert(:user)
|
||||
user = insert(:user, %{following: [User.ap_followers(followed)]})
|
||||
|
||||
{:ok, user, _activity} = User.unfollow(user, followed)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
assert user.following == []
|
||||
end
|
||||
|
||||
test "unfollow doesn't unfollow yourself" do
|
||||
user = insert(:user)
|
||||
|
||||
{:error, _} = User.unfollow(user, user)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
assert user.following == [user.ap_id]
|
||||
end
|
||||
end
|
||||
|
||||
test "test if a user is following another user" do
|
||||
|
@ -247,6 +289,9 @@ defmodule Pleroma.UserTest do
|
|||
password_confirmation: "test",
|
||||
email: "email@example.com"
|
||||
}
|
||||
clear_config([:instance, :autofollowed_nicknames])
|
||||
clear_config([:instance, :welcome_message])
|
||||
clear_config([:instance, :welcome_user_nickname])
|
||||
|
||||
test "it autofollows accounts that are set for it" do
|
||||
user = insert(:user)
|
||||
|
@ -263,8 +308,6 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert User.following?(registered_user, user)
|
||||
refute User.following?(registered_user, remote_user)
|
||||
|
||||
Pleroma.Config.put([:instance, :autofollowed_nicknames], [])
|
||||
end
|
||||
|
||||
test "it sends a welcome message if it is set" do
|
||||
|
@ -280,9 +323,6 @@ defmodule Pleroma.UserTest do
|
|||
assert registered_user.ap_id in activity.recipients
|
||||
assert Object.normalize(activity).data["content"] =~ "cool site"
|
||||
assert activity.actor == welcome_user.ap_id
|
||||
|
||||
Pleroma.Config.put([:instance, :welcome_user_nickname], nil)
|
||||
Pleroma.Config.put([:instance, :welcome_message], nil)
|
||||
end
|
||||
|
||||
test "it requires an email, name, nickname and password, bio is optional" do
|
||||
|
@ -348,15 +388,8 @@ defmodule Pleroma.UserTest do
|
|||
email: "email@example.com"
|
||||
}
|
||||
|
||||
setup do
|
||||
setting = Pleroma.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)
|
||||
end
|
||||
|
||||
:ok
|
||||
clear_config([:instance, :account_activation_required]) do
|
||||
Pleroma.Config.put([:instance, :account_activation_required], true)
|
||||
end
|
||||
|
||||
test "it creates unconfirmed user" do
|
||||
|
@ -508,6 +541,9 @@ defmodule Pleroma.UserTest do
|
|||
avatar: %{some: "avatar"}
|
||||
}
|
||||
|
||||
clear_config([:instance, :user_bio_length])
|
||||
clear_config([:instance, :user_name_length])
|
||||
|
||||
test "it confirms validity" do
|
||||
cs = User.remote_user_creation(@valid_remote)
|
||||
assert cs.valid?
|
||||
|
@ -1003,6 +1039,8 @@ defmodule Pleroma.UserTest do
|
|||
[user: user]
|
||||
end
|
||||
|
||||
clear_config([:instance, :federating])
|
||||
|
||||
test ".delete_user_activities deletes all create activities", %{user: user} do
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
|
||||
|
||||
|
@ -1012,6 +1050,13 @@ defmodule Pleroma.UserTest do
|
|||
refute Activity.get_by_id(activity.id)
|
||||
end
|
||||
|
||||
test "it deletes deactivated user" do
|
||||
{:ok, user} = insert(:user, info: %{deactivated: true}) |> User.set_cache()
|
||||
|
||||
assert {:ok, _} = User.delete(user)
|
||||
refute User.get_by_id(user.id)
|
||||
end
|
||||
|
||||
test "it deletes a user, all follow relationships and all activities", %{user: user} do
|
||||
follower = insert(:user)
|
||||
{:ok, follower} = User.follow(follower, user)
|
||||
|
@ -1053,9 +1098,7 @@ defmodule Pleroma.UserTest do
|
|||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
config_path = [:instance, :federating]
|
||||
initial_setting = Pleroma.Config.get(config_path)
|
||||
Pleroma.Config.put(config_path, true)
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
|
||||
{:ok, follower} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
|
||||
{:ok, _} = User.follow(follower, user)
|
||||
|
@ -1067,8 +1110,6 @@ defmodule Pleroma.UserTest do
|
|||
inbox: "http://mastodon.example.org/inbox"
|
||||
})
|
||||
)
|
||||
|
||||
Pleroma.Config.put(config_path, initial_setting)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1134,8 +1175,6 @@ defmodule Pleroma.UserTest do
|
|||
refute User.auth_active?(local_user)
|
||||
assert User.auth_active?(confirmed_user)
|
||||
assert User.auth_active?(remote_user)
|
||||
|
||||
Pleroma.Config.put([:instance, :account_activation_required], false)
|
||||
end
|
||||
|
||||
describe "superuser?/1" do
|
||||
|
@ -1180,8 +1219,6 @@ defmodule Pleroma.UserTest do
|
|||
other_user = insert(:user, local: true)
|
||||
|
||||
refute User.visible_for?(user, other_user)
|
||||
|
||||
Pleroma.Config.put([:instance, :account_activation_required], false)
|
||||
end
|
||||
|
||||
test "returns true when the account is unauthenticated and auth is not required" do
|
||||
|
@ -1198,8 +1235,6 @@ defmodule Pleroma.UserTest do
|
|||
other_user = insert(:user, local: true, info: %{is_admin: true})
|
||||
|
||||
assert User.visible_for?(user, other_user)
|
||||
|
||||
Pleroma.Config.put([:instance, :account_activation_required], false)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1218,18 +1253,18 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
|
||||
test "Adds rel=me on linkbacked urls" do
|
||||
user = insert(:user, ap_id: "http://social.example.org/users/lain")
|
||||
user = insert(:user, ap_id: "https://social.example.org/users/lain")
|
||||
|
||||
bio = "http://example.org/rel_me/null"
|
||||
bio = "http://example.com/rel_me/null"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
|
||||
bio = "http://example.org/rel_me/link"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
bio = "http://example.com/rel_me/link"
|
||||
expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
|
||||
bio = "http://example.org/rel_me/anchor"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
bio = "http://example.com/rel_me/anchor"
|
||||
expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
end
|
||||
end
|
||||
|
@ -1512,10 +1547,7 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
|
||||
describe "following/followers synchronization" do
|
||||
setup do
|
||||
sync = Pleroma.Config.get([:instance, :external_user_synchronization])
|
||||
on_exit(fn -> Pleroma.Config.put([:instance, :external_user_synchronization], sync) end)
|
||||
end
|
||||
clear_config([:instance, :external_user_synchronization])
|
||||
|
||||
test "updates the counters normally on following/getting a follow when disabled" do
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], false)
|
||||
|
|
|
@ -10,23 +10,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
||||
config_path = [:instance, :federating]
|
||||
initial_setting = Pleroma.Config.get(config_path)
|
||||
|
||||
Pleroma.Config.put(config_path, true)
|
||||
on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
clear_config_all([:instance, :federating],
|
||||
do: Pleroma.Config.put([:instance, :federating], true)
|
||||
)
|
||||
|
||||
describe "/relay" do
|
||||
clear_config([:instance, :allow_relay])
|
||||
|
||||
test "with the relay active, it returns the relay user", %{conn: conn} do
|
||||
res =
|
||||
conn
|
||||
|
@ -43,8 +43,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|> get(activity_pub_path(conn, :relay))
|
||||
|> json_response(404)
|
||||
|> assert
|
||||
|
||||
Pleroma.Config.put([:instance, :allow_relay], true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -596,6 +594,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "/relay/followers" do
|
||||
test "it returns relay followers", %{conn: conn} do
|
||||
relay_actor = Relay.get_actor()
|
||||
user = insert(:user)
|
||||
User.follow(user, relay_actor)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:relay, true)
|
||||
|> get("/relay/followers")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == [user.ap_id]
|
||||
end
|
||||
end
|
||||
|
||||
describe "/relay/following" do
|
||||
test "it returns relay following", %{conn: conn} do
|
||||
result =
|
||||
conn
|
||||
|> assign(:relay, true)
|
||||
|> get("/relay/following")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "/users/:nickname/followers" do
|
||||
test "it returns the followers in a collection", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
|
|
@ -21,6 +21,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
clear_config([:instance, :federating])
|
||||
|
||||
describe "streaming out participations" do
|
||||
test "it streams them out" do
|
||||
user = insert(:user)
|
||||
|
@ -555,7 +557,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
note_two = insert(:note, data: %{"context" => "suya.."})
|
||||
activity_two = insert(:note_activity, note: note_two)
|
||||
|
||||
{:ok, activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||
|
||||
assert [_activity_two, _activity_one] =
|
||||
ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
|
||||
|
@ -676,6 +678,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
|
||||
describe "like an object" do
|
||||
test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
note_activity = insert(:note_activity)
|
||||
assert object_activity = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||
assert called(Pleroma.Web.Federator.publish(like_activity, 5))
|
||||
end
|
||||
|
||||
test "returns exist activity if object already liked" do
|
||||
note_activity = insert(:note_activity)
|
||||
assert object_activity = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||
|
||||
{:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
|
||||
assert like_activity == like_activity_exist
|
||||
end
|
||||
|
||||
test "adds a like activity to the db" do
|
||||
note_activity = insert(:note_activity)
|
||||
assert object = Object.normalize(note_activity)
|
||||
|
@ -706,6 +731,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
|
||||
describe "unliking" do
|
||||
test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, object} = ActivityPub.unlike(user, object)
|
||||
refute called(Pleroma.Web.Federator.publish())
|
||||
|
||||
{:ok, _like_activity, object} = ActivityPub.like(user, object)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
assert called(Pleroma.Web.Federator.publish(unlike_activity, 5))
|
||||
end
|
||||
|
||||
test "unliking a previously liked object" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
test "subdomains_regex/1" do
|
||||
|
@ -59,6 +60,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
end
|
||||
|
||||
describe "describe/0" do
|
||||
clear_config([:instance, :rewrite_policy])
|
||||
|
||||
test "it works as expected with noop policy" do
|
||||
expected = %{
|
||||
mrf_policies: ["NoOpPolicy"],
|
||||
|
@ -69,7 +72,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
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 = %{
|
||||
|
@ -79,8 +81,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
}
|
||||
|
||||
{:ok, ^expected} = MRF.describe()
|
||||
|
||||
Pleroma.Config.put([:instance, :rewrite_policy], config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,12 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do
|
|||
|
||||
alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic
|
||||
|
||||
setup do
|
||||
policy = Pleroma.Config.get([:mrf_rejectnonpublic])
|
||||
on_exit(fn -> Pleroma.Config.put([:mrf_rejectnonpublic], policy) end)
|
||||
|
||||
:ok
|
||||
end
|
||||
clear_config([:mrf_rejectnonpublic])
|
||||
|
||||
describe "public message" do
|
||||
test "it's allowed when address is public" do
|
||||
|
|
|
@ -8,9 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
|
||||
setup do
|
||||
orig = Config.get!(:mrf_simple)
|
||||
|
||||
clear_config([:mrf_simple]) do
|
||||
Config.put(:mrf_simple,
|
||||
media_removal: [],
|
||||
media_nsfw: [],
|
||||
|
@ -21,10 +19,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
avatar_removal: [],
|
||||
banner_removal: []
|
||||
)
|
||||
|
||||
on_exit(fn ->
|
||||
Config.put(:mrf_simple, orig)
|
||||
end)
|
||||
end
|
||||
|
||||
describe "when :media_removal" do
|
||||
|
|
|
@ -7,12 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do
|
|||
|
||||
alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy
|
||||
|
||||
setup do
|
||||
policy = Pleroma.Config.get([:mrf_user_allowlist]) || []
|
||||
on_exit(fn -> Pleroma.Config.put([:mrf_user_allowlist], policy) end)
|
||||
|
||||
:ok
|
||||
end
|
||||
clear_config([:mrf_user_allowlist, :localhost])
|
||||
|
||||
test "pass filter if allow list is empty" do
|
||||
actor = insert(:user)
|
||||
|
|
|
@ -8,8 +8,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy
|
||||
|
||||
describe "accept" do
|
||||
clear_config([:mrf_vocabulary, :accept])
|
||||
|
||||
test "it accepts based on parent activity type" do
|
||||
config = Pleroma.Config.get([:mrf_vocabulary, :accept])
|
||||
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"])
|
||||
|
||||
message = %{
|
||||
|
@ -18,12 +19,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{: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 = %{
|
||||
|
@ -35,12 +33,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{: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 = %{
|
||||
|
@ -52,12 +47,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{: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 = %{
|
||||
|
@ -69,14 +61,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{:reject, nil} = VocabularyPolicy.filter(message)
|
||||
|
||||
Pleroma.Config.put([:mrf_vocabulary, :accept], config)
|
||||
end
|
||||
end
|
||||
|
||||
describe "reject" do
|
||||
clear_config([:mrf_vocabulary, :reject])
|
||||
|
||||
test "it rejects based on parent activity type" do
|
||||
config = Pleroma.Config.get([:mrf_vocabulary, :reject])
|
||||
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"])
|
||||
|
||||
message = %{
|
||||
|
@ -85,12 +76,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{: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 = %{
|
||||
|
@ -102,12 +90,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{: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 = %{
|
||||
|
@ -116,8 +101,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
|
|||
}
|
||||
|
||||
{:ok, ^message} = VocabularyPolicy.filter(message)
|
||||
|
||||
Pleroma.Config.put([:mrf_vocabulary, :reject], config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
alias Pleroma.Web.ActivityPub.Relay
|
||||
|
||||
import Pleroma.Factory
|
||||
import Mock
|
||||
|
||||
test "gets an actor for the relay" do
|
||||
user = Relay.get_actor()
|
||||
|
@ -43,16 +44,21 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
user = insert(:user)
|
||||
service_actor = Relay.get_actor()
|
||||
ActivityPub.follow(service_actor, user)
|
||||
Pleroma.User.follow(service_actor, user)
|
||||
assert "#{user.ap_id}/followers" in refresh_record(service_actor).following
|
||||
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]
|
||||
refute "#{user.ap_id}/followers" in refresh_record(service_actor).following
|
||||
end
|
||||
end
|
||||
|
||||
describe "publish/1" do
|
||||
clear_config([:instance, :federating])
|
||||
|
||||
test "returns error when activity not `Create` type" do
|
||||
activity = insert(:like_activity)
|
||||
assert Relay.publish(activity) == {:error, "Not implemented"}
|
||||
|
@ -63,13 +69,44 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
assert Relay.publish(activity) == {:error, false}
|
||||
end
|
||||
|
||||
test "returns announce activity" do
|
||||
test "returns error when object is unknown" do
|
||||
activity =
|
||||
insert(:note_activity,
|
||||
data: %{
|
||||
"type" => "Create",
|
||||
"object" => "http://mastodon.example.org/eee/99541947525187367"
|
||||
}
|
||||
)
|
||||
|
||||
assert Relay.publish(activity) == {:error, nil}
|
||||
end
|
||||
|
||||
test_with_mock "returns announce activity and publish to federate",
|
||||
Pleroma.Web.Federator,
|
||||
[:passthrough],
|
||||
[] do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
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"]
|
||||
assert called(Pleroma.Web.Federator.publish(activity, 5))
|
||||
end
|
||||
|
||||
test_with_mock "returns announce activity and not publish to federate",
|
||||
Pleroma.Web.Federator,
|
||||
[:passthrough],
|
||||
[] do
|
||||
Pleroma.Config.put([:instance, :federating], false)
|
||||
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"]
|
||||
refute called(Pleroma.Web.Federator.publish(activity, 5))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
|
|||
end
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it works for osada follow request" do
|
||||
user = insert(:user)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/osada-follow-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", user.ap_id)
|
||||
|
||||
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://apfed.club/channel/indio"
|
||||
assert data["type"] == "Follow"
|
||||
assert data["id"] == "https://apfed.club/follow/9"
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
assert activity.data["state"] == "accept"
|
||||
assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||
end
|
||||
|
||||
test "it works for incoming follow requests" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
clear_config([:instance, :max_remote_account_fields])
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it ignores an incoming notice if we already have it" do
|
||||
activity = insert(:note_activity)
|
||||
|
@ -509,6 +511,68 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert user.bio == "<p>Some bio</p>"
|
||||
end
|
||||
|
||||
test "it works with custom profile fields" do
|
||||
{:ok, activity} =
|
||||
"test/fixtures/mastodon-post-activity.json"
|
||||
|> File.read!()
|
||||
|> Poison.decode!()
|
||||
|> Transmogrifier.handle_incoming()
|
||||
|
||||
user = User.get_cached_by_ap_id(activity.actor)
|
||||
|
||||
assert User.Info.fields(user.info) == [
|
||||
%{"name" => "foo", "value" => "bar"},
|
||||
%{"name" => "foo1", "value" => "bar1"}
|
||||
]
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
|
||||
|
||||
object =
|
||||
update_data["object"]
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("id", user.ap_id)
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("object", object)
|
||||
|
||||
{:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.Info.fields(user.info) == [
|
||||
%{"name" => "foo", "value" => "updated"},
|
||||
%{"name" => "foo1", "value" => "updated"}
|
||||
]
|
||||
|
||||
Pleroma.Config.put([:instance, :max_remote_account_fields], 2)
|
||||
|
||||
update_data =
|
||||
put_in(update_data, ["object", "attachment"], [
|
||||
%{"name" => "foo", "type" => "PropertyValue", "value" => "bar"},
|
||||
%{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"},
|
||||
%{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"}
|
||||
])
|
||||
|
||||
{:ok, _} = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.Info.fields(user.info) == [
|
||||
%{"name" => "foo", "value" => "updated"},
|
||||
%{"name" => "foo1", "value" => "updated"}
|
||||
]
|
||||
|
||||
update_data = put_in(update_data, ["object", "attachment"], [])
|
||||
|
||||
{:ok, _} = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.Info.fields(user.info) == []
|
||||
end
|
||||
|
||||
test "it works for incoming update activities which lock the account" do
|
||||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
describe "fetch the latest Follow" do
|
||||
test "fetches the latest Follow activity" do
|
||||
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
||||
|
@ -87,6 +89,32 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "make_unlike_data/3" do
|
||||
test "returns data for unlike activity" do
|
||||
user = insert(:user)
|
||||
like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
|
||||
|
||||
assert Utils.make_unlike_data(user, like_activity, nil) == %{
|
||||
"type" => "Undo",
|
||||
"actor" => user.ap_id,
|
||||
"object" => like_activity.data,
|
||||
"to" => [user.follower_address, like_activity.data["actor"]],
|
||||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => like_activity.data["context"]
|
||||
}
|
||||
|
||||
assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
|
||||
"type" => "Undo",
|
||||
"actor" => user.ap_id,
|
||||
"object" => like_activity.data,
|
||||
"to" => [user.follower_address, like_activity.data["actor"]],
|
||||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => like_activity.data["context"],
|
||||
"id" => "9mJEZK0tky1w2xD2vY"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "make_like_data" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
@ -299,4 +327,78 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_element_in_object/3" do
|
||||
test "updates likes" do
|
||||
user = insert(:user)
|
||||
activity = insert(:note_activity)
|
||||
object = Object.normalize(activity)
|
||||
|
||||
assert {:ok, updated_object} =
|
||||
Utils.update_element_in_object(
|
||||
"like",
|
||||
[user.ap_id],
|
||||
object
|
||||
)
|
||||
|
||||
assert updated_object.data["likes"] == [user.ap_id]
|
||||
assert updated_object.data["like_count"] == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "add_like_to_object/2" do
|
||||
test "add actor to likes" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
object = insert(:note)
|
||||
|
||||
assert {:ok, updated_object} =
|
||||
Utils.add_like_to_object(
|
||||
%Activity{data: %{"actor" => user.ap_id}},
|
||||
object
|
||||
)
|
||||
|
||||
assert updated_object.data["likes"] == [user.ap_id]
|
||||
assert updated_object.data["like_count"] == 1
|
||||
|
||||
assert {:ok, updated_object2} =
|
||||
Utils.add_like_to_object(
|
||||
%Activity{data: %{"actor" => user2.ap_id}},
|
||||
updated_object
|
||||
)
|
||||
|
||||
assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
|
||||
assert updated_object2.data["like_count"] == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "remove_like_from_object/2" do
|
||||
test "removes ap_id from likes" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
|
||||
|
||||
assert {:ok, updated_object} =
|
||||
Utils.remove_like_from_object(
|
||||
%Activity{data: %{"actor" => user.ap_id}},
|
||||
object
|
||||
)
|
||||
|
||||
assert updated_object.data["likes"] == [user2.ap_id]
|
||||
assert updated_object.data["like_count"] == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_existing_like/2" do
|
||||
test "fetches existing like" do
|
||||
note_activity = insert(:note_activity)
|
||||
assert object = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
refute Utils.get_existing_like(user.ap_id, object)
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object)
|
||||
|
||||
assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,21 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY")
|
||||
end
|
||||
|
||||
test "Renders profile fields" do
|
||||
fields = [
|
||||
%{"name" => "foo", "value" => "bar"}
|
||||
]
|
||||
|
||||
{:ok, user} =
|
||||
insert(:user)
|
||||
|> User.upgrade_changeset(%{info: %{fields: fields}})
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
assert %{
|
||||
"attachment" => [%{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}]
|
||||
} = UserView.render("user.json", %{user: user})
|
||||
end
|
||||
|
||||
test "Does not add an avatar image if the user hasn't set one" do
|
||||
user = insert(:user)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
@ -24,6 +26,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert log_entry.data["subject"]["nickname"] == user.nickname
|
||||
assert log_entry.data["action"] == "delete"
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deleted user @#{user.nickname}"
|
||||
|
||||
assert json_response(conn, 200) == user.nickname
|
||||
end
|
||||
|
||||
|
@ -35,12 +45,135 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> post("/api/pleroma/admin/users", %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@example.org",
|
||||
"password" => "test"
|
||||
"users" => [
|
||||
%{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@example.org",
|
||||
"password" => "test"
|
||||
},
|
||||
%{
|
||||
"nickname" => "lain2",
|
||||
"email" => "lain2@example.org",
|
||||
"password" => "test"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert json_response(conn, 200) == "lain"
|
||||
response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
|
||||
assert response == ["success", "success"]
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
|
||||
end
|
||||
|
||||
test "Cannot create user with exisiting email" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> post("/api/pleroma/admin/users", %{
|
||||
"users" => [
|
||||
%{
|
||||
"nickname" => "lain",
|
||||
"email" => user.email,
|
||||
"password" => "test"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert json_response(conn, 409) == [
|
||||
%{
|
||||
"code" => 409,
|
||||
"data" => %{
|
||||
"email" => user.email,
|
||||
"nickname" => "lain"
|
||||
},
|
||||
"error" => "email has already been taken",
|
||||
"type" => "error"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "Cannot create user with exisiting nickname" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> post("/api/pleroma/admin/users", %{
|
||||
"users" => [
|
||||
%{
|
||||
"nickname" => user.nickname,
|
||||
"email" => "someuser@plerama.social",
|
||||
"password" => "test"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert json_response(conn, 409) == [
|
||||
%{
|
||||
"code" => 409,
|
||||
"data" => %{
|
||||
"email" => "someuser@plerama.social",
|
||||
"nickname" => user.nickname
|
||||
},
|
||||
"error" => "nickname has already been taken",
|
||||
"type" => "error"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "Multiple user creation works in transaction" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> post("/api/pleroma/admin/users", %{
|
||||
"users" => [
|
||||
%{
|
||||
"nickname" => "newuser",
|
||||
"email" => "newuser@pleroma.social",
|
||||
"password" => "test"
|
||||
},
|
||||
%{
|
||||
"nickname" => "lain",
|
||||
"email" => user.email,
|
||||
"password" => "test"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert json_response(conn, 409) == [
|
||||
%{
|
||||
"code" => 409,
|
||||
"data" => %{
|
||||
"email" => user.email,
|
||||
"nickname" => "lain"
|
||||
},
|
||||
"error" => "email has already been taken",
|
||||
"type" => "error"
|
||||
},
|
||||
%{
|
||||
"code" => 409,
|
||||
"data" => %{
|
||||
"email" => "newuser@pleroma.social",
|
||||
"nickname" => "newuser"
|
||||
},
|
||||
"error" => "",
|
||||
"type" => "error"
|
||||
}
|
||||
]
|
||||
|
||||
assert User.get_by_nickname("newuser") === nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -99,6 +232,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
follower = User.get_cached_by_id(follower.id)
|
||||
|
||||
assert User.following?(follower, user)
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -122,6 +260,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
follower = User.get_cached_by_id(follower.id)
|
||||
|
||||
refute User.following?(follower, user)
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -142,17 +285,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
}&tags[]=foo&tags[]=bar"
|
||||
)
|
||||
|
||||
%{conn: conn, user1: user1, user2: user2, user3: user3}
|
||||
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
|
||||
end
|
||||
|
||||
test "it appends specified tags to users with specified nicknames", %{
|
||||
conn: conn,
|
||||
admin: admin,
|
||||
user1: user1,
|
||||
user2: user2
|
||||
} do
|
||||
assert json_response(conn, :no_content)
|
||||
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
|
||||
assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
users =
|
||||
[user1.nickname, user2.nickname]
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags = ["foo", "bar"] |> Enum.join(", ")
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} added tags: #{tags} to users: #{users}"
|
||||
end
|
||||
|
||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||
|
@ -178,17 +334,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
}&tags[]=x&tags[]=z"
|
||||
)
|
||||
|
||||
%{conn: conn, user1: user1, user2: user2, user3: user3}
|
||||
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
|
||||
end
|
||||
|
||||
test "it removes specified tags from users with specified nicknames", %{
|
||||
conn: conn,
|
||||
admin: admin,
|
||||
user1: user1,
|
||||
user2: user2
|
||||
} do
|
||||
assert json_response(conn, :no_content)
|
||||
assert User.get_cached_by_id(user1.id).tags == []
|
||||
assert User.get_cached_by_id(user2.id).tags == ["y"]
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
users =
|
||||
[user1.nickname, user2.nickname]
|
||||
|> Enum.map(&"@#{&1}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
tags = ["x", "z"] |> Enum.join(", ")
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} removed tags: #{tags} from users: #{users}"
|
||||
end
|
||||
|
||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||
|
@ -226,6 +395,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert json_response(conn, 200) == %{
|
||||
"is_admin" => true
|
||||
}
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} made @#{user.nickname} admin"
|
||||
end
|
||||
|
||||
test "/:right DELETE, can remove from a permission group" do
|
||||
|
@ -241,6 +415,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert json_response(conn, 200) == %{
|
||||
"is_admin" => false
|
||||
}
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} revoked admin role from @#{user.nickname}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -253,10 +432,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|
||||
%{conn: conn}
|
||||
%{conn: conn, admin: admin}
|
||||
end
|
||||
|
||||
test "deactivates the user", %{conn: conn} do
|
||||
test "deactivates the user", %{conn: conn, admin: admin} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
|
@ -266,9 +445,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
user = User.get_cached_by_id(user.id)
|
||||
assert user.info.deactivated == true
|
||||
assert json_response(conn, :no_content)
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deactivated user @#{user.nickname}"
|
||||
end
|
||||
|
||||
test "activates the user", %{conn: conn} do
|
||||
test "activates the user", %{conn: conn, admin: admin} do
|
||||
user = insert(:user, info: %{deactivated: true})
|
||||
|
||||
conn =
|
||||
|
@ -278,6 +462,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
user = User.get_cached_by_id(user.id)
|
||||
assert user.info.deactivated == false
|
||||
assert json_response(conn, :no_content)
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} activated user @#{user.nickname}"
|
||||
end
|
||||
|
||||
test "returns 403 when requested by a non-admin", %{conn: conn} do
|
||||
|
@ -294,20 +483,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
describe "POST /api/pleroma/admin/email_invite, with valid config" do
|
||||
setup do
|
||||
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
|
||||
invites_enabled = Pleroma.Config.get([:instance, :invites_enabled])
|
||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :registrations_open], registrations_open)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], invites_enabled)
|
||||
:ok
|
||||
end)
|
||||
|
||||
[user: insert(:user, info: %{is_admin: true})]
|
||||
end
|
||||
|
||||
clear_config([:instance, :registrations_open]) do
|
||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||
end
|
||||
|
||||
clear_config([:instance, :invites_enabled]) do
|
||||
Pleroma.Config.put([:instance, :invites_enabled], true)
|
||||
end
|
||||
|
||||
test "sends invitation and returns 204", %{conn: conn, user: user} do
|
||||
recipient_email = "foo@bar.com"
|
||||
recipient_name = "J. D."
|
||||
|
@ -360,18 +546,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
[user: insert(:user, info: %{is_admin: true})]
|
||||
end
|
||||
|
||||
clear_config([:instance, :registrations_open])
|
||||
clear_config([:instance, :invites_enabled])
|
||||
|
||||
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn, user: user} do
|
||||
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
|
||||
invites_enabled = Pleroma.Config.get([:instance, :invites_enabled])
|
||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], false)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :registrations_open], registrations_open)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], invites_enabled)
|
||||
:ok
|
||||
end)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|
@ -381,17 +562,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
end
|
||||
|
||||
test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: user} do
|
||||
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
|
||||
invites_enabled = Pleroma.Config.get([:instance, :invites_enabled])
|
||||
Pleroma.Config.put([:instance, :registrations_open], true)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :registrations_open], registrations_open)
|
||||
Pleroma.Config.put([:instance, :invites_enabled], invites_enabled)
|
||||
:ok
|
||||
end)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|
@ -884,6 +1057,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
||||
}
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deactivated user @#{user.nickname}"
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/admin/users/invite_token" do
|
||||
|
@ -1069,25 +1247,35 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
"status_ids" => [activity.id]
|
||||
})
|
||||
|
||||
%{conn: assign(conn, :user, admin), id: report_id}
|
||||
%{conn: assign(conn, :user, admin), id: report_id, admin: admin}
|
||||
end
|
||||
|
||||
test "mark report as resolved", %{conn: conn, id: id} do
|
||||
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response["state"] == "resolved"
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
||||
end
|
||||
|
||||
test "closes report", %{conn: conn, id: id} do
|
||||
test "closes report", %{conn: conn, id: id, admin: admin} do
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response["state"] == "closed"
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} updated report ##{id} with 'closed' state"
|
||||
end
|
||||
|
||||
test "returns 400 when state is unknown", %{conn: conn, id: id} do
|
||||
|
@ -1218,14 +1406,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
describe "POST /api/pleroma/admin/reports/:id/respond" do
|
||||
setup %{conn: conn} do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
%{conn: assign(conn, :user, admin)}
|
||||
%{conn: assign(conn, :user, admin), admin: admin}
|
||||
end
|
||||
|
||||
test "returns created dm", %{conn: conn} do
|
||||
test "returns created dm", %{conn: conn, admin: admin} do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
activity = insert(:note_activity, user: target_user)
|
||||
|
||||
|
@ -1248,6 +1437,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert reporter.nickname in recipients
|
||||
assert response["content"] == "I will check it out"
|
||||
assert response["visibility"] == "direct"
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} responded with 'I will check it out' to report ##{
|
||||
response["id"]
|
||||
}"
|
||||
end
|
||||
|
||||
test "returns 400 when status is missing", %{conn: conn} do
|
||||
|
@ -1271,10 +1467,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
admin = insert(:user, info: %{is_admin: true})
|
||||
activity = insert(:note_activity)
|
||||
|
||||
%{conn: assign(conn, :user, admin), id: activity.id}
|
||||
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
|
||||
end
|
||||
|
||||
test "toggle sensitive flag", %{conn: conn, id: id} do
|
||||
test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|
||||
|
@ -1282,6 +1478,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
assert response["sensitive"]
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|
||||
|
@ -1290,7 +1491,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
refute response["sensitive"]
|
||||
end
|
||||
|
||||
test "change visibility flag", %{conn: conn, id: id} do
|
||||
test "change visibility flag", %{conn: conn, id: id, admin: admin} do
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
|
||||
|
@ -1298,6 +1499,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
assert response["visibility"] == "public"
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
|
||||
|
@ -1327,15 +1533,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
admin = insert(:user, info: %{is_admin: true})
|
||||
activity = insert(:note_activity)
|
||||
|
||||
%{conn: assign(conn, :user, admin), id: activity.id}
|
||||
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
|
||||
end
|
||||
|
||||
test "deletes status", %{conn: conn, id: id} do
|
||||
test "deletes status", %{conn: conn, id: id, admin: admin} do
|
||||
conn
|
||||
|> delete("/api/pleroma/admin/statuses/#{id}")
|
||||
|> json_response(:ok)
|
||||
|
||||
refute Activity.get_by_id(id)
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deleted status ##{id}"
|
||||
end
|
||||
|
||||
test "returns error when status is not exist", %{conn: conn} do
|
||||
|
@ -1402,17 +1613,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
:ok = File.rm(temp_file)
|
||||
end)
|
||||
|
||||
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||
end)
|
||||
|
||||
%{conn: assign(conn, :user, admin)}
|
||||
end
|
||||
|
||||
clear_config([:instance, :dynamic_configuration]) do
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
end
|
||||
|
||||
test "create new config setting in db", %{conn: conn} do
|
||||
conn =
|
||||
post(conn, "/api/pleroma/admin/config", %{
|
||||
|
@ -1961,17 +2168,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
:ok = File.rm(temp_file)
|
||||
end)
|
||||
|
||||
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||
end)
|
||||
|
||||
%{conn: assign(conn, :user, admin), admin: admin}
|
||||
end
|
||||
|
||||
clear_config([:instance, :dynamic_configuration]) do
|
||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||
end
|
||||
|
||||
test "transfer settings to DB and to file", %{conn: conn, admin: admin} do
|
||||
assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
|
||||
conn = get(conn, "/api/pleroma/admin/config/migrate_to_db")
|
||||
|
@ -2044,6 +2247,108 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert json_response(conn, 200) |> length() == 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/admin/moderation_log" do
|
||||
setup %{conn: conn} do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
%{conn: assign(conn, :user, admin), admin: admin}
|
||||
end
|
||||
|
||||
test "returns the log", %{conn: conn, admin: admin} do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: %{
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname,
|
||||
"type" => "user"
|
||||
},
|
||||
action: "relay_follow",
|
||||
target: "https://example.org/relay"
|
||||
},
|
||||
inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
|
||||
})
|
||||
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: %{
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname,
|
||||
"type" => "user"
|
||||
},
|
||||
action: "relay_unfollow",
|
||||
target: "https://example.org/relay"
|
||||
},
|
||||
inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
|
||||
})
|
||||
|
||||
conn = get(conn, "/api/pleroma/admin/moderation_log")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
[first_entry, second_entry] = response
|
||||
|
||||
assert response |> length() == 2
|
||||
assert first_entry["data"]["action"] == "relay_unfollow"
|
||||
|
||||
assert first_entry["message"] ==
|
||||
"@#{admin.nickname} unfollowed relay: https://example.org/relay"
|
||||
|
||||
assert second_entry["data"]["action"] == "relay_follow"
|
||||
|
||||
assert second_entry["message"] ==
|
||||
"@#{admin.nickname} followed relay: https://example.org/relay"
|
||||
end
|
||||
|
||||
test "returns the log with pagination", %{conn: conn, admin: admin} do
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: %{
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname,
|
||||
"type" => "user"
|
||||
},
|
||||
action: "relay_follow",
|
||||
target: "https://example.org/relay"
|
||||
},
|
||||
inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
|
||||
})
|
||||
|
||||
Repo.insert(%ModerationLog{
|
||||
data: %{
|
||||
actor: %{
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname,
|
||||
"type" => "user"
|
||||
},
|
||||
action: "relay_unfollow",
|
||||
target: "https://example.org/relay"
|
||||
},
|
||||
inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
|
||||
})
|
||||
|
||||
conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
|
||||
|
||||
response1 = json_response(conn1, 200)
|
||||
[first_entry] = response1
|
||||
|
||||
assert response1 |> length() == 1
|
||||
assert first_entry["data"]["action"] == "relay_unfollow"
|
||||
|
||||
assert first_entry["message"] ==
|
||||
"@#{admin.nickname} unfollowed relay: https://example.org/relay"
|
||||
|
||||
conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
|
||||
|
||||
response2 = json_response(conn2, 200)
|
||||
[second_entry] = response2
|
||||
|
||||
assert response2 |> length() == 1
|
||||
assert second_entry["data"]["action"] == "relay_follow"
|
||||
|
||||
assert second_entry["message"] ==
|
||||
"@#{admin.nickname} followed relay: https://example.org/relay"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Needed for testing
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue