[#468] Merged upstream/develop
, resolved conflicts.
This commit is contained in:
commit
bc4f77b10b
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# You can give explicit globs or simply directories.
|
# You can give explicit globs or simply directories.
|
||||||
# In the latter case `**/*.{ex,exs}` will be used.
|
# In the latter case `**/*.{ex,exs}` will be used.
|
||||||
included: ["lib/", "src/", "web/", "apps/"],
|
included: ["lib/", "src/", "web/", "apps/", "test/"],
|
||||||
excluded: [~r"/_build/", ~r"/deps/"]
|
excluded: [~r"/_build/", ~r"/deps/"]
|
||||||
},
|
},
|
||||||
#
|
#
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
# For some checks, like AliasUsage, you can only customize the priority
|
# For some checks, like AliasUsage, you can only customize the priority
|
||||||
# Priority values are: `low, normal, high, higher`
|
# Priority values are: `low, normal, high, higher`
|
||||||
{Credo.Check.Design.AliasUsage, priority: :low},
|
{Credo.Check.Design.AliasUsage, priority: :low, if_called_more_often_than: 3},
|
||||||
|
|
||||||
# For others you can set parameters
|
# For others you can set parameters
|
||||||
|
|
||||||
|
@ -104,7 +104,8 @@
|
||||||
{Credo.Check.Warning.BoolOperationOnSameValues},
|
{Credo.Check.Warning.BoolOperationOnSameValues},
|
||||||
{Credo.Check.Warning.IExPry},
|
{Credo.Check.Warning.IExPry},
|
||||||
{Credo.Check.Warning.IoInspect},
|
{Credo.Check.Warning.IoInspect},
|
||||||
{Credo.Check.Warning.LazyLogging},
|
# Got too much of them, not sure if relevant
|
||||||
|
{Credo.Check.Warning.LazyLogging, false},
|
||||||
{Credo.Check.Warning.OperationOnSameValues},
|
{Credo.Check.Warning.OperationOnSameValues},
|
||||||
{Credo.Check.Warning.OperationWithConstantResult},
|
{Credo.Check.Warning.OperationWithConstantResult},
|
||||||
{Credo.Check.Warning.UnusedEnumOperation},
|
{Credo.Check.Warning.UnusedEnumOperation},
|
||||||
|
|
15
README.md
15
README.md
|
@ -8,20 +8,7 @@ Pleroma is written in Elixir, high-performance and can run on small devices like
|
||||||
|
|
||||||
For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md).
|
For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md).
|
||||||
|
|
||||||
Client applications that are committed to supporting Pleroma:
|
- [Client Applications for Pleroma](docs/Clients.md)
|
||||||
|
|
||||||
* Mastalab (Android, Streaming Ready)
|
|
||||||
* nekonium (Android, Streaming Ready)
|
|
||||||
* Tusky (Android, No Streaming)
|
|
||||||
* Twidere (Android, No Streaming)
|
|
||||||
* Mast (iOS, Paid)
|
|
||||||
* Amaroq (iOS, No Streaming)
|
|
||||||
|
|
||||||
Client applications that are known to work well:
|
|
||||||
|
|
||||||
* Tootdon (Android + iOS, No Streaming)
|
|
||||||
* Tootle (iOS, No Streaming)
|
|
||||||
* Whalebird (Windows + Mac + Linux)
|
|
||||||
|
|
||||||
No release has been made yet, but several servers have been online for months already. If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>.
|
No release has been made yet, but several servers have been online for months already. If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>.
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,9 @@
|
||||||
mrf_transparency: true,
|
mrf_transparency: true,
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
max_pinned_statuses: 1,
|
max_pinned_statuses: 1,
|
||||||
no_attachment_links: false
|
no_attachment_links: false,
|
||||||
|
welcome_user_nickname: nil,
|
||||||
|
welcome_message: nil
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
@ -228,8 +230,8 @@
|
||||||
allow_direct: false
|
allow_direct: false
|
||||||
|
|
||||||
config :pleroma, :mrf_hellthread,
|
config :pleroma, :mrf_hellthread,
|
||||||
delist_threshold: 5,
|
delist_threshold: 10,
|
||||||
reject_threshold: 10
|
reject_threshold: 20
|
||||||
|
|
||||||
config :pleroma, :mrf_simple,
|
config :pleroma, :mrf_simple,
|
||||||
media_removal: [],
|
media_removal: [],
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
debug_errors: true,
|
debug_errors: true,
|
||||||
code_reloader: true,
|
code_reloader: true,
|
||||||
check_origin: false,
|
check_origin: false,
|
||||||
watchers: []
|
watchers: [],
|
||||||
|
secure_cookie_flag: false
|
||||||
|
|
||||||
config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local
|
config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local
|
||||||
|
|
||||||
|
|
100
docs/Clients.md
Normal file
100
docs/Clients.md
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
# Pleroma Clients
|
||||||
|
Note: Additionnal clients may be working but theses are officially supporting Pleroma.
|
||||||
|
Feel free to contact us to be added to this list!
|
||||||
|
|
||||||
|
## Desktop
|
||||||
|
### Roma for Desktop
|
||||||
|
- Homepage: <http://www.pleroma.com/desktop-app/>
|
||||||
|
- Source Code: ???
|
||||||
|
- Platforms: Windows, Mac, (Linux?)
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Social
|
||||||
|
- Source Code: <https://gitlab.gnome.org/BrainBlasted/Social>
|
||||||
|
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
|
||||||
|
- Platforms: Linux (GNOME)
|
||||||
|
- Note(2019-01-28): Not at a pre-alpha stage yet
|
||||||
|
|
||||||
|
### Whalebird
|
||||||
|
- Homepage: <https://whalebird.org/>
|
||||||
|
- Source Code: <https://github.com/h3poteto/whalebird-desktop>
|
||||||
|
- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
|
||||||
|
- Platforms: Windows, Mac, Linux
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
## Handheld
|
||||||
|
### Amaroq
|
||||||
|
- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
|
||||||
|
- Source Code: <https://github.com/ReticentJohn/Amaroq>
|
||||||
|
- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
|
||||||
|
- Platforms: iOS
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Nekonium
|
||||||
|
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
||||||
|
- Source: <https://git.gdgd.jp.net/lin/nekonium/>
|
||||||
|
- Contact: [@lin@pleroma.gdgd.jp.net](https://pleroma.gdgd.jp.net/users/lin)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Mastalab
|
||||||
|
- Source Code: <https://gitlab.com/tom79/mastalab/>
|
||||||
|
- Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Roma
|
||||||
|
- Homepage: <http://www.pleroma.com/>
|
||||||
|
- Source Code: ???
|
||||||
|
- Platforms: iOS, Android
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Tootdon
|
||||||
|
- Homepage: <http://tootdon.club/>, <http://blog.mastodon-tootdon.com/>
|
||||||
|
- Source Code: ???
|
||||||
|
- Contact: [@tootdon@mstdn.jp](https://mstdn.jp/users/tootdon)
|
||||||
|
- Platforms: Android, iOS
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Tusky
|
||||||
|
- Homepage: <https://tuskyapp.github.io/>
|
||||||
|
- Source Code: <https://github.com/tuskyapp/Tusky>
|
||||||
|
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Twidere
|
||||||
|
- Homepage: <https://twidere.mariotaku.org/>
|
||||||
|
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>, <https://github.com/TwidereProject/Twidere-iOS/>
|
||||||
|
- Contact: <me@mariotaku.org>
|
||||||
|
- Platform: Android, iOS
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
## Alternative Web Interfaces
|
||||||
|
### Brutaldon
|
||||||
|
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
|
||||||
|
- Source Code: <https://github.com/jfmcbrayer/brutaldon>
|
||||||
|
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Feather
|
||||||
|
- Source Code: <https://github.com/kaniini/feather>
|
||||||
|
- Contact: [@kaniini@pleroma.site](https://pleroma.site/kaniini)
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Halcyon
|
||||||
|
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
||||||
|
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Pinafore
|
||||||
|
- Homepage: <https://pinafore.social/>
|
||||||
|
- Source Code: <https://github.com/nolanlawson/pinafore>
|
||||||
|
- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
|
||||||
|
- Note: Pleroma support is a secondary goal
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Sengi
|
||||||
|
- Source Code: <https://github.com/NicolasConstant/sengi>
|
||||||
|
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
||||||
|
- Note(2019-01-28): The development is currently in a early stage.
|
|
@ -97,6 +97,8 @@ config :pleroma, Pleroma.Mailer,
|
||||||
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||||
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
||||||
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
|
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
|
||||||
|
* `welcome_message`: A message that will be send to a newly registered users as a direct message.
|
||||||
|
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/sbin/openrc-run
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
# Requires OpenRC >= 0.35
|
# Requires OpenRC >= 0.35
|
||||||
directory=~pleroma/pleroma
|
directory=/opt/pleroma
|
||||||
|
|
||||||
command=/usr/bin/mix
|
command=/usr/bin/mix
|
||||||
command_args="phx.server"
|
command_args="phx.server"
|
||||||
|
|
|
@ -15,12 +15,13 @@ server {
|
||||||
return 301 https://$server_name$request_uri;
|
return 301 https://$server_name$request_uri;
|
||||||
|
|
||||||
# Uncomment this if you need to use the 'webroot' method with certbot. Make sure
|
# Uncomment this if you need to use the 'webroot' method with certbot. Make sure
|
||||||
# that you also create the .well-known/acme-challenge directory structure in pleroma/priv/static and
|
# that the directory exists and that it is accessible by the webserver. If you followed
|
||||||
# that is is accessible by the webserver. You may need to load this file with the ssl
|
# the guide, you already ran 'sudo mkdir -p /var/lib/letsencrypt' to create the folder.
|
||||||
# server block commented out, run certbot to get the certificate, and then uncomment it.
|
# You may need to load this file with the ssl server block commented out, run certbot
|
||||||
|
# to get the certificate, and then uncomment it.
|
||||||
#
|
#
|
||||||
# location ~ /\.well-known/acme-challenge {
|
# location ~ /\.well-known/acme-challenge {
|
||||||
# root <path to install>/pleroma/priv/static/;
|
# root /var/lib/letsencrypt/.well-known/acme-challenge;
|
||||||
# }
|
# }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,17 @@ Environment="MIX_ENV=prod"
|
||||||
|
|
||||||
; Make sure that all paths fit your installation.
|
; Make sure that all paths fit your installation.
|
||||||
; Path to the home directory of the user running the Pleroma service.
|
; Path to the home directory of the user running the Pleroma service.
|
||||||
Environment="HOME=/home/pleroma"
|
Environment="HOME=/var/lib/pleroma"
|
||||||
; Path to the folder containing the Pleroma installation.
|
; Path to the folder containing the Pleroma installation.
|
||||||
WorkingDirectory=/home/pleroma/pleroma
|
WorkingDirectory=/opt/pleroma
|
||||||
; Path to the Mix binary.
|
; Path to the Mix binary.
|
||||||
ExecStart=/usr/bin/mix phx.server
|
ExecStart=/usr/bin/mix phx.server
|
||||||
|
|
||||||
; Some security directives.
|
; Some security directives.
|
||||||
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
|
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
; The /home, /root, and /run/user folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to false.
|
||||||
|
ProtectHome=true
|
||||||
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
|
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
|
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Uploads do
|
defmodule Mix.Tasks.Pleroma.Uploads do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
alias Pleroma.{Upload, Uploaders.Local}
|
alias Pleroma.Upload
|
||||||
|
alias Pleroma.Uploaders.Local
|
||||||
alias Mix.Tasks.Pleroma.Common
|
alias Mix.Tasks.Pleroma.Common
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ defmodule Mix.Tasks.Pleroma.Uploads do
|
||||||
- `--delete` - delete local uploads after migrating them to the target uploader
|
- `--delete` - delete local uploads after migrating them to the target uploader
|
||||||
|
|
||||||
|
|
||||||
A list of avalible uploaders can be seen in config.exs
|
A list of available uploaders can be seen in config.exs
|
||||||
"""
|
"""
|
||||||
def run(["migrate_local", target_uploader | args]) do
|
def run(["migrate_local", target_uploader | args]) do
|
||||||
delete? = Enum.member?(args, "--delete")
|
delete? = Enum.member?(args, "--delete")
|
||||||
|
@ -96,6 +97,7 @@ def run(["migrate_local", target_uploader | args]) do
|
||||||
timeout: 150_000
|
timeout: 150_000
|
||||||
)
|
)
|
||||||
|> Stream.chunk_every(@log_every)
|
|> Stream.chunk_every(@log_every)
|
||||||
|
# credo:disable-for-next-line Credo.Check.Warning.UnusedEnumOperation
|
||||||
|> Enum.reduce(0, fn done, count ->
|
|> Enum.reduce(0, fn done, count ->
|
||||||
count = count + length(done)
|
count = count + length(done)
|
||||||
Mix.shell().info("Uploaded #{count}/#{total_count} files")
|
Mix.shell().info("Uploaded #{count}/#{total_count} files")
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
defmodule Mix.Tasks.Pleroma.User do
|
defmodule Mix.Tasks.Pleroma.User do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Pleroma.{Repo, User}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Mix.Tasks.Pleroma.Common
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
|
||||||
@shortdoc "Manages Pleroma users"
|
@shortdoc "Manages Pleroma users"
|
||||||
|
@ -211,7 +212,7 @@ def run(["unsubscribe", nickname]) do
|
||||||
|
|
||||||
user = Repo.get(User, user.id)
|
user = Repo.get(User, user.id)
|
||||||
|
|
||||||
if length(user.following) == 0 do
|
if Enum.empty?(user.following) do
|
||||||
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
|
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -7,7 +7,9 @@ defmodule Pleroma.PasswordResetToken do
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.{User, PasswordResetToken, Repo}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.PasswordResetToken
|
||||||
|
|
||||||
schema "password_reset_tokens" do
|
schema "password_reset_tokens" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Activity do
|
defmodule Pleroma.Activity do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{Repo, Activity, Notification}
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Captcha do
|
defmodule Pleroma.Captcha do
|
||||||
|
alias Calendar.DateTime
|
||||||
alias Plug.Crypto.KeyGenerator
|
alias Plug.Crypto.KeyGenerator
|
||||||
alias Plug.Crypto.MessageEncryptor
|
alias Plug.Crypto.MessageEncryptor
|
||||||
alias Calendar.DateTime
|
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ defmodule Pleroma.UserEmail do
|
||||||
|
|
||||||
import Swoosh.Email
|
import Swoosh.Email
|
||||||
|
|
||||||
alias Pleroma.Web.{Endpoint, Router}
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
defp instance_config, do: Pleroma.Config.get(:instance)
|
defp instance_config, do: Pleroma.Config.get(:instance)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Filter do
|
defmodule Pleroma.Filter do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.{Changeset, Query}
|
|
||||||
alias Pleroma.{User, Repo}
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
schema "filters" do
|
schema "filters" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
|
@ -27,7 +27,7 @@ def to_string(<<0::integer-size(64), id::integer-size(64)>>) do
|
||||||
Kernel.to_string(id)
|
Kernel.to_string(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_string(flake = <<_::integer-size(64), _::integer-size(48), _::integer-size(16)>>) do
|
def to_string(<<_::integer-size(64), _::integer-size(48), _::integer-size(16)>> = flake) do
|
||||||
encode_base62(flake)
|
encode_base62(flake)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def from_string(unquote(i)), do: <<0::integer-size(128)>>
|
||||||
def from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
|
def from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
|
||||||
end
|
end
|
||||||
|
|
||||||
def from_string(flake = <<_::integer-size(128)>>), do: flake
|
def from_string(<<_::integer-size(128)>> = flake), do: flake
|
||||||
|
|
||||||
def from_string(string) when is_binary(string) and byte_size(string) < 18 do
|
def from_string(string) when is_binary(string) and byte_size(string) < 18 do
|
||||||
case Integer.parse(string) do
|
case Integer.parse(string) do
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Formatter do
|
defmodule Pleroma.Formatter do
|
||||||
|
alias Pleroma.Emoji
|
||||||
|
alias Pleroma.HTML
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.HTML
|
|
||||||
alias Pleroma.Emoji
|
|
||||||
|
|
||||||
@tag_regex ~r/((?<=[^&])|\A)(\#)(\w+)/u
|
@tag_regex ~r/((?<=[^&])|\A)(\#)(\w+)/u
|
||||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
|
|
|
@ -37,17 +37,17 @@ def init([ip, port]) do
|
||||||
|
|
||||||
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
def start_link(ref, socket, transport, opts) do
|
def start_link(ref, socket, transport, opts) do
|
||||||
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
||||||
{:ok, pid}
|
{:ok, pid}
|
||||||
end
|
end
|
||||||
|
|
||||||
def init(ref, socket, transport, _Opts = []) do
|
def init(ref, socket, transport, [] = _Opts) do
|
||||||
:ok = :ranch.accept_ack(ref)
|
:ok = :ranch.accept_ack(ref)
|
||||||
loop(socket, transport)
|
loop(socket, transport)
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,8 +83,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@markup Application.get_env(:pleroma, :markup)
|
@markup Application.get_env(:pleroma, :markup)
|
||||||
@uri_schemes Application.get_env(:pleroma, :uri_schemes, [])
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||||
@valid_schemes Keyword.get(@uri_schemes, :valid_schemes, [])
|
|
||||||
|
|
||||||
require HtmlSanitizeEx.Scrubber.Meta
|
require HtmlSanitizeEx.Scrubber.Meta
|
||||||
alias HtmlSanitizeEx.Scrubber.Meta
|
alias HtmlSanitizeEx.Scrubber.Meta
|
||||||
|
@ -126,10 +125,11 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
|
|
||||||
require HtmlSanitizeEx.Scrubber.Meta
|
require HtmlSanitizeEx.Scrubber.Meta
|
||||||
alias HtmlSanitizeEx.Scrubber.Meta
|
alias HtmlSanitizeEx.Scrubber.Meta
|
||||||
|
# credo:disable-for-previous-line
|
||||||
|
# No idea how to fix this one…
|
||||||
|
|
||||||
@markup Application.get_env(:pleroma, :markup)
|
@markup Application.get_env(:pleroma, :markup)
|
||||||
@uri_schemes Application.get_env(:pleroma, :uri_schemes, [])
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||||
@valid_schemes Keyword.get(@uri_schemes, :valid_schemes, [])
|
|
||||||
|
|
||||||
Meta.remove_cdata_sections_before_scrub()
|
Meta.remove_cdata_sections_before_scrub()
|
||||||
Meta.strip_comments()
|
Meta.strip_comments()
|
||||||
|
|
|
@ -2,13 +2,13 @@ defmodule Pleroma.Instances.Instance do
|
||||||
@moduledoc "Instance."
|
@moduledoc "Instance."
|
||||||
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Instances.Instance
|
alias Pleroma.Instances.Instance
|
||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.{Query, Changeset}
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
alias Pleroma.Repo
|
|
||||||
|
|
||||||
schema "instances" do
|
schema "instances" do
|
||||||
field(:host, :string)
|
field(:host, :string)
|
||||||
|
|
|
@ -4,8 +4,13 @@
|
||||||
|
|
||||||
defmodule Pleroma.List do
|
defmodule Pleroma.List do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.{Changeset, Query}
|
|
||||||
alias Pleroma.{User, Repo, Activity}
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
schema "lists" do
|
schema "lists" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
|
@ -4,8 +4,14 @@
|
||||||
|
|
||||||
defmodule Pleroma.Notification do
|
defmodule Pleroma.Notification do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{User, Activity, Notification, Repo}
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
schema "notifications" do
|
schema "notifications" do
|
||||||
|
@ -112,7 +118,7 @@ def create_notifications(_), do: {:ok, []}
|
||||||
# TODO move to sql, too.
|
# TODO move to sql, too.
|
||||||
def create_notification(%Activity{} = activity, %User{} = user) do
|
def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
|
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
|
||||||
user.ap_id == activity.data["actor"] or
|
CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or
|
||||||
(activity.data["type"] == "Follow" and
|
(activity.data["type"] == "Follow" and
|
||||||
Enum.any?(Notification.for_user(user), fn notif ->
|
Enum.any?(Notification.for_user(user), fn notif ->
|
||||||
notif.activity.data["type"] == "Follow" and
|
notif.activity.data["type"] == "Follow" and
|
||||||
|
|
|
@ -4,8 +4,15 @@
|
||||||
|
|
||||||
defmodule Pleroma.Object do
|
defmodule Pleroma.Object do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{Repo, Object, User, Activity, ObjectTombstone}
|
|
||||||
import Ecto.{Query, Changeset}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ObjectTombstone
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
schema "objects" do
|
schema "objects" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
|
|
|
@ -33,7 +33,22 @@ defp headers do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp csp_string do
|
defp csp_string do
|
||||||
protocol = Config.get([Pleroma.Web.Endpoint, :protocol])
|
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
||||||
|
websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
|
||||||
|
|
||||||
|
connect_src =
|
||||||
|
if Mix.env() == :dev do
|
||||||
|
"connect-src 'self' http://localhost:3035/ " <> websocket_url
|
||||||
|
else
|
||||||
|
"connect-src 'self' " <> websocket_url
|
||||||
|
end
|
||||||
|
|
||||||
|
script_src =
|
||||||
|
if Mix.env() == :dev do
|
||||||
|
"script-src 'self' 'unsafe-eval'"
|
||||||
|
else
|
||||||
|
"script-src 'self'"
|
||||||
|
end
|
||||||
|
|
||||||
[
|
[
|
||||||
"default-src 'none'",
|
"default-src 'none'",
|
||||||
|
@ -43,10 +58,10 @@ defp csp_string do
|
||||||
"media-src 'self' https:",
|
"media-src 'self' https:",
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"font-src 'self'",
|
"font-src 'self'",
|
||||||
"script-src 'self'",
|
|
||||||
"connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"),
|
|
||||||
"manifest-src 'self'",
|
"manifest-src 'self'",
|
||||||
if protocol == "https" do
|
connect_src,
|
||||||
|
script_src,
|
||||||
|
if scheme == "https" do
|
||||||
"upgrade-insecure-requests"
|
"upgrade-insecure-requests"
|
||||||
end
|
end
|
||||||
]
|
]
|
||||||
|
|
|
@ -33,7 +33,7 @@ def init(opts) do
|
||||||
for only <- @only do
|
for only <- @only do
|
||||||
at = Plug.Router.Utils.split("/")
|
at = Plug.Router.Utils.split("/")
|
||||||
|
|
||||||
def call(conn = %{request_path: "/" <> unquote(only) <> _}, opts) do
|
def call(%{request_path: "/" <> unquote(only) <> _} = conn, opts) do
|
||||||
call_static(
|
call_static(
|
||||||
conn,
|
conn,
|
||||||
opts,
|
opts,
|
||||||
|
|
|
@ -6,11 +6,9 @@ defmodule Pleroma.Plugs.OAuthPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.{
|
alias Pleroma.User
|
||||||
User,
|
alias Pleroma.Repo
|
||||||
Repo,
|
alias Pleroma.Web.OAuth.Token
|
||||||
Web.OAuth.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ def init(_opts) do
|
||||||
%{static_plug_opts: static_plug_opts}
|
%{static_plug_opts: static_plug_opts}
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn = %{request_path: <<"/", @path, "/", file::binary>>}, opts) do
|
def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
config = Pleroma.Config.get([Pleroma.Upload])
|
config = Pleroma.Config.get([Pleroma.Upload])
|
||||||
|
|
||||||
with uploader <- Keyword.fetch!(config, :uploader),
|
with uploader <- Keyword.fetch!(config, :uploader),
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.UserFetcherPlug do
|
defmodule Pleroma.Plugs.UserFetcherPlug do
|
||||||
import Plug.Conn
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Stats do
|
defmodule Pleroma.Stats do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.{User, Repo}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
def start_link do
|
def start_link do
|
||||||
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
|
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
|
||||||
|
@ -23,7 +24,7 @@ def get_peers do
|
||||||
def schedule_update do
|
def schedule_update do
|
||||||
spawn(fn ->
|
spawn(fn ->
|
||||||
# 1 hour
|
# 1 hour
|
||||||
Process.sleep(1000 * 60 * 60 * 1)
|
Process.sleep(1000 * 60 * 60)
|
||||||
schedule_update()
|
schedule_update()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
45
lib/pleroma/thread_mute.ex
Normal file
45
lib/pleroma/thread_mute.ex
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.ThreadMute do
|
||||||
|
use Ecto.Schema
|
||||||
|
alias Pleroma.{Repo, User, ThreadMute}
|
||||||
|
require Ecto.Query
|
||||||
|
|
||||||
|
schema "thread_mutes" do
|
||||||
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
field(:context, :string)
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(mute, params \\ %{}) do
|
||||||
|
mute
|
||||||
|
|> Ecto.Changeset.cast(params, [:user_id, :context])
|
||||||
|
|> Ecto.Changeset.foreign_key_constraint(:user_id)
|
||||||
|
|> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def query(user_id, context) do
|
||||||
|
user_id = Pleroma.FlakeId.from_string(user_id)
|
||||||
|
|
||||||
|
ThreadMute
|
||||||
|
|> Ecto.Query.where(user_id: ^user_id)
|
||||||
|
|> Ecto.Query.where(context: ^context)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_mute(user_id, context) do
|
||||||
|
%ThreadMute{}
|
||||||
|
|> changeset(%{user_id: user_id, context: context})
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_mute(user_id, context) do
|
||||||
|
query(user_id, context)
|
||||||
|
|> Repo.delete_all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_muted(user_id, context) do
|
||||||
|
query(user_id, context)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
end
|
|
@ -180,7 +180,7 @@ defp prepare_upload(%{"img" => "data:image/" <> image_data}, opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# For Mix.Tasks.MigrateLocalUploads
|
# For Mix.Tasks.MigrateLocalUploads
|
||||||
defp prepare_upload(upload = %__MODULE__{tempfile: path}, _opts) do
|
defp prepare_upload(%__MODULE__{tempfile: path} = upload, _opts) do
|
||||||
with {:ok, content_type} <- Pleroma.MIME.file_mime_type(path) do
|
with {:ok, content_type} <- Pleroma.MIME.file_mime_type(path) do
|
||||||
{:ok, %__MODULE__{upload | content_type: content_type}}
|
{:ok, %__MODULE__{upload | content_type: content_type}}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Upload.Filter.Dedupe do
|
||||||
@behaviour Pleroma.Upload.Filter
|
@behaviour Pleroma.Upload.Filter
|
||||||
alias Pleroma.Upload
|
alias Pleroma.Upload
|
||||||
|
|
||||||
def filter(upload = %Upload{name: name}) do
|
def filter(%Upload{name: name} = upload) do
|
||||||
extension = String.split(name, ".") |> List.last()
|
extension = String.split(name, ".") |> List.last()
|
||||||
shasum = :crypto.hash(:sha256, File.read!(upload.tempfile)) |> Base.encode16(case: :lower)
|
shasum = :crypto.hash(:sha256, File.read!(upload.tempfile)) |> Base.encode16(case: :lower)
|
||||||
filename = shasum <> "." <> extension
|
filename = shasum <> "." <> extension
|
||||||
|
|
|
@ -25,7 +25,7 @@ def put_file(upload) do
|
||||||
query = "#{cgi}?#{extension}"
|
query = "#{cgi}?#{extension}"
|
||||||
|
|
||||||
with {:ok, %{status: 200, body: body}} <-
|
with {:ok, %{status: 200, body: body}} <-
|
||||||
@httpoison.post(query, file_data, adapter: [pool: :default]) do
|
@httpoison.post(query, file_data, [], adapter: [pool: :default]) do
|
||||||
remote_file_name = String.split(body) |> List.first()
|
remote_file_name = String.split(body) |> List.first()
|
||||||
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
||||||
{:ok, {:url, public_url}}
|
{:ok, {:url, public_url}}
|
||||||
|
|
|
@ -27,7 +27,7 @@ def get_file(file) do
|
||||||
])}}
|
])}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_file(upload = %Pleroma.Upload{}) do
|
def put_file(%Pleroma.Upload{} = upload) do
|
||||||
config = Pleroma.Config.get([__MODULE__])
|
config = Pleroma.Config.get([__MODULE__])
|
||||||
bucket = Keyword.get(config, :bucket)
|
bucket = Keyword.get(config, :bucket)
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,23 @@
|
||||||
defmodule Pleroma.User do
|
defmodule Pleroma.User do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.{Changeset, Query}
|
import Ecto.Changeset
|
||||||
alias Pleroma.{Repo, User, Object, Web, Activity, Notification}
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||||
alias Pleroma.Web.{OStatus, Websub, OAuth}
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
|
alias Pleroma.Web.Websub
|
||||||
|
alias Pleroma.Web.OAuth
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -96,12 +106,6 @@ def ap_followers(%User{} = user) do
|
||||||
"#{ap_id(user)}/followers"
|
"#{ap_id(user)}/followers"
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_changeset(struct, params \\ %{}) do
|
|
||||||
struct
|
|
||||||
|> cast(params, [:following])
|
|
||||||
|> validate_required([:following])
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_info(%User{} = user) do
|
def user_info(%User{} = user) do
|
||||||
oneself = if user.local, do: 1, else: 0
|
oneself = if user.local, do: 1, else: 0
|
||||||
|
|
||||||
|
@ -256,8 +260,9 @@ defp autofollow_users(user) do
|
||||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||||
def register(%Ecto.Changeset{} = changeset) do
|
def register(%Ecto.Changeset{} = changeset) do
|
||||||
with {:ok, user} <- Repo.insert(changeset),
|
with {:ok, user} <- Repo.insert(changeset),
|
||||||
{:ok, _} <- try_send_confirmation_email(user),
|
{:ok, user} <- autofollow_users(user),
|
||||||
{:ok, user} <- autofollow_users(user) do
|
{:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
|
||||||
|
{:ok, _} <- try_send_confirmation_email(user) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -307,10 +312,13 @@ def maybe_follow(%User{} = follower, %User{info: _info} = followed) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "A mass follow for local users. Ignores blocks and has no side effects"
|
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
||||||
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
||||||
def follow_all(follower, followeds) do
|
def follow_all(follower, followeds) do
|
||||||
followed_addresses = Enum.map(followeds, fn %{follower_address: fa} -> fa end)
|
followed_addresses =
|
||||||
|
followeds
|
||||||
|
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
|
||||||
|
|> Enum.map(fn %{follower_address: fa} -> fa end)
|
||||||
|
|
||||||
q =
|
q =
|
||||||
from(u in User,
|
from(u in User,
|
||||||
|
@ -724,7 +732,7 @@ def search(query, resolve \\ false, for_user \\ nil) do
|
||||||
# Strip the beginning @ off if there is a query
|
# Strip the beginning @ off if there is a query
|
||||||
query = String.trim_leading(query, "@")
|
query = String.trim_leading(query, "@")
|
||||||
|
|
||||||
if resolve, do: User.get_or_fetch_by_nickname(query)
|
if resolve, do: get_or_fetch(query)
|
||||||
|
|
||||||
fts_results = do_search(fts_search_subquery(query), for_user)
|
fts_results = do_search(fts_search_subquery(query), for_user)
|
||||||
|
|
||||||
|
|
30
lib/pleroma/user/welcome_message.ex
Normal file
30
lib/pleroma/user/welcome_message.ex
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
defmodule Pleroma.User.WelcomeMessage do
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
def post_welcome_message_to_user(user) do
|
||||||
|
with %User{} = sender_user <- welcome_user(),
|
||||||
|
message when is_binary(message) <- welcome_message() do
|
||||||
|
CommonAPI.post(sender_user, %{
|
||||||
|
"visibility" => "direct",
|
||||||
|
"status" => "@#{user.nickname}\n#{message}"
|
||||||
|
})
|
||||||
|
else
|
||||||
|
_ -> {:ok, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp welcome_user() do
|
||||||
|
with nickname when is_binary(nickname) <-
|
||||||
|
Pleroma.Config.get([:instance, :welcome_user_nickname]),
|
||||||
|
%User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
|
user
|
||||||
|
else
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp welcome_message() do
|
||||||
|
Pleroma.Config.get([:instance, :welcome_message])
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,13 +3,22 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification, Instances}
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Upload
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Pleroma.Web.ActivityPub.Utils
|
import Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||||
|
@ -19,10 +28,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
defp get_recipients(%{"type" => "Announce"} = data) do
|
defp get_recipients(%{"type" => "Announce"} = data) do
|
||||||
to = data["to"] || []
|
to = data["to"] || []
|
||||||
cc = data["cc"] || []
|
cc = data["cc"] || []
|
||||||
recipients = to ++ cc
|
|
||||||
actor = User.get_cached_by_ap_id(data["actor"])
|
actor = User.get_cached_by_ap_id(data["actor"])
|
||||||
|
|
||||||
recipients
|
recipients =
|
||||||
|
(to ++ cc)
|
||||||
|> Enum.filter(fn recipient ->
|
|> Enum.filter(fn recipient ->
|
||||||
case User.get_cached_by_ap_id(recipient) do
|
case User.get_cached_by_ap_id(recipient) do
|
||||||
nil ->
|
nil ->
|
||||||
|
@ -119,7 +128,7 @@ def stream_out(activity) do
|
||||||
activity.data["object"]
|
activity.data["object"]
|
||||||
|> Map.get("tag", [])
|
|> Map.get("tag", [])
|
||||||
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
||||||
|> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
|
|> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
|
||||||
|
|
||||||
if activity.data["object"]["attachment"] != [] do
|
if activity.data["object"]["attachment"] != [] do
|
||||||
Pleroma.Web.Streamer.stream("public:media", activity)
|
Pleroma.Web.Streamer.stream("public:media", activity)
|
||||||
|
@ -809,8 +818,6 @@ def fetch_object_from_id(id) do
|
||||||
if object = Object.get_cached_by_ap_id(id) do
|
if object = Object.get_cached_by_ap_id(id) do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
else
|
else
|
||||||
Logger.info("Fetching #{id} via AP")
|
|
||||||
|
|
||||||
with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
|
with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
|
||||||
nil <- Object.normalize(data),
|
nil <- Object.normalize(data),
|
||||||
params <- %{
|
params <- %{
|
||||||
|
@ -842,7 +849,7 @@ def fetch_object_from_id(id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_and_contain_remote_object_from_id(id) do
|
def fetch_and_contain_remote_object_from_id(id) do
|
||||||
Logger.info("Fetching #{id} via AP")
|
Logger.info("Fetching object #{id} via AP")
|
||||||
|
|
||||||
with true <- String.starts_with?(id, "http"),
|
with true <- String.starts_with?(id, "http"),
|
||||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.{Activity, User, Object}
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectView
|
||||||
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -6,40 +6,80 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp delist_message(message) do
|
defp delist_message(message, threshold) when threshold > 0 do
|
||||||
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
||||||
|
|
||||||
|
follower_collection? = Enum.member?(message["to"] ++ message["cc"], follower_collection)
|
||||||
|
|
||||||
|
message =
|
||||||
|
case recipients = get_recipient_count(message) do
|
||||||
|
{:public, _}
|
||||||
|
when follower_collection? and recipients > threshold ->
|
||||||
message
|
message
|
||||||
|> Map.put("to", [follower_collection])
|
|> Map.put("to", [follower_collection])
|
||||||
|> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"])
|
|> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"])
|
||||||
|
|
||||||
|
{:public, _} when recipients > threshold ->
|
||||||
|
message
|
||||||
|
|> Map.put("to", [])
|
||||||
|
|> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"])
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, message}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delist_message(message, _threshold), do: {:ok, message}
|
||||||
|
|
||||||
|
defp reject_message(message, threshold) when threshold > 0 do
|
||||||
|
with {_, recipients} <- get_recipient_count(message) do
|
||||||
|
if recipients > threshold do
|
||||||
|
{:reject, nil}
|
||||||
|
else
|
||||||
|
{:ok, message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reject_message(message, _threshold), do: {:ok, message}
|
||||||
|
|
||||||
|
defp get_recipient_count(message) do
|
||||||
|
recipients = (message["to"] || []) ++ (message["cc"] || [])
|
||||||
|
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
||||||
|
|
||||||
|
if Enum.member?(recipients, "https://www.w3.org/ns/activitystreams#Public") do
|
||||||
|
recipients =
|
||||||
|
recipients
|
||||||
|
|> List.delete("https://www.w3.org/ns/activitystreams#Public")
|
||||||
|
|> List.delete(follower_collection)
|
||||||
|
|
||||||
|
{:public, length(recipients)}
|
||||||
|
else
|
||||||
|
recipients =
|
||||||
|
recipients
|
||||||
|
|> List.delete(follower_collection)
|
||||||
|
|
||||||
|
{:not_public, length(recipients)}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"type" => "Create"} = message) do
|
def filter(%{"type" => "Create"} = message) do
|
||||||
delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold])
|
|
||||||
|
|
||||||
reject_threshold =
|
reject_threshold =
|
||||||
Pleroma.Config.get(
|
Pleroma.Config.get(
|
||||||
[:mrf_hellthread, :reject_threshold],
|
[:mrf_hellthread, :reject_threshold],
|
||||||
Pleroma.Config.get([:mrf_hellthread, :threshold])
|
Pleroma.Config.get([:mrf_hellthread, :threshold])
|
||||||
)
|
)
|
||||||
|
|
||||||
recipients = (message["to"] || []) ++ (message["cc"] || [])
|
delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold])
|
||||||
|
|
||||||
cond do
|
with {:ok, message} <- reject_message(message, reject_threshold),
|
||||||
length(recipients) > reject_threshold and reject_threshold > 0 ->
|
{:ok, message} <- delist_message(message, delist_threshold) do
|
||||||
{:reject, nil}
|
{:ok, message}
|
||||||
|
|
||||||
length(recipients) > delist_threshold and delist_threshold > 0 ->
|
|
||||||
if Enum.member?(message["to"], "https://www.w3.org/ns/activitystreams#Public") or
|
|
||||||
Enum.member?(message["cc"], "https://www.w3.org/ns/activitystreams#Public") do
|
|
||||||
{:ok, delist_message(message)}
|
|
||||||
else
|
else
|
||||||
{:ok, message}
|
_e -> {:reject, nil}
|
||||||
end
|
|
||||||
|
|
||||||
true ->
|
|
||||||
{:ok, message}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ defp string_matches?(string, pattern) do
|
||||||
String.match?(string, pattern)
|
String.match?(string, pattern)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_reject(%{"object" => %{"content" => content}} = message) do
|
defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||||
string_matches?(content, pattern)
|
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||||
end) do
|
end) do
|
||||||
{:reject, nil}
|
{:reject, nil}
|
||||||
else
|
else
|
||||||
|
@ -22,10 +22,12 @@ defp check_reject(%{"object" => %{"content" => content}} = message) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_ftl_removal(%{"to" => to, "object" => %{"content" => content}} = message) do
|
defp check_ftl_removal(
|
||||||
|
%{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message
|
||||||
|
) do
|
||||||
if "https://www.w3.org/ns/activitystreams#Public" in to and
|
if "https://www.w3.org/ns/activitystreams#Public" in to and
|
||||||
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||||
string_matches?(content, pattern)
|
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||||
end) do
|
end) do
|
||||||
to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public")
|
to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public")
|
||||||
cc = ["https://www.w3.org/ns/activitystreams#Public" | message["cc"] || []]
|
cc = ["https://www.w3.org/ns/activitystreams#Public" | message["cc"] || []]
|
||||||
|
@ -41,14 +43,20 @@ defp check_ftl_removal(%{"to" => to, "object" => %{"content" => content}} = mess
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_replace(%{"object" => %{"content" => content}} = message) do
|
defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||||
content =
|
{content, summary} =
|
||||||
Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), content, fn {pattern, replacement},
|
Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), {content, summary}, fn {pattern,
|
||||||
acc ->
|
replacement},
|
||||||
String.replace(acc, pattern, replacement)
|
{content_acc,
|
||||||
|
summary_acc} ->
|
||||||
|
{String.replace(content_acc, pattern, replacement),
|
||||||
|
String.replace(summary_acc, pattern, replacement)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, put_in(message["object"]["content"], content)}
|
{:ok,
|
||||||
|
message
|
||||||
|
|> put_in(["object", "content"], content)
|
||||||
|
|> put_in(["object", "summary"], summary)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||||
alias Pleroma.{User, Object, Activity}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
A module to handle coding from internal to wire ActivityPub and back.
|
A module to handle coding from internal to wire ActivityPub and back.
|
||||||
"""
|
"""
|
||||||
|
alias Pleroma.Activity
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
@ -649,7 +649,7 @@ def get_obj_helper(id) do
|
||||||
if object = Object.normalize(id), do: {:ok, object}, else: nil
|
if object = Object.normalize(id), do: {:ok, object}, else: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) do
|
def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) when is_binary(inReplyTo) do
|
||||||
with false <- String.starts_with?(inReplyTo, "http"),
|
with false <- String.starts_with?(inReplyTo, "http"),
|
||||||
{:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do
|
{:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do
|
||||||
Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo)
|
Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo)
|
||||||
|
@ -765,12 +765,18 @@ def maybe_fix_object_url(data) do
|
||||||
def add_hashtags(object) do
|
def add_hashtags(object) do
|
||||||
tags =
|
tags =
|
||||||
(object["tag"] || [])
|
(object["tag"] || [])
|
||||||
|> Enum.map(fn tag ->
|
|> Enum.map(fn
|
||||||
|
# Expand internal representation tags into AS2 tags.
|
||||||
|
tag when is_binary(tag) ->
|
||||||
%{
|
%{
|
||||||
"href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}",
|
"href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}",
|
||||||
"name" => "##{tag}",
|
"name" => "##{tag}",
|
||||||
"type" => "Hashtag"
|
"type" => "Hashtag"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Do not process tags which are already AS2 tag objects.
|
||||||
|
tag when is_map(tag) ->
|
||||||
|
tag
|
||||||
end)
|
end)
|
||||||
|
|
||||||
object
|
object
|
||||||
|
|
|
@ -3,11 +3,19 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Utils do
|
defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Pleroma.{Repo, Web, Object, Activity, User, Notification}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Web.Router.Helpers
|
alias Pleroma.Web.Router.Helpers
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.Changeset
|
||||||
|
alias Ecto.UUID
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@supported_object_types ["Article", "Note", "Video", "Page"]
|
@supported_object_types ["Article", "Note", "Video", "Page"]
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectView do
|
defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.{Object, Activity}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
def render("object.json", %{object: %Object{} = object}) do
|
def render("object.json", %{object: %Object{} = object}) do
|
||||||
|
|
|
@ -4,15 +4,34 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.UserView do
|
defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.Salmon
|
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
|
alias Pleroma.Web.Salmon
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do
|
||||||
|
%{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("endpoints.json", %{user: %User{local: true} = _user}) do
|
||||||
|
%{
|
||||||
|
"oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize),
|
||||||
|
"oauthRegistrationEndpoint" => Helpers.mastodon_api_url(Endpoint, :create_app),
|
||||||
|
"oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange),
|
||||||
|
"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("endpoints.json", _), do: %{}
|
||||||
|
|
||||||
# the instance itself is not a Person, but instead an Application
|
# the instance itself is not a Person, but instead an Application
|
||||||
def render("user.json", %{user: %{nickname: nil} = user}) do
|
def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||||
|
@ -20,6 +39,8 @@ def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
public_key = :public_key.pem_encode([public_key])
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
|
||||||
|
endpoints = render("endpoints.json", %{user: user})
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
"type" => "Application",
|
"type" => "Application",
|
||||||
|
@ -35,9 +56,7 @@ def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||||
"owner" => user.ap_id,
|
"owner" => user.ap_id,
|
||||||
"publicKeyPem" => public_key
|
"publicKeyPem" => public_key
|
||||||
},
|
},
|
||||||
"endpoints" => %{
|
"endpoints" => endpoints
|
||||||
"sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
@ -48,6 +67,8 @@ def render("user.json", %{user: user}) do
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
public_key = :public_key.pem_encode([public_key])
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
|
||||||
|
endpoints = render("endpoints.json", %{user: user})
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
"type" => "Person",
|
"type" => "Person",
|
||||||
|
@ -65,9 +86,7 @@ def render("user.json", %{user: user}) do
|
||||||
"owner" => user.ap_id,
|
"owner" => user.ap_id,
|
||||||
"publicKeyPem" => public_key
|
"publicKeyPem" => public_key
|
||||||
},
|
},
|
||||||
"endpoints" => %{
|
"endpoints" => endpoints,
|
||||||
"sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
|
|
||||||
},
|
|
||||||
"icon" => %{
|
"icon" => %{
|
||||||
"type" => "Image",
|
"type" => "Image",
|
||||||
"url" => User.avatar_url(user)
|
"url" => User.avatar_url(user)
|
||||||
|
@ -86,7 +105,14 @@ def render("following.json", %{user: user, page: page}) do
|
||||||
query = from(user in query, select: [:ap_id])
|
query = from(user in query, select: [:ap_id])
|
||||||
following = Repo.all(query)
|
following = Repo.all(query)
|
||||||
|
|
||||||
collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows)
|
total =
|
||||||
|
if !user.info.hide_follows do
|
||||||
|
length(following)
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows, total)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -95,10 +121,17 @@ def render("following.json", %{user: user}) do
|
||||||
query = from(user in query, select: [:ap_id])
|
query = from(user in query, select: [:ap_id])
|
||||||
following = Repo.all(query)
|
following = Repo.all(query)
|
||||||
|
|
||||||
|
total =
|
||||||
|
if !user.info.hide_follows do
|
||||||
|
length(following)
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => "#{user.ap_id}/following",
|
"id" => "#{user.ap_id}/following",
|
||||||
"type" => "OrderedCollection",
|
"type" => "OrderedCollection",
|
||||||
"totalItems" => length(following),
|
"totalItems" => total,
|
||||||
"first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
|
"first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
@ -109,7 +142,14 @@ def render("followers.json", %{user: user, page: page}) do
|
||||||
query = from(user in query, select: [:ap_id])
|
query = from(user in query, select: [:ap_id])
|
||||||
followers = Repo.all(query)
|
followers = Repo.all(query)
|
||||||
|
|
||||||
collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers)
|
total =
|
||||||
|
if !user.info.hide_followers do
|
||||||
|
length(followers)
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers, total)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -118,19 +158,24 @@ def render("followers.json", %{user: user}) do
|
||||||
query = from(user in query, select: [:ap_id])
|
query = from(user in query, select: [:ap_id])
|
||||||
followers = Repo.all(query)
|
followers = Repo.all(query)
|
||||||
|
|
||||||
|
total =
|
||||||
|
if !user.info.hide_followers do
|
||||||
|
length(followers)
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => "#{user.ap_id}/followers",
|
"id" => "#{user.ap_id}/followers",
|
||||||
"type" => "OrderedCollection",
|
"type" => "OrderedCollection",
|
||||||
"totalItems" => length(followers),
|
"totalItems" => total,
|
||||||
"first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers)
|
"first" =>
|
||||||
|
collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers, total)
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("outbox.json", %{user: user, max_id: max_qid}) do
|
def render("outbox.json", %{user: user, max_id: max_qid}) do
|
||||||
# XXX: technically note_count is wrong for this, but it's better than nothing
|
|
||||||
info = User.user_info(user)
|
|
||||||
|
|
||||||
params = %{
|
params = %{
|
||||||
"limit" => "10"
|
"limit" => "10"
|
||||||
}
|
}
|
||||||
|
@ -158,7 +203,6 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
||||||
"id" => "#{iri}?max_id=#{max_id}",
|
"id" => "#{iri}?max_id=#{max_id}",
|
||||||
"type" => "OrderedCollectionPage",
|
"type" => "OrderedCollectionPage",
|
||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"totalItems" => info.note_count,
|
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection,
|
||||||
"next" => "#{iri}?max_id=#{min_id}"
|
"next" => "#{iri}?max_id=#{min_id}"
|
||||||
}
|
}
|
||||||
|
@ -167,7 +211,6 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
||||||
%{
|
%{
|
||||||
"id" => iri,
|
"id" => iri,
|
||||||
"type" => "OrderedCollection",
|
"type" => "OrderedCollection",
|
||||||
"totalItems" => info.note_count,
|
|
||||||
"first" => page
|
"first" => page
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
@ -205,7 +248,6 @@ def render("inbox.json", %{user: user, max_id: max_qid}) do
|
||||||
"id" => "#{iri}?max_id=#{max_id}",
|
"id" => "#{iri}?max_id=#{max_id}",
|
||||||
"type" => "OrderedCollectionPage",
|
"type" => "OrderedCollectionPage",
|
||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"totalItems" => -1,
|
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection,
|
||||||
"next" => "#{iri}?max_id=#{min_id}"
|
"next" => "#{iri}?max_id=#{min_id}"
|
||||||
}
|
}
|
||||||
|
@ -214,7 +256,6 @@ def render("inbox.json", %{user: user, max_id: max_qid}) do
|
||||||
%{
|
%{
|
||||||
"id" => iri,
|
"id" => iri,
|
||||||
"type" => "OrderedCollection",
|
"type" => "OrderedCollection",
|
||||||
"totalItems" => -1,
|
|
||||||
"first" => page
|
"first" => page
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI do
|
defmodule Pleroma.Web.CommonAPI do
|
||||||
alias Pleroma.{User, Repo, Activity, Object}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.ThreadMute
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
|
@ -216,4 +220,27 @@ def unpin(id_or_ap_id, user) do
|
||||||
{:error, "Could not unpin"}
|
{:error, "Could not unpin"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_mute(user, activity) do
|
||||||
|
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:error, _} -> {:error, "conversation is already muted"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_mute(user, activity) do
|
||||||
|
ThreadMute.remove_mute(user.id, activity.data["context"])
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
|
||||||
|
def thread_muted?(%{id: nil} = _user, _activity), do: false
|
||||||
|
|
||||||
|
def thread_muted?(user, activity) do
|
||||||
|
with [] <- ThreadMute.check_muted(user.id, activity.data["context"]) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Calendar.Strftime
|
alias Calendar.Strftime
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.{Activity, Formatter, Object, Repo}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -95,7 +98,7 @@ def make_content_html(
|
||||||
def make_context(%Activity{data: %{"context" => context}}), do: context
|
def make_context(%Activity{data: %{"context" => context}}), do: context
|
||||||
def make_context(_), do: Utils.generate_context_id()
|
def make_context(_), do: Utils.generate_context_id()
|
||||||
|
|
||||||
def maybe_add_attachments(text, _attachments, _no_links = true), do: text
|
def maybe_add_attachments(text, _attachments, true = _no_links), do: text
|
||||||
|
|
||||||
def maybe_add_attachments(text, attachments, _no_links) do
|
def maybe_add_attachments(text, attachments, _no_links) do
|
||||||
add_attachments(text, attachments)
|
add_attachments(text, attachments)
|
||||||
|
|
|
@ -4,15 +4,19 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Federator do
|
defmodule Pleroma.Web.Federator do
|
||||||
use GenServer
|
use GenServer
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.{WebFinger, Websub, Salmon}
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Federator.RetryQueue
|
alias Pleroma.Web.WebFinger
|
||||||
|
alias Pleroma.Web.Websub
|
||||||
|
alias Pleroma.Web.Salmon
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.Federator.RetryQueue
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@websub Application.get_env(:pleroma, :websub)
|
@websub Application.get_env(:pleroma, :websub)
|
||||||
|
@ -25,7 +29,7 @@ def init(args) do
|
||||||
def start_link do
|
def start_link do
|
||||||
spawn(fn ->
|
spawn(fn ->
|
||||||
# 1 minute
|
# 1 minute
|
||||||
Process.sleep(1000 * 60 * 1)
|
Process.sleep(1000 * 60)
|
||||||
enqueue(:refresh_subscriptions, nil)
|
enqueue(:refresh_subscriptions, nil)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -196,8 +200,7 @@ def handle_cast({:enqueue, type, payload, _priority}, state) do
|
||||||
{:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}}
|
{:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(m, state) do
|
def handle_cast(_, state) do
|
||||||
IO.inspect("Unknown: #{inspect(m)}, #{inspect(state)}")
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
||||||
defmodule Pleroma.Web.HTTPSignatures do
|
defmodule Pleroma.Web.HTTPSignatures do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def split_signature(sig) do
|
def split_signature(sig) do
|
||||||
|
|
|
@ -4,23 +4,31 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Filter
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Stats
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.Push
|
||||||
|
alias Push.Subscription
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.{
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
StatusView,
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
AccountView,
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
MastodonView,
|
alias Pleroma.Web.MastodonAPI.MastodonView
|
||||||
ListView,
|
alias Pleroma.Web.MastodonAPI.PushSubscriptionView
|
||||||
FilterView,
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
PushSubscriptionView
|
|
||||||
}
|
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -140,7 +148,7 @@ def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||||
@mastodon_api_level "2.5.0"
|
@mastodon_api_level "2.5.0"
|
||||||
|
|
||||||
def masto_instance(conn, _params) do
|
def masto_instance(conn, _params) do
|
||||||
instance = Pleroma.Config.get(:instance)
|
instance = Config.get(:instance)
|
||||||
|
|
||||||
response = %{
|
response = %{
|
||||||
uri: Web.base_url(),
|
uri: Web.base_url(),
|
||||||
|
@ -236,7 +244,8 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put("user", user)
|
|> Map.put("user", user)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
[user.ap_id | user.following]
|
||||||
|
|> ActivityPub.fetch_activities(params)
|
||||||
|> ActivityPub.contain_timeline(user)
|
|> ActivityPub.contain_timeline(user)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
@ -249,14 +258,12 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
local_only = params["local"] in [true, "True", "true", "1"]
|
local_only = params["local"] in [true, "True", "true", "1"]
|
||||||
|
|
||||||
params =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|> Map.put("local_only", local_only)
|
|> Map.put("local_only", local_only)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put("blocking_user", user)
|
||||||
|
|> ActivityPub.fetch_public_activities()
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_public_activities(params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -325,6 +332,7 @@ def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
as: :activity
|
as: :activity
|
||||||
)
|
)
|
||||||
|> Enum.reverse(),
|
|> Enum.reverse(),
|
||||||
|
# credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
|
||||||
descendants:
|
descendants:
|
||||||
StatusView.render(
|
StatusView.render(
|
||||||
"index.json",
|
"index.json",
|
||||||
|
@ -333,6 +341,7 @@ def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
as: :activity
|
as: :activity
|
||||||
)
|
)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
# credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
|
||||||
}
|
}
|
||||||
|
|
||||||
json(conn, result)
|
json(conn, result)
|
||||||
|
@ -456,13 +465,37 @@ def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
|
||||||
|
with {:ok, activity} <- CommonAPI.add_mute(user, activity) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
else
|
||||||
|
{:error, reason} ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
|
||||||
|
with {:ok, activity} <- CommonAPI.remove_mute(user, activity) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def notifications(%{assigns: %{user: user}} = conn, params) do
|
def notifications(%{assigns: %{user: user}} = conn, params) do
|
||||||
notifications = Notification.for_user(user, params)
|
notifications = Notification.for_user(user, params)
|
||||||
|
|
||||||
result =
|
result =
|
||||||
Enum.map(notifications, fn x ->
|
notifications
|
||||||
render_notification(user, x)
|
|> Enum.map(fn x -> render_notification(user, x) end)
|
||||||
end)
|
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -591,7 +624,7 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
[]
|
[]
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
query_params =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", "Create")
|
|> Map.put("type", "Create")
|
||||||
|> Map.put("local_only", local_only)
|
|> Map.put("local_only", local_only)
|
||||||
|
@ -599,9 +632,7 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put("tag", tags)
|
|> Map.put("tag", tags)
|
||||||
|> Map.put("tag_all", tag_all)
|
|> Map.put("tag_all", tag_all)
|
||||||
|> Map.put("tag_reject", tag_reject)
|
|> Map.put("tag_reject", tag_reject)
|
||||||
|
|> ActivityPub.fetch_public_activities()
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_public_activities(query_params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -701,7 +732,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followed),
|
{:ok, _activity} <- ActivityPub.follow(follower, followed),
|
||||||
{:ok, follower, followed} <-
|
{:ok, follower, followed} <-
|
||||||
User.wait_and_refresh(
|
User.wait_and_refresh(
|
||||||
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
|
Config.get([:activitypub, :follow_handshake_timeout]),
|
||||||
follower,
|
follower,
|
||||||
followed
|
followed
|
||||||
) do
|
) do
|
||||||
|
@ -830,7 +861,8 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
tags_path = Web.base_url() <> "/tag/"
|
tags_path = Web.base_url() <> "/tag/"
|
||||||
|
|
||||||
tags =
|
tags =
|
||||||
String.split(query)
|
query
|
||||||
|
|> String.split()
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
||||||
|
@ -852,7 +884,8 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
statuses = status_search(user, query)
|
statuses = status_search(user, query)
|
||||||
|
|
||||||
tags =
|
tags =
|
||||||
String.split(query)
|
query
|
||||||
|
|> String.split()
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
||||||
|
@ -876,14 +909,12 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d
|
||||||
end
|
end
|
||||||
|
|
||||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
params =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", "Create")
|
|> Map.put("type", "Create")
|
||||||
|> Map.put("favorited_by", user.ap_id)
|
|> Map.put("favorited_by", user.ap_id)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put("blocking_user", user)
|
||||||
|
|> ActivityPub.fetch_public_activities()
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_public_activities(params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -999,12 +1030,10 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|
||||||
|
|
||||||
# we must filter the following list for the user to avoid leaking statuses the user
|
# we must filter the following list for the user to avoid leaking statuses the user
|
||||||
# does not actually have permission to see (for more info, peruse security issue #270).
|
# does not actually have permission to see (for more info, peruse security issue #270).
|
||||||
following_to =
|
activities =
|
||||||
following
|
following
|
||||||
|> Enum.filter(fn x -> x in user.following end)
|
|> Enum.filter(fn x -> x in user.following end)
|
||||||
|
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_activities_bounded(following_to, following, params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -1026,7 +1055,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
if user && token do
|
if user && token do
|
||||||
mastodon_emoji = mastodonized_emoji()
|
mastodon_emoji = mastodonized_emoji()
|
||||||
|
|
||||||
limit = Pleroma.Config.get([:instance, :limit])
|
limit = Config.get([:instance, :limit])
|
||||||
|
|
||||||
accounts =
|
accounts =
|
||||||
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
|
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
|
||||||
|
@ -1050,8 +1079,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
max_toot_chars: limit
|
max_toot_chars: limit
|
||||||
},
|
},
|
||||||
rights: %{
|
rights: %{
|
||||||
delete_others_notice: !!user.info.is_moderator,
|
delete_others_notice: present?(user.info.is_moderator),
|
||||||
admin: !!user.info.is_admin
|
admin: present?(user.info.is_admin)
|
||||||
},
|
},
|
||||||
compose: %{
|
compose: %{
|
||||||
me: "#{user.id}",
|
me: "#{user.id}",
|
||||||
|
@ -1247,7 +1276,7 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_filters(%{assigns: %{user: user}} = conn, _) do
|
def get_filters(%{assigns: %{user: user}} = conn, _) do
|
||||||
filters = Pleroma.Filter.get_filters(user)
|
filters = Filter.get_filters(user)
|
||||||
res = FilterView.render("filters.json", filters: filters)
|
res = FilterView.render("filters.json", filters: filters)
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
@ -1256,7 +1285,7 @@ def create_filter(
|
||||||
%{assigns: %{user: user}} = conn,
|
%{assigns: %{user: user}} = conn,
|
||||||
%{"phrase" => phrase, "context" => context} = params
|
%{"phrase" => phrase, "context" => context} = params
|
||||||
) do
|
) do
|
||||||
query = %Pleroma.Filter{
|
query = %Filter{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
phrase: phrase,
|
phrase: phrase,
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -1265,13 +1294,13 @@ def create_filter(
|
||||||
# expires_at
|
# expires_at
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, response} = Pleroma.Filter.create(query)
|
{:ok, response} = Filter.create(query)
|
||||||
res = FilterView.render("filter.json", filter: response)
|
res = FilterView.render("filter.json", filter: response)
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||||
filter = Pleroma.Filter.get(filter_id, user)
|
filter = Filter.get(filter_id, user)
|
||||||
res = FilterView.render("filter.json", filter: filter)
|
res = FilterView.render("filter.json", filter: filter)
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
@ -1280,7 +1309,7 @@ def update_filter(
|
||||||
%{assigns: %{user: user}} = conn,
|
%{assigns: %{user: user}} = conn,
|
||||||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
|
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
|
||||||
) do
|
) do
|
||||||
query = %Pleroma.Filter{
|
query = %Filter{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: filter_id,
|
filter_id: filter_id,
|
||||||
phrase: phrase,
|
phrase: phrase,
|
||||||
|
@ -1290,32 +1319,32 @@ def update_filter(
|
||||||
# expires_at
|
# expires_at
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, response} = Pleroma.Filter.update(query)
|
{:ok, response} = Filter.update(query)
|
||||||
res = FilterView.render("filter.json", filter: response)
|
res = FilterView.render("filter.json", filter: response)
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||||
query = %Pleroma.Filter{
|
query = %Filter{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: filter_id
|
filter_id: filter_id
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, _} = Pleroma.Filter.delete(query)
|
{:ok, _} = Filter.delete(query)
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
|
def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
true = Pleroma.Web.Push.enabled()
|
true = Push.enabled()
|
||||||
Pleroma.Web.Push.Subscription.delete_if_exists(user, token)
|
Subscription.delete_if_exists(user, token)
|
||||||
{:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params)
|
{:ok, subscription} = Subscription.create(user, token, params)
|
||||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
json(conn, view)
|
json(conn, view)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
true = Pleroma.Web.Push.enabled()
|
true = Push.enabled()
|
||||||
subscription = Pleroma.Web.Push.Subscription.get(user, token)
|
subscription = Subscription.get(user, token)
|
||||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
json(conn, view)
|
json(conn, view)
|
||||||
end
|
end
|
||||||
|
@ -1324,15 +1353,15 @@ def update_push_subscription(
|
||||||
%{assigns: %{user: user, token: token}} = conn,
|
%{assigns: %{user: user, token: token}} = conn,
|
||||||
params
|
params
|
||||||
) do
|
) do
|
||||||
true = Pleroma.Web.Push.enabled()
|
true = Push.enabled()
|
||||||
{:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params)
|
{:ok, subscription} = Subscription.update(user, token, params)
|
||||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
json(conn, view)
|
json(conn, view)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
true = Pleroma.Web.Push.enabled()
|
true = Push.enabled()
|
||||||
{:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token)
|
{:ok, _response} = Subscription.delete(user, token)
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1343,17 +1372,21 @@ def errors(conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def suggestions(%{assigns: %{user: user}} = conn, _) do
|
def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||||
suggestions = Pleroma.Config.get(:suggestions)
|
suggestions = Config.get(:suggestions)
|
||||||
|
|
||||||
if Keyword.get(suggestions, :enabled, false) do
|
if Keyword.get(suggestions, :enabled, false) do
|
||||||
api = Keyword.get(suggestions, :third_party_engine, "")
|
api = Keyword.get(suggestions, :third_party_engine, "")
|
||||||
timeout = Keyword.get(suggestions, :timeout, 5000)
|
timeout = Keyword.get(suggestions, :timeout, 5000)
|
||||||
limit = Keyword.get(suggestions, :limit, 23)
|
limit = Keyword.get(suggestions, :limit, 23)
|
||||||
|
|
||||||
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
|
host = Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||||
|
|
||||||
user = user.nickname
|
user = user.nickname
|
||||||
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
|
|
||||||
|
url =
|
||||||
|
api
|
||||||
|
|> String.replace("{{host}}", host)
|
||||||
|
|> String.replace("{{user}}", user)
|
||||||
|
|
||||||
with {:ok, %{status: 200, body: body}} <-
|
with {:ok, %{status: 200, body: body}} <-
|
||||||
@httpoison.get(
|
@httpoison.get(
|
||||||
|
@ -1366,8 +1399,9 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
{:ok, data} <- Jason.decode(body) do
|
{:ok, data} <- Jason.decode(body) do
|
||||||
data2 =
|
data =
|
||||||
Enum.slice(data, 0, limit)
|
data
|
||||||
|
|> Enum.slice(0, limit)
|
||||||
|> Enum.map(fn x ->
|
|> Enum.map(fn x ->
|
||||||
Map.put(
|
Map.put(
|
||||||
x,
|
x,
|
||||||
|
@ -1386,7 +1420,7 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(data2)
|
|> json(data)
|
||||||
else
|
else
|
||||||
e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}")
|
e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}")
|
||||||
end
|
end
|
||||||
|
@ -1429,4 +1463,8 @@ def try_render(conn, _, _) do
|
||||||
|> put_status(501)
|
|> put_status(501)
|
||||||
|> json(%{error: "Can't display this activity"})
|
|> json(%{error: "Can't display this activity"})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp present?(nil), do: false
|
||||||
|
defp present?(false), do: false
|
||||||
|
defp present?(_), do: true
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
|
||||||
alias Pleroma.Web.MediaProxy
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
def render("accounts.json", %{users: users} = opts) do
|
def render("accounts.json", %{users: users} = opts) do
|
||||||
users
|
users
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.FilterView do
|
defmodule Pleroma.Web.MastodonAPI.FilterView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
|
|
||||||
def render("filters.json", %{filters: filters} = opts) do
|
def render("filters.json", %{filters: filters} = opts) do
|
||||||
render_many(filters, FilterView, "filter.json", opts)
|
render_many(filters, FilterView, "filter.json", opts)
|
||||||
|
|
|
@ -9,10 +9,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MediaProxy
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
# TODO: Add cached version.
|
# TODO: Add cached version.
|
||||||
defp get_replied_to_activities(activities) do
|
defp get_replied_to_activities(activities) do
|
||||||
|
@ -160,7 +161,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
reblogged: present?(repeated),
|
reblogged: present?(repeated),
|
||||||
favourited: present?(favorited),
|
favourited: present?(favorited),
|
||||||
bookmarked: present?(bookmarked),
|
bookmarked: present?(bookmarked),
|
||||||
muted: false,
|
muted: CommonAPI.thread_muted?(user, activity),
|
||||||
pinned: pinned?(activity, user),
|
pinned: pinned?(activity, user),
|
||||||
sensitive: sensitive,
|
sensitive: sensitive,
|
||||||
spoiler_text: object["summary"] || "",
|
spoiler_text: object["summary"] || "",
|
||||||
|
|
|
@ -6,7 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.{User, Repo}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@behaviour :cowboy_websocket_handler
|
@behaviour :cowboy_websocket_handler
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.{Web.MediaProxy, ReverseProxy}
|
alias Pleroma.ReverseProxy
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
@default_proxy_opts [max_body_length: 25 * 1_048_576, http: [follow_redirect: true]]
|
@default_proxy_opts [max_body_length: 25 * 1_048_576, http: [follow_redirect: true]]
|
||||||
|
|
||||||
def remote(conn, params = %{"sig" => sig64, "url" => url64}) do
|
def remote(conn, %{"sig" => sig64, "url" => url64} = params) do
|
||||||
with config <- Pleroma.Config.get([:media_proxy], []),
|
with config <- Pleroma.Config.get([:media_proxy], []),
|
||||||
true <- Keyword.get(config, :enabled, false),
|
true <- Keyword.get(config, :enabled, false),
|
||||||
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
||||||
|
|
|
@ -9,7 +9,7 @@ def url(nil), do: nil
|
||||||
|
|
||||||
def url(""), do: nil
|
def url(""), do: nil
|
||||||
|
|
||||||
def url(url = "/" <> _), do: url
|
def url("/" <> _ = url), do: url
|
||||||
|
|
||||||
def url(url) do
|
def url(url) do
|
||||||
config = Application.get_env(:pleroma, :media_proxy, [])
|
config = Application.get_env(:pleroma, :media_proxy, [])
|
||||||
|
@ -19,11 +19,16 @@ def url(url) do
|
||||||
else
|
else
|
||||||
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
||||||
|
|
||||||
|
# Must preserve `%2F` for compatibility with S3 (https://git.pleroma.social/pleroma/pleroma/issues/580)
|
||||||
|
replacement = get_replacement(url, ":2F:")
|
||||||
|
|
||||||
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
||||||
base64 =
|
base64 =
|
||||||
url
|
url
|
||||||
|
|> String.replace("%2F", replacement)
|
||||||
|> URI.decode()
|
|> URI.decode()
|
||||||
|> URI.encode()
|
|> URI.encode()
|
||||||
|
|> String.replace(replacement, "%2F")
|
||||||
|> Base.url_encode64(@base64_opts)
|
|> Base.url_encode64(@base64_opts)
|
||||||
|
|
||||||
sig = :crypto.hmac(:sha, secret, base64)
|
sig = :crypto.hmac(:sha, secret, base64)
|
||||||
|
@ -60,4 +65,12 @@ def build_url(sig_base64, url_base64, filename \\ nil) do
|
||||||
|> Enum.filter(fn value -> value end)
|
|> Enum.filter(fn value -> value end)
|
||||||
|> Path.join()
|
|> Path.join()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_replacement(url, replacement) do
|
||||||
|
if String.contains?(url, replacement) do
|
||||||
|
get_replacement(url, replacement <> replacement)
|
||||||
|
else
|
||||||
|
replacement
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
||||||
alias Pleroma.Web.Metadata.Providers.Provider
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Metadata
|
alias Pleroma.Web.Metadata
|
||||||
alias Pleroma.{HTML, Formatter, User}
|
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.Metadata.Providers.Provider
|
||||||
|
|
||||||
@behaviour Provider
|
@behaviour Provider
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Stats
|
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.{User, Repo}
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Stats
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug)
|
plug(Pleroma.Web.FederatingPlug)
|
||||||
|
@ -32,7 +33,7 @@ def schemas(conn, _params) do
|
||||||
|
|
||||||
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
|
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
|
||||||
# under software.
|
# under software.
|
||||||
def raw_nodeinfo() do
|
def raw_nodeinfo do
|
||||||
instance = Application.get_env(:pleroma, :instance)
|
instance = Application.get_env(:pleroma, :instance)
|
||||||
media_proxy = Application.get_env(:pleroma, :media_proxy)
|
media_proxy = Application.get_env(:pleroma, :media_proxy)
|
||||||
suggestions = Application.get_env(:pleroma, :suggestions)
|
suggestions = Application.get_env(:pleroma, :suggestions)
|
||||||
|
@ -93,10 +94,8 @@ def raw_nodeinfo() do
|
||||||
Config.get([:mrf_user_allowlist], [])
|
Config.get([:mrf_user_allowlist], [])
|
||||||
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
|
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
|
||||||
|
|
||||||
mrf_transparency = Keyword.get(instance, :mrf_transparency)
|
|
||||||
|
|
||||||
federation_response =
|
federation_response =
|
||||||
if mrf_transparency do
|
if Keyword.get(instance, :mrf_transparency) do
|
||||||
%{
|
%{
|
||||||
mrf_policies: mrf_policies,
|
mrf_policies: mrf_policies,
|
||||||
mrf_simple: mrf_simple,
|
mrf_simple: mrf_simple,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OAuth.App do
|
defmodule Pleroma.Web.OAuth.App do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.{Changeset}
|
import Ecto.Changeset
|
||||||
|
|
||||||
schema "apps" do
|
schema "apps" do
|
||||||
field(:client_name, :string)
|
field(:client_name, :string)
|
||||||
|
@ -25,8 +25,14 @@ def register_changeset(struct, params \\ %{}) do
|
||||||
|
|
||||||
if changeset.valid? do
|
if changeset.valid? do
|
||||||
changeset
|
changeset
|
||||||
|> put_change(:client_id, :crypto.strong_rand_bytes(32) |> Base.url_encode64())
|
|> put_change(
|
||||||
|> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64())
|
:client_id,
|
||||||
|
:crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
|
)
|
||||||
|
|> put_change(
|
||||||
|
:client_secret,
|
||||||
|
:crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
changeset
|
changeset
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,13 @@
|
||||||
defmodule Pleroma.Web.OAuth.Authorization do
|
defmodule Pleroma.Web.OAuth.Authorization do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.{User, Repo}
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.{Authorization, App}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
|
||||||
import Ecto.{Changeset, Query}
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
schema "oauth_authorizations" do
|
schema "oauth_authorizations" do
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
|
@ -23,7 +26,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
||||||
|
|
||||||
def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do
|
def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do
|
||||||
scopes = scopes || app.scopes
|
scopes = scopes || app.scopes
|
||||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
|
|
||||||
authorization = %Authorization{
|
authorization = %Authorization{
|
||||||
token: token,
|
token: token,
|
||||||
|
|
|
@ -5,8 +5,11 @@
|
||||||
defmodule Pleroma.Web.OAuth.OAuthController do
|
defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.{Repo, User}
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
||||||
|
@ -186,7 +189,7 @@ defp fix_padding(token) do
|
||||||
token
|
token
|
||||||
|> URI.decode()
|
|> URI.decode()
|
||||||
|> Base.url_decode64!(padding: false)
|
|> Base.url_decode64!(padding: false)
|
||||||
|> Base.url_encode64()
|
|> Base.url_encode64(padding: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_app_from_request(conn, params) do
|
defp get_app_from_request(conn, params) do
|
||||||
|
|
|
@ -7,8 +7,11 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.{User, Repo}
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.{Token, App, Authorization}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
|
||||||
schema "oauth_tokens" do
|
schema "oauth_tokens" do
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
|
@ -30,8 +33,8 @@ def exchange_token(app, auth) do
|
||||||
|
|
||||||
def create_token(%App{} = app, %User{} = user, scopes \\ nil) do
|
def create_token(%App{} = app, %User{} = user, scopes \\ nil) do
|
||||||
scopes = scopes || app.scopes
|
scopes = scopes || app.scopes
|
||||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
|
|
||||||
token = %Token{
|
token = %Token{
|
||||||
token: token,
|
token: token,
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
||||||
alias Pleroma.{Activity, User, Object}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.OStatus.UserRepresenter
|
alias Pleroma.Web.OStatus.UserRepresenter
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
defp get_href(id) do
|
defp get_href(id) do
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.FeedRepresenter do
|
defmodule Pleroma.Web.OStatus.FeedRepresenter do
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.OStatus.{UserRepresenter, ActivityRepresenter}
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
alias Pleroma.Web.OStatus.UserRepresenter
|
||||||
|
|
||||||
def to_simple_form(user, activities, _users) do
|
def to_simple_form(user, activities, _users) do
|
||||||
most_recent_update =
|
most_recent_update =
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.FollowHandler do
|
defmodule Pleroma.Web.OStatus.FollowHandler do
|
||||||
alias Pleroma.Web.{XML, OStatus}
|
alias Pleroma.Web.XML
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.NoteHandler do
|
defmodule Pleroma.Web.OStatus.NoteHandler do
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.Web.{XML, OStatus}
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.{Object, Activity}
|
alias Pleroma.Web.XML
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.UnfollowHandler do
|
defmodule Pleroma.Web.OStatus.UnfollowHandler do
|
||||||
alias Pleroma.Web.{XML, OStatus}
|
alias Pleroma.Web.XML
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,19 @@ defmodule Pleroma.Web.OStatus do
|
||||||
import Pleroma.Web.XML
|
import Pleroma.Web.XML
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.{Repo, User, Web, Object, Activity}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.{WebFinger, Websub}
|
|
||||||
alias Pleroma.Web.OStatus.{FollowHandler, UnfollowHandler, NoteHandler, DeleteHandler}
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.WebFinger
|
||||||
|
alias Pleroma.Web.Websub
|
||||||
|
alias Pleroma.Web.OStatus.FollowHandler
|
||||||
|
alias Pleroma.Web.OStatus.UnfollowHandler
|
||||||
|
alias Pleroma.Web.OStatus.NoteHandler
|
||||||
|
alias Pleroma.Web.OStatus.DeleteHandler
|
||||||
|
|
||||||
def is_representable?(%Activity{data: data}) do
|
def is_representable?(%Activity{data: data}) do
|
||||||
object = Object.normalize(data["object"])
|
object = Object.normalize(data["object"])
|
||||||
|
|
|
@ -5,13 +5,17 @@
|
||||||
defmodule Pleroma.Web.OStatus.OStatusController do
|
defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.{User, Activity, Object}
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.OStatus.{FeedRepresenter, ActivityRepresenter}
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.{OStatus, Federator}
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Web.ActivityPub.ObjectView
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectView
|
||||||
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
alias Pleroma.Web.OStatus.FeedRepresenter
|
||||||
|
alias Pleroma.Web.Federator
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
defmodule Pleroma.Web.Push do
|
defmodule Pleroma.Web.Push do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
alias Pleroma.{Repo, User}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Push.Subscription do
|
defmodule Pleroma.Web.Push.Subscription do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Pleroma.{Repo, User}
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.RichMedia.Helpers do
|
defmodule Pleroma.Web.RichMedia.Helpers do
|
||||||
alias Pleroma.{Activity, Object, HTML}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Web.RichMedia.Parser
|
alias Pleroma.Web.RichMedia.Parser
|
||||||
|
|
||||||
def fetch_data_for_activity(%Activity{} = activity) do
|
def fetch_data_for_activity(%Activity{} = activity) do
|
||||||
|
|
|
@ -9,6 +9,13 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||||
Pleroma.Web.RichMedia.Parsers.OEmbed
|
Pleroma.Web.RichMedia.Parsers.OEmbed
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@hackney_options [
|
||||||
|
pool: :media,
|
||||||
|
timeout: 2_000,
|
||||||
|
recv_timeout: 2_000,
|
||||||
|
max_body: 2_000_000
|
||||||
|
]
|
||||||
|
|
||||||
def parse(nil), do: {:error, "No URL provided"}
|
def parse(nil), do: {:error, "No URL provided"}
|
||||||
|
|
||||||
if Mix.env() == :test do
|
if Mix.env() == :test do
|
||||||
|
@ -28,7 +35,7 @@ def parse(url) do
|
||||||
|
|
||||||
defp parse_url(url) do
|
defp parse_url(url) do
|
||||||
try do
|
try do
|
||||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: [pool: :media])
|
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
|
||||||
|
|
||||||
html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data()
|
html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data()
|
||||||
rescue
|
rescue
|
||||||
|
|
|
@ -248,6 +248,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
|
post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
|
||||||
post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
|
post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
|
||||||
post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
|
post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
|
||||||
|
post("/statuses/:id/mute", MastodonAPIController, :mute_conversation)
|
||||||
|
post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation)
|
||||||
|
|
||||||
post("/media", MastodonAPIController, :upload)
|
post("/media", MastodonAPIController, :upload)
|
||||||
put("/media/:id", MastodonAPIController, :update_media)
|
put("/media/:id", MastodonAPIController, :update_media)
|
||||||
|
@ -550,9 +552,8 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
pipe_through(:activitypub)
|
pipe_through(:activitypub)
|
||||||
|
|
||||||
post("/users/:nickname/inbox", ActivityPubController, :inbox)
|
|
||||||
post("/inbox", ActivityPubController, :inbox)
|
post("/inbox", ActivityPubController, :inbox)
|
||||||
|
post("/users/:nickname/inbox", ActivityPubController, :inbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/.well-known", Pleroma.Web do
|
scope "/.well-known", Pleroma.Web do
|
||||||
|
|
|
@ -6,10 +6,12 @@ defmodule Pleroma.Web.Salmon do
|
||||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||||
|
|
||||||
use Bitwise
|
use Bitwise
|
||||||
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.XML
|
alias Pleroma.Web.XML
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
alias Pleroma.User
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def decode(salmon) do
|
def decode(salmon) do
|
||||||
|
|
|
@ -5,7 +5,11 @@
|
||||||
defmodule Pleroma.Web.Streamer do
|
defmodule Pleroma.Web.Streamer do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.{User, Notification, Activity, Object, Repo}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
@keepalive_interval :timer.seconds(30)
|
@keepalive_interval :timer.seconds(30)
|
||||||
|
|
|
@ -71,6 +71,32 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
color: #D8000C;
|
||||||
|
background-color: #FFD2D2;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-info {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
color: #00529B;
|
||||||
|
background-color: #BDE5F8;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
<%= if get_flash(@conn, :info) do %>
|
||||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||||
|
<% end %>
|
||||||
|
<%= if get_flash(@conn, :error) do %>
|
||||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||||
|
<% end %>
|
||||||
<h2>OAuth Authorization</h2>
|
<h2>OAuth Authorization</h2>
|
||||||
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
||||||
<%= label f, :name, "Name or email" %>
|
<%= label f, :name, "Name or email" %>
|
||||||
|
|
|
@ -4,14 +4,19 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.UtilController do
|
defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
alias Comeonin.Pbkdf2
|
||||||
|
alias Pleroma.Emoji
|
||||||
|
alias Pleroma.PasswordResetToken
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Comeonin.Pbkdf2
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.{Repo, PasswordResetToken, User, Emoji}
|
|
||||||
|
|
||||||
def show_password_reset(conn, %{"token" => token}) do
|
def show_password_reset(conn, %{"token" => token}) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
|
|
|
@ -2,16 +2,20 @@
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# FIXME: Remove this module?
|
||||||
# THIS MODULE IS DEPRECATED! DON'T USE IT!
|
# THIS MODULE IS DEPRECATED! DON'T USE IT!
|
||||||
# USE THE Pleroma.Web.TwitterAPI.Views.ActivityView MODULE!
|
# USE THE Pleroma.Web.TwitterAPI.Views.ActivityView MODULE!
|
||||||
defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
||||||
use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter
|
use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter
|
||||||
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
|
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
|
||||||
alias Pleroma.{Activity, User}
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView}
|
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.TwitterAPI.ActivityView
|
||||||
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
alias Pleroma.Web.TwitterAPI.UserView
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
defp user_by_ap_id(user_list, ap_id) do
|
defp user_by_ap_id(user_list, ap_id) do
|
||||||
|
|
|
@ -3,8 +3,13 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||||
alias Pleroma.{UserInviteToken, User, Activity, Repo, Object}
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.{UserEmail, Mailer}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.UserEmail
|
||||||
|
alias Pleroma.Mailer
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.TwitterAPI.UserView
|
alias Pleroma.Web.TwitterAPI.UserView
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
|
@ -7,12 +7,19 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.{Repo, Activity, Object, User, Notification}
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Ecto.Changeset
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.TwitterAPI.ActivityView
|
||||||
|
alias Pleroma.Web.TwitterAPI.NotificationView
|
||||||
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
alias Pleroma.Web.TwitterAPI.UserView
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,18 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.TwitterAPI.UserView
|
|
||||||
alias Pleroma.Web.TwitterAPI.ActivityView
|
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
|
||||||
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
|
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
alias Pleroma.Web.TwitterAPI.ActivityView
|
||||||
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
alias Pleroma.Web.TwitterAPI.UserView
|
||||||
|
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.NotificationView do
|
defmodule Pleroma.Web.TwitterAPI.NotificationView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.{Notification, User}
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.TwitterAPI.UserView
|
|
||||||
alias Pleroma.Web.TwitterAPI.ActivityView
|
alias Pleroma.Web.TwitterAPI.ActivityView
|
||||||
|
alias Pleroma.Web.TwitterAPI.UserView
|
||||||
|
|
||||||
defp get_user(ap_id, opts) do
|
defp get_user(ap_id, opts) do
|
||||||
cond do
|
cond do
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.UserView do
|
defmodule Pleroma.Web.TwitterAPI.UserView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.HTML
|
|
||||||
|
|
||||||
def render("show.json", %{user: user = %User{}} = assigns) do
|
def render("show.json", %{user: user = %User{}} = assigns) do
|
||||||
render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns)
|
render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns)
|
||||||
|
|
|
@ -3,7 +3,7 @@ defmodule Pleroma.Web.UploaderController do
|
||||||
|
|
||||||
alias Pleroma.Uploaders.Uploader
|
alias Pleroma.Uploaders.Uploader
|
||||||
|
|
||||||
def callback(conn, params = %{"upload_path" => upload_path}) do
|
def callback(conn, %{"upload_path" => upload_path} = params) do
|
||||||
process_callback(conn, :global.whereis_name({Uploader, upload_path}), params)
|
process_callback(conn, :global.whereis_name({Uploader, upload_path}), params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ def controller do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.Controller, namespace: Pleroma.Web
|
use Phoenix.Controller, namespace: Pleroma.Web
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Pleroma.Web.{Gettext, Router.Helpers}
|
import Pleroma.Web.Gettext
|
||||||
|
import Pleroma.Web.Router.Helpers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,7 +38,9 @@ def view do
|
||||||
# Import convenience functions from controllers
|
# Import convenience functions from controllers
|
||||||
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
||||||
|
|
||||||
import Pleroma.Web.{ErrorHelpers, Gettext, Router.Helpers}
|
import Pleroma.Web.ErrorHelpers
|
||||||
|
import Pleroma.Web.Gettext
|
||||||
|
import Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -71,6 +74,7 @@ def safe_render_many(collection, view, template, assigns \\ %{}) do
|
||||||
def router do
|
def router do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.Router
|
use Phoenix.Router
|
||||||
|
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Phoenix.Controller
|
import Phoenix.Controller
|
||||||
end
|
end
|
||||||
|
@ -78,6 +82,7 @@ def router do
|
||||||
|
|
||||||
def channel do
|
def channel do
|
||||||
quote do
|
quote do
|
||||||
|
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
||||||
use Phoenix.Channel
|
use Phoenix.Channel
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
defmodule Pleroma.Web.WebFinger do
|
defmodule Pleroma.Web.WebFinger do
|
||||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||||
|
|
||||||
alias Pleroma.{User, XmlBuilder}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.XmlBuilder
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.{XML, Salmon, OStatus}
|
alias Pleroma.Web.XML
|
||||||
|
alias Pleroma.Web.Salmon
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
require Jason
|
require Jason
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,14 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Websub do
|
defmodule Pleroma.Web.Websub do
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.Web.Websub.{WebsubServerSubscription, WebsubClientSubscription}
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.Websub.WebsubServerSubscription
|
||||||
|
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||||
alias Pleroma.Web.OStatus.FeedRepresenter
|
alias Pleroma.Web.OStatus.FeedRepresenter
|
||||||
alias Pleroma.Web.{XML, Endpoint, OStatus}
|
alias Pleroma.Web.XML
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.Router.Helpers
|
alias Pleroma.Web.Router.Helpers
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
defmodule Pleroma.Web.Websub.WebsubController do
|
defmodule Pleroma.Web.Websub.WebsubController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.{Repo, User}
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.{Websub, Federator}
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Websub
|
||||||
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
12
priv/repo/migrations/20190205114625_create_thread_mutes.exs
Normal file
12
priv/repo/migrations/20190205114625_create_thread_mutes.exs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateThreadMutes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:thread_mutes) do
|
||||||
|
add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
|
||||||
|
add :context, :string
|
||||||
|
end
|
||||||
|
|
||||||
|
create unique_index(:thread_mutes, [:user_id, :context], name: :unique_index)
|
||||||
|
end
|
||||||
|
end
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue