Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
12
.dockerignore
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.*
|
||||||
|
*.md
|
||||||
|
AGPL-3
|
||||||
|
CC-BY-SA-4.0
|
||||||
|
COPYING
|
||||||
|
*file
|
||||||
|
elixir_buildpack.config
|
||||||
|
docs/
|
||||||
|
test/
|
||||||
|
|
||||||
|
# Required to get version
|
||||||
|
!.git
|
13
CHANGELOG.md
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Security
|
||||||
|
- OStatus: eliminate the possibility of a protocol downgrade attack.
|
||||||
|
- OStatus: prevent following locked accounts, bypassing the approval process.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
|
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
|
||||||
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
|
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
|
||||||
|
@ -17,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Not being able to pin unlisted posts
|
- Not being able to pin unlisted posts
|
||||||
|
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised.
|
||||||
- Metadata rendering errors resulting in the entire page being inaccessible
|
- Metadata rendering errors resulting in the entire page being inaccessible
|
||||||
- Federation/MediaProxy not working with instances that have wrong certificate order
|
- Federation/MediaProxy not working with instances that have wrong certificate order
|
||||||
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
|
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
|
||||||
|
@ -30,6 +35,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
|
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
|
||||||
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
|
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
|
||||||
- Not being able to access the Mastodon FE login page on private instances
|
- Not being able to access the Mastodon FE login page on private instances
|
||||||
|
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
|
||||||
|
- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
|
||||||
|
- Report email not being sent to admins when the reporter is a remote user
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
||||||
|
@ -63,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub: Optional signing of ActivityPub object fetches.
|
- ActivityPub: Optional signing of ActivityPub object fetches.
|
||||||
- Admin API: Endpoint for fetching latest user's statuses
|
- Admin API: Endpoint for fetching latest user's statuses
|
||||||
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
|
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
|
||||||
|
- Relays: Added a task to list relay subscriptions.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
||||||
|
@ -70,6 +79,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- RichMedia: parsers and their order are configured in `rich_media` config.
|
- RichMedia: parsers and their order are configured in `rich_media` config.
|
||||||
- RichMedia: add the rich media ttl based on image expiration time.
|
- RichMedia: add the rich media ttl based on image expiration time.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Emoji: Remove longfox emojis.
|
||||||
|
- Remove `Reply-To` header from report emails for admins.
|
||||||
|
|
||||||
## [1.0.1] - 2019-07-14
|
## [1.0.1] - 2019-07-14
|
||||||
### Security
|
### Security
|
||||||
- OStatus: fix an object spoofing vulnerability.
|
- OStatus: fix an object spoofing vulnerability.
|
||||||
|
|
39
Dockerfile
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
FROM rinpatch/elixir:1.9.0-rc.0-alpine as build
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV MIX_ENV=prod
|
||||||
|
|
||||||
|
RUN apk add git gcc g++ musl-dev make &&\
|
||||||
|
echo "import Mix.Config" > config/prod.secret.exs &&\
|
||||||
|
mix local.hex --force &&\
|
||||||
|
mix local.rebar --force &&\
|
||||||
|
mix deps.get --only prod &&\
|
||||||
|
mkdir release &&\
|
||||||
|
mix release --path release
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
ARG HOME=/opt/pleroma
|
||||||
|
ARG DATA=/var/lib/pleroma
|
||||||
|
|
||||||
|
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
||||||
|
apk update &&\
|
||||||
|
apk add ncurses postgresql-client &&\
|
||||||
|
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||||
|
mkdir -p ${DATA}/uploads &&\
|
||||||
|
mkdir -p ${DATA}/static &&\
|
||||||
|
chown -R pleroma ${DATA} &&\
|
||||||
|
mkdir -p /etc/pleroma &&\
|
||||||
|
chown -R pleroma /etc/pleroma
|
||||||
|
|
||||||
|
USER pleroma
|
||||||
|
|
||||||
|
COPY --from=build --chown=pleroma:0 /release ${HOME}
|
||||||
|
|
||||||
|
COPY ./config/docker.exs /etc/pleroma/config.exs
|
||||||
|
COPY ./docker-entrypoint.sh ${HOME}
|
||||||
|
|
||||||
|
EXPOSE 4000
|
||||||
|
|
||||||
|
ENTRYPOINT ["/opt/pleroma/docker-entrypoint.sh"]
|
|
@ -21,7 +21,7 @@ If you want to run your own server, feel free to contact us at @lain@pleroma.soy
|
||||||
Currently Pleroma is not packaged by any OS/Distros, but feel free to reach out to us at [#pleroma-dev on freenode](https://webchat.freenode.net/?channels=%23pleroma-dev) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma-dev:matrix.org> for assistance. If you want to change default options in your Pleroma package, please **discuss it with us first**.
|
Currently Pleroma is not packaged by any OS/Distros, but feel free to reach out to us at [#pleroma-dev on freenode](https://webchat.freenode.net/?channels=%23pleroma-dev) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma-dev:matrix.org> for assistance. If you want to change default options in your Pleroma package, please **discuss it with us first**.
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://github.com/sn0w/pleroma-docker>.
|
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
|
|
68
config/docker.exs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import Config
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
|
url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
|
||||||
|
http: [ip: {0, 0, 0, 0}, port: 4000]
|
||||||
|
|
||||||
|
config :pleroma, :instance,
|
||||||
|
name: System.get_env("INSTANCE_NAME", "Pleroma"),
|
||||||
|
email: System.get_env("ADMIN_EMAIL"),
|
||||||
|
notify_email: System.get_env("NOTIFY_EMAIL"),
|
||||||
|
limit: 5000,
|
||||||
|
registrations_open: false,
|
||||||
|
dynamic_configuration: true
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Repo,
|
||||||
|
adapter: Ecto.Adapters.Postgres,
|
||||||
|
username: System.get_env("DB_USER", "pleroma"),
|
||||||
|
password: System.fetch_env!("DB_PASS"),
|
||||||
|
database: System.get_env("DB_NAME", "pleroma"),
|
||||||
|
hostname: System.get_env("DB_HOST", "db"),
|
||||||
|
pool_size: 10
|
||||||
|
|
||||||
|
# Configure web push notifications
|
||||||
|
config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
|
||||||
|
|
||||||
|
config :pleroma, :database, rum_enabled: false
|
||||||
|
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||||
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||||
|
|
||||||
|
# We can't store the secrets in this file, since this is baked into the docker image
|
||||||
|
if not File.exists?("/var/lib/pleroma/secret.exs") do
|
||||||
|
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||||
|
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||||
|
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||||
|
|
||||||
|
secret_file =
|
||||||
|
EEx.eval_string(
|
||||||
|
"""
|
||||||
|
import Config
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
|
secret_key_base: "<%= secret %>",
|
||||||
|
signing_salt: "<%= signing_salt %>"
|
||||||
|
|
||||||
|
config :web_push_encryption, :vapid_details,
|
||||||
|
public_key: "<%= web_push_public_key %>",
|
||||||
|
private_key: "<%= web_push_private_key %>"
|
||||||
|
""",
|
||||||
|
secret: secret,
|
||||||
|
signing_salt: signing_salt,
|
||||||
|
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||||
|
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
|
||||||
|
)
|
||||||
|
|
||||||
|
File.write("/var/lib/pleroma/secret.exs", secret_file)
|
||||||
|
end
|
||||||
|
|
||||||
|
import_config("/var/lib/pleroma/secret.exs")
|
||||||
|
|
||||||
|
# For additional user config
|
||||||
|
if File.exists?("/var/lib/pleroma/config.exs"),
|
||||||
|
do: import_config("/var/lib/pleroma/config.exs"),
|
||||||
|
else:
|
||||||
|
File.write("/var/lib/pleroma/config.exs", """
|
||||||
|
import Config
|
||||||
|
|
||||||
|
# For additional configuration outside of environmental variables
|
||||||
|
""")
|
|
@ -1,43 +1,2 @@
|
||||||
firefox, /emoji/Firefox.gif, Gif,Fun
|
firefox, /emoji/Firefox.gif, Gif,Fun
|
||||||
blank, /emoji/blank.png, Fun
|
blank, /emoji/blank.png, Fun
|
||||||
f_00b, /emoji/f_00b.png
|
|
||||||
f_00b11b, /emoji/f_00b11b.png
|
|
||||||
f_00b33b, /emoji/f_00b33b.png
|
|
||||||
f_00h, /emoji/f_00h.png
|
|
||||||
f_00t, /emoji/f_00t.png
|
|
||||||
f_01b, /emoji/f_01b.png
|
|
||||||
f_03b, /emoji/f_03b.png
|
|
||||||
f_10b, /emoji/f_10b.png
|
|
||||||
f_11b, /emoji/f_11b.png
|
|
||||||
f_11b00b, /emoji/f_11b00b.png
|
|
||||||
f_11b22b, /emoji/f_11b22b.png
|
|
||||||
f_11h, /emoji/f_11h.png
|
|
||||||
f_11t, /emoji/f_11t.png
|
|
||||||
f_12b, /emoji/f_12b.png
|
|
||||||
f_21b, /emoji/f_21b.png
|
|
||||||
f_22b, /emoji/f_22b.png
|
|
||||||
f_22b11b, /emoji/f_22b11b.png
|
|
||||||
f_22b33b, /emoji/f_22b33b.png
|
|
||||||
f_22h, /emoji/f_22h.png
|
|
||||||
f_22t, /emoji/f_22t.png
|
|
||||||
f_23b, /emoji/f_23b.png
|
|
||||||
f_30b, /emoji/f_30b.png
|
|
||||||
f_32b, /emoji/f_32b.png
|
|
||||||
f_33b, /emoji/f_33b.png
|
|
||||||
f_33b00b, /emoji/f_33b00b.png
|
|
||||||
f_33b22b, /emoji/f_33b22b.png
|
|
||||||
f_33h, /emoji/f_33h.png
|
|
||||||
f_33t, /emoji/f_33t.png
|
|
||||||
mayushii, /emoji/mayushii.png
|
|
||||||
hahaendme, /emoji/death.png
|
|
||||||
stabby, /emoji/stabs.png
|
|
||||||
endme, /emoji/end_me.png
|
|
||||||
fingerguns, /emoji/fingergun.png
|
|
||||||
marismug, /emoji/marismug.png
|
|
||||||
laffeydrink, /emoji/laffeydrink.png
|
|
||||||
kagaangry, /emoji/kagaangry.png
|
|
||||||
hammbite, /emoji/hammbite.png
|
|
||||||
longislandcry, /emoji/longislandcry.png
|
|
||||||
thinktirpitz, /emoji/thinktirpitz.png
|
|
||||||
shiratsuyusleep, /emoji/shiratsuyusleep.png
|
|
||||||
enterprisequestion, /emoji/enterprisequestion.png
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ config :pleroma, :instance,
|
||||||
email: "admin@example.com",
|
email: "admin@example.com",
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
skip_thread_containment: false,
|
skip_thread_containment: false,
|
||||||
federating: false
|
federating: false,
|
||||||
|
external_user_synchronization: false
|
||||||
|
|
||||||
config :pleroma, :activitypub, sign_object_fetches: false
|
config :pleroma, :activitypub, sign_object_fetches: false
|
||||||
|
|
||||||
|
|
14
docker-entrypoint.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/ash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "-- Waiting for database..."
|
||||||
|
while ! pg_isready -U ${DB_USER:-pleroma} -d postgres://${DB_HOST:-db}:5432/${DB_NAME:-pleroma} -t 1; do
|
||||||
|
sleep 1s
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "-- Running migrations..."
|
||||||
|
$HOME/bin/pleroma_ctl migrate
|
||||||
|
|
||||||
|
echo "-- Starting!"
|
||||||
|
exec $HOME/bin/pleroma start
|
|
@ -627,6 +627,9 @@ Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
|
||||||
Keywords can be passed as lists with 2 child tuples, e.g.
|
Keywords can be passed as lists with 2 child tuples, e.g.
|
||||||
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
|
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
|
||||||
|
|
||||||
|
If value contains list of settings `[subkey: val1, subkey2: val2, subkey3: val3]`, it's possible to remove only subkeys instead of all settings passing `subkeys` parameter. E.g.:
|
||||||
|
{"group": "pleroma", "key": "some_key", "delete": "true", "subkeys": [":subkey", ":subkey3"]}.
|
||||||
|
|
||||||
Compile time settings (need instance reboot):
|
Compile time settings (need instance reboot):
|
||||||
- all settings by this keys:
|
- all settings by this keys:
|
||||||
- `:hackney_pools`
|
- `:hackney_pools`
|
||||||
|
@ -645,6 +648,7 @@ Compile time settings (need instance reboot):
|
||||||
- `key` (string or string with leading `:` for atoms)
|
- `key` (string or string with leading `:` for atoms)
|
||||||
- `value` (string, [], {} or {"tuple": []})
|
- `value` (string, [], {} or {"tuple": []})
|
||||||
- `delete` = true (optional, if parameter must be deleted)
|
- `delete` = true (optional, if parameter must be deleted)
|
||||||
|
- `subkeys` [(string with leading `:` for atoms)] (optional, works only if `delete=true` parameter is passed, otherwise will be ignored)
|
||||||
]
|
]
|
||||||
|
|
||||||
- Request (example):
|
- Request (example):
|
||||||
|
|
|
@ -25,7 +25,7 @@ At this time, write CNAME to CDN in public_endpoint.
|
||||||
|
|
||||||
## Pleroma.Upload.Filter.Mogrify
|
## Pleroma.Upload.Filter.Mogrify
|
||||||
|
|
||||||
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"impode", "1"}]`.
|
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.
|
||||||
|
|
||||||
## Pleroma.Upload.Filter.Dedupe
|
## Pleroma.Upload.Filter.Dedupe
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# How to activate mediaproxy
|
# How to activate mediaproxy
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Without the `mediaproxy` function, Pleroma don't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
|
Without the `mediaproxy` function, Pleroma doesn't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
|
||||||
With the `mediaproxy` function you can use the cache ability of nginx, to cache these content, so user can access it faster, cause it's loaded from your server.
|
With the `mediaproxy` function you can use nginx to cache this content, so users can access it faster, because it's loaded from your server.
|
||||||
|
|
||||||
## Activate it
|
## Activate it
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Mix.Tasks.Pleroma.Relay do
|
defmodule Mix.Tasks.Pleroma.Relay do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Mix.Pleroma
|
import Mix.Pleroma
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
@shortdoc "Manages remote relays"
|
@shortdoc "Manages remote relays"
|
||||||
|
@ -22,6 +23,10 @@ defmodule Mix.Tasks.Pleroma.Relay do
|
||||||
``mix pleroma.relay unfollow <relay_url>``
|
``mix pleroma.relay unfollow <relay_url>``
|
||||||
|
|
||||||
Example: ``mix pleroma.relay unfollow https://example.org/relay``
|
Example: ``mix pleroma.relay unfollow https://example.org/relay``
|
||||||
|
|
||||||
|
## List relay subscriptions
|
||||||
|
|
||||||
|
``mix pleroma.relay list``
|
||||||
"""
|
"""
|
||||||
def run(["follow", target]) do
|
def run(["follow", target]) do
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
@ -44,4 +49,19 @@ defmodule Mix.Tasks.Pleroma.Relay do
|
||||||
{:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")
|
{:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
else
|
||||||
|
e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,8 +31,8 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||||
mix pleroma.user invite [OPTION...]
|
mix pleroma.user invite [OPTION...]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
- `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05")
|
- `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
|
||||||
- `--max_use NUMBER` - maximum numbers of token uses
|
- `--max-use NUMBER` - maximum numbers of token uses
|
||||||
|
|
||||||
## List generated invites
|
## List generated invites
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,6 @@ defmodule Pleroma.Emails.AdminEmail do
|
||||||
new()
|
new()
|
||||||
|> to({to.name, to.email})
|
|> to({to.name, to.email})
|
||||||
|> from({instance_name(), instance_notify_email()})
|
|> from({instance_name(), instance_notify_email()})
|
||||||
|> reply_to({reporter.name, reporter.email})
|
|
||||||
|> subject("#{instance_name()} Report")
|
|> subject("#{instance_name()} Report")
|
||||||
|> html_body(html_body)
|
|> html_body(html_body)
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,6 +66,16 @@ defmodule Pleroma.FlakeId do
|
||||||
@spec get :: binary
|
@spec get :: binary
|
||||||
def get, do: to_string(:gen_server.call(:flake, :get))
|
def get, do: to_string(:gen_server.call(:flake, :get))
|
||||||
|
|
||||||
|
# checks that ID is is valid FlakeID
|
||||||
|
#
|
||||||
|
@spec is_flake_id?(String.t()) :: boolean
|
||||||
|
def is_flake_id?(id), do: is_flake_id?(String.to_charlist(id), true)
|
||||||
|
defp is_flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: is_flake_id?(cs, true)
|
||||||
|
defp is_flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: is_flake_id?(cs, true)
|
||||||
|
defp is_flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: is_flake_id?(cs, true)
|
||||||
|
defp is_flake_id?([], true), do: true
|
||||||
|
defp is_flake_id?(_, _), do: false
|
||||||
|
|
||||||
# -- Ecto.Type API
|
# -- Ecto.Type API
|
||||||
@impl Ecto.Type
|
@impl Ecto.Type
|
||||||
def type, do: :uuid
|
def type, do: :uuid
|
||||||
|
|
|
@ -114,7 +114,7 @@ defmodule Pleroma.Object.Fetcher do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_and_contain_remote_object_from_id(id) do
|
def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
|
||||||
Logger.info("Fetching object #{id} via AP")
|
Logger.info("Fetching object #{id} via AP")
|
||||||
|
|
||||||
date =
|
date =
|
||||||
|
@ -141,4 +141,9 @@ defmodule Pleroma.Object.Fetcher do
|
||||||
{:error, e}
|
{:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_and_contain_remote_object_from_id(%{"id" => id}),
|
||||||
|
do: fetch_and_contain_remote_object_from_id(id)
|
||||||
|
|
||||||
|
def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"}
|
||||||
end
|
end
|
||||||
|
|
|
@ -114,7 +114,9 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
def user_info(%User{} = user, args \\ %{}) do
|
def user_info(%User{} = user, args \\ %{}) do
|
||||||
following_count =
|
following_count =
|
||||||
if args[:following_count], do: args[:following_count], else: following_count(user)
|
if args[:following_count],
|
||||||
|
do: args[:following_count],
|
||||||
|
else: user.info.following_count || following_count(user)
|
||||||
|
|
||||||
follower_count =
|
follower_count =
|
||||||
if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
|
if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
|
||||||
|
@ -226,6 +228,7 @@ defmodule Pleroma.User do
|
||||||
|> put_password_hash
|
|> put_password_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def reset_password(%User{id: user_id} = user, data) do
|
def reset_password(%User{id: user_id} = user, data) do
|
||||||
multi =
|
multi =
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|
@ -330,6 +333,7 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
def needs_update?(_), do: true
|
def needs_update?(_), do: true
|
||||||
|
|
||||||
|
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
|
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
end
|
end
|
||||||
|
@ -404,6 +408,8 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
{1, [follower]} = Repo.update_all(q, [])
|
{1, [follower]} = Repo.update_all(q, [])
|
||||||
|
|
||||||
|
follower = maybe_update_following_count(follower)
|
||||||
|
|
||||||
{:ok, _} = update_follower_count(followed)
|
{:ok, _} = update_follower_count(followed)
|
||||||
|
|
||||||
set_cache(follower)
|
set_cache(follower)
|
||||||
|
@ -423,6 +429,8 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
{1, [follower]} = Repo.update_all(q, [])
|
{1, [follower]} = Repo.update_all(q, [])
|
||||||
|
|
||||||
|
follower = maybe_update_following_count(follower)
|
||||||
|
|
||||||
{:ok, followed} = update_follower_count(followed)
|
{:ok, followed} = update_follower_count(followed)
|
||||||
|
|
||||||
set_cache(follower)
|
set_cache(follower)
|
||||||
|
@ -707,32 +715,73 @@ defmodule Pleroma.User do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_follower_count(%User{} = user) do
|
def maybe_fetch_follow_information(user) do
|
||||||
follower_count_query =
|
with {:ok, user} <- fetch_follow_information(user) do
|
||||||
User.Query.build(%{followers: user, deactivated: false})
|
user
|
||||||
|> select([u], %{count: count(u.id)})
|
else
|
||||||
|
e ->
|
||||||
|
Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}")
|
||||||
|
|
||||||
User
|
user
|
||||||
|> where(id: ^user.id)
|
|
||||||
|> join(:inner, [u], s in subquery(follower_count_query))
|
|
||||||
|> update([u, s],
|
|
||||||
set: [
|
|
||||||
info:
|
|
||||||
fragment(
|
|
||||||
"jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
|
|
||||||
u.info,
|
|
||||||
s.count
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> select([u], u)
|
|
||||||
|> Repo.update_all([])
|
|
||||||
|> case do
|
|
||||||
{1, [user]} -> set_cache(user)
|
|
||||||
_ -> {:error, user}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_follow_information(user) do
|
||||||
|
with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
|
||||||
|
info_cng = User.Info.follow_information_update(user.info, info)
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
user
|
||||||
|
|> change()
|
||||||
|
|> put_embed(:info, info_cng)
|
||||||
|
|
||||||
|
update_and_set_cache(changeset)
|
||||||
|
else
|
||||||
|
{:error, _} = e -> e
|
||||||
|
e -> {:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_follower_count(%User{} = user) do
|
||||||
|
if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||||
|
follower_count_query =
|
||||||
|
User.Query.build(%{followers: user, deactivated: false})
|
||||||
|
|> select([u], %{count: count(u.id)})
|
||||||
|
|
||||||
|
User
|
||||||
|
|> where(id: ^user.id)
|
||||||
|
|> join(:inner, [u], s in subquery(follower_count_query))
|
||||||
|
|> update([u, s],
|
||||||
|
set: [
|
||||||
|
info:
|
||||||
|
fragment(
|
||||||
|
"jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
|
||||||
|
u.info,
|
||||||
|
s.count
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> select([u], u)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [user]} -> set_cache(user)
|
||||||
|
_ -> {:error, user}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:ok, maybe_fetch_follow_information(user)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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)}
|
||||||
|
else
|
||||||
|
user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_update_following_count(user), do: user
|
||||||
|
|
||||||
def remove_duplicated_following(%User{following: following} = user) do
|
def remove_duplicated_following(%User{following: following} = user) do
|
||||||
uniq_following = Enum.uniq(following)
|
uniq_following = Enum.uniq(following)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.User.Info do
|
||||||
field(:source_data, :map, default: %{})
|
field(:source_data, :map, default: %{})
|
||||||
field(:note_count, :integer, default: 0)
|
field(:note_count, :integer, default: 0)
|
||||||
field(:follower_count, :integer, default: 0)
|
field(:follower_count, :integer, default: 0)
|
||||||
|
# Should be filled in only for remote users
|
||||||
|
field(:following_count, :integer, default: nil)
|
||||||
field(:locked, :boolean, default: false)
|
field(:locked, :boolean, default: false)
|
||||||
field(:confirmation_pending, :boolean, default: false)
|
field(:confirmation_pending, :boolean, default: false)
|
||||||
field(:confirmation_token, :string, default: nil)
|
field(:confirmation_token, :string, default: nil)
|
||||||
|
@ -223,7 +225,11 @@ defmodule Pleroma.User.Info do
|
||||||
:uri,
|
:uri,
|
||||||
:hub,
|
:hub,
|
||||||
:topic,
|
:topic,
|
||||||
:salmon
|
:salmon,
|
||||||
|
:hide_followers,
|
||||||
|
:hide_follows,
|
||||||
|
:follower_count,
|
||||||
|
:following_count
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -234,7 +240,11 @@ defmodule Pleroma.User.Info do
|
||||||
:source_data,
|
:source_data,
|
||||||
:banner,
|
:banner,
|
||||||
:locked,
|
:locked,
|
||||||
:magic_key
|
:magic_key,
|
||||||
|
:follower_count,
|
||||||
|
:following_count,
|
||||||
|
:hide_follows,
|
||||||
|
:hide_followers
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -348,4 +358,14 @@ defmodule Pleroma.User.Info do
|
||||||
|
|
||||||
cast(info, params, [:muted_reblogs])
|
cast(info, params, [:muted_reblogs])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def follow_information_update(info, params) do
|
||||||
|
info
|
||||||
|
|> cast(params, [
|
||||||
|
:hide_followers,
|
||||||
|
:hide_follows,
|
||||||
|
:follower_count,
|
||||||
|
:following_count
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ defmodule Pleroma.User.Search do
|
||||||
query_string = String.trim_leading(query_string, "@")
|
query_string = String.trim_leading(query_string, "@")
|
||||||
|
|
||||||
with [name, domain] <- String.split(query_string, "@"),
|
with [name, domain] <- String.split(query_string, "@"),
|
||||||
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
|
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
|
||||||
name <> "@" <> to_string(:idna.encode(formatted_domain))
|
name <> "@" <> to_string(:idna.encode(formatted_domain))
|
||||||
else
|
else
|
||||||
_ -> query_string
|
_ -> query_string
|
||||||
|
|
|
@ -267,6 +267,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
else
|
else
|
||||||
{:fake, true, activity} ->
|
{:fake, true, activity} ->
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
|
||||||
|
{:error, message} ->
|
||||||
|
{:error, message}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -746,8 +749,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
[_activity, object] in query,
|
||||||
where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
|
where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1009,10 +1012,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
user_data = %{
|
user_data = %{
|
||||||
ap_id: data["id"],
|
ap_id: data["id"],
|
||||||
info: %{
|
info: %{
|
||||||
"ap_enabled" => true,
|
ap_enabled: true,
|
||||||
"source_data" => data,
|
source_data: data,
|
||||||
"banner" => banner,
|
banner: banner,
|
||||||
"locked" => locked
|
locked: locked
|
||||||
},
|
},
|
||||||
avatar: avatar,
|
avatar: avatar,
|
||||||
name: data["name"],
|
name: data["name"],
|
||||||
|
@ -1036,6 +1039,71 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
{:ok, user_data}
|
{:ok, user_data}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_follow_information_for_user(user) do
|
||||||
|
with {:ok, following_data} <-
|
||||||
|
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
|
||||||
|
following_count when is_integer(following_count) <- following_data["totalItems"],
|
||||||
|
{:ok, hide_follows} <- collection_private(following_data),
|
||||||
|
{:ok, followers_data} <-
|
||||||
|
Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
|
||||||
|
followers_count when is_integer(followers_count) <- followers_data["totalItems"],
|
||||||
|
{:ok, hide_followers} <- collection_private(followers_data) do
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
hide_follows: hide_follows,
|
||||||
|
follower_count: followers_count,
|
||||||
|
following_count: following_count,
|
||||||
|
hide_followers: hide_followers
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{:error, _} = e ->
|
||||||
|
e
|
||||||
|
|
||||||
|
e ->
|
||||||
|
{:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_update_follow_information(data) do
|
||||||
|
with {:enabled, true} <-
|
||||||
|
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
|
||||||
|
{:ok, info} <- fetch_follow_information_for_user(data) do
|
||||||
|
info = Map.merge(data.info, info)
|
||||||
|
Map.put(data, :info, info)
|
||||||
|
else
|
||||||
|
{:enabled, false} ->
|
||||||
|
data
|
||||||
|
|
||||||
|
e ->
|
||||||
|
Logger.error(
|
||||||
|
"Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
|
||||||
|
)
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp collection_private(data) do
|
||||||
|
if is_map(data["first"]) and
|
||||||
|
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||||
|
{:ok, false}
|
||||||
|
else
|
||||||
|
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||||
|
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
|
||||||
|
{:ok, false}
|
||||||
|
else
|
||||||
|
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||||
|
{:ok, true}
|
||||||
|
|
||||||
|
{:error, _} = e ->
|
||||||
|
e
|
||||||
|
|
||||||
|
e ->
|
||||||
|
{:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def user_data_from_user_object(data) do
|
def user_data_from_user_object(data) do
|
||||||
with {:ok, data} <- MRF.filter(data),
|
with {:ok, data} <- MRF.filter(data),
|
||||||
{:ok, data} <- object_to_user_data(data) do
|
{:ok, data} <- object_to_user_data(data) do
|
||||||
|
@ -1047,7 +1115,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
def fetch_and_prepare_user_from_ap_id(ap_id) do
|
def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
||||||
{:ok, data} <- user_data_from_user_object(data) do
|
{:ok, data} <- user_data_from_user_object(data),
|
||||||
|
data <- maybe_update_follow_information(data) do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
else
|
else
|
||||||
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||||
|
|
|
@ -608,13 +608,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
||||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||||
|
|
||||||
banner = new_user_data[:info]["banner"]
|
banner = new_user_data[:info][:banner]
|
||||||
locked = new_user_data[:info]["locked"] || false
|
locked = new_user_data[:info][:locked] || false
|
||||||
|
|
||||||
update_data =
|
update_data =
|
||||||
new_user_data
|
new_user_data
|
||||||
|> Map.take([:name, :bio, :avatar])
|
|> Map.take([:name, :bio, :avatar])
|
||||||
|> Map.put(:info, %{"banner" => banner, "locked" => locked})
|
|> Map.put(:info, %{banner: banner, locked: locked})
|
||||||
|
|
||||||
actor
|
actor
|
||||||
|> User.upgrade_changeset(update_data)
|
|> User.upgrade_changeset(update_data)
|
||||||
|
@ -1076,10 +1076,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
|
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
|
||||||
end
|
end
|
||||||
|
|
||||||
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
|
||||||
update_following_followers_counters(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
%User{} = user -> {:ok, user}
|
%User{} = user -> {:ok, user}
|
||||||
|
@ -1112,27 +1108,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
data
|
data
|
||||||
|> maybe_fix_user_url
|
|> maybe_fix_user_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_following_followers_counters(user) do
|
|
||||||
info = %{}
|
|
||||||
|
|
||||||
following = fetch_counter(user.following_address)
|
|
||||||
info = if following, do: Map.put(info, :following_count, following), else: info
|
|
||||||
|
|
||||||
followers = fetch_counter(user.follower_address)
|
|
||||||
info = if followers, do: Map.put(info, :follower_count, followers), else: info
|
|
||||||
|
|
||||||
User.set_info_cache(user, info)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fetch_counter(url) do
|
|
||||||
with {:ok, %{body: body, status: code}} when code in 200..299 <-
|
|
||||||
Pleroma.HTTP.get(
|
|
||||||
url,
|
|
||||||
[{:Accept, "application/activity+json"}]
|
|
||||||
),
|
|
||||||
{:ok, data} <- Jason.decode(body) do
|
|
||||||
data["totalItems"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -251,20 +251,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
|
|
||||||
def insert_full_object(map), do: {:ok, map, nil}
|
def insert_full_object(map), do: {:ok, map, nil}
|
||||||
|
|
||||||
def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
|
||||||
# TODO
|
|
||||||
# Update activities that already had this. Could be done in a seperate process.
|
|
||||||
# Alternatively, just don't do this and fetch the current object each time. Most
|
|
||||||
# could probably be taken from cache.
|
|
||||||
relevant_activities = Activity.get_all_create_by_object_ap_id(id)
|
|
||||||
|
|
||||||
Enum.map(relevant_activities, fn activity ->
|
|
||||||
new_activity_data = activity.data |> Map.put("object", object.data)
|
|
||||||
changeset = Changeset.change(activity, data: new_activity_data)
|
|
||||||
Repo.update(changeset)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
#### Like-related helpers
|
#### Like-related helpers
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -347,8 +333,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
|> Map.put("#{property}_count", length(element))
|
|> Map.put("#{property}_count", length(element))
|
||||||
|> Map.put("#{property}s", element),
|
|> Map.put("#{property}s", element),
|
||||||
changeset <- Changeset.change(object, data: new_data),
|
changeset <- Changeset.change(object, data: new_data),
|
||||||
{:ok, object} <- Object.update_and_set_cache(changeset),
|
{:ok, object} <- Object.update_and_set_cache(changeset) do
|
||||||
_ <- update_object_in_activities(object) do
|
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,8 +66,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||||
"orderedItems" => items
|
"orderedItems" => items
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset < total do
|
if offset + length(items) < total do
|
||||||
Map.put(map, "next", "#{iri}?page=#{page + 1}")
|
Map.put(map, "next", "#{iri}?page=#{page + 1}")
|
||||||
|
else
|
||||||
|
map
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,7 +65,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
do: render("service.json", %{user: user})
|
do: render("service.json", %{user: user})
|
||||||
|
|
||||||
def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
|
def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
|
||||||
do: render("service.json", %{user: user})
|
do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
|
||||||
|
|
||||||
def render("user.json", %{user: user}) do
|
def render("user.json", %{user: user}) do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
{:ok, user} = User.ensure_keys_present(user)
|
||||||
|
|
|
@ -402,9 +402,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
|
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
|
||||||
updated =
|
updated =
|
||||||
Enum.map(configs, fn
|
Enum.map(configs, fn
|
||||||
%{"group" => group, "key" => key, "delete" => "true"} ->
|
%{"group" => group, "key" => key, "delete" => "true"} = params ->
|
||||||
{:ok, _} = Config.delete(%{group: group, key: key})
|
{:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
|
||||||
nil
|
config
|
||||||
|
|
||||||
%{"group" => group, "key" => key, "value" => value} ->
|
%{"group" => group, "key" => key, "value" => value} ->
|
||||||
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
|
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
|
||||||
|
|
|
@ -55,8 +55,19 @@ defmodule Pleroma.Web.AdminAPI.Config do
|
||||||
|
|
||||||
@spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
|
@spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
|
||||||
def delete(params) do
|
def delete(params) do
|
||||||
with %Config{} = config <- Config.get_by_params(params) do
|
with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do
|
||||||
Repo.delete(config)
|
if params[:subkeys] do
|
||||||
|
updated_value =
|
||||||
|
Keyword.drop(
|
||||||
|
:erlang.binary_to_term(config.value),
|
||||||
|
Enum.map(params[:subkeys], &do_transform_string(&1))
|
||||||
|
)
|
||||||
|
|
||||||
|
Config.update(config, %{value: updated_value})
|
||||||
|
else
|
||||||
|
Repo.delete(config)
|
||||||
|
{:ok, nil}
|
||||||
|
end
|
||||||
else
|
else
|
||||||
nil ->
|
nil ->
|
||||||
err =
|
err =
|
||||||
|
|
|
@ -24,7 +24,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
def get_by_id_or_ap_id(id) do
|
||||||
activity =
|
activity =
|
||||||
Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id)
|
with true <- Pleroma.FlakeId.is_flake_id?(id),
|
||||||
|
%Activity{} = activity <- Activity.get_by_id_with_object(id) do
|
||||||
|
activity
|
||||||
|
else
|
||||||
|
_ -> Activity.get_create_by_object_ap_id_with_object(id)
|
||||||
|
end
|
||||||
|
|
||||||
activity &&
|
activity &&
|
||||||
if activity.data["type"] == "Create" do
|
if activity.data["type"] == "Create" do
|
||||||
|
@ -42,26 +47,43 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
||||||
def get_replied_to_activity(_), do: nil
|
def get_replied_to_activity(_), do: nil
|
||||||
|
|
||||||
def attachments_from_ids(data) do
|
def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do
|
||||||
if Map.has_key?(data, "descriptions") do
|
attachments_from_ids_descs(ids, desc)
|
||||||
attachments_from_ids_descs(data["media_ids"], data["descriptions"])
|
|
||||||
else
|
|
||||||
attachments_from_ids_no_descs(data["media_ids"])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachments_from_ids_no_descs(ids) do
|
def attachments_from_ids(%{"media_ids" => ids} = _) do
|
||||||
Enum.map(ids || [], fn media_id ->
|
attachments_from_ids_no_descs(ids)
|
||||||
Repo.get(Object, media_id).data
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def attachments_from_ids(_), do: []
|
||||||
|
|
||||||
|
def attachments_from_ids_no_descs([]), do: []
|
||||||
|
|
||||||
|
def attachments_from_ids_no_descs(ids) do
|
||||||
|
Enum.map(ids, fn media_id ->
|
||||||
|
case Repo.get(Object, media_id) do
|
||||||
|
%Object{data: data} = _ -> data
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def attachments_from_ids_descs([], _), do: []
|
||||||
|
|
||||||
def attachments_from_ids_descs(ids, descs_str) do
|
def attachments_from_ids_descs(ids, descs_str) do
|
||||||
{_, descs} = Jason.decode(descs_str)
|
{_, descs} = Jason.decode(descs_str)
|
||||||
|
|
||||||
Enum.map(ids || [], fn media_id ->
|
Enum.map(ids, fn media_id ->
|
||||||
Map.put(Repo.get(Object, media_id).data, "name", descs[media_id])
|
case Repo.get(Object, media_id) do
|
||||||
|
%Object{data: data} = _ ->
|
||||||
|
Map.put(data, "name", descs[media_id])
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
|
@spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
|
||||||
|
@ -242,20 +264,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_attachments(text, attachments) do
|
def add_attachments(text, attachments) do
|
||||||
attachment_text =
|
attachment_text = Enum.map(attachments, &build_attachment_link/1)
|
||||||
Enum.map(attachments, fn
|
|
||||||
%{"url" => [%{"href" => href} | _]} = attachment ->
|
|
||||||
name = attachment["name"] || URI.decode(Path.basename(href))
|
|
||||||
href = MediaProxy.url(href)
|
|
||||||
"<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
""
|
|
||||||
end)
|
|
||||||
|
|
||||||
Enum.join([text | attachment_text], "<br>")
|
Enum.join([text | attachment_text], "<br>")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp build_attachment_link(%{"url" => [%{"href" => href} | _]} = attachment) do
|
||||||
|
name = attachment["name"] || URI.decode(Path.basename(href))
|
||||||
|
href = MediaProxy.url(href)
|
||||||
|
"<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_attachment_link(_), do: ""
|
||||||
|
|
||||||
def format_input(text, format, options \\ [])
|
def format_input(text, format, options \\ [])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -315,7 +335,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
sensitive \\ false,
|
sensitive \\ false,
|
||||||
merge \\ %{}
|
merge \\ %{}
|
||||||
) do
|
) do
|
||||||
object = %{
|
%{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"to" => to,
|
"to" => to,
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
|
@ -325,18 +345,20 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
"context" => context,
|
"context" => context,
|
||||||
"attachment" => attachments,
|
"attachment" => attachments,
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
|
"tag" => Keyword.values(tags) |> Enum.uniq()
|
||||||
}
|
}
|
||||||
|
|> add_in_reply_to(in_reply_to)
|
||||||
|
|> Map.merge(merge)
|
||||||
|
end
|
||||||
|
|
||||||
object =
|
defp add_in_reply_to(object, nil), do: object
|
||||||
with false <- is_nil(in_reply_to),
|
|
||||||
%Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
|
|
||||||
Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
|
|
||||||
else
|
|
||||||
_ -> object
|
|
||||||
end
|
|
||||||
|
|
||||||
Map.merge(object, merge)
|
defp add_in_reply_to(object, in_reply_to) do
|
||||||
|
with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
|
||||||
|
Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
|
||||||
|
else
|
||||||
|
_ -> object
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_naive_asctime(date) do
|
def format_naive_asctime(date) do
|
||||||
|
@ -368,17 +390,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
|
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_masto_date(date) do
|
def to_masto_date(date) when is_binary(date) do
|
||||||
try do
|
with {:ok, date} <- NaiveDateTime.from_iso8601(date) do
|
||||||
date
|
to_masto_date(date)
|
||||||
|> NaiveDateTime.from_iso8601!()
|
else
|
||||||
|> NaiveDateTime.to_iso8601()
|
_ -> ""
|
||||||
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
|
|
||||||
rescue
|
|
||||||
_e -> ""
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_masto_date(_), do: ""
|
||||||
|
|
||||||
defp shortname(name) do
|
defp shortname(name) do
|
||||||
if String.length(name) < 30 do
|
if String.length(name) < 30 do
|
||||||
name
|
name
|
||||||
|
@ -423,7 +444,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
||||||
object_data =
|
object_data =
|
||||||
cond do
|
cond do
|
||||||
!is_nil(object) ->
|
not is_nil(object) ->
|
||||||
object.data
|
object.data
|
||||||
|
|
||||||
is_map(data["object"]) ->
|
is_map(data["object"]) ->
|
||||||
|
@ -467,9 +488,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
||||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||||
tag
|
tag
|
||||||
|> Enum.filter(fn x -> is_map(x) end)
|
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
||||||
|> Enum.filter(fn x -> x["type"] == "Mention" end)
|
|
||||||
|> Enum.map(fn x -> x["href"] end)
|
|> Enum.map(fn x -> x["href"] end)
|
||||||
|
|> Enum.uniq()
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_extract_mentions(_), do: []
|
def maybe_extract_mentions(_), do: []
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MediaProxy do
|
defmodule Pleroma.Web.MediaProxy do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Upload
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
|
||||||
@base64_opts [padding: false]
|
@base64_opts [padding: false]
|
||||||
|
@ -26,7 +27,18 @@ defmodule Pleroma.Web.MediaProxy do
|
||||||
defp whitelisted?(url) do
|
defp whitelisted?(url) do
|
||||||
%{host: domain} = URI.parse(url)
|
%{host: domain} = URI.parse(url)
|
||||||
|
|
||||||
Enum.any?(Config.get([:media_proxy, :whitelist]), fn pattern ->
|
mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
|
||||||
|
|
||||||
|
upload_base_url_domain =
|
||||||
|
if !is_nil(Config.get([Upload, :base_url])) do
|
||||||
|
[URI.parse(Config.get([Upload, :base_url])).host]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
whitelist = mediaproxy_whitelist ++ upload_base_url_domain
|
||||||
|
|
||||||
|
Enum.any?(whitelist, fn pattern ->
|
||||||
String.equivalent?(domain, pattern)
|
String.equivalent?(domain, pattern)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -183,6 +183,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||||
|
|
||||||
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
|
retweeted_object = Object.normalize(retweeted_activity)
|
||||||
retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
|
retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
|
||||||
|
|
||||||
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
|
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
|
||||||
|
@ -197,7 +198,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
||||||
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
|
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
|
||||||
{:id, h.(activity.data["id"])},
|
{:id, h.(activity.data["id"])},
|
||||||
{:title, ['#{user.nickname} repeated a notice']},
|
{:title, ['#{user.nickname} repeated a notice']},
|
||||||
{:content, [type: 'html'], ['RT #{retweeted_activity.data["object"]["content"]}']},
|
{:content, [type: 'html'], ['RT #{retweeted_object.data["content"]}']},
|
||||||
{:published, h.(inserted_at)},
|
{:published, h.(inserted_at)},
|
||||||
{:updated, h.(updated_at)},
|
{:updated, h.(updated_at)},
|
||||||
{:"ostatus:conversation", [ref: h.(activity.data["context"])],
|
{:"ostatus:conversation", [ref: h.(activity.data["context"])],
|
||||||
|
|
|
@ -9,14 +9,18 @@ defmodule Pleroma.Web.OStatus.FollowHandler do
|
||||||
alias Pleroma.Web.XML
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
def handle(entry, doc) do
|
def handle(entry, doc) do
|
||||||
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
|
||||||
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
|
||||||
followed_uri when not is_nil(followed_uri) <-
|
followed_uri when not is_nil(followed_uri) <-
|
||||||
XML.string_from_xpath("/entry/activity:object/id", entry),
|
XML.string_from_xpath("/entry/activity:object/id", entry),
|
||||||
{:ok, followed} <- OStatus.find_or_make_user(followed_uri),
|
{:ok, followed} <- OStatus.find_or_make_user(followed_uri),
|
||||||
|
{:locked, false} <- {:locked, followed.info.locked},
|
||||||
{:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
|
{:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
|
||||||
User.follow(actor, followed)
|
User.follow(actor, followed)
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:locked, true} ->
|
||||||
|
{:error, "It's not possible to follow locked accounts over OStatus"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -111,7 +111,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
||||||
with id <- XML.string_from_xpath("//id", entry),
|
with id <- XML.string_from_xpath("//id", entry),
|
||||||
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
|
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
|
||||||
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
||||||
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
{:ok, actor} <- OStatus.find_make_or_update_actor(author),
|
||||||
content_html <- OStatus.get_content(entry),
|
content_html <- OStatus.get_content(entry),
|
||||||
cw <- OStatus.get_cw(entry),
|
cw <- OStatus.get_cw(entry),
|
||||||
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
|
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.OStatus.UnfollowHandler do
|
||||||
alias Pleroma.Web.XML
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
def handle(entry, doc) do
|
def handle(entry, doc) do
|
||||||
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
|
||||||
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
|
||||||
followed_uri when not is_nil(followed_uri) <-
|
followed_uri when not is_nil(followed_uri) <-
|
||||||
XML.string_from_xpath("/entry/activity:object/id", entry),
|
XML.string_from_xpath("/entry/activity:object/id", entry),
|
||||||
|
|
|
@ -56,7 +56,7 @@ defmodule Pleroma.Web.OStatus do
|
||||||
|
|
||||||
def handle_incoming(xml_string, options \\ []) do
|
def handle_incoming(xml_string, options \\ []) do
|
||||||
with doc when doc != :error <- parse_document(xml_string) do
|
with doc when doc != :error <- parse_document(xml_string) do
|
||||||
with {:ok, actor_user} <- find_make_or_update_user(doc),
|
with {:ok, actor_user} <- find_make_or_update_actor(doc),
|
||||||
do: Pleroma.Instances.set_reachable(actor_user.ap_id)
|
do: Pleroma.Instances.set_reachable(actor_user.ap_id)
|
||||||
|
|
||||||
entries = :xmerl_xpath.string('//entry', doc)
|
entries = :xmerl_xpath.string('//entry', doc)
|
||||||
|
@ -120,7 +120,7 @@ defmodule Pleroma.Web.OStatus do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_share(entry, doc, retweeted_activity) do
|
def make_share(entry, doc, retweeted_activity) do
|
||||||
with {:ok, actor} <- find_make_or_update_user(doc),
|
with {:ok, actor} <- find_make_or_update_actor(doc),
|
||||||
%Object{} = object <- Object.normalize(retweeted_activity),
|
%Object{} = object <- Object.normalize(retweeted_activity),
|
||||||
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
||||||
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
|
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
|
||||||
|
@ -138,7 +138,7 @@ defmodule Pleroma.Web.OStatus do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_favorite(entry, doc, favorited_activity) do
|
def make_favorite(entry, doc, favorited_activity) do
|
||||||
with {:ok, actor} <- find_make_or_update_user(doc),
|
with {:ok, actor} <- find_make_or_update_actor(doc),
|
||||||
%Object{} = object <- Object.normalize(favorited_activity),
|
%Object{} = object <- Object.normalize(favorited_activity),
|
||||||
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
|
||||||
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
|
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
|
||||||
|
@ -264,11 +264,18 @@ defmodule Pleroma.Web.OStatus do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_make_or_update_user(doc) do
|
def find_make_or_update_actor(doc) do
|
||||||
uri = string_from_xpath("//author/uri[1]", doc)
|
uri = string_from_xpath("//author/uri[1]", doc)
|
||||||
|
|
||||||
with {:ok, user} <- find_or_make_user(uri) do
|
with {:ok, %User{} = user} <- find_or_make_user(uri),
|
||||||
|
{:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do
|
||||||
maybe_update(doc, user)
|
maybe_update(doc, user)
|
||||||
|
else
|
||||||
|
{:ap_enabled, true} ->
|
||||||
|
{:error, :invalid_protocol}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, :unknown_user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,20 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
|
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
|
||||||
|
alias Pleroma.Web.RichMedia.Parsers.MetaTagsParser
|
||||||
|
|
||||||
|
@spec parse(String.t(), map()) :: {:ok, map()} | {:error, String.t()}
|
||||||
def parse(html, data) do
|
def parse(html, data) do
|
||||||
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
|
data
|
||||||
html,
|
|> parse_name_attrs(html)
|
||||||
data,
|
|> parse_property_attrs(html)
|
||||||
"twitter",
|
end
|
||||||
"No twitter card metadata found",
|
|
||||||
"name"
|
defp parse_name_attrs(data, html) do
|
||||||
)
|
MetaTagsParser.parse(html, data, "twitter", %{}, "name")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_property_attrs({_, data}, html) do
|
||||||
|
MetaTagsParser.parse(html, data, "twitter", "No twitter card metadata found", "property")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: color: #d8a070;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
|
|
||||||
|
plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
|
||||||
|
|
||||||
def help_test(conn, _params) do
|
def help_test(conn, _params) do
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
end
|
end
|
||||||
|
@ -58,27 +60,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
|
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||||
redirect(conn, to: "/notice/#{activity_id}")
|
redirect(conn, to: "/notice/#{activity_id}")
|
||||||
else
|
else
|
||||||
{err, followee} = User.get_or_fetch(acct)
|
with {:ok, followee} <- User.get_or_fetch(acct) do
|
||||||
avatar = User.avatar_url(followee)
|
|
||||||
name = followee.nickname
|
|
||||||
id = followee.id
|
|
||||||
|
|
||||||
if !!user do
|
|
||||||
conn
|
conn
|
||||||
|> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
|
|> render(follow_template(user), %{
|
||||||
else
|
|
||||||
conn
|
|
||||||
|> render("follow_login.html", %{
|
|
||||||
error: false,
|
error: false,
|
||||||
acct: acct,
|
acct: acct,
|
||||||
avatar: avatar,
|
avatar: User.avatar_url(followee),
|
||||||
name: name,
|
name: followee.nickname,
|
||||||
id: id
|
id: followee.id
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
{:error, _reason} ->
|
||||||
|
render(conn, follow_template(user), %{error: :error})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp follow_template(%User{} = _user), do: "follow.html"
|
||||||
|
defp follow_template(_), do: "follow_login.html"
|
||||||
|
|
||||||
defp is_status?(acct) do
|
defp is_status?(acct) do
|
||||||
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
|
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
|
||||||
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
|
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
|
||||||
|
@ -92,48 +92,53 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
def do_remote_follow(conn, %{
|
def do_remote_follow(conn, %{
|
||||||
"authorization" => %{"name" => username, "password" => password, "id" => id}
|
"authorization" => %{"name" => username, "password" => password, "id" => id}
|
||||||
}) do
|
}) do
|
||||||
followee = User.get_cached_by_id(id)
|
with %User{} = followee <- User.get_cached_by_id(id),
|
||||||
avatar = User.avatar_url(followee)
|
{_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
|
||||||
name = followee.nickname
|
{_, true, _} <- {
|
||||||
|
:auth,
|
||||||
with %User{} = user <- User.get_cached_by_nickname(username),
|
AuthenticationPlug.checkpw(password, user.password_hash),
|
||||||
true <- AuthenticationPlug.checkpw(password, user.password_hash),
|
followee
|
||||||
%User{} = _followed <- User.get_cached_by_id(id),
|
},
|
||||||
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
||||||
conn
|
conn
|
||||||
|> render("followed.html", %{error: false})
|
|> render("followed.html", %{error: false})
|
||||||
else
|
else
|
||||||
# Was already following user
|
# Was already following user
|
||||||
{:error, "Could not follow user:" <> _rest} ->
|
{:error, "Could not follow user:" <> _rest} ->
|
||||||
render(conn, "followed.html", %{error: false})
|
render(conn, "followed.html", %{error: "Error following account"})
|
||||||
|
|
||||||
_e ->
|
{:auth, _, followee} ->
|
||||||
conn
|
conn
|
||||||
|> render("follow_login.html", %{
|
|> render("follow_login.html", %{
|
||||||
error: "Wrong username or password",
|
error: "Wrong username or password",
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: followee.nickname,
|
||||||
avatar: avatar
|
avatar: User.avatar_url(followee)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
e ->
|
||||||
|
Logger.debug("Remote follow failed with error #{inspect(e)}")
|
||||||
|
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
|
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
|
||||||
with %User{} = followee <- User.get_cached_by_id(id),
|
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||||
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
||||||
conn
|
conn
|
||||||
|> render("followed.html", %{error: false})
|
|> render("followed.html", %{error: false})
|
||||||
else
|
else
|
||||||
# Was already following user
|
# Was already following user
|
||||||
{:error, "Could not follow user:" <> _rest} ->
|
{:error, "Could not follow user:" <> _rest} ->
|
||||||
conn
|
render(conn, "followed.html", %{error: "Error following account"})
|
||||||
|> render("followed.html", %{error: false})
|
|
||||||
|
{:fetch_user, error} ->
|
||||||
|
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||||
|
render(conn, "followed.html", %{error: "Could not find user"})
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
Logger.debug("Remote follow failed with error #{inspect(e)}")
|
Logger.debug("Remote follow failed with error #{inspect(e)}")
|
||||||
|
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||||
conn
|
|
||||||
|> render("followed.html", %{error: inspect(e)})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -148,67 +153,70 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def config(%{assigns: %{format: "xml"}} = conn, _params) do
|
||||||
|
instance = Pleroma.Config.get(:instance)
|
||||||
|
|
||||||
|
response = """
|
||||||
|
<config>
|
||||||
|
<site>
|
||||||
|
<name>#{Keyword.get(instance, :name)}</name>
|
||||||
|
<site>#{Web.base_url()}</site>
|
||||||
|
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
||||||
|
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
||||||
|
</site>
|
||||||
|
</config>
|
||||||
|
"""
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/xml")
|
||||||
|
|> send_resp(200, response)
|
||||||
|
end
|
||||||
|
|
||||||
def config(conn, _params) do
|
def config(conn, _params) do
|
||||||
instance = Pleroma.Config.get(:instance)
|
instance = Pleroma.Config.get(:instance)
|
||||||
|
|
||||||
case get_format(conn) do
|
vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||||
"xml" ->
|
|
||||||
response = """
|
|
||||||
<config>
|
|
||||||
<site>
|
|
||||||
<name>#{Keyword.get(instance, :name)}</name>
|
|
||||||
<site>#{Web.base_url()}</site>
|
|
||||||
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
|
||||||
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
|
||||||
</site>
|
|
||||||
</config>
|
|
||||||
"""
|
|
||||||
|
|
||||||
conn
|
uploadlimit = %{
|
||||||
|> put_resp_content_type("application/xml")
|
uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
|
||||||
|> send_resp(200, response)
|
avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
|
||||||
|
backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
|
||||||
|
bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
|
||||||
|
}
|
||||||
|
|
||||||
_ ->
|
data = %{
|
||||||
vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
name: Keyword.get(instance, :name),
|
||||||
|
description: Keyword.get(instance, :description),
|
||||||
|
server: Web.base_url(),
|
||||||
|
textlimit: to_string(Keyword.get(instance, :limit)),
|
||||||
|
uploadlimit: uploadlimit,
|
||||||
|
closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
|
||||||
|
private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
|
||||||
|
vapidPublicKey: vapid_public_key,
|
||||||
|
accountActivationRequired:
|
||||||
|
bool_to_val(Keyword.get(instance, :account_activation_required, false)),
|
||||||
|
invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
|
||||||
|
safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
|
||||||
|
}
|
||||||
|
|
||||||
uploadlimit = %{
|
managed_config = Keyword.get(instance, :managed_config)
|
||||||
uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
|
|
||||||
avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
|
|
||||||
backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
|
|
||||||
bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
data = %{
|
|
||||||
name: Keyword.get(instance, :name),
|
|
||||||
description: Keyword.get(instance, :description),
|
|
||||||
server: Web.base_url(),
|
|
||||||
textlimit: to_string(Keyword.get(instance, :limit)),
|
|
||||||
uploadlimit: uploadlimit,
|
|
||||||
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
|
|
||||||
private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
|
|
||||||
vapidPublicKey: vapid_public_key,
|
|
||||||
accountActivationRequired:
|
|
||||||
if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
|
|
||||||
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
|
|
||||||
safeDMMentionsEnabled:
|
|
||||||
if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
data =
|
||||||
|
if managed_config do
|
||||||
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
||||||
|
Map.put(data, "pleromafe", pleroma_fe)
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
managed_config = Keyword.get(instance, :managed_config)
|
json(conn, %{site: data})
|
||||||
|
|
||||||
data =
|
|
||||||
if managed_config do
|
|
||||||
data |> Map.put("pleromafe", pleroma_fe)
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, %{site: data})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp bool_to_val(true), do: "1"
|
||||||
|
defp bool_to_val(_), do: "0"
|
||||||
|
defp bool_to_val(true, val, _), do: val
|
||||||
|
defp bool_to_val(_, _, val), do: val
|
||||||
|
|
||||||
def frontend_configurations(conn, _params) do
|
def frontend_configurations(conn, _params) do
|
||||||
config =
|
config =
|
||||||
Pleroma.Config.get(:frontend_configurations, %{})
|
Pleroma.Config.get(:frontend_configurations, %{})
|
||||||
|
@ -217,20 +225,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
json(conn, config)
|
json(conn, config)
|
||||||
end
|
end
|
||||||
|
|
||||||
def version(conn, _params) do
|
def version(%{assigns: %{format: "xml"}} = conn, _params) do
|
||||||
version = Pleroma.Application.named_version()
|
version = Pleroma.Application.named_version()
|
||||||
|
|
||||||
case get_format(conn) do
|
conn
|
||||||
"xml" ->
|
|> put_resp_content_type("application/xml")
|
||||||
response = "<version>#{version}</version>"
|
|> send_resp(200, "<version>#{version}</version>")
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
def version(conn, _params) do
|
||||||
|> put_resp_content_type("application/xml")
|
json(conn, Pleroma.Application.named_version())
|
||||||
|> send_resp(200, response)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
json(conn, version)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def emoji(conn, _params) do
|
def emoji(conn, _params) do
|
||||||
|
|
23
mix.exs
|
@ -190,12 +190,13 @@ defmodule Pleroma.Mixfile do
|
||||||
tag = String.trim(tag),
|
tag = String.trim(tag),
|
||||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
|
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
|
||||||
describe = String.trim(describe),
|
describe = String.trim(describe),
|
||||||
ahead <- String.replace(describe, tag, "") do
|
ahead <- String.replace(describe, tag, ""),
|
||||||
|
ahead <- String.trim_leading(ahead, "-") do
|
||||||
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
{nil, "-0-g" <> String.trim(commit_hash)}
|
{nil, "0-g" <> String.trim(commit_hash)}
|
||||||
end
|
end
|
||||||
|
|
||||||
if git_tag && version != git_tag do
|
if git_tag && version != git_tag do
|
||||||
|
@ -207,14 +208,15 @@ defmodule Pleroma.Mixfile do
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
branch_name =
|
branch_name =
|
||||||
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||||
|
branch_name <- String.trim(branch_name),
|
||||||
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
||||||
true <- branch_name != "master" do
|
true <- branch_name not in ["master", "HEAD"] do
|
||||||
branch_name =
|
branch_name =
|
||||||
branch_name
|
branch_name
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|> String.replace(identifier_filter, "-")
|
|> String.replace(identifier_filter, "-")
|
||||||
|
|
||||||
"." <> branch_name
|
branch_name
|
||||||
end
|
end
|
||||||
|
|
||||||
build_name =
|
build_name =
|
||||||
|
@ -234,6 +236,17 @@ defmodule Pleroma.Mixfile do
|
||||||
env_override -> env_override
|
env_override -> env_override
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Pre-release version, denoted by appending a hyphen
|
||||||
|
# and a series of dot separated identifiers
|
||||||
|
pre_release =
|
||||||
|
[git_pre_release, branch_name]
|
||||||
|
|> Enum.filter(fn string -> string && string != "" end)
|
||||||
|
|> Enum.join(".")
|
||||||
|
|> (fn
|
||||||
|
"" -> nil
|
||||||
|
string -> "-" <> String.replace(string, identifier_filter, "-")
|
||||||
|
end).()
|
||||||
|
|
||||||
# Build metadata, denoted with a plus sign
|
# Build metadata, denoted with a plus sign
|
||||||
build_metadata =
|
build_metadata =
|
||||||
[build_name, env_name]
|
[build_name, env_name]
|
||||||
|
@ -244,7 +257,7 @@ defmodule Pleroma.Mixfile do
|
||||||
string -> "+" <> String.replace(string, identifier_filter, "-")
|
string -> "+" <> String.replace(string, identifier_filter, "-")
|
||||||
end).()
|
end).()
|
||||||
|
|
||||||
[version, git_pre_release, branch_name, build_metadata]
|
[version, pre_release, build_metadata]
|
||||||
|> Enum.filter(fn string -> string && string != "" end)
|
|> Enum.filter(fn string -> string && string != "" end)
|
||||||
|> Enum.join()
|
|> Enum.join()
|
||||||
end
|
end
|
||||||
|
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 661 B |
Before Width: | Height: | Size: 662 B |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 541 B |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 447 B |
Before Width: | Height: | Size: 615 B |
Before Width: | Height: | Size: 618 B |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 666 B |
Before Width: | Height: | Size: 663 B |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 549 B |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 623 B |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 839 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 42 KiB |
|
@ -24,7 +24,6 @@ defmodule Pleroma.Emails.AdminEmailTest do
|
||||||
|
|
||||||
assert res.to == [{to_user.name, to_user.email}]
|
assert res.to == [{to_user.name, to_user.email}]
|
||||||
assert res.from == {config[:name], config[:notify_email]}
|
assert res.from == {config[:name], config[:notify_email]}
|
||||||
assert res.reply_to == {reporter.name, reporter.email}
|
|
||||||
assert res.subject == "#{config[:name]} Report"
|
assert res.subject == "#{config[:name]} Report"
|
||||||
|
|
||||||
assert res.html_body ==
|
assert res.html_body ==
|
||||||
|
@ -34,4 +33,17 @@ defmodule Pleroma.Emails.AdminEmailTest do
|
||||||
status_url
|
status_url
|
||||||
}\">#{status_url}</li>\n </ul>\n</p>\n\n"
|
}\">#{status_url}</li>\n </ul>\n</p>\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works when the reporter is a remote user without email" do
|
||||||
|
config = Pleroma.Config.get(:instance)
|
||||||
|
to_user = insert(:user)
|
||||||
|
reporter = insert(:user, email: nil, local: false)
|
||||||
|
account = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
|
||||||
|
|
||||||
|
assert res.to == [{to_user.name, to_user.email}]
|
||||||
|
assert res.from == {config[:name], config[:notify_email]}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
227
test/fixtures/nypd-facial-recognition-children-teenagers.html
vendored
Normal file
226
test/fixtures/nypd-facial-recognition-children-teenagers2.html
vendored
Normal file
227
test/fixtures/nypd-facial-recognition-children-teenagers3.html
vendored
Normal file
1
test/fixtures/users_mock/masto_closed_followers_page.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/followers?page=1","type":"OrderedCollectionPage","totalItems":437,"next":"http://localhost:4001/users/masto_closed/followers?page=2","partOf":"http://localhost:4001/users/masto_closed/followers","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}
|
1
test/fixtures/users_mock/masto_closed_following_page.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/following?page=1","type":"OrderedCollectionPage","totalItems":152,"next":"http://localhost:4001/users/masto_closed/following?page=2","partOf":"http://localhost:4001/users/masto_closed/following","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}
|
|
@ -39,4 +39,9 @@ defmodule Pleroma.FlakeIdTest do
|
||||||
assert dump(flake_s) == {:ok, flake}
|
assert dump(flake_s) == {:ok, flake}
|
||||||
assert dump(flake) == {:ok, flake}
|
assert dump(flake) == {:ok, flake}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "is_flake_id?/1" do
|
||||||
|
assert is_flake_id?("9eoozpwTul5mjSEDRI")
|
||||||
|
refute is_flake_id?("http://example.com/activities/3ebbadd1-eb14-4e20-8118-b6f79c0c7b0b")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -182,8 +182,8 @@ defmodule Pleroma.Factory do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def like_activity_factory do
|
def like_activity_factory(attrs \\ %{}) do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = attrs[:note_activity] || insert(:note_activity)
|
||||||
object = Object.normalize(note_activity)
|
object = Object.normalize(note_activity)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,10 @@ defmodule HttpRequestMock do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("https://mastodon.social/users/not_found", _, _, _) do
|
||||||
|
{:ok, %Tesla.Env{status: 404}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do
|
def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
@ -792,6 +796,14 @@ defmodule HttpRequestMock do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:4001/users/masto_closed/followers?page=1", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
|
def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
@ -800,6 +812,14 @@ defmodule HttpRequestMock do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
|
|
@ -1393,4 +1393,78 @@ defmodule Pleroma.UserTest do
|
||||||
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
|
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
|
||||||
end
|
end
|
||||||
end
|
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
|
||||||
|
|
||||||
|
test "updates the counters normally on following/getting a follow when disabled" do
|
||||||
|
Pleroma.Config.put([:instance, :external_user_synchronization], false)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
other_user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||||
|
following_address: "http://localhost:4001/users/masto_closed/following",
|
||||||
|
info: %{ap_enabled: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert User.user_info(other_user).following_count == 0
|
||||||
|
assert User.user_info(other_user).follower_count == 0
|
||||||
|
|
||||||
|
{:ok, user} = Pleroma.User.follow(user, other_user)
|
||||||
|
other_user = Pleroma.User.get_by_id(other_user.id)
|
||||||
|
|
||||||
|
assert User.user_info(user).following_count == 1
|
||||||
|
assert User.user_info(other_user).follower_count == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "syncronizes the counters with the remote instance for the followed when enabled" do
|
||||||
|
Pleroma.Config.put([:instance, :external_user_synchronization], false)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
other_user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||||
|
following_address: "http://localhost:4001/users/masto_closed/following",
|
||||||
|
info: %{ap_enabled: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert User.user_info(other_user).following_count == 0
|
||||||
|
assert User.user_info(other_user).follower_count == 0
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||||
|
{:ok, _user} = User.follow(user, other_user)
|
||||||
|
other_user = User.get_by_id(other_user.id)
|
||||||
|
|
||||||
|
assert User.user_info(other_user).follower_count == 437
|
||||||
|
end
|
||||||
|
|
||||||
|
test "syncronizes the counters with the remote instance for the follower when enabled" do
|
||||||
|
Pleroma.Config.put([:instance, :external_user_synchronization], false)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
other_user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||||
|
following_address: "http://localhost:4001/users/masto_closed/following",
|
||||||
|
info: %{ap_enabled: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert User.user_info(other_user).following_count == 0
|
||||||
|
assert User.user_info(other_user).follower_count == 0
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||||
|
{:ok, other_user} = User.follow(other_user, user)
|
||||||
|
|
||||||
|
assert User.user_info(other_user).following_count == 152
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -180,18 +180,65 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/object/:uuid/likes" do
|
describe "/object/:uuid/likes" do
|
||||||
test "it returns the like activities in a collection", %{conn: conn} do
|
setup do
|
||||||
like = insert(:like_activity)
|
like = insert(:like_activity)
|
||||||
like_object_ap_id = Object.normalize(like).data["id"]
|
like_object_ap_id = Object.normalize(like).data["id"]
|
||||||
uuid = String.split(like_object_ap_id, "/") |> List.last()
|
|
||||||
|
|
||||||
|
uuid =
|
||||||
|
like_object_ap_id
|
||||||
|
|> String.split("/")
|
||||||
|
|> List.last()
|
||||||
|
|
||||||
|
[id: like.data["id"], uuid: uuid]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns the like activities in a collection", %{conn: conn, id: id, uuid: uuid} do
|
||||||
result =
|
result =
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|> get("/objects/#{uuid}/likes")
|
|> get("/objects/#{uuid}/likes")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert List.first(result["first"]["orderedItems"])["id"] == like.data["id"]
|
assert List.first(result["first"]["orderedItems"])["id"] == id
|
||||||
|
assert result["type"] == "OrderedCollection"
|
||||||
|
assert result["totalItems"] == 1
|
||||||
|
refute result["first"]["next"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it does not crash when page number is exceeded total pages", %{conn: conn, uuid: uuid} do
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/objects/#{uuid}/likes?page=2")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert result["type"] == "OrderedCollectionPage"
|
||||||
|
assert result["totalItems"] == 1
|
||||||
|
refute result["next"]
|
||||||
|
assert Enum.empty?(result["orderedItems"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it contains the next key when likes count is more than 10", %{conn: conn} do
|
||||||
|
note = insert(:note_activity)
|
||||||
|
insert_list(11, :like_activity, note_activity: note)
|
||||||
|
|
||||||
|
uuid =
|
||||||
|
note
|
||||||
|
|> Object.normalize()
|
||||||
|
|> Map.get(:data)
|
||||||
|
|> Map.get("id")
|
||||||
|
|> String.split("/")
|
||||||
|
|> List.last()
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/objects/#{uuid}/likes?page=1")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert result["totalItems"] == 11
|
||||||
|
assert length(result["orderedItems"]) == 10
|
||||||
|
assert result["next"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -677,14 +677,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
assert object.data["likes"] == [user.ap_id]
|
assert object.data["likes"] == [user.ap_id]
|
||||||
assert object.data["like_count"] == 1
|
assert object.data["like_count"] == 1
|
||||||
|
|
||||||
[note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
|
|
||||||
assert note_activity.data["object"]["like_count"] == 1
|
|
||||||
|
|
||||||
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
|
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
|
||||||
assert object.data["like_count"] == 2
|
assert object.data["like_count"] == 2
|
||||||
|
|
||||||
[note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
|
|
||||||
assert note_activity.data["object"]["like_count"] == 2
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1128,4 +1122,65 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
assert result.id == activity.id
|
assert result.id == activity.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "fetch_follow_information_for_user" do
|
||||||
|
test "syncronizes following/followers counters" do
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/fuser2/followers",
|
||||||
|
following_address: "http://localhost:4001/users/fuser2/following"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||||
|
assert info.follower_count == 527
|
||||||
|
assert info.following_count == 267
|
||||||
|
end
|
||||||
|
|
||||||
|
test "detects hidden followers" do
|
||||||
|
mock(fn env ->
|
||||||
|
case env.url do
|
||||||
|
"http://localhost:4001/users/masto_closed/followers?page=1" ->
|
||||||
|
%Tesla.Env{status: 403, body: ""}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
apply(HttpRequestMock, :request, [env])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||||
|
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||||
|
assert info.hide_followers == true
|
||||||
|
assert info.hide_follows == false
|
||||||
|
end
|
||||||
|
|
||||||
|
test "detects hidden follows" do
|
||||||
|
mock(fn env ->
|
||||||
|
case env.url do
|
||||||
|
"http://localhost:4001/users/masto_closed/following?page=1" ->
|
||||||
|
%Tesla.Env{status: 403, body: ""}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
apply(HttpRequestMock, :request, [env])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||||
|
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||||
|
assert info.hide_followers == false
|
||||||
|
assert info.hide_follows == true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1373,32 +1373,4 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
refute recipient.follower_address in fixed_object["to"]
|
refute recipient.follower_address in fixed_object["to"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_following_followers_counters/1" do
|
|
||||||
user1 =
|
|
||||||
insert(:user,
|
|
||||||
local: false,
|
|
||||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
|
||||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
|
||||||
)
|
|
||||||
|
|
||||||
user2 =
|
|
||||||
insert(:user,
|
|
||||||
local: false,
|
|
||||||
follower_address: "http://localhost:4001/users/fuser2/followers",
|
|
||||||
following_address: "http://localhost:4001/users/fuser2/following"
|
|
||||||
)
|
|
||||||
|
|
||||||
Transmogrifier.update_following_followers_counters(user1)
|
|
||||||
Transmogrifier.update_following_followers_counters(user2)
|
|
||||||
|
|
||||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
|
|
||||||
assert followers == 437
|
|
||||||
assert following == 152
|
|
||||||
|
|
||||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
|
|
||||||
|
|
||||||
assert followers == 527
|
|
||||||
assert following == 267
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1914,6 +1914,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "delete part of settings by atom subkeys", %{conn: conn} do
|
||||||
|
config =
|
||||||
|
insert(:config,
|
||||||
|
key: "keyaa1",
|
||||||
|
value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
|
||||||
|
)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/api/pleroma/admin/config", %{
|
||||||
|
configs: [
|
||||||
|
%{
|
||||||
|
group: config.group,
|
||||||
|
key: config.key,
|
||||||
|
subkeys: [":subkey1", ":subkey3"],
|
||||||
|
delete: "true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert(
|
||||||
|
json_response(conn, 200) == %{
|
||||||
|
"configs" => [
|
||||||
|
%{
|
||||||
|
"group" => "pleroma",
|
||||||
|
"key" => "keyaa1",
|
||||||
|
"value" => [%{"tuple" => [":subkey2", "val2"]}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "config mix tasks run" do
|
describe "config mix tasks run" do
|
||||||
|
@ -1922,7 +1954,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
|
|
||||||
temp_file = "config/test.exported_from_db.secret.exs"
|
temp_file = "config/test.exported_from_db.secret.exs"
|
||||||
|
|
||||||
|
Mix.shell(Mix.Shell.Quiet)
|
||||||
|
|
||||||
on_exit(fn ->
|
on_exit(fn ->
|
||||||
|
Mix.shell(Mix.Shell.IO)
|
||||||
:ok = File.rm(temp_file)
|
:ok = File.rm(temp_file)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -306,7 +306,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
mentions = [mentioned_user.ap_id]
|
mentions = [mentioned_user.ap_id]
|
||||||
|
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private")
|
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private")
|
||||||
|
|
||||||
assert length(to) == 2
|
assert length(to) == 2
|
||||||
assert length(cc) == 0
|
assert length(cc) == 0
|
||||||
|
|
||||||
|
@ -360,4 +359,242 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
assert third_user.ap_id in to
|
assert third_user.ap_id in to
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "get_by_id_or_ap_id/1" do
|
||||||
|
test "get activity by id" do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id)
|
||||||
|
assert note.id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get activity by ap_id" do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"])
|
||||||
|
assert note.id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get activity by object when type isn't `Create` " do
|
||||||
|
activity = insert(:like_activity)
|
||||||
|
%Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id)
|
||||||
|
assert like.data["object"] == activity.data["object"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "to_master_date/1" do
|
||||||
|
test "removes microseconds from date (NaiveDateTime)" do
|
||||||
|
assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "removes microseconds from date (String)" do
|
||||||
|
assert Utils.to_masto_date("2015-01-23T23:50:07.123Z") == "2015-01-23T23:50:07.000Z"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns empty string when date invalid" do
|
||||||
|
assert Utils.to_masto_date("2015-01?23T23:50:07.123Z") == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "conversation_id_to_context/1" do
|
||||||
|
test "returns id" do
|
||||||
|
object = insert(:note)
|
||||||
|
assert Utils.conversation_id_to_context(object.id) == object.data["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error if object not found" do
|
||||||
|
assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "maybe_notify_mentioned_recipients/2" do
|
||||||
|
test "returns recipients when activity is not `Create`" do
|
||||||
|
activity = insert(:like_activity)
|
||||||
|
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == ["test"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns recipients from tag" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
object =
|
||||||
|
insert(:note,
|
||||||
|
user: user,
|
||||||
|
data: %{
|
||||||
|
"tag" => [
|
||||||
|
%{"type" => "Hashtag"},
|
||||||
|
"",
|
||||||
|
%{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
|
||||||
|
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
|
||||||
|
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
activity = insert(:note_activity, user: user, note: object)
|
||||||
|
|
||||||
|
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
|
||||||
|
"test",
|
||||||
|
"https://testing.pleroma.lol/users/lain",
|
||||||
|
"https://shitposter.club/user/5381"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns recipients when object is map" do
|
||||||
|
user = insert(:user)
|
||||||
|
object = insert(:note, user: user)
|
||||||
|
|
||||||
|
activity =
|
||||||
|
insert(:note_activity,
|
||||||
|
user: user,
|
||||||
|
note: object,
|
||||||
|
data_attrs: %{
|
||||||
|
"object" => %{
|
||||||
|
"tag" => [
|
||||||
|
%{"type" => "Hashtag"},
|
||||||
|
"",
|
||||||
|
%{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
|
||||||
|
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
|
||||||
|
%{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Pleroma.Repo.delete(object)
|
||||||
|
|
||||||
|
assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
|
||||||
|
"test",
|
||||||
|
"https://testing.pleroma.lol/users/lain",
|
||||||
|
"https://shitposter.club/user/5381"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns recipients when object not found" do
|
||||||
|
user = insert(:user)
|
||||||
|
object = insert(:note, user: user)
|
||||||
|
|
||||||
|
activity = insert(:note_activity, user: user, note: object)
|
||||||
|
Pleroma.Repo.delete(object)
|
||||||
|
|
||||||
|
assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [
|
||||||
|
"test-test"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "attachments_from_ids_descs/2" do
|
||||||
|
test "returns [] when attachment ids is empty" do
|
||||||
|
assert Utils.attachments_from_ids_descs([], "{}") == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns list attachments with desc" do
|
||||||
|
object = insert(:note)
|
||||||
|
desc = Jason.encode!(%{object.id => "test-desc"})
|
||||||
|
|
||||||
|
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [
|
||||||
|
Map.merge(object.data, %{"name" => "test-desc"})
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "attachments_from_ids/1" do
|
||||||
|
test "returns attachments with descs" do
|
||||||
|
object = insert(:note)
|
||||||
|
desc = Jason.encode!(%{object.id => "test-desc"})
|
||||||
|
|
||||||
|
assert Utils.attachments_from_ids(%{
|
||||||
|
"media_ids" => ["#{object.id}"],
|
||||||
|
"descriptions" => desc
|
||||||
|
}) == [
|
||||||
|
Map.merge(object.data, %{"name" => "test-desc"})
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns attachments without descs" do
|
||||||
|
object = insert(:note)
|
||||||
|
assert Utils.attachments_from_ids(%{"media_ids" => ["#{object.id}"]}) == [object.data]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns [] when not pass media_ids" do
|
||||||
|
assert Utils.attachments_from_ids(%{}) == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "maybe_add_list_data/3" do
|
||||||
|
test "adds list params when found user list" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||||
|
|
||||||
|
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||||
|
%{
|
||||||
|
additional: %{"bcc" => [list.ap_id], "listMessage" => list.ap_id},
|
||||||
|
object: %{"listMessage" => list.ap_id}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns original params when list not found" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user))
|
||||||
|
|
||||||
|
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||||
|
%{additional: %{}, object: %{}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "make_note_data/11" do
|
||||||
|
test "returns note data" do
|
||||||
|
user = insert(:user)
|
||||||
|
note = insert(:note)
|
||||||
|
user2 = insert(:user)
|
||||||
|
user3 = insert(:user)
|
||||||
|
|
||||||
|
assert Utils.make_note_data(
|
||||||
|
user.ap_id,
|
||||||
|
[user2.ap_id],
|
||||||
|
"2hu",
|
||||||
|
"<h1>This is :moominmamma: note</h1>",
|
||||||
|
[],
|
||||||
|
note.id,
|
||||||
|
[name: "jimm"],
|
||||||
|
"test summary",
|
||||||
|
[user3.ap_id],
|
||||||
|
false,
|
||||||
|
%{"custom_tag" => "test"}
|
||||||
|
) == %{
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attachment" => [],
|
||||||
|
"cc" => [user3.ap_id],
|
||||||
|
"content" => "<h1>This is :moominmamma: note</h1>",
|
||||||
|
"context" => "2hu",
|
||||||
|
"sensitive" => false,
|
||||||
|
"summary" => "test summary",
|
||||||
|
"tag" => ["jimm"],
|
||||||
|
"to" => [user2.ap_id],
|
||||||
|
"type" => "Note",
|
||||||
|
"custom_tag" => "test"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "maybe_add_attachments/3" do
|
||||||
|
test "returns parsed results when no_links is true" do
|
||||||
|
assert Utils.maybe_add_attachments(
|
||||||
|
{"test", [], ["tags"]},
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
) == {"test", [], ["tags"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "adds attachments to parsed results" do
|
||||||
|
attachment = %{"url" => [%{"href" => "SakuraPM.png"}]}
|
||||||
|
|
||||||
|
assert Utils.maybe_add_attachments(
|
||||||
|
{"test", [], ["tags"]},
|
||||||
|
[attachment],
|
||||||
|
false
|
||||||
|
) == {
|
||||||
|
"test<br><a href=\"SakuraPM.png\" class='attachment'>SakuraPM.png</a>",
|
||||||
|
[],
|
||||||
|
["tags"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -229,5 +229,21 @@ defmodule Pleroma.Web.FederatorTest do
|
||||||
|
|
||||||
:error = Federator.incoming_ap_doc(params)
|
:error = Federator.incoming_ap_doc(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it does not crash if MRF rejects the post" do
|
||||||
|
policies = Pleroma.Config.get([:instance, :rewrite_policy])
|
||||||
|
mrf_keyword_policy = Pleroma.Config.get(:mrf_keyword)
|
||||||
|
Pleroma.Config.put([:mrf_keyword, :reject], ["lain"])
|
||||||
|
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.KeywordPolicy)
|
||||||
|
|
||||||
|
params =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
assert Federator.incoming_ap_doc(params) == :error
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :rewrite_policy], policies)
|
||||||
|
Pleroma.Config.put(:mrf_keyword, mrf_keyword_policy)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1671,40 +1671,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
object = Repo.get(Object, media["id"])
|
object = Repo.get(Object, media["id"])
|
||||||
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
|
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
|
|
||||||
Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
|
|
||||||
|
|
||||||
proxy_url = "https://cache.pleroma.social"
|
|
||||||
Pleroma.Config.put([:media_proxy, :enabled], true)
|
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
|
|
||||||
|
|
||||||
media =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/media", %{"file" => image})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert String.starts_with?(media["url"], proxy_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns media url when proxy is enabled but media url is whitelisted", %{
|
|
||||||
conn: conn,
|
|
||||||
image: image
|
|
||||||
} do
|
|
||||||
media_url = "https://media.pleroma.social"
|
|
||||||
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
|
|
||||||
|
|
||||||
Pleroma.Config.put([:media_proxy, :enabled], true)
|
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
|
||||||
Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
|
|
||||||
|
|
||||||
media =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/media", %{"file" => image})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert String.starts_with?(media["url"], media_url)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "locked accounts" do
|
describe "locked accounts" do
|
||||||
|
|
|
@ -95,6 +95,18 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
||||||
|
|
||||||
assert user_three.nickname in result_ids
|
assert user_three.nickname in result_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns account if query contains a space", %{conn: conn} do
|
||||||
|
user = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert length(results) == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".search" do
|
describe ".search" do
|
||||||
|
|
|
@ -171,21 +171,6 @@ defmodule Pleroma.Web.MediaProxyTest do
|
||||||
encoded = url(url)
|
encoded = url(url)
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not change whitelisted urls" do
|
|
||||||
upload_config = Pleroma.Config.get([Pleroma.Upload])
|
|
||||||
media_url = "https://media.pleroma.social"
|
|
||||||
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
|
|
||||||
Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
|
|
||||||
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
|
||||||
|
|
||||||
url = "#{media_url}/static/logo.png"
|
|
||||||
encoded = url(url)
|
|
||||||
|
|
||||||
assert String.starts_with?(encoded, media_url)
|
|
||||||
|
|
||||||
Pleroma.Config.put([Pleroma.Upload], upload_config)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when disabled" do
|
describe "when disabled" do
|
||||||
|
@ -215,12 +200,43 @@ defmodule Pleroma.Web.MediaProxyTest do
|
||||||
decoded
|
decoded
|
||||||
end
|
end
|
||||||
|
|
||||||
test "mediaproxy whitelist" do
|
describe "whitelist" do
|
||||||
Pleroma.Config.put([:media_proxy, :enabled], true)
|
setup do
|
||||||
Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
|
Pleroma.Config.put([:media_proxy, :enabled], true)
|
||||||
url = "https://feld.me/foo.png"
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
unencoded = url(url)
|
test "mediaproxy whitelist" do
|
||||||
assert unencoded == url
|
Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
|
||||||
|
url = "https://feld.me/foo.png"
|
||||||
|
|
||||||
|
unencoded = url(url)
|
||||||
|
assert unencoded == url
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not change whitelisted urls" do
|
||||||
|
Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"])
|
||||||
|
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
|
||||||
|
|
||||||
|
media_url = "https://mycdn.akamai.com"
|
||||||
|
|
||||||
|
url = "#{media_url}/static/logo.png"
|
||||||
|
encoded = url(url)
|
||||||
|
|
||||||
|
assert String.starts_with?(encoded, media_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ensure Pleroma.Upload base_url is always whitelisted" do
|
||||||
|
upload_config = Pleroma.Config.get([Pleroma.Upload])
|
||||||
|
media_url = "https://media.pleroma.social"
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
|
||||||
|
|
||||||
|
url = "#{media_url}/static/logo.png"
|
||||||
|
encoded = url(url)
|
||||||
|
|
||||||
|
assert String.starts_with?(encoded, media_url)
|
||||||
|
|
||||||
|
Pleroma.Config.put([Pleroma.Upload], upload_config)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -199,7 +199,7 @@ defmodule Pleroma.Web.OStatusTest do
|
||||||
assert retweeted_activity.data["type"] == "Create"
|
assert retweeted_activity.data["type"] == "Create"
|
||||||
assert retweeted_activity.data["actor"] == user.ap_id
|
assert retweeted_activity.data["actor"] == user.ap_id
|
||||||
assert retweeted_activity.local
|
assert retweeted_activity.local
|
||||||
assert retweeted_activity.data["object"]["announcement_count"] == 1
|
assert Object.normalize(retweeted_activity).data["announcement_count"] == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handle incoming retweets - Mastodon, salmon" do
|
test "handle incoming retweets - Mastodon, salmon" do
|
||||||
|
@ -326,6 +326,14 @@ defmodule Pleroma.Web.OStatusTest do
|
||||||
assert User.following?(follower, followed)
|
assert User.following?(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "refuse following over OStatus if the followed's account is locked" do
|
||||||
|
incoming = File.read!("test/fixtures/follow.xml")
|
||||||
|
_user = insert(:user, info: %{locked: true}, ap_id: "https://pawoo.net/users/pekorino")
|
||||||
|
|
||||||
|
{:ok, [{:error, "It's not possible to follow locked accounts over OStatus"}]} =
|
||||||
|
OStatus.handle_incoming(incoming)
|
||||||
|
end
|
||||||
|
|
||||||
test "handle incoming unfollows with existing follow" do
|
test "handle incoming unfollows with existing follow" do
|
||||||
incoming_follow = File.read!("test/fixtures/follow.xml")
|
incoming_follow = File.read!("test/fixtures/follow.xml")
|
||||||
{:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
|
{:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
|
||||||
|
@ -426,7 +434,7 @@ defmodule Pleroma.Web.OStatusTest do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find_make_or_update_user takes an author element and returns an updated user" do
|
test "find_make_or_update_actor takes an author element and returns an updated user" do
|
||||||
uri = "https://social.heldscal.la/user/23211"
|
uri = "https://social.heldscal.la/user/23211"
|
||||||
|
|
||||||
{:ok, user} = OStatus.find_or_make_user(uri)
|
{:ok, user} = OStatus.find_or_make_user(uri)
|
||||||
|
@ -439,14 +447,56 @@ defmodule Pleroma.Web.OStatusTest do
|
||||||
|
|
||||||
doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
|
doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
|
||||||
[author] = :xmerl_xpath.string('//author[1]', doc)
|
[author] = :xmerl_xpath.string('//author[1]', doc)
|
||||||
{:ok, user} = OStatus.find_make_or_update_user(author)
|
{:ok, user} = OStatus.find_make_or_update_actor(author)
|
||||||
assert user.avatar["type"] == "Image"
|
assert user.avatar["type"] == "Image"
|
||||||
assert user.name == old_name
|
assert user.name == old_name
|
||||||
assert user.bio == old_bio
|
assert user.bio == old_bio
|
||||||
|
|
||||||
{:ok, user_again} = OStatus.find_make_or_update_user(author)
|
{:ok, user_again} = OStatus.find_make_or_update_actor(author)
|
||||||
assert user_again == user
|
assert user_again == user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "find_or_make_user disallows protocol downgrade" do
|
||||||
|
user = insert(:user, %{local: true})
|
||||||
|
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
|
||||||
|
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
ap_id: "https://social.heldscal.la/user/23211",
|
||||||
|
info: %{ap_enabled: true},
|
||||||
|
local: false
|
||||||
|
})
|
||||||
|
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
|
||||||
|
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "find_make_or_update_actor disallows protocol downgrade" do
|
||||||
|
user = insert(:user, %{local: true})
|
||||||
|
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
|
||||||
|
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
ap_id: "https://social.heldscal.la/user/23211",
|
||||||
|
info: %{ap_enabled: true},
|
||||||
|
local: false
|
||||||
|
})
|
||||||
|
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
|
||||||
|
{:ok, user} = OStatus.find_or_make_user(user.ap_id)
|
||||||
|
assert User.ap_enabled?(user)
|
||||||
|
|
||||||
|
doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
|
||||||
|
[author] = :xmerl_xpath.string('//author[1]', doc)
|
||||||
|
{:error, :invalid_protocol} = OStatus.find_make_or_update_actor(author)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "gathering user info from a user id" do
|
describe "gathering user info from a user id" do
|
||||||
|
|
69
test/web/rich_media/parsers/twitter_card_test.exs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
alias Pleroma.Web.RichMedia.Parsers.TwitterCard
|
||||||
|
|
||||||
|
test "returns error when html not contains twitter card" do
|
||||||
|
assert TwitterCard.parse("", %{}) == {:error, "No twitter card metadata found"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "parses twitter card with only name attributes" do
|
||||||
|
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html")
|
||||||
|
|
||||||
|
assert TwitterCard.parse(html, %{}) ==
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
"app:id:googleplay": "com.nytimes.android",
|
||||||
|
"app:name:googleplay": "NYTimes",
|
||||||
|
"app:url:googleplay": "nytimes://reader/id/100000006583622",
|
||||||
|
site: nil,
|
||||||
|
title:
|
||||||
|
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database. - The New York Times"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "parses twitter card with only property attributes" do
|
||||||
|
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html")
|
||||||
|
|
||||||
|
assert TwitterCard.parse(html, %{}) ==
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
card: "summary_large_image",
|
||||||
|
description:
|
||||||
|
"With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
|
||||||
|
image:
|
||||||
|
"https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
|
||||||
|
"image:alt": "",
|
||||||
|
title:
|
||||||
|
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
|
||||||
|
url:
|
||||||
|
"https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "parses twitter card with name & property attributes" do
|
||||||
|
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html")
|
||||||
|
|
||||||
|
assert TwitterCard.parse(html, %{}) ==
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
"app:id:googleplay": "com.nytimes.android",
|
||||||
|
"app:name:googleplay": "NYTimes",
|
||||||
|
"app:url:googleplay": "nytimes://reader/id/100000006583622",
|
||||||
|
card: "summary_large_image",
|
||||||
|
description:
|
||||||
|
"With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
|
||||||
|
image:
|
||||||
|
"https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
|
||||||
|
"image:alt": "",
|
||||||
|
site: nil,
|
||||||
|
title:
|
||||||
|
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
|
||||||
|
url:
|
||||||
|
"https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|