Merge remote-tracking branch 'MAIN/develop' into feature/jobs
This commit is contained in:
commit
d3677d2b4d
67
README.md
67
README.md
|
@ -8,76 +8,81 @@ 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).
|
||||
|
||||
Client applications that are committed to supporting Pleroma:
|
||||
|
||||
* Mastalab (Android)
|
||||
* Tusky (Android)
|
||||
* Twidere (Android)
|
||||
* Mast (iOS)
|
||||
* Amaroq (iOS)
|
||||
|
||||
Client applications that are known to work well:
|
||||
|
||||
* Twidere
|
||||
* Tusky
|
||||
* Pawoo (Android + iOS)
|
||||
* Subway Tooter
|
||||
* Amaroq (iOS)
|
||||
* Tootdon (Android + iOS)
|
||||
* Tootle (iOS)
|
||||
* 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>.
|
||||
|
||||
## Installation
|
||||
|
||||
### Docker
|
||||
|
||||
While we don't provide docker files, other people have written very good ones. Take a look at https://github.com/angristan/docker-pleroma or https://github.com/sn0w/pleroma-docker.
|
||||
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://github.com/sn0w/pleroma-docker>.
|
||||
|
||||
### Dependencies
|
||||
|
||||
* Postgresql version 9.6 or newer
|
||||
* Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||
* Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir’s install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||
* Build-essential tools
|
||||
|
||||
### Configuration
|
||||
|
||||
* Run `mix deps.get` to install elixir dependencies.
|
||||
|
||||
* Run `mix pleroma.instance gen`. This will ask you questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`, which you should run as the PostgreSQL superuser (i.e., `sudo -u postgres psql -f config/setup_db.psql`). It will create the database, user, and password you gave `mix pleroma.gen.instance` earlier, as well as set up the necessary extensions in the database. PostgreSQL superuser privileges are only needed for this step.
|
||||
|
||||
* For these next steps, the default will be to run pleroma using the dev configuration file, `config/dev.secret.exs`. To run them using the prod config file, prefix each command at the shell with `MIX_ENV=prod`. For example: `MIX_ENV=prod mix phx.server`. Documentation for the config can be found at [``docs/config.md``](docs/config.md)
|
||||
|
||||
* Run `mix ecto.migrate` to run the database migrations. You will have to do this again after certain updates.
|
||||
|
||||
* You can check if your instance is configured correctly by running it with `mix phx.server` and checking the instance info endpoint at `/api/v1/instance`. If it shows your uri, name and email correctly, you are configured correctly. If it shows something like `localhost:4000`, your configuration is probably wrong, unless you are running a local development setup.
|
||||
|
||||
* The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: <https://letsencrypt.org/>. The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically.
|
||||
* Run `mix deps.get` to install elixir dependencies.
|
||||
* Run `mix pleroma.instance gen`. This will ask you questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`, which you should run as the PostgreSQL superuser (i.e., `sudo -u postgres psql -f config/setup_db.psql`). It will create the database, user, and password you gave `mix pleroma.gen.instance` earlier, as well as set up the necessary extensions in the database. PostgreSQL superuser privileges are only needed for this step.
|
||||
* For these next steps, the default will be to run pleroma using the dev configuration file, `config/dev.secret.exs`. To run them using the prod config file, prefix each command at the shell with `MIX_ENV=prod`. For example: `MIX_ENV=prod mix phx.server`. Documentation for the config can be found at [`docs/config.md`](docs/config.md)
|
||||
* Run `mix ecto.migrate` to run the database migrations. You will have to do this again after certain updates.
|
||||
* You can check if your instance is configured correctly by running it with `mix phx.server` and checking the instance info endpoint at `/api/v1/instance`. If it shows your uri, name and email correctly, you are configured correctly. If it shows something like `localhost:4000`, your configuration is probably wrong, unless you are running a local development setup.
|
||||
* The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: <https://letsencrypt.org/>. The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically.
|
||||
|
||||
## Running
|
||||
|
||||
* By default, it listens on port 4000 (TCP), so you can access it on http://localhost:4000/ (if you are on the same machine). In case of an error it will restart automatically.
|
||||
* By default, it listens on port 4000 (TCP), so you can access it on <http://localhost:4000/> (if you are on the same machine). In case of an error it will restart automatically.
|
||||
|
||||
### Frontends
|
||||
|
||||
Pleroma comes with two frontends. The first one, Pleroma FE, can be reached by normally visiting the site. The other one, based on the Mastodon project, can be found by visiting the /web path of your site.
|
||||
|
||||
### As systemd service (with provided .service file)
|
||||
Example .service file can be found in `installation/pleroma.service`. Copy this to `/etc/systemd/system/`.
|
||||
Running `systemctl enable --now pleroma.service` will run Pleroma and enable startup on boot.
|
||||
Logs can be watched by using `journalctl -fu pleroma.service`.
|
||||
|
||||
Example .service file can be found in `installation/pleroma.service`. Copy this to `/etc/systemd/system/`. Running `systemctl enable --now pleroma.service` will run Pleroma and enable startup on boot. Logs can be watched by using `journalctl -fu pleroma.service`.
|
||||
|
||||
### As OpenRC service (with provided RC file)
|
||||
Copy ``installation/init.d/pleroma`` to ``/etc/init.d/pleroma``.
|
||||
You can add it to the services ran by default with:
|
||||
``rc-update add pleroma``
|
||||
|
||||
Copy `installation/init.d/pleroma` to `/etc/init.d/pleroma`. You can add it to the services ran by default with: `rc-update add pleroma`
|
||||
|
||||
### Standalone/run by other means
|
||||
Run `mix phx.server` in repository's root, it will output log into stdout/stderr.
|
||||
|
||||
Run `mix phx.server` in repository’s root, it will output log into stdout/stderr.
|
||||
|
||||
### Using an upstream proxy for federation
|
||||
|
||||
Add the following to your `dev.secret.exs` or `prod.secret.exs` if you want to proxify all http requests that pleroma makes to an upstream proxy server:
|
||||
Add the following to your `dev.secret.exs` or `prod.secret.exs` if you want to proxify all http requests that Pleroma makes to an upstream proxy server:
|
||||
|
||||
config :pleroma, :http,
|
||||
proxy_url: "127.0.0.1:8123"
|
||||
```elixir
|
||||
config :pleroma, :http,
|
||||
proxy_url: "127.0.0.1:8123"
|
||||
```
|
||||
|
||||
This is useful for running pleroma inside Tor or i2p.
|
||||
This is useful for running Pleroma inside Tor or I2P.
|
||||
|
||||
## Customization and contribution
|
||||
|
||||
The [Pleroma Wiki](https://git.pleroma.social/pleroma/pleroma/wikis/home) offers manuals and guides on how to further customize your instance to your liking and how you can contribute to the project.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No incoming federation
|
||||
|
||||
Check that you correctly forward the "host" header to backend. It is needed to validate signatures.
|
||||
Check that you correctly forward the `host` header to the backend. It is needed to validate signatures.
|
||||
|
|
|
@ -15,6 +15,20 @@ config :pleroma, Pleroma.Captcha,
|
|||
seconds_valid: 60,
|
||||
method: Pleroma.Captcha.Kocaptcha
|
||||
|
||||
config :pleroma, :hackney_pools,
|
||||
federation: [
|
||||
max_connections: 50,
|
||||
timeout: 150_000
|
||||
],
|
||||
media: [
|
||||
max_connections: 50,
|
||||
timeout: 150_000
|
||||
],
|
||||
upload: [
|
||||
max_connections: 25,
|
||||
timeout: 300_000
|
||||
]
|
||||
|
||||
config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch"
|
||||
|
||||
# Upload configuration
|
||||
|
@ -22,7 +36,14 @@ config :pleroma, Pleroma.Upload,
|
|||
uploader: Pleroma.Uploaders.Local,
|
||||
filters: [],
|
||||
proxy_remote: false,
|
||||
proxy_opts: []
|
||||
proxy_opts: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :upload
|
||||
]
|
||||
]
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||
|
||||
|
@ -154,6 +175,7 @@ config :pleroma, :markup,
|
|||
Pleroma.HTML.Scrubber.Default
|
||||
]
|
||||
|
||||
# Deprecated, will be gone in 1.0
|
||||
config :pleroma, :fe,
|
||||
theme: "pleroma-dark",
|
||||
logo: "/static/logo.png",
|
||||
|
@ -172,6 +194,24 @@ config :pleroma, :fe,
|
|||
subject_line_behavior: "email",
|
||||
always_show_subject_input: true
|
||||
|
||||
config :pleroma, :frontend_configurations,
|
||||
pleroma_fe: %{
|
||||
theme: "pleroma-dark",
|
||||
logo: "/static/logo.png",
|
||||
background: "/images/city.jpg",
|
||||
redirectRootNoLogin: "/main/all",
|
||||
redirectRootLogin: "/main/friends",
|
||||
showInstanceSpecificPanel: true,
|
||||
scopeOptionsEnabled: false,
|
||||
formattingOptionsEnabled: false,
|
||||
collapseMessageWithSubject: false,
|
||||
hidePostStats: false,
|
||||
hideUserStats: false,
|
||||
scopeCopy: true,
|
||||
subjectLineBehavior: "email",
|
||||
alwaysShowSubjectInput: true
|
||||
}
|
||||
|
||||
config :pleroma, :activitypub,
|
||||
accept_blocks: true,
|
||||
unfollow_blocked: true,
|
||||
|
@ -195,7 +235,16 @@ config :pleroma, :mrf_simple,
|
|||
reject: [],
|
||||
accept: []
|
||||
|
||||
config :pleroma, :media_proxy, enabled: false
|
||||
config :pleroma, :media_proxy,
|
||||
enabled: false,
|
||||
proxy_opts: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :media
|
||||
]
|
||||
]
|
||||
|
||||
config :pleroma, :chat, enabled: true
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
# Authentication
|
||||
# Pleroma API
|
||||
|
||||
Requests that require it can be authenticated with [an OAuth token](https://tools.ietf.org/html/rfc6749), the `_pleroma_key` cookie, or [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization).
|
||||
|
||||
# Request parameters
|
||||
|
||||
Request parameters can be passed via [query strings](https://en.wikipedia.org/wiki/Query_string) or as [form data](https://www.w3.org/TR/html401/interact/forms.html). Files must be uploaded as `multipart/form-data`.
|
||||
|
||||
# Endpoints
|
||||
|
||||
## `/api/pleroma/emoji`
|
||||
### Lists the custom emoji on that server.
|
||||
* Method: `GET`
|
||||
|
@ -15,6 +11,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
|
|||
* Params: none
|
||||
* Response: JSON
|
||||
* Example response: `{"kalsarikannit_f":"/finmoji/128px/kalsarikannit_f-128.png","perkele":"/finmoji/128px/perkele-128.png","blobdab":"/emoji/blobdab.png","happiness":"/finmoji/128px/happiness-128.png"}`
|
||||
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
|
||||
|
||||
## `/api/pleroma/follow_import`
|
||||
### Imports your follows, for example from a Mastodon CSV file.
|
||||
|
|
|
@ -102,7 +102,24 @@ config :pleroma, Pleroma.Mailer,
|
|||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
||||
See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
|
||||
|
||||
|
||||
## :frontend_configurations
|
||||
|
||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured.
|
||||
|
||||
Frontends can access these settings at `/api/pleroma/frontend_configurations`
|
||||
|
||||
To add your own configuration for PleromaFE, use it like this:
|
||||
|
||||
`config :pleroma, :frontend_configurations, pleroma_fe: %{redirectRootNoLogin: "/main/all", ...}`
|
||||
|
||||
These settings need to be complete, they will override the defaults. See `priv/static/static/config.json` for the available keys.
|
||||
|
||||
## :fe
|
||||
__THIS IS DEPRECATED__
|
||||
|
||||
If you are using this method, please change it to the `frontend_configurations` method. Please set this option to false in your config like this: `config :pleroma, :fe, false`.
|
||||
|
||||
This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:instance`` is set to false.
|
||||
|
||||
* `theme`: Which theme to use, they are defined in ``styles.json``
|
||||
|
@ -234,3 +251,20 @@ This config contains two queues: `federator_incoming` and `federator_outgoing`.
|
|||
* Pleroma.Web.Metadata.Providers.OpenGraph
|
||||
* Pleroma.Web.Metadata.Providers.TwitterCard
|
||||
* `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews
|
||||
|
||||
## :hackney_pools
|
||||
|
||||
Advanced. Tweaks Hackney (http client) connections pools.
|
||||
|
||||
There's three pools used:
|
||||
|
||||
* `:federation` for the federation jobs.
|
||||
You may want this pool max_connections to be at least equal to the number of federator jobs + retry queue jobs.
|
||||
* `:media` for rich media, media proxy
|
||||
* `:upload` for uploaded media (if using a remote uploader and `proxy_remote: true`)
|
||||
|
||||
For each pool, the options are:
|
||||
|
||||
* `max_connections` - how much connections a pool can hold
|
||||
* `timeout` - retention duration for connections
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export PORT=4000
|
|||
export MIX_ENV=prod
|
||||
|
||||
# Ask process to terminate within 30 seconds, otherwise kill it
|
||||
retry="SIGTERM/30 SIGKILL/5"
|
||||
retry="SIGTERM/30/SIGKILL/5"
|
||||
|
||||
pidfile="/var/run/pleroma.pid"
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ defmodule Pleroma.Application do
|
|||
def start(_type, _args) do
|
||||
import Cachex.Spec
|
||||
|
||||
Pleroma.Config.DeprecationWarnings.warn()
|
||||
|
||||
# Define workers and child supervisors to be supervised
|
||||
children =
|
||||
[
|
||||
|
@ -99,13 +101,16 @@ defmodule Pleroma.Application do
|
|||
],
|
||||
id: :cachex_idem
|
||||
),
|
||||
worker(Pleroma.FlakeId, []),
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Pleroma.Web.Push, []),
|
||||
worker(Pleroma.Jobs, []),
|
||||
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary)
|
||||
worker(Pleroma.FlakeId, [])
|
||||
] ++
|
||||
hackney_pool_children() ++
|
||||
[
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Pleroma.Web.Push, []),
|
||||
worker(Pleroma.Jobs, []),
|
||||
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary)
|
||||
] ++
|
||||
streamer_child() ++
|
||||
chat_child() ++
|
||||
[
|
||||
|
@ -120,6 +125,20 @@ defmodule Pleroma.Application do
|
|||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
def enabled_hackney_pools() do
|
||||
[:media] ++
|
||||
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||
[:federation]
|
||||
else
|
||||
[]
|
||||
end ++
|
||||
if Pleroma.Config.get([Pleroma.Uploader, :proxy_remote]) do
|
||||
[:upload]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
if Mix.env() == :test do
|
||||
defp streamer_child(), do: []
|
||||
defp chat_child(), do: []
|
||||
|
@ -136,4 +155,11 @@ defmodule Pleroma.Application do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp hackney_pool_children() do
|
||||
for pool <- enabled_hackney_pools() do
|
||||
options = Pleroma.Config.get([:hackney_pools, pool])
|
||||
:hackney_pool.child_spec(pool, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.DeprecationWarnings do
|
||||
require Logger
|
||||
|
||||
def check_frontend_config_mechanism() do
|
||||
if Pleroma.Config.get(:fe) do
|
||||
Logger.warn("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
You are using the old configuration mechanism for the frontend. Please check config.md.
|
||||
""")
|
||||
end
|
||||
end
|
||||
|
||||
def warn do
|
||||
check_frontend_config_mechanism()
|
||||
end
|
||||
end
|
|
@ -33,6 +33,10 @@ defmodule Pleroma.FlakeId do
|
|||
|
||||
def to_string(s), do: s
|
||||
|
||||
def from_string(int) when is_integer(int) do
|
||||
from_string(Kernel.to_string(int))
|
||||
end
|
||||
|
||||
for i <- [-1, 0] do
|
||||
def from_string(unquote(i)), do: <<0::integer-size(128)>>
|
||||
def from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
|
||||
|
@ -90,7 +94,7 @@ defmodule Pleroma.FlakeId do
|
|||
|
||||
@impl GenServer
|
||||
def init([]) do
|
||||
{:ok, %FlakeId{node: mac(), time: time()}}
|
||||
{:ok, %FlakeId{node: worker_id(), time: time()}}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
|
@ -161,23 +165,8 @@ defmodule Pleroma.FlakeId do
|
|||
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
|
||||
end
|
||||
|
||||
def mac do
|
||||
{:ok, addresses} = :inet.getifaddrs()
|
||||
|
||||
macids =
|
||||
Enum.reduce(addresses, [], fn {_iface, attrs}, acc ->
|
||||
case attrs[:hwaddr] do
|
||||
[0, 0, 0 | _] -> acc
|
||||
mac when is_list(mac) -> [mac_to_worker_id(mac) | acc]
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
List.first(macids)
|
||||
end
|
||||
|
||||
def mac_to_worker_id(mac) do
|
||||
<<worker::integer-size(48)>> = :binary.list_to_bin(mac)
|
||||
defp worker_id() do
|
||||
<<worker::integer-size(48)>> = :crypto.strong_rand_bytes(6)
|
||||
worker
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,6 +58,20 @@ defmodule Pleroma.HTML do
|
|||
"#{signature}#{to_string(scrubber)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def extract_first_external_url(object, content) do
|
||||
key = "URL|#{object.id}"
|
||||
|
||||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
result =
|
||||
content
|
||||
|> Floki.filter_out("a.mention")
|
||||
|> Floki.attribute("a", "href")
|
||||
|> Enum.at(0)
|
||||
|
||||
{:commit, {:ok, result}}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||
|
|
|
@ -10,7 +10,8 @@ defmodule Pleroma.HTTP.Connection do
|
|||
@hackney_options [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
follow_redirect: true
|
||||
follow_redirect: true,
|
||||
pool: :federation
|
||||
]
|
||||
@adapter Application.get_env(:tesla, :adapter)
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ defmodule Pleroma.Notification do
|
|||
n in Notification,
|
||||
where: n.user_id == ^user.id,
|
||||
order_by: [desc: n.id],
|
||||
preload: [:activity],
|
||||
join: activity in assoc(n, :activity),
|
||||
preload: [activity: activity],
|
||||
limit: 20
|
||||
)
|
||||
|
||||
|
@ -66,7 +67,8 @@ defmodule Pleroma.Notification do
|
|||
from(
|
||||
n in Notification,
|
||||
where: n.id == ^id,
|
||||
preload: [:activity]
|
||||
join: activity in assoc(n, :activity),
|
||||
preload: [activity: activity]
|
||||
)
|
||||
|
||||
notification = Repo.one(query)
|
||||
|
|
|
@ -33,7 +33,12 @@ defmodule Pleroma.Plugs.OAuthPlug do
|
|||
#
|
||||
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
|
||||
defp fetch_user_and_token(token) do
|
||||
query = from(q in Token, where: q.token == ^token, preload: [:user])
|
||||
query =
|
||||
from(t in Token,
|
||||
where: t.token == ^token,
|
||||
join: user in assoc(t, :user),
|
||||
preload: [user: user]
|
||||
)
|
||||
|
||||
with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do
|
||||
{:ok, user, token_record}
|
||||
|
|
|
@ -24,7 +24,8 @@ defmodule Pleroma.Uploaders.MDII do
|
|||
extension = String.split(upload.name, ".") |> List.last()
|
||||
query = "#{cgi}?#{extension}"
|
||||
|
||||
with {:ok, %{status: 200, body: body}} <- @httpoison.post(query, file_data) do
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
@httpoison.post(query, file_data, adapter: [pool: :default]) do
|
||||
remote_file_name = String.split(body) |> List.first()
|
||||
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
||||
{:ok, {:url, public_url}}
|
||||
|
|
|
@ -309,20 +309,21 @@ defmodule Pleroma.User do
|
|||
@doc "A mass follow for local users. Ignores blocks and has no side effects"
|
||||
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
||||
def follow_all(follower, followeds) do
|
||||
following =
|
||||
(follower.following ++ Enum.map(followeds, fn %{follower_address: fa} -> fa end))
|
||||
|> Enum.uniq()
|
||||
followed_addresses = Enum.map(followeds, fn %{follower_address: fa} -> fa end)
|
||||
|
||||
{:ok, follower} =
|
||||
follower
|
||||
|> follow_changeset(%{following: following})
|
||||
|> update_and_set_cache
|
||||
q =
|
||||
from(u in User,
|
||||
where: u.id == ^follower.id,
|
||||
update: [set: [following: fragment("array_cat(?, ?)", u.following, ^followed_addresses)]]
|
||||
)
|
||||
|
||||
{1, [follower]} = Repo.update_all(q, [], returning: true)
|
||||
|
||||
Enum.each(followeds, fn followed ->
|
||||
update_follower_count(followed)
|
||||
end)
|
||||
|
||||
{:ok, follower}
|
||||
set_cache(follower)
|
||||
end
|
||||
|
||||
def follow(%User{} = follower, %User{info: info} = followed) do
|
||||
|
@ -343,18 +344,17 @@ defmodule Pleroma.User do
|
|||
Websub.subscribe(follower, followed)
|
||||
end
|
||||
|
||||
following =
|
||||
[ap_followers | follower.following]
|
||||
|> Enum.uniq()
|
||||
q =
|
||||
from(u in User,
|
||||
where: u.id == ^follower.id,
|
||||
update: [push: [following: ^ap_followers]]
|
||||
)
|
||||
|
||||
follower =
|
||||
follower
|
||||
|> follow_changeset(%{following: following})
|
||||
|> update_and_set_cache
|
||||
{1, [follower]} = Repo.update_all(q, [], returning: true)
|
||||
|
||||
{:ok, _} = update_follower_count(followed)
|
||||
|
||||
follower
|
||||
set_cache(follower)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -362,17 +362,18 @@ defmodule Pleroma.User do
|
|||
ap_followers = followed.follower_address
|
||||
|
||||
if following?(follower, followed) and follower.ap_id != followed.ap_id do
|
||||
following =
|
||||
follower.following
|
||||
|> List.delete(ap_followers)
|
||||
q =
|
||||
from(u in User,
|
||||
where: u.id == ^follower.id,
|
||||
update: [pull: [following: ^ap_followers]]
|
||||
)
|
||||
|
||||
{:ok, follower} =
|
||||
follower
|
||||
|> follow_changeset(%{following: following})
|
||||
|> update_and_set_cache
|
||||
{1, [follower]} = Repo.update_all(q, [], returning: true)
|
||||
|
||||
{:ok, followed} = update_follower_count(followed)
|
||||
|
||||
set_cache(follower)
|
||||
|
||||
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
||||
else
|
||||
{:error, "Not subscribed!"}
|
||||
|
@ -423,12 +424,16 @@ defmodule Pleroma.User do
|
|||
get_by_nickname(nickname)
|
||||
end
|
||||
|
||||
def set_cache(user) do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
def update_and_set_cache(changeset) do
|
||||
with {:ok, user} <- Repo.update(changeset) do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
||||
{:ok, user}
|
||||
set_cache(user)
|
||||
else
|
||||
e -> e
|
||||
end
|
||||
|
|
|
@ -64,7 +64,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) do
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
|
||||
limit = Pleroma.Config.get([:instance, :remote_limit])
|
||||
String.length(content) <= limit
|
||||
end
|
||||
|
@ -88,6 +88,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
recipients: recipients
|
||||
})
|
||||
|
||||
Task.start(fn ->
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
end)
|
||||
|
||||
Notification.create_notifications(activity)
|
||||
stream_out(activity)
|
||||
{:ok, activity}
|
||||
|
@ -426,7 +430,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_since(query, _), do: query
|
||||
|
||||
defp restrict_tag(query, %{"tag" => tag}) do
|
||||
defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
|
||||
when is_list(tag_reject) and tag_reject != [] do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(not (? #> '{\"object\",\"tag\"}') \\?| ?)", activity.data, ^tag_reject)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_tag_reject(query, _), do: query
|
||||
|
||||
defp restrict_tag_all(query, %{"tag_all" => tag_all})
|
||||
when is_list(tag_all) and tag_all != [] do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(? #> '{\"object\",\"tag\"}') \\?& ?", activity.data, ^tag_all)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_tag_all(query, _), do: query
|
||||
|
||||
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(? #> '{\"object\",\"tag\"}') \\?| ?", activity.data, ^tag)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("? <@ (? #> '{\"object\",\"tag\"}')", ^tag, activity.data)
|
||||
|
@ -575,6 +606,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
base_query
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|> restrict_tag(opts)
|
||||
|> restrict_tag_reject(opts)
|
||||
|> restrict_tag_all(opts)
|
||||
|> restrict_since(opts)
|
||||
|> restrict_local(opts)
|
||||
|> restrict_limit(opts)
|
||||
|
|
|
@ -141,11 +141,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|> Map.put("actor", get_actor(%{"actor" => actor}))
|
||||
end
|
||||
|
||||
def fix_likes(%{"likes" => likes} = object)
|
||||
when is_bitstring(likes) do
|
||||
# Check for standardisation
|
||||
# This is what Peertube does
|
||||
# curl -H 'Accept: application/activity+json' $likes | jq .totalItems
|
||||
# Check for standardisation
|
||||
# This is what Peertube does
|
||||
# curl -H 'Accept: application/activity+json' $likes | jq .totalItems
|
||||
# Prismo returns only an integer (count) as "likes"
|
||||
def fix_likes(%{"likes" => likes} = object) when not is_map(likes) do
|
||||
object
|
||||
|> Map.put("likes", [])
|
||||
|> Map.put("like_count", 0)
|
||||
|
@ -453,9 +453,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
||||
{:ok, activity} <-
|
||||
ActivityPub.accept(%{
|
||||
ActivityPub.reject(%{
|
||||
to: follow_activity.data["to"],
|
||||
type: "Accept",
|
||||
type: "Reject",
|
||||
actor: followed.ap_id,
|
||||
object: follow_activity.data["id"],
|
||||
local: false
|
||||
|
|
|
@ -316,6 +316,25 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
@doc """
|
||||
Updates a follow activity's state (for locked accounts).
|
||||
"""
|
||||
def update_follow_state(
|
||||
%Activity{data: %{"actor" => actor, "object" => object, "state" => "pending"}} = activity,
|
||||
state
|
||||
) do
|
||||
try do
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Repo,
|
||||
"UPDATE activities SET data = jsonb_set(data, '{state}', $1) WHERE data->>'type' = 'Follow' AND data->>'actor' = $2 AND data->>'object' = $3 AND data->>'state' = 'pending'",
|
||||
[state, actor, object]
|
||||
)
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
{:ok, activity}
|
||||
rescue
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def update_follow_state(%Activity{} = activity, state) do
|
||||
with new_data <-
|
||||
activity.data
|
||||
|
|
|
@ -499,7 +499,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(file,
|
||||
ActivityPub.upload(
|
||||
file,
|
||||
actor: User.ap_id(user),
|
||||
description: Map.get(data, "description")
|
||||
) do
|
||||
|
@ -540,15 +541,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = params["local"] in [true, "True", "true", "1"]
|
||||
|
||||
params =
|
||||
tags =
|
||||
[params["tag"], params["any"]]
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_all =
|
||||
params["all"] ||
|
||||
[]
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_reject =
|
||||
params["none"] ||
|
||||
[]
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
query_params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("local_only", local_only)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("tag", String.downcase(params["tag"]))
|
||||
|> Map.put("tag", tags)
|
||||
|> Map.put("tag_all", tag_all)
|
||||
|> Map.put("tag_reject", tag_reject)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_public_activities(params)
|
||||
ActivityPub.fetch_public_activities(query_params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|
@ -1081,7 +1101,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
def login(conn, _) do
|
||||
with {:ok, app} <- get_or_make_app() do
|
||||
path =
|
||||
o_auth_path(conn, :authorize,
|
||||
o_auth_path(
|
||||
conn,
|
||||
:authorize,
|
||||
response_type: "code",
|
||||
client_id: app.client_id,
|
||||
redirect_uri: ".",
|
||||
|
@ -1289,7 +1311,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
[],
|
||||
adapter: [
|
||||
timeout: timeout,
|
||||
recv_timeout: timeout
|
||||
recv_timeout: timeout,
|
||||
pool: :default
|
||||
]
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
|
@ -1322,6 +1345,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def status_card(conn, %{"id" => status_id}) do
|
||||
with %Activity{} = activity <- Repo.get(Activity, status_id),
|
||||
true <- ActivityPub.is_public?(activity) do
|
||||
data =
|
||||
StatusView.render(
|
||||
"card.json",
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
)
|
||||
|
||||
json(conn, data)
|
||||
else
|
||||
_e ->
|
||||
%{}
|
||||
end
|
||||
end
|
||||
|
||||
def try_render(conn, target, params)
|
||||
when is_binary(target) do
|
||||
res = render(conn, target, params)
|
||||
|
|
|
@ -112,7 +112,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
# Pleroma extension
|
||||
pleroma: %{
|
||||
confirmation_pending: user_info.confirmation_pending,
|
||||
tags: user.tags
|
||||
tags: user.tags,
|
||||
is_moderator: user.info.is_moderator,
|
||||
is_admin: user.info.is_admin
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -49,12 +49,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
replied_to_activities = get_replied_to_activities(opts.activities)
|
||||
|
||||
opts.activities
|
||||
|> render_many(
|
||||
|> safe_render_many(
|
||||
StatusView,
|
||||
"status.json",
|
||||
Map.put(opts, :replied_to_activities, replied_to_activities)
|
||||
)
|
||||
|> Enum.filter(fn x -> not is_nil(x) end)
|
||||
end
|
||||
|
||||
def render(
|
||||
|
@ -140,6 +139,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
__MODULE__
|
||||
)
|
||||
|
||||
card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))
|
||||
|
||||
%{
|
||||
id: to_string(activity.id),
|
||||
uri: object["id"],
|
||||
|
@ -148,6 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||
reblog: nil,
|
||||
card: card,
|
||||
content: content,
|
||||
created_at: created_at,
|
||||
reblogs_count: announcement_count,
|
||||
|
@ -176,6 +178,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
nil
|
||||
end
|
||||
|
||||
def render("card.json", %{rich_media: rich_media, page_url: page_url}) do
|
||||
page_url = rich_media[:url] || page_url
|
||||
page_url_data = URI.parse(page_url)
|
||||
site_name = rich_media[:site_name] || page_url_data.host
|
||||
|
||||
%{
|
||||
type: "link",
|
||||
provider_name: site_name,
|
||||
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
|
||||
url: page_url,
|
||||
image: rich_media[:image] |> MediaProxy.url(),
|
||||
title: rich_media[:title],
|
||||
description: rich_media[:description],
|
||||
pleroma: %{
|
||||
opengraph: rich_media
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def render("card.json", _) do
|
||||
nil
|
||||
end
|
||||
|
||||
def render("attachment.json", %{attachment: attachment}) do
|
||||
[attachment_url | _] = attachment["url"]
|
||||
media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule Pleroma.Web.OAuth.FallbackController do
|
|||
# No user/password
|
||||
def call(conn, _) do
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> put_flash(:error, "Invalid Username/Password")
|
||||
|> OAuthController.authorize(conn.params)
|
||||
|> OAuthController.authorize(conn.params["authorization"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -166,10 +166,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
end
|
||||
else
|
||||
{:public?, false} ->
|
||||
{:error, :not_found}
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> Fallback.RedirectController.redirector(nil, 404)
|
||||
|
||||
{:activity, nil} ->
|
||||
{:error, :not_found}
|
||||
conn
|
||||
|> Fallback.RedirectController.redirector(nil, 404)
|
||||
|
||||
e ->
|
||||
e
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
defmodule Pleroma.Web.RichMedia.RichMediaController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
def parse(conn, %{"url" => url}) do
|
||||
case Pleroma.Web.RichMedia.Parser.parse(url) do
|
||||
{:ok, data} ->
|
||||
conn
|
||||
|> json_response(200, data)
|
||||
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> json_response(404, msg)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright _ 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.RichMedia.Helpers do
|
||||
alias Pleroma.{Activity, Object, HTML}
|
||||
alias Pleroma.Web.RichMedia.Parser
|
||||
|
||||
def fetch_data_for_activity(%Activity{} = activity) do
|
||||
with %Object{} = object <- Object.normalize(activity.data["object"]),
|
||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||
%{page_url: page_url, rich_media: rich_media}
|
||||
else
|
||||
_ -> %{}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,7 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.RichMedia.Parser do
|
||||
@parsers [
|
||||
Pleroma.Web.RichMedia.Parsers.OGP,
|
||||
|
@ -5,17 +9,32 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
Pleroma.Web.RichMedia.Parsers.OEmbed
|
||||
]
|
||||
|
||||
def parse(nil), do: {:error, "No URL provided"}
|
||||
|
||||
if Mix.env() == :test do
|
||||
def parse(url), do: parse_url(url)
|
||||
else
|
||||
def parse(url),
|
||||
do: Cachex.fetch!(:rich_media_cache, url, fn _ -> parse_url(url) end)
|
||||
def parse(url) do
|
||||
try do
|
||||
Cachex.fetch!(:rich_media_cache, url, fn _ ->
|
||||
{:commit, parse_url(url)}
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
{:error, "Cachex error: #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_url(url) do
|
||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url)
|
||||
try do
|
||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: [pool: :media])
|
||||
|
||||
html |> maybe_parse() |> get_parsed_data()
|
||||
html |> maybe_parse() |> get_parsed_data()
|
||||
rescue
|
||||
e ->
|
||||
{:error, "Parsing error: #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_parse(html) do
|
||||
|
@ -27,11 +46,11 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
end)
|
||||
end
|
||||
|
||||
defp get_parsed_data(data) when data == %{} do
|
||||
{:error, "No metadata found"}
|
||||
defp get_parsed_data(%{title: title} = data) when is_binary(title) and byte_size(title) > 0 do
|
||||
{:ok, data}
|
||||
end
|
||||
|
||||
defp get_parsed_data(data) do
|
||||
{:ok, data}
|
||||
{:error, "Found metadata was invalid or incomplete: #{inspect(data)}"}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,8 +20,12 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
|
|||
end
|
||||
|
||||
defp get_oembed_data(url) do
|
||||
{:ok, %Tesla.Env{body: json}} = Pleroma.HTTP.get(url)
|
||||
{:ok, %Tesla.Env{body: json}} = Pleroma.HTTP.get(url, [], adapter: [pool: :media])
|
||||
|
||||
{:ok, Poison.decode!(json)}
|
||||
{:ok, data} = Jason.decode(json)
|
||||
|
||||
data = data |> Map.new(fn {k, v} -> {String.to_atom(k), v} end)
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -239,12 +239,6 @@ defmodule Pleroma.Web.Router do
|
|||
put("/settings", MastodonAPIController, :put_settings)
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web.RichMedia do
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
get("/rich_media/parse", RichMediaController, :parse)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:api)
|
||||
get("/instance", MastodonAPIController, :masto_instance)
|
||||
|
@ -258,7 +252,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/statuses/:id", MastodonAPIController, :get_status)
|
||||
get("/statuses/:id/context", MastodonAPIController, :get_context)
|
||||
get("/statuses/:id/card", MastodonAPIController, :empty_object)
|
||||
get("/statuses/:id/card", MastodonAPIController, :status_card)
|
||||
get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by)
|
||||
get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by)
|
||||
|
||||
|
@ -284,6 +278,7 @@ defmodule Pleroma.Web.Router do
|
|||
post("/help/test", TwitterAPI.UtilController, :help_test)
|
||||
get("/statusnet/config", TwitterAPI.UtilController, :config)
|
||||
get("/statusnet/version", TwitterAPI.UtilController, :version)
|
||||
get("/pleroma/frontend_configurations", TwitterAPI.UtilController, :frontend_configurations)
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web do
|
||||
|
@ -523,10 +518,10 @@ defmodule Fallback.RedirectController do
|
|||
alias Pleroma.Web.Metadata
|
||||
alias Pleroma.User
|
||||
|
||||
def redirector(conn, _params) do
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_file(200, index_file_path())
|
||||
|> send_file(code, index_file_path())
|
||||
end
|
||||
|
||||
def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
|
||||
|
|
|
@ -183,25 +183,31 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0")
|
||||
}
|
||||
|
||||
pleroma_fe = %{
|
||||
theme: Keyword.get(instance_fe, :theme),
|
||||
background: Keyword.get(instance_fe, :background),
|
||||
logo: Keyword.get(instance_fe, :logo),
|
||||
logoMask: Keyword.get(instance_fe, :logo_mask),
|
||||
logoMargin: Keyword.get(instance_fe, :logo_margin),
|
||||
redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
|
||||
redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
|
||||
chatDisabled: !Keyword.get(instance_chat, :enabled),
|
||||
showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
|
||||
scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
|
||||
formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
|
||||
collapseMessageWithSubject: Keyword.get(instance_fe, :collapse_message_with_subject),
|
||||
hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
|
||||
hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
|
||||
scopeCopy: Keyword.get(instance_fe, :scope_copy),
|
||||
subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
|
||||
alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
|
||||
}
|
||||
pleroma_fe =
|
||||
if instance_fe do
|
||||
%{
|
||||
theme: Keyword.get(instance_fe, :theme),
|
||||
background: Keyword.get(instance_fe, :background),
|
||||
logo: Keyword.get(instance_fe, :logo),
|
||||
logoMask: Keyword.get(instance_fe, :logo_mask),
|
||||
logoMargin: Keyword.get(instance_fe, :logo_margin),
|
||||
redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
|
||||
redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
|
||||
chatDisabled: !Keyword.get(instance_chat, :enabled),
|
||||
showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
|
||||
scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
|
||||
formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
|
||||
collapseMessageWithSubject:
|
||||
Keyword.get(instance_fe, :collapse_message_with_subject),
|
||||
hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
|
||||
hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
|
||||
scopeCopy: Keyword.get(instance_fe, :scope_copy),
|
||||
subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
|
||||
alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
|
||||
}
|
||||
else
|
||||
Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
||||
end
|
||||
|
||||
managed_config = Keyword.get(instance, :managed_config)
|
||||
|
||||
|
@ -216,6 +222,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
end
|
||||
|
||||
def frontend_configurations(conn, _params) do
|
||||
config =
|
||||
Pleroma.Config.get(:frontend_configurations, %{})
|
||||
|> Enum.into(%{})
|
||||
|
||||
json(conn, config)
|
||||
end
|
||||
|
||||
def version(conn, _params) do
|
||||
version = Pleroma.Application.named_version()
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
|||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
defp user_by_ap_id(user_list, ap_id) do
|
||||
Enum.find(user_list, fn %{ap_id: user_id} -> ap_id == user_id end)
|
||||
|
@ -186,6 +187,12 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
|||
|
||||
summary = HTML.strip_tags(object["summary"])
|
||||
|
||||
card =
|
||||
StatusView.render(
|
||||
"card.json",
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
)
|
||||
|
||||
%{
|
||||
"id" => activity.id,
|
||||
"uri" => activity.data["object"]["id"],
|
||||
|
@ -214,7 +221,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
|||
"possibly_sensitive" => possibly_sensitive,
|
||||
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
|
||||
"summary" => summary,
|
||||
"summary_html" => summary |> Formatter.emojify(object["emoji"])
|
||||
"summary_html" => summary |> Formatter.emojify(object["emoji"]),
|
||||
"card" => card
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
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.HTML
|
||||
alias Pleroma.Object
|
||||
|
@ -114,7 +115,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
|> Map.put(:context_ids, context_ids)
|
||||
|> Map.put(:users, users)
|
||||
|
||||
render_many(
|
||||
safe_render_many(
|
||||
opts.activities,
|
||||
ActivityView,
|
||||
"activity.json",
|
||||
|
@ -274,6 +275,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
|
||||
summary = HTML.strip_tags(summary)
|
||||
|
||||
card =
|
||||
StatusView.render(
|
||||
"card.json",
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
)
|
||||
|
||||
%{
|
||||
"id" => activity.id,
|
||||
"uri" => activity.data["object"]["id"],
|
||||
|
@ -300,9 +307,10 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
"tags" => tags,
|
||||
"activity_type" => "post",
|
||||
"possibly_sensitive" => possibly_sensitive,
|
||||
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
|
||||
"visibility" => StatusView.get_visibility(object),
|
||||
"summary" => summary,
|
||||
"summary_html" => summary |> Formatter.emojify(object["emoji"])
|
||||
"summary_html" => summary |> Formatter.emojify(object["emoji"]),
|
||||
"card" => card
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -38,6 +38,33 @@ defmodule Pleroma.Web do
|
|||
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
||||
|
||||
import Pleroma.Web.{ErrorHelpers, Gettext, Router.Helpers}
|
||||
|
||||
require Logger
|
||||
|
||||
@doc "Same as `render/3` but wrapped in a rescue block"
|
||||
def safe_render(view, template, assigns \\ %{}) do
|
||||
Phoenix.View.render(view, template, assigns)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error(
|
||||
"#{__MODULE__} failed to render #{inspect({view, template})}: #{inspect(error)}"
|
||||
)
|
||||
|
||||
Logger.error(inspect(__STACKTRACE__))
|
||||
nil
|
||||
end
|
||||
|
||||
@doc """
|
||||
Same as `render_many/4` but wrapped in rescue block.
|
||||
"""
|
||||
def safe_render_many(collection, view, template, assigns \\ %{}) do
|
||||
Enum.map(collection, fn resource ->
|
||||
as = Map.get(assigns, :as) || view.__resource__
|
||||
assigns = Map.put(assigns, as, resource)
|
||||
safe_render(view, template, assigns)
|
||||
end)
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Pleroma.Repo.Migrations.ChangePushSubscriptionsVarchar do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:push_subscriptions) do
|
||||
modify(:endpoint, :varchar)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddActivitiesLikesIndex do
|
||||
use Ecto.Migration
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def change do
|
||||
create index(:activities, ["((data #> '{\"object\",\"likes\"}'))"], concurrently: true, name: :activities_likes, using: :gin)
|
||||
end
|
||||
end
|
Binary file not shown.
After Width: | Height: | Size: 3.0 MiB |
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.3d3e30a9afb8c41739656f496e8c79e6.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.8dc8d7a1dc85bfdf2b14.js></script><script type=text/javascript src=/static/js/vendor.61fd03d8471aaadcf63c.js></script><script type=text/javascript src=/static/js/app.ddbd2a89e264d04e0d6d.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.42c43da15d7ab16ad8e42d21fcfc5a43.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.6aa5664a1a2c0832ce7b.js></script><script type=text/javascript src=/static/js/vendor.56a115a1d7339d6811a0.js></script><script type=text/javascript src=/static/js/app.59ebcfb47f86a7a5ba3f.js></script></body></html>
|
|
@ -19,5 +19,8 @@
|
|||
"loginMethod": "password",
|
||||
"webPushNotifications": false,
|
||||
"noAttachmentLinks": false,
|
||||
"nsfwCensorImage": ""
|
||||
"nsfwCensorImage": "",
|
||||
"useOneClickNsfw": true,
|
||||
"playVideosInline": false,
|
||||
"useContainFit": false
|
||||
}
|
||||
|
|
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
|
@ -221,6 +221,18 @@
|
|||
"css": "plus",
|
||||
"code": 59413,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "41087bc74d4b20b55059c60a33bf4008",
|
||||
"css": "edit",
|
||||
"code": 59415,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "5717236f6134afe2d2a278a5c9b3927a",
|
||||
"css": "play-circled",
|
||||
"code": 61764,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
.icon-attention:before { content: '\e814'; } /* '' */
|
||||
.icon-plus:before { content: '\e815'; } /* '' */
|
||||
.icon-adjust:before { content: '\e816'; } /* '' */
|
||||
.icon-edit:before { content: '\e817'; } /* '' */
|
||||
.icon-spin3:before { content: '\e832'; } /* '' */
|
||||
.icon-spin4:before { content: '\e834'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
|
@ -32,6 +33,7 @@
|
|||
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
||||
.icon-reply:before { content: '\f112'; } /* '' */
|
||||
.icon-lock-open-alt:before { content: '\f13e'; } /* '' */
|
||||
.icon-play-circled:before { content: '\f144'; } /* '' */
|
||||
.icon-thumbs-up-alt:before { content: '\f164'; } /* '' */
|
||||
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
||||
.icon-user-plus:before { content: '\f234'; } /* '' */
|
File diff suppressed because one or more lines are too long
|
@ -22,6 +22,7 @@
|
|||
.icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
@ -32,6 +33,7 @@
|
|||
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-play-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-user-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
|
@ -33,6 +33,7 @@
|
|||
.icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
@ -43,6 +44,7 @@
|
|||
.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-play-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-user-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
|
@ -1,11 +1,11 @@
|
|||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?97335193');
|
||||
src: url('../font/fontello.eot?97335193#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?97335193') format('woff2'),
|
||||
url('../font/fontello.woff?97335193') format('woff'),
|
||||
url('../font/fontello.ttf?97335193') format('truetype'),
|
||||
url('../font/fontello.svg?97335193#fontello') format('svg');
|
||||
src: url('../font/fontello.eot?94672585');
|
||||
src: url('../font/fontello.eot?94672585#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?94672585') format('woff2'),
|
||||
url('../font/fontello.woff?94672585') format('woff'),
|
||||
url('../font/fontello.ttf?94672585') format('truetype'),
|
||||
url('../font/fontello.svg?94672585#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
|||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?97335193#fontello') format('svg');
|
||||
src: url('../font/fontello.svg?94672585#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -78,6 +78,7 @@
|
|||
.icon-attention:before { content: '\e814'; } /* '' */
|
||||
.icon-plus:before { content: '\e815'; } /* '' */
|
||||
.icon-adjust:before { content: '\e816'; } /* '' */
|
||||
.icon-edit:before { content: '\e817'; } /* '' */
|
||||
.icon-spin3:before { content: '\e832'; } /* '' */
|
||||
.icon-spin4:before { content: '\e834'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
|
@ -88,6 +89,7 @@
|
|||
.icon-plus-squared:before { content: '\f0fe'; } /* '' */
|
||||
.icon-reply:before { content: '\f112'; } /* '' */
|
||||
.icon-lock-open-alt:before { content: '\f13e'; } /* '' */
|
||||
.icon-play-circled:before { content: '\f144'; } /* '' */
|
||||
.icon-thumbs-up-alt:before { content: '\f164'; } /* '' */
|
||||
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
||||
.icon-user-plus:before { content: '\f234'; } /* '' */
|
|
@ -229,11 +229,11 @@ body {
|
|||
}
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('./font/fontello.eot?32716429');
|
||||
src: url('./font/fontello.eot?32716429#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?32716429') format('woff'),
|
||||
url('./font/fontello.ttf?32716429') format('truetype'),
|
||||
url('./font/fontello.svg?32716429#fontello') format('svg');
|
||||
src: url('./font/fontello.eot?28736547');
|
||||
src: url('./font/fontello.eot?28736547#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?28736547') format('woff'),
|
||||
url('./font/fontello.ttf?28736547') format('truetype'),
|
||||
url('./font/fontello.svg?28736547#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -331,23 +331,27 @@ body {
|
|||
<div class="the-icons span3" title="Code: 0xe814"><i class="demo-icon icon-attention"></i> <span class="i-name">icon-attention</span><span class="i-code">0xe814</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe815"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe815</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe816"><i class="demo-icon icon-adjust"></i> <span class="i-name">icon-adjust</span><span class="i-code">0xe816</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe817"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe817</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
||||
</div>
|
||||
|
|
Binary file not shown.
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
|
||||
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="857" descent="-143" />
|
||||
|
@ -52,6 +52,8 @@
|
|||
|
||||
<glyph glyph-name="adjust" unicode="" d="M429 53v608q-83 0-153-41t-110-111-41-152 41-152 110-111 153-41z m428 304q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
||||
|
@ -72,6 +74,8 @@
|
|||
|
||||
<glyph glyph-name="lock-open-alt" unicode="" d="M589 428q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" />
|
||||
|
||||
<glyph glyph-name="play-circled" unicode="" d="M429 786q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m214-460q18 10 18 31t-18 31l-304 178q-17 11-35 1-18-11-18-31v-358q0-20 18-31 9-4 17-4 10 0 18 5z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="thumbs-up-alt" unicode="" d="M143 107q0 15-11 25t-25 11q-15 0-25-11t-11-25q0-15 11-25t25-11q15 0 25 11t11 25z m89 286v-357q0-15-10-25t-26-11h-160q-15 0-25 11t-11 25v357q0 14 11 25t25 10h160q15 0 26-10t10-25z m661 0q0-48-31-83 9-25 9-43 1-42-24-76 9-31 0-66-9-31-31-52 5-62-27-101-36-43-110-44h-72q-37 0-80 9t-68 16-67 22q-69 24-88 25-15 0-25 11t-11 25v357q0 14 10 25t24 11q13 1 42 33t57 67q38 49 56 67 10 10 17 27t10 27 8 34q4 22 7 34t11 29 19 28q10 11 25 11 25 0 46-6t33-15 22-22 14-25 7-28 2-25 1-22q0-21-6-43t-10-33-16-31q-1-4-5-10t-6-13-5-13h155q43 0 75-32t32-75z" horiz-adv-x="928.6" />
|
||||
|
||||
<glyph glyph-name="binoculars" unicode="" d="M393 678v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
||||
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
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
|
@ -1,2 +1,2 @@
|
|||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,c){for(var p,d,l=0,s=[];l<o.length;l++)d=o[l],n[d]&&s.push.apply(s,n[d]),n[d]=0;for(p in c)Object.prototype.hasOwnProperty.call(c,p)&&(e[p]=c[p]);for(a&&a(o,c);s.length;)s.shift().call(null,t);if(c[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"61fd03d8471aaadcf63c",2:"ddbd2a89e264d04e0d6d"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.8dc8d7a1dc85bfdf2b14.js.map
|
||||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,p){for(var c,l,s=0,i=[];s<o.length;s++)l=o[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(c in p)Object.prototype.hasOwnProperty.call(p,c)&&(e[c]=p[c]);for(a&&a(o,p);i.length;)i.shift().call(null,t);if(p[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"56a115a1d7339d6811a0",2:"59ebcfb47f86a7a5ba3f"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.6aa5664a1a2c0832ce7b.js.map
|
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
|
@ -1,4 +1,4 @@
|
|||
var serviceWorkerOption = {"assets":["/static/img/nsfw.50fd83c.png","/static/js/manifest.8dc8d7a1dc85bfdf2b14.js","/static/js/vendor.61fd03d8471aaadcf63c.js","/static/js/app.ddbd2a89e264d04e0d6d.js","/static/css/app.3d3e30a9afb8c41739656f496e8c79e6.css"]};
|
||||
var serviceWorkerOption = {"assets":["/static/img/nsfw.50fd83c.png","/static/js/manifest.1a1e43570daf20d0d379.js","/static/js/vendor.2aa502e9b803bd9bec46.js","/static/js/app.22e1b6d1b344d3ac82f1.js","/static/css/app.25013bd2908c3860c9e71320642033a5.css"]};
|
||||
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var t={};return n.m=e,n.c=t,n.p="/",n(0)}([function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){return u.default.getItem("vuex-lz").then(function(e){return e.config.webPushNotifications})}function i(){return clients.matchAll({includeUncontrolled:!0}).then(function(e){return e.filter(function(e){var n=e.type;return"window"===n})})}var a=t(1),u=r(a);self.addEventListener("push",function(e){e.data&&e.waitUntil(o().then(function(n){return n&&i().then(function(n){var t=e.data.json();if(0===n.length)return self.registration.showNotification(t.title,t)})}))}),self.addEventListener("notificationclick",function(e){e.notification.close(),e.waitUntil(i().then(function(e){for(var n=0;n<e.length;n++){var t=e[n];if("/"===t.url&&"focus"in t)return t.focus()}if(clients.openWindow)return clients.openWindow("/")}))})},function(e,n){/*!
|
||||
localForage -- Offline Storage, Improved
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -11,6 +11,7 @@ defmodule Pleroma.FlakeIdTest do
|
|||
test "from_string/1" do
|
||||
fake_flake = <<0::integer-size(64), 42::integer-size(64)>>
|
||||
assert from_string("42") == fake_flake
|
||||
assert from_string(42) == fake_flake
|
||||
end
|
||||
|
||||
test "zero or -1 is a null flake" do
|
||||
|
|
|
@ -653,6 +653,14 @@ defmodule HttpRequestMock do
|
|||
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
||||
end
|
||||
|
||||
def get("http://example.com/ogp", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}}
|
||||
end
|
||||
|
||||
def get("http://example.com/empty", _, _, _) do
|
||||
{:ok, %Tesla.Env{status: 200, body: "hello"}}
|
||||
end
|
||||
|
||||
def get(url, query, body, headers) do
|
||||
{:error,
|
||||
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
|
|
|
@ -50,13 +50,19 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
test "follow_all follows mutliple users" do
|
||||
user = insert(:user)
|
||||
followed_zero = insert(:user)
|
||||
followed_one = insert(:user)
|
||||
followed_two = insert(:user)
|
||||
not_followed = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed_zero)
|
||||
|
||||
{:ok, user} = User.follow_all(user, [followed_one, followed_two])
|
||||
|
||||
assert User.following?(user, followed_one)
|
||||
assert User.following?(user, followed_two)
|
||||
assert User.following?(user, followed_zero)
|
||||
refute User.following?(user, not_followed)
|
||||
end
|
||||
|
||||
test "follow takes a user and another user" do
|
||||
|
|
|
@ -64,6 +64,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert user.info.ap_enabled
|
||||
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
|
||||
end
|
||||
|
||||
test "it fetches the appropriate tag-restricted posts" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
|
||||
{:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
|
||||
{:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
|
||||
|
||||
fetch_one = ActivityPub.fetch_activities([], %{"tag" => "test"})
|
||||
fetch_two = ActivityPub.fetch_activities([], %{"tag" => ["test", "essais"]})
|
||||
|
||||
fetch_three =
|
||||
ActivityPub.fetch_activities([], %{
|
||||
"tag" => ["test", "essais"],
|
||||
"tag_reject" => ["reject"]
|
||||
})
|
||||
|
||||
fetch_four =
|
||||
ActivityPub.fetch_activities([], %{
|
||||
"tag" => ["test"],
|
||||
"tag_all" => ["test", "reject"]
|
||||
})
|
||||
|
||||
assert fetch_one == [status_one, status_three]
|
||||
assert fetch_two == [status_one, status_two, status_three]
|
||||
assert fetch_three == [status_one, status_two]
|
||||
assert fetch_four == [status_three]
|
||||
end
|
||||
end
|
||||
|
||||
describe "insertion" do
|
||||
|
@ -85,6 +113,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
|
||||
end
|
||||
|
||||
test "doesn't drop activities with content being null" do
|
||||
data = %{
|
||||
"ok" => true,
|
||||
"object" => %{
|
||||
"content" => nil
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, _} = ActivityPub.insert(data)
|
||||
end
|
||||
|
||||
test "returns the activity if one with the same id is already in" do
|
||||
activity = insert(:note_activity)
|
||||
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
||||
|
@ -584,8 +623,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
"in_reply_to_status_id" => private_activity_2.id
|
||||
})
|
||||
|
||||
assert user1.following == [user3.ap_id <> "/followers", user1.ap_id]
|
||||
|
||||
activities = ActivityPub.fetch_activities([user1.ap_id | user1.following])
|
||||
|
||||
assert [public_activity, private_activity_1, private_activity_3] == activities
|
||||
|
|
|
@ -61,7 +61,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
},
|
||||
pleroma: %{
|
||||
confirmation_pending: false,
|
||||
tags: []
|
||||
tags: [],
|
||||
is_admin: false,
|
||||
is_moderator: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +104,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
},
|
||||
pleroma: %{
|
||||
confirmation_pending: false,
|
||||
tags: []
|
||||
tags: [],
|
||||
is_admin: false,
|
||||
is_moderator: false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
assert Repo.get(Activity, id)
|
||||
end
|
||||
|
||||
test "posting a status with OGP link preview", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status" => "http://example.com/ogp"
|
||||
})
|
||||
|
||||
assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
|
||||
assert Repo.get(Activity, id)
|
||||
end
|
||||
|
||||
test "posting a direct status", %{conn: conn} do
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
@ -1044,6 +1058,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "multi-hashtag timeline", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
|
||||
{:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
|
||||
{:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
|
||||
|
||||
any_test =
|
||||
conn
|
||||
|> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
|
||||
|
||||
[status_none, status_test1, status_test] = json_response(any_test, 200)
|
||||
|
||||
assert to_string(activity_test.id) == status_test["id"]
|
||||
assert to_string(activity_test1.id) == status_test1["id"]
|
||||
assert to_string(activity_none.id) == status_none["id"]
|
||||
|
||||
restricted_test =
|
||||
conn
|
||||
|> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
|
||||
|
||||
assert [status_test1] == json_response(restricted_test, 200)
|
||||
|
||||
all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
|
||||
|
||||
assert [status_none] == json_response(all_test, 200)
|
||||
end
|
||||
|
||||
test "getting followers", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
@ -1623,5 +1665,32 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
|> post("/api/v1/statuses/#{activity_two.id}/pin")
|
||||
|> json_response(400)
|
||||
end
|
||||
|
||||
test "Status rich-media Card", %{conn: conn, user: user} do
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v1/statuses/#{activity.id}/card")
|
||||
|> json_response(200)
|
||||
|
||||
assert response == %{
|
||||
"image" => "http://ia.media-imdb.com/images/rock.jpg",
|
||||
"provider_name" => "www.imdb.com",
|
||||
"provider_url" => "http://www.imdb.com",
|
||||
"title" => "The Rock",
|
||||
"type" => "link",
|
||||
"url" => "http://www.imdb.com/title/tt0117500/",
|
||||
"description" => nil,
|
||||
"pleroma" => %{
|
||||
"opengraph" => %{
|
||||
"image" => "http://ia.media-imdb.com/images/rock.jpg",
|
||||
"title" => "The Rock",
|
||||
"type" => "video.movie",
|
||||
"url" => "http://www.imdb.com/title/tt0117500/"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -84,6 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
account: AccountView.render("account.json", %{user: user}),
|
||||
in_reply_to_id: nil,
|
||||
in_reply_to_account_id: nil,
|
||||
card: nil,
|
||||
reblog: nil,
|
||||
content: HtmlSanitizeEx.basic_html(note.data["object"]["content"]),
|
||||
created_at: created_at,
|
||||
|
|
|
@ -34,6 +34,31 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
|||
assert Repo.get_by(Authorization, token: code)
|
||||
end
|
||||
|
||||
test "correctly handles wrong credentials", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
app = insert(:oauth_app)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> post("/oauth/authorize", %{
|
||||
"authorization" => %{
|
||||
"name" => user.nickname,
|
||||
"password" => "wrong",
|
||||
"client_id" => app.client_id,
|
||||
"redirect_uri" => app.redirect_uris,
|
||||
"state" => "statepassed"
|
||||
}
|
||||
})
|
||||
|> html_response(:unauthorized)
|
||||
|
||||
# Keep the details
|
||||
assert result =~ app.client_id
|
||||
assert result =~ app.redirect_uris
|
||||
|
||||
# Error message
|
||||
assert result =~ "Invalid"
|
||||
end
|
||||
|
||||
test "issues a token for an all-body request" do
|
||||
user = insert(:user)
|
||||
app = insert(:oauth_app)
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
defmodule Pleroma.Web.RichMedia.RichMediaControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
|
||||
setup do
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/ogp"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
|
||||
|
||||
%{method: :get, url: "http://example.com/empty"} ->
|
||||
%Tesla.Env{status: 200, body: "hello"}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "GET /api/rich_media/parse" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
||||
[user: user]
|
||||
end
|
||||
|
||||
test "returns 404 if not metadata found", %{user: user} do
|
||||
build_conn()
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/rich_media/parse", %{"url" => "http://example.com/empty"})
|
||||
|> json_response(404)
|
||||
end
|
||||
|
||||
test "returns OGP metadata", %{user: user} do
|
||||
response =
|
||||
build_conn()
|
||||
|> with_credentials(user.nickname, "test")
|
||||
|> get("/api/rich_media/parse", %{"url" => "http://example.com/ogp"})
|
||||
|> json_response(200)
|
||||
|
||||
assert response == %{
|
||||
"image" => "http://ia.media-imdb.com/images/rock.jpg",
|
||||
"title" => "The Rock",
|
||||
"type" => "video.movie",
|
||||
"url" => "http://www.imdb.com/title/tt0117500/"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
defp with_credentials(conn, username, password) do
|
||||
header_content = "Basic " <> Base.encode64("#{username}:#{password}")
|
||||
put_req_header(conn, "authorization", header_content)
|
||||
end
|
||||
end
|
|
@ -65,28 +65,27 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
|
|||
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") ==
|
||||
{:ok,
|
||||
%{
|
||||
"author_name" => "bees",
|
||||
"author_url" => "https://www.flickr.com/photos/bees/",
|
||||
"cache_age" => 3600,
|
||||
"flickr_type" => "photo",
|
||||
"height" => "768",
|
||||
"html" =>
|
||||
author_name: "bees",
|
||||
author_url: "https://www.flickr.com/photos/bees/",
|
||||
cache_age: 3600,
|
||||
flickr_type: "photo",
|
||||
height: "768",
|
||||
html:
|
||||
"<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
|
||||
"license" => "All Rights Reserved",
|
||||
"license_id" => 0,
|
||||
"provider_name" => "Flickr",
|
||||
"provider_url" => "https://www.flickr.com/",
|
||||
"thumbnail_height" => 150,
|
||||
"thumbnail_url" =>
|
||||
"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
|
||||
"thumbnail_width" => 150,
|
||||
"title" => "Bacon Lollys",
|
||||
"type" => "photo",
|
||||
"url" => "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
|
||||
"version" => "1.0",
|
||||
"web_page" => "https://www.flickr.com/photos/bees/2362225867/",
|
||||
"web_page_short_url" => "https://flic.kr/p/4AK2sc",
|
||||
"width" => "1024"
|
||||
license: "All Rights Reserved",
|
||||
license_id: 0,
|
||||
provider_name: "Flickr",
|
||||
provider_url: "https://www.flickr.com/",
|
||||
thumbnail_height: 150,
|
||||
thumbnail_url: "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
|
||||
thumbnail_width: 150,
|
||||
title: "Bacon Lollys",
|
||||
type: "photo",
|
||||
url: "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
|
||||
version: "1.0",
|
||||
web_page: "https://www.flickr.com/photos/bees/2362225867/",
|
||||
web_page_short_url: "https://flic.kr/p/4AK2sc",
|
||||
width: "1024"
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -164,6 +164,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
|
|||
"possibly_sensitive" => true,
|
||||
"uri" => activity.data["object"]["id"],
|
||||
"visibility" => "direct",
|
||||
"card" => nil,
|
||||
"summary" => "2hu :2hu:",
|
||||
"summary_html" =>
|
||||
"2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />"
|
||||
|
|
|
@ -32,4 +32,71 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
|||
assert response == "job started"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/statusnet/config.json" do
|
||||
test "it returns the managed config", %{conn: conn} do
|
||||
Pleroma.Config.put([:instance, :managed_config], false)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/statusnet/config.json")
|
||||
|> json_response(:ok)
|
||||
|
||||
refute response["site"]["pleromafe"]
|
||||
|
||||
Pleroma.Config.put([:instance, :managed_config], true)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/statusnet/config.json")
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response["site"]["pleromafe"]
|
||||
end
|
||||
|
||||
test "if :pleroma, :fe is false, it returns the new style config settings", %{conn: conn} do
|
||||
Pleroma.Config.put([:instance, :managed_config], true)
|
||||
Pleroma.Config.put([:fe, :theme], "rei-ayanami-towel")
|
||||
Pleroma.Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/statusnet/config.json")
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response["site"]["pleromafe"]["theme"] == "rei-ayanami-towel"
|
||||
|
||||
Pleroma.Config.put([:fe], false)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/statusnet/config.json")
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response["site"]["pleromafe"]["theme"] == "asuka-hospital"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/frontend_configurations" do
|
||||
test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do
|
||||
config = [
|
||||
frontend_a: %{
|
||||
x: 1,
|
||||
y: 2
|
||||
},
|
||||
frontend_b: %{
|
||||
z: 3
|
||||
}
|
||||
]
|
||||
|
||||
Pleroma.Config.put(:frontend_configurations, config)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/frontend_configurations")
|
||||
|> json_response(:ok)
|
||||
|
||||
assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -148,7 +148,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
|
|||
"text" => "Hey @shp!",
|
||||
"uri" => activity.data["object"]["id"],
|
||||
"user" => UserView.render("show.json", %{user: user}),
|
||||
"visibility" => "direct"
|
||||
"visibility" => "direct",
|
||||
"card" => nil
|
||||
}
|
||||
|
||||
assert result == expected
|
||||
|
|
Loading…
Reference in New Issue