forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
b134bd83ca
519 changed files with 1666 additions and 64810 deletions
91
CHANGELOG.md
Normal file
91
CHANGELOG.md
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
### Added
|
||||||
|
- LDAP authentication
|
||||||
|
- External OAuth provider authentication
|
||||||
|
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
|
||||||
|
- [Prometheus](https://prometheus.io/) metrics
|
||||||
|
- Support for Mastodon's remote interaction
|
||||||
|
- Federation: Support for reports
|
||||||
|
- Configuration: `safe_dm_mentions` option
|
||||||
|
- Configuration: `link_name` option
|
||||||
|
- Configuration: `fetch_initial_posts` option
|
||||||
|
- Pleroma API: User subscribtions
|
||||||
|
- Admin API: Endpoints for listing/revoking invite tokens
|
||||||
|
- Admin API: Endpoints for making users follow/unfollow each other
|
||||||
|
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||||
|
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
||||||
|
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
||||||
|
- ActivityPub C2S: OAuth endpoints
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
||||||
|
- Enforcement of OAuth scopes
|
||||||
|
- Add multiple use/time expiring invite token
|
||||||
|
- Restyled OAuth pages to fit with Pleroma's default theme
|
||||||
|
- Link/mention/hashtag detection is now handled by [auto_linker](https://git.pleroma.social/pleroma/auto_linker)
|
||||||
|
- NodeInfo: Return `safe_dm_mentions` feature flag
|
||||||
|
- Federation: Expand the audience of delete activities to all recipients of the deleted object
|
||||||
|
- Configuration: Dedupe enabled by default
|
||||||
|
- Pleroma API: Support for emoji tags in `/api/pleroma/emoji` resulting in a breaking API change
|
||||||
|
- Mastodon API: Support for `exclude_types`, `limit` and `min_id` in `/api/v1/notifications`
|
||||||
|
- Mastodon API: Add `languages` and `registrations` to `/api/v1/instance`
|
||||||
|
- Mastodon API: Provide plaintext versions of cw/content in the Status entity
|
||||||
|
- Mastodon API: Add `pleroma.conversation_id` field to the Status entity
|
||||||
|
- Mastodon API: Add `pleroma.tags`, `pleroma.relationship{}`, `pleroma.is_moderator`, `pleroma.is_admin`, `pleroma.confirmation_pending` fields to the User entity
|
||||||
|
- Mastodon API: Add `pleroma.is_seen` to the Notification entity
|
||||||
|
- Mastodon API: Add `pleroma.local` to the Status entity
|
||||||
|
- Mastodon API: Add `preview` parameter to `POST /api/v1/statuses`
|
||||||
|
- Mastodon API: Add `with_muted` parameter to timeline endpoints
|
||||||
|
- Mastodon API: Actual reblog hiding instead of a dummy
|
||||||
|
- Mastodon API: Remove attachment limit in the Status entity
|
||||||
|
- Deps: Updated Cowboy to 2.6
|
||||||
|
- Deps: Updated Ecto to 3.0.7
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Followers counter not being updated when a follower is blocked
|
||||||
|
- Deactivated users being able to request an access token
|
||||||
|
- Limit on request body in rich media/relme parsers being ignored resulting in a possible memory leak
|
||||||
|
- proper Twitter Card generation instead of a dummy
|
||||||
|
- NodeInfo: Include admins in `staffAccounts`
|
||||||
|
- ActivityPub: Crashing when requesting empty local user's outbox
|
||||||
|
- Federation: Handling of objects without `summary` property
|
||||||
|
- Federation: Add a language tag to activities as required by ActivityStreams 2.0
|
||||||
|
- Federation: Do not federate avatar/banner if set to default allowing other servers/clients to use their defaults
|
||||||
|
- Federation: Cope with missing or explicitly nulled address lists
|
||||||
|
- Federation: Explicitly ensure activities addressed to `as:Public` become addressed to the followers collection
|
||||||
|
- Federation: Better cope with actors which do not declare a followers collection and use `as:Public` with these semantics
|
||||||
|
- MediaProxy: Parse name from content disposition headers even for non-whitelisted types
|
||||||
|
- MediaProxy: S3 link encoding
|
||||||
|
- Rich Media: Reject any data which cannot be explicitly encoded into JSON
|
||||||
|
- Mastodon API: `/api/v1/favourites` serving only public activities
|
||||||
|
- Mastodon API: Reblogs having `in_reply_to_id` - `null` even when they are replies
|
||||||
|
- Mastodon API: Streaming API broadcasting wrong activity id
|
||||||
|
- Mastodon API: 500 errors when requesting a card for a private conversation
|
||||||
|
|
||||||
|
## [0.9.9999] - 2019-04-05
|
||||||
|
### Security
|
||||||
|
- Mastodon API: Fix content warnings skipping HTML sanitization
|
||||||
|
|
||||||
|
## [0.9.999] - 2019-03-13
|
||||||
|
Frontend changes only.
|
||||||
|
### Added
|
||||||
|
- Added floating action button for posting status on mobile
|
||||||
|
### Changed
|
||||||
|
- Changed user-settings icon to a pencil
|
||||||
|
### Fixed
|
||||||
|
- Keyboard shortcuts activating when typing a message
|
||||||
|
- Gaps when scrolling down on a timeline after showing new
|
||||||
|
|
||||||
|
## [0.9.99] - 2019-03-08
|
||||||
|
### Changed
|
||||||
|
- Update the frontend to the 0.9.99 tag
|
||||||
|
### Fixed
|
||||||
|
- Sign the date header in federation to fix Mastodon federation.
|
||||||
|
|
||||||
|
## [0.9.9] - 2019-02-22
|
||||||
|
This is our first stable release.
|
|
@ -413,7 +413,7 @@
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
||||||
|
|
||||||
config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Sendmail
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail
|
||||||
|
|
||||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
watchers: [],
|
watchers: [],
|
||||||
secure_cookie_flag: false
|
secure_cookie_flag: false
|
||||||
|
|
||||||
config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local
|
||||||
|
|
||||||
# ## SSL Support
|
# ## SSL Support
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||||
|
|
||||||
config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Test
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test
|
||||||
|
|
||||||
# Configure your database
|
# Configure your database
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
|
|
|
@ -31,14 +31,14 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
|
||||||
|
|
||||||
* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used.
|
* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used.
|
||||||
|
|
||||||
## Pleroma.Mailer
|
## Pleroma.Emails.Mailer
|
||||||
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
|
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
|
||||||
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
|
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
|
||||||
|
|
||||||
An example for Sendgrid adapter:
|
An example for Sendgrid adapter:
|
||||||
|
|
||||||
```exs
|
```exs
|
||||||
config :pleroma, Pleroma.Mailer,
|
config :pleroma, Pleroma.Emails.Mailer,
|
||||||
adapter: Swoosh.Adapters.Sendgrid,
|
adapter: Swoosh.Adapters.Sendgrid,
|
||||||
api_key: "YOUR_API_KEY"
|
api_key: "YOUR_API_KEY"
|
||||||
```
|
```
|
||||||
|
@ -46,7 +46,7 @@ config :pleroma, Pleroma.Mailer,
|
||||||
An example for SMTP adapter:
|
An example for SMTP adapter:
|
||||||
|
|
||||||
```exs
|
```exs
|
||||||
config :pleroma, Pleroma.Mailer,
|
config :pleroma, Pleroma.Emails.Mailer,
|
||||||
adapter: Swoosh.Adapters.SMTP,
|
adapter: Swoosh.Adapters.SMTP,
|
||||||
relay: "smtp.gmail.com",
|
relay: "smtp.gmail.com",
|
||||||
username: "YOUR_USERNAME@gmail.com",
|
username: "YOUR_USERNAME@gmail.com",
|
||||||
|
@ -317,7 +317,7 @@ Pleroma has the following queues:
|
||||||
|
|
||||||
* `federator_outgoing` - Outgoing federation
|
* `federator_outgoing` - Outgoing federation
|
||||||
* `federator_incoming` - Incoming federation
|
* `federator_incoming` - Incoming federation
|
||||||
* `mailer` - Email sender, see [`Pleroma.Mailer`](#pleroma-mailer)
|
* `mailer` - Email sender, see [`Pleroma.Emails.Mailer`](#pleroma-emails-mailer)
|
||||||
* `transmogrifier` - Transmogrifier
|
* `transmogrifier` - Transmogrifier
|
||||||
* `web_push` - Web push notifications
|
* `web_push` - Web push notifications
|
||||||
* `scheduled_activities` - Scheduled activities, see [`Pleroma.ScheduledActivities`](#pleromascheduledactivity)
|
* `scheduled_activities` - Scheduled activities, see [`Pleroma.ScheduledActivities`](#pleromascheduledactivity)
|
||||||
|
|
|
@ -7,7 +7,6 @@ This guide will assume that you have administrative rights, either as root or a
|
||||||
|
|
||||||
* `postgresql`
|
* `postgresql`
|
||||||
* `elixir`
|
* `elixir`
|
||||||
* `erlang-unixodbc`
|
|
||||||
* `git`
|
* `git`
|
||||||
* `base-devel`
|
* `base-devel`
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ sudo pacman -Syu
|
||||||
* Install some of the above mentioned programs:
|
* Install some of the above mentioned programs:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo pacman -S git base-devel elixir erlang-unixodbc
|
sudo pacman -S git base-devel elixir
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install PostgreSQL
|
### Install PostgreSQL
|
||||||
|
|
|
@ -37,7 +37,7 @@ server {
|
||||||
listen [::]:443 ssl http2;
|
listen [::]:443 ssl http2;
|
||||||
ssl_session_timeout 5m;
|
ssl_session_timeout 5m;
|
||||||
|
|
||||||
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
|
||||||
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# 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
|
||||||
|
|
||||||
defmodule Pleroma.AdminEmail do
|
defmodule Pleroma.Emails.AdminEmail do
|
||||||
@moduledoc "Admin emails"
|
@moduledoc "Admin emails"
|
||||||
|
|
||||||
import Swoosh.Email
|
import Swoosh.Email
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# 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
|
||||||
|
|
||||||
defmodule Pleroma.Mailer do
|
defmodule Pleroma.Emails.Mailer do
|
||||||
use Swoosh.Mailer, otp_app: :pleroma
|
use Swoosh.Mailer, otp_app: :pleroma
|
||||||
|
|
||||||
def deliver_async(email, config \\ []) do
|
def deliver_async(email, config \\ []) do
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# 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
|
||||||
|
|
||||||
defmodule Pleroma.UserEmail do
|
defmodule Pleroma.Emails.UserEmail do
|
||||||
@moduledoc "User emails"
|
@moduledoc "User emails"
|
||||||
|
|
||||||
import Swoosh.Email
|
import Swoosh.Email
|
||||||
|
|
|
@ -9,20 +9,31 @@ defmodule Pleroma.Formatter do
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
||||||
|
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
||||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
@link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
|
|
||||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
|
||||||
|
|
||||||
@auto_linker_config hashtag: true,
|
@auto_linker_config hashtag: true,
|
||||||
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
|
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
|
||||||
mention: true,
|
mention: true,
|
||||||
mention_handler: &Pleroma.Formatter.mention_handler/4
|
mention_handler: &Pleroma.Formatter.mention_handler/4
|
||||||
|
|
||||||
|
def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
|
||||||
|
case User.get_cached_by_nickname(nickname) do
|
||||||
|
%User{} ->
|
||||||
|
# escape markdown characters with `\\`
|
||||||
|
# (we don't want something like @user__name to be parsed by markdown)
|
||||||
|
String.replace(mention, @markdown_characters_regex, "\\\\\\1")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
buffer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||||
case User.get_cached_by_nickname(nickname) do
|
case User.get_cached_by_nickname(nickname) do
|
||||||
%User{id: id} = user ->
|
%User{id: id} = user ->
|
||||||
ap_id = get_ap_id(user)
|
ap_id = get_ap_id(user)
|
||||||
nickname_text = get_nickname_text(nickname, opts) |> maybe_escape(opts)
|
nickname_text = get_nickname_text(nickname, opts)
|
||||||
|
|
||||||
link =
|
link =
|
||||||
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
|
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
|
||||||
|
@ -70,6 +81,25 @@ def linkify(text, options \\ []) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Escapes a special characters in mention names.
|
||||||
|
"""
|
||||||
|
def mentions_escape(text, options \\ []) do
|
||||||
|
options =
|
||||||
|
Keyword.merge(options,
|
||||||
|
mention: true,
|
||||||
|
url: false,
|
||||||
|
mention_handler: &Pleroma.Formatter.escape_mention_handler/4
|
||||||
|
)
|
||||||
|
|
||||||
|
if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
|
||||||
|
%{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
|
||||||
|
AutoLinker.link(mentions, options) <> AutoLinker.link(rest, options)
|
||||||
|
else
|
||||||
|
AutoLinker.link(text, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def emojify(text) do
|
def emojify(text) do
|
||||||
emojify(text, Emoji.get_all())
|
emojify(text, Emoji.get_all())
|
||||||
end
|
end
|
||||||
|
@ -140,10 +170,4 @@ defp get_ap_id(%User{ap_id: ap_id}), do: ap_id
|
||||||
|
|
||||||
defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)
|
defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)
|
||||||
defp get_nickname_text(nickname, _), do: User.local_nickname(nickname)
|
defp get_nickname_text(nickname, _), do: User.local_nickname(nickname)
|
||||||
|
|
||||||
defp maybe_escape(str, %{mentions_escape: true}) do
|
|
||||||
String.replace(str, @markdown_characters_regex, "\\\\\\1")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp maybe_escape(str, _), do: str
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -98,6 +98,14 @@ def clear(user) do
|
||||||
|> Repo.delete_all()
|
|> Repo.delete_all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy_multiple(%{id: user_id} = _user, ids) do
|
||||||
|
from(n in Notification,
|
||||||
|
where: n.id in ^ids,
|
||||||
|
where: n.user_id == ^user_id
|
||||||
|
)
|
||||||
|
|> Repo.delete_all()
|
||||||
|
end
|
||||||
|
|
||||||
def dismiss(%{id: user_id} = _user, id) do
|
def dismiss(%{id: user_id} = _user, id) do
|
||||||
notification = Repo.get(Notification, id)
|
notification = Repo.get(Notification, id)
|
||||||
|
|
||||||
|
@ -173,8 +181,7 @@ def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" =
|
||||||
def skip?(:muted, activity, user) do
|
def skip?(:muted, activity, user) do
|
||||||
actor = activity.data["actor"]
|
actor = activity.data["actor"]
|
||||||
|
|
||||||
User.mutes?(user, %{ap_id: actor}) or
|
User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity)
|
||||||
CommonAPI.thread_muted?(user, activity)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip?(
|
def skip?(
|
||||||
|
|
|
@ -36,6 +36,12 @@ defp cast_params(params) do
|
||||||
limit: :integer
|
limit: :integer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params =
|
||||||
|
Enum.reduce(params, %{}, fn
|
||||||
|
{key, _value}, acc when is_atom(key) -> Map.drop(acc, [key])
|
||||||
|
{key, value}, acc -> Map.put(acc, key, value)
|
||||||
|
end)
|
||||||
|
|
||||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
changeset.changes
|
changeset.changes
|
||||||
end
|
end
|
||||||
|
|
|
@ -279,8 +279,10 @@ def try_send_confirmation_email(%User{} = user) do
|
||||||
if user.info.confirmation_pending &&
|
if user.info.confirmation_pending &&
|
||||||
Pleroma.Config.get([:instance, :account_activation_required]) do
|
Pleroma.Config.get([:instance, :account_activation_required]) do
|
||||||
user
|
user
|
||||||
|> Pleroma.UserEmail.account_confirmation_email()
|
|> Pleroma.Emails.UserEmail.account_confirmation_email()
|
||||||
|> Pleroma.Mailer.deliver_async()
|
|> Pleroma.Emails.Mailer.deliver_async()
|
||||||
|
|
||||||
|
{:ok, :enqueued}
|
||||||
else
|
else
|
||||||
{:ok, :noop}
|
{:ok, :noop}
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Upload
|
alias Pleroma.Upload
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -449,8 +450,8 @@ def flag(
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
Enum.each(User.all_superusers(), fn superuser ->
|
Enum.each(User.all_superusers(), fn superuser ->
|
||||||
superuser
|
superuser
|
||||||
|> Pleroma.AdminEmail.report(actor, account, statuses, content)
|
|> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
|
||||||
|> Pleroma.Mailer.deliver_async()
|
|> Pleroma.Emails.Mailer.deliver_async()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -493,7 +494,7 @@ def fetch_public_activities(opts \\ %{}) do
|
||||||
|
|
||||||
q
|
q
|
||||||
|> restrict_unlisted()
|
|> restrict_unlisted()
|
||||||
|> Repo.all()
|
|> Pagination.fetch_paginated(opts)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -636,26 +637,12 @@ defp restrict_recipients(query, recipients, user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_limit(query, %{"limit" => limit}) do
|
|
||||||
from(activity in query, limit: ^limit)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp restrict_limit(query, _), do: query
|
|
||||||
|
|
||||||
defp restrict_local(query, %{"local_only" => true}) do
|
defp restrict_local(query, %{"local_only" => true}) do
|
||||||
from(activity in query, where: activity.local == true)
|
from(activity in query, where: activity.local == true)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_local(query, _), do: query
|
defp restrict_local(query, _), do: query
|
||||||
|
|
||||||
defp restrict_max(query, %{"max_id" => ""}), do: query
|
|
||||||
|
|
||||||
defp restrict_max(query, %{"max_id" => max_id}) do
|
|
||||||
from(activity in query, where: activity.id < ^max_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp restrict_max(query, _), do: query
|
|
||||||
|
|
||||||
defp restrict_actor(query, %{"actor_id" => actor_id}) do
|
defp restrict_actor(query, %{"actor_id" => actor_id}) do
|
||||||
from(activity in query, where: activity.actor == ^actor_id)
|
from(activity in query, where: activity.actor == ^actor_id)
|
||||||
end
|
end
|
||||||
|
@ -776,12 +763,7 @@ defp maybe_preload_objects(query, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query = from(activity in Activity)
|
||||||
from(
|
|
||||||
activity in Activity,
|
|
||||||
limit: 20,
|
|
||||||
order_by: [fragment("? desc nulls last", activity.id)]
|
|
||||||
)
|
|
||||||
|
|
||||||
base_query
|
base_query
|
||||||
|> maybe_preload_objects(opts)
|
|> maybe_preload_objects(opts)
|
||||||
|
@ -791,8 +773,6 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_tag_all(opts)
|
|> restrict_tag_all(opts)
|
||||||
|> restrict_since(opts)
|
|> restrict_since(opts)
|
||||||
|> restrict_local(opts)
|
|> restrict_local(opts)
|
||||||
|> restrict_limit(opts)
|
|
||||||
|> restrict_max(opts)
|
|
||||||
|> restrict_actor(opts)
|
|> restrict_actor(opts)
|
||||||
|> restrict_type(opts)
|
|> restrict_type(opts)
|
||||||
|> restrict_favorited_by(opts)
|
|> restrict_favorited_by(opts)
|
||||||
|
@ -808,14 +788,14 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}) do
|
def fetch_activities(recipients, opts \\ %{}) do
|
||||||
fetch_activities_query(recipients, opts)
|
fetch_activities_query(recipients, opts)
|
||||||
|> Repo.all()
|
|> Pagination.fetch_paginated(opts)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
|
def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
|
||||||
fetch_activities_query([], opts)
|
fetch_activities_query([], opts)
|
||||||
|> restrict_to_cc(recipients_to, recipients_cc)
|
|> restrict_to_cc(recipients_to, recipients_cc)
|
||||||
|> Repo.all()
|
|> Pagination.fetch_paginated(opts)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -238,8 +238,13 @@ def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params)
|
||||||
!Pleroma.Config.get([:instance, :registrations_open]),
|
!Pleroma.Config.get([:instance, :registrations_open]),
|
||||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||||
email <-
|
email <-
|
||||||
Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),
|
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
{:ok, _} <- Pleroma.Mailer.deliver(email) do
|
user,
|
||||||
|
invite_token,
|
||||||
|
email,
|
||||||
|
params["name"]
|
||||||
|
),
|
||||||
|
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -195,11 +195,10 @@ def format_input(text, "text/html", options) do
|
||||||
Formatting text to markdown.
|
Formatting text to markdown.
|
||||||
"""
|
"""
|
||||||
def format_input(text, "text/markdown", options) do
|
def format_input(text, "text/markdown", options) do
|
||||||
options = Keyword.put(options, :mentions_escape, true)
|
|
||||||
|
|
||||||
text
|
text
|
||||||
|
|> Formatter.mentions_escape(options)
|
||||||
|
|> Earmark.as_html!()
|
||||||
|> Formatter.linkify(options)
|
|> Formatter.linkify(options)
|
||||||
|> (fn {text, mentions, tags} -> {Earmark.as_html!(text), mentions, tags} end).()
|
|
||||||
|> Formatter.html_escape("text/html")
|
|> Formatter.html_escape("text/html")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ScheduledActivity
|
alias Pleroma.ScheduledActivity
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
|
@ -202,15 +203,29 @@ def custom_emojis(conn, _params) do
|
||||||
defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
||||||
params =
|
params =
|
||||||
conn.params
|
conn.params
|
||||||
|> Map.drop(["since_id", "max_id"])
|
|> Map.drop(["since_id", "max_id", "min_id"])
|
||||||
|> Map.merge(params)
|
|> Map.merge(params)
|
||||||
|
|
||||||
last = List.last(activities)
|
last = List.last(activities)
|
||||||
first = List.first(activities)
|
|
||||||
|
|
||||||
if last do
|
if last do
|
||||||
min = last.id
|
max_id = last.id
|
||||||
max = first.id
|
|
||||||
|
limit =
|
||||||
|
params
|
||||||
|
|> Map.get("limit", "20")
|
||||||
|
|> String.to_integer()
|
||||||
|
|
||||||
|
min_id =
|
||||||
|
if length(activities) <= limit do
|
||||||
|
activities
|
||||||
|
|> List.first()
|
||||||
|
|> Map.get(:id)
|
||||||
|
else
|
||||||
|
activities
|
||||||
|
|> Enum.at(limit * -1)
|
||||||
|
|> Map.get(:id)
|
||||||
|
end
|
||||||
|
|
||||||
{next_url, prev_url} =
|
{next_url, prev_url} =
|
||||||
if param do
|
if param do
|
||||||
|
@ -219,13 +234,13 @@ defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
||||||
Pleroma.Web.Endpoint,
|
Pleroma.Web.Endpoint,
|
||||||
method,
|
method,
|
||||||
param,
|
param,
|
||||||
Map.merge(params, %{max_id: min})
|
Map.merge(params, %{max_id: max_id})
|
||||||
),
|
),
|
||||||
mastodon_api_url(
|
mastodon_api_url(
|
||||||
Pleroma.Web.Endpoint,
|
Pleroma.Web.Endpoint,
|
||||||
method,
|
method,
|
||||||
param,
|
param,
|
||||||
Map.merge(params, %{since_id: max})
|
Map.merge(params, %{min_id: min_id})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -233,12 +248,12 @@ defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
||||||
mastodon_api_url(
|
mastodon_api_url(
|
||||||
Pleroma.Web.Endpoint,
|
Pleroma.Web.Endpoint,
|
||||||
method,
|
method,
|
||||||
Map.merge(params, %{max_id: min})
|
Map.merge(params, %{max_id: max_id})
|
||||||
),
|
),
|
||||||
mastodon_api_url(
|
mastodon_api_url(
|
||||||
Pleroma.Web.Endpoint,
|
Pleroma.Web.Endpoint,
|
||||||
method,
|
method,
|
||||||
Map.merge(params, %{since_id: max})
|
Map.merge(params, %{min_id: min_id})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -314,7 +329,7 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
activities =
|
activities =
|
||||||
[user.ap_id]
|
[user.ap_id]
|
||||||
|> ActivityPub.fetch_activities_query(params)
|
|> ActivityPub.fetch_activities_query(params)
|
||||||
|> Repo.all()
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:dm_timeline, activities)
|
|> add_link_headers(:dm_timeline, activities)
|
||||||
|
@ -612,6 +627,11 @@ def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _para
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy_multiple(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do
|
||||||
|
Notification.destroy_multiple(user, ids)
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
|
||||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
id = List.wrap(id)
|
id = List.wrap(id)
|
||||||
q = from(u in User, where: u.id in ^id)
|
q = from(u in User, where: u.id in ^id)
|
||||||
|
|
|
@ -301,8 +301,10 @@ def render("attachment.json", %{attachment: attachment}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
||||||
_id = activity.data["object"]["inReplyTo"]
|
with nil <- replied_to_activities[activity.data["object"]["inReplyTo"]] do
|
||||||
replied_to_activities[activity.data["object"]["inReplyTo"]]
|
# If user didn't participate in the thread
|
||||||
|
Activity.get_in_reply_to_activity(activity)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_reply_to(%{data: %{"object" => object}}, _) do
|
def get_reply_to(%{data: %{"object" => object}}, _) do
|
||||||
|
|
|
@ -6,7 +6,8 @@ defmodule Pleroma.Web.RelMe do
|
||||||
@hackney_options [
|
@hackney_options [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
recv_timeout: 2_000,
|
recv_timeout: 2_000,
|
||||||
max_body: 2_000_000
|
max_body: 2_000_000,
|
||||||
|
with_body: true
|
||||||
]
|
]
|
||||||
|
|
||||||
if Mix.env() == :test do
|
if Mix.env() == :test do
|
||||||
|
|
|
@ -12,7 +12,8 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||||
@hackney_options [
|
@hackney_options [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
recv_timeout: 2_000,
|
recv_timeout: 2_000,
|
||||||
max_body: 2_000_000
|
max_body: 2_000_000,
|
||||||
|
with_body: true
|
||||||
]
|
]
|
||||||
|
|
||||||
def parse(nil), do: {:error, "No URL provided"}
|
def parse(nil), do: {:error, "No URL provided"}
|
||||||
|
|
|
@ -261,6 +261,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
||||||
get("/notifications", MastodonAPIController, :notifications)
|
get("/notifications", MastodonAPIController, :notifications)
|
||||||
get("/notifications/:id", MastodonAPIController, :get_notification)
|
get("/notifications/:id", MastodonAPIController, :get_notification)
|
||||||
|
delete("/notifications/destroy_multiple", MastodonAPIController, :destroy_multiple)
|
||||||
|
|
||||||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
||||||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
||||||
|
|
|
@ -179,6 +179,17 @@
|
||||||
flex-basis: 50%;
|
flex-basis: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.form-row > label {
|
||||||
|
text-align: left;
|
||||||
|
line-height: 47px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.form-row > input {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<h2>Password Reset for <%= @user.nickname %></h2>
|
<h2>Password Reset for <%= @user.nickname %></h2>
|
||||||
<%= form_for @conn, util_path(@conn, :password_reset), [as: "data"], fn f -> %>
|
<%= form_for @conn, util_path(@conn, :password_reset), [as: "data"], fn f -> %>
|
||||||
<%= label f, :password, "Password" %>
|
<div class="form-row">
|
||||||
<%= password_input f, :password %>
|
<%= label f, :password, "Password" %>
|
||||||
<br>
|
<%= password_input f, :password %>
|
||||||
|
</div>
|
||||||
<%= label f, :password_confirmation, "Confirmation" %>
|
<div class="form-row">
|
||||||
<%= password_input f, :password_confirmation %>
|
<%= label f, :password_confirmation, "Confirmation" %>
|
||||||
<br>
|
<%= password_input f, :password_confirmation %>
|
||||||
<%= hidden_input f, :token, value: @token.token %>
|
</div>
|
||||||
<%= submit "Reset" %>
|
<%= hidden_input f, :token, value: @token.token %>
|
||||||
|
<%= submit "Reset" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Mailer
|
alias Pleroma.Emails.Mailer
|
||||||
|
alias Pleroma.Emails.UserEmail
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserEmail
|
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
4
mix.exs
4
mix.exs
|
@ -22,7 +22,7 @@ def project do
|
||||||
homepage_url: "https://pleroma.social/",
|
homepage_url: "https://pleroma.social/",
|
||||||
docs: [
|
docs: [
|
||||||
logo: "priv/static/static/logo.png",
|
logo: "priv/static/static/logo.png",
|
||||||
extras: ["README.md" | Path.wildcard("docs/**/*.md")],
|
extras: ["README.md", "CHANGELOG.md"] ++ Path.wildcard("docs/**/*.md"),
|
||||||
groups_for_extras: [
|
groups_for_extras: [
|
||||||
"Installation manuals": Path.wildcard("docs/installation/*.md"),
|
"Installation manuals": Path.wildcard("docs/installation/*.md"),
|
||||||
Configuration: Path.wildcard("docs/config/*.md"),
|
Configuration: Path.wildcard("docs/config/*.md"),
|
||||||
|
@ -101,7 +101,7 @@ defp deps do
|
||||||
{:ueberauth, "~> 0.4"},
|
{:ueberauth, "~> 0.4"},
|
||||||
{:auto_linker,
|
{:auto_linker,
|
||||||
git: "https://git.pleroma.social/pleroma/auto_linker.git",
|
git: "https://git.pleroma.social/pleroma/auto_linker.git",
|
||||||
ref: "479dd343f4e563ff91215c8275f3b5c67e032850"},
|
ref: "90613b4bae875a3610c275b7056b61ffdd53210d"},
|
||||||
{:pleroma_job_queue, "~> 0.2.0"},
|
{:pleroma_job_queue, "~> 0.2.0"},
|
||||||
{:telemetry, "~> 0.3"},
|
{:telemetry, "~> 0.3"},
|
||||||
{:prometheus_ex, "~> 3.0"},
|
{:prometheus_ex, "~> 3.0"},
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -1,6 +1,6 @@
|
||||||
%{
|
%{
|
||||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm"},
|
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm"},
|
||||||
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "479dd343f4e563ff91215c8275f3b5c67e032850", [ref: "479dd343f4e563ff91215c8275f3b5c67e032850"]},
|
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "90613b4bae875a3610c275b7056b61ffdd53210d", [ref: "90613b4bae875a3610c275b7056b61ffdd53210d"]},
|
||||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||||
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 168 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue