forked from AkkomaGang/akkoma
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into issue/1880
This commit is contained in:
commit
cdc153db31
131 changed files with 677 additions and 626 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -16,11 +16,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
||||||
|
- **Breaking:** Pleroma API: The routes to update avatar, banner and background have been removed.
|
||||||
- **Breaking:** Image description length is limited now.
|
- **Breaking:** Image description length is limited now.
|
||||||
- **Breaking:** Emoji API: changed methods and renamed routes.
|
- **Breaking:** Emoji API: changed methods and renamed routes.
|
||||||
|
- MastodonAPI: Allow removal of avatar, banner and background.
|
||||||
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
|
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
|
||||||
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
|
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
|
||||||
- Mastodon API: On deletion, returns the original post text.
|
- Mastodon API: On deletion, returns the original post text.
|
||||||
|
- Mastodon API: Add `pleroma.unread_count` to the Marker entity.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -59,8 +62,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: Extended `/api/v1/instance`.
|
- Mastodon API: Extended `/api/v1/instance`.
|
||||||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
||||||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
||||||
- Mastodon API: Add support for filtering replies in public and home timelines
|
- Mastodon API: Add support for filtering replies in public and home timelines.
|
||||||
- Mastodon API: Support for `bot` field in `/api/v1/accounts/update_credentials`
|
- Mastodon API: Support for `bot` field in `/api/v1/accounts/update_credentials`.
|
||||||
|
- Mastodon API: Support irreversible property for filters.
|
||||||
- Admin API: endpoints for create/update/delete OAuth Apps.
|
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||||
- Admin API: endpoint for status view.
|
- Admin API: endpoint for status view.
|
||||||
- OTP: Add command to reload emoji packs
|
- OTP: Add command to reload emoji packs
|
||||||
|
@ -74,6 +78,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Resolving Peertube accounts with Webfinger
|
- Resolving Peertube accounts with Webfinger
|
||||||
- `blob:` urls not being allowed by connect-src CSP
|
- `blob:` urls not being allowed by connect-src CSP
|
||||||
- Mastodon API: fix `GET /api/v1/notifications` not returning the full result set
|
- Mastodon API: fix `GET /api/v1/notifications` not returning the full result set
|
||||||
|
- Rich Media Previews for Twitter links
|
||||||
|
|
||||||
## [Unreleased (patch)]
|
## [Unreleased (patch)]
|
||||||
|
|
||||||
|
@ -215,7 +220,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: `pleroma.thread_muted` to the Status entity
|
- Mastodon API: `pleroma.thread_muted` to the Status entity
|
||||||
- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
|
- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
|
||||||
- Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
|
- Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
|
||||||
- Mastodon API: Add `pleroma.unread_count` to the Marker entity
|
|
||||||
- Admin API: Render whole status in grouped reports
|
- Admin API: Render whole status in grouped reports
|
||||||
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
||||||
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
||||||
|
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
||||||
@visibility ~w(public private direct unlisted)
|
@visibility ~w(public private direct unlisted)
|
||||||
@types [
|
@types [
|
||||||
:simple,
|
:simple,
|
||||||
|
:simple_filtered,
|
||||||
:emoji,
|
:emoji,
|
||||||
:mentions,
|
:mentions,
|
||||||
:hell_thread,
|
:hell_thread,
|
||||||
|
@ -242,6 +243,15 @@ defp insert_activity(:simple, visibility, group, users, _opts) do
|
||||||
insert_local_activity(visibility, group, users, "Simple status")
|
insert_local_activity(visibility, group, users, "Simple status")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:simple_filtered, visibility, group, users, _opts)
|
||||||
|
when group in @remote_groups do
|
||||||
|
insert_remote_activity(visibility, group, users, "Remote status which must be filtered")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:simple_filtered, visibility, group, users, _opts) do
|
||||||
|
insert_local_activity(visibility, group, users, "Simple status which must be filtered")
|
||||||
|
end
|
||||||
|
|
||||||
defp insert_activity(:emoji, visibility, group, users, _opts)
|
defp insert_activity(:emoji, visibility, group, users, _opts)
|
||||||
when group in @remote_groups do
|
when group in @remote_groups do
|
||||||
insert_remote_activity(visibility, group, users, "Remote status with emoji :firefox:")
|
insert_remote_activity(visibility, group, users, "Remote status with emoji :firefox:")
|
||||||
|
|
|
@ -32,10 +32,22 @@ defp fetch_user(user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp create_filter(user) do
|
||||||
|
Pleroma.Filter.create(%Pleroma.Filter{
|
||||||
|
user_id: user.id,
|
||||||
|
phrase: "must be filtered",
|
||||||
|
hide: true
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_filter(filter), do: Repo.delete(filter)
|
||||||
|
|
||||||
defp fetch_timelines(user) do
|
defp fetch_timelines(user) do
|
||||||
fetch_home_timeline(user)
|
fetch_home_timeline(user)
|
||||||
|
fetch_home_timeline_with_filter(user)
|
||||||
fetch_direct_timeline(user)
|
fetch_direct_timeline(user)
|
||||||
fetch_public_timeline(user)
|
fetch_public_timeline(user)
|
||||||
|
fetch_public_timeline_with_filter(user)
|
||||||
fetch_public_timeline(user, :with_blocks)
|
fetch_public_timeline(user, :with_blocks)
|
||||||
fetch_public_timeline(user, :local)
|
fetch_public_timeline(user, :local)
|
||||||
fetch_public_timeline(user, :tag)
|
fetch_public_timeline(user, :tag)
|
||||||
|
@ -61,7 +73,7 @@ defp opts_for_home_timeline(user) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_home_timeline(user) do
|
defp fetch_home_timeline(user, title_end \\ "") do
|
||||||
opts = opts_for_home_timeline(user)
|
opts = opts_for_home_timeline(user)
|
||||||
|
|
||||||
recipients = [user.ap_id | User.following(user)]
|
recipients = [user.ap_id | User.following(user)]
|
||||||
|
@ -84,9 +96,11 @@ defp fetch_home_timeline(user) do
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
|
title = "home timeline " <> title_end
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"home timeline" => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
|
title => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
|
||||||
},
|
},
|
||||||
inputs: %{
|
inputs: %{
|
||||||
"1 page" => opts,
|
"1 page" => opts,
|
||||||
|
@ -108,6 +122,14 @@ defp fetch_home_timeline(user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_home_timeline_with_filter(user) do
|
||||||
|
{:ok, filter} = create_filter(user)
|
||||||
|
|
||||||
|
fetch_home_timeline(user, "with filters")
|
||||||
|
|
||||||
|
delete_filter(filter)
|
||||||
|
end
|
||||||
|
|
||||||
defp opts_for_direct_timeline(user) do
|
defp opts_for_direct_timeline(user) do
|
||||||
%{
|
%{
|
||||||
visibility: "direct",
|
visibility: "direct",
|
||||||
|
@ -210,6 +232,14 @@ defp fetch_public_timeline(user) do
|
||||||
fetch_public_timeline(opts, "public timeline")
|
fetch_public_timeline(opts, "public timeline")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_public_timeline_with_filter(user) do
|
||||||
|
{:ok, filter} = create_filter(user)
|
||||||
|
opts = opts_for_public_timeline(user)
|
||||||
|
|
||||||
|
fetch_public_timeline(opts, "public timeline with filters")
|
||||||
|
delete_filter(filter)
|
||||||
|
end
|
||||||
|
|
||||||
defp fetch_public_timeline(user, :local) do
|
defp fetch_public_timeline(user, :local) do
|
||||||
opts = opts_for_public_timeline(user, :local)
|
opts = opts_for_public_timeline(user, :local)
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
"dat",
|
"dat",
|
||||||
"dweb",
|
"dweb",
|
||||||
"gopher",
|
"gopher",
|
||||||
|
"hyper",
|
||||||
"ipfs",
|
"ipfs",
|
||||||
"ipns",
|
"ipns",
|
||||||
"irc",
|
"irc",
|
||||||
|
@ -437,8 +438,7 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.Preload,
|
config :pleroma, Pleroma.Web.Preload,
|
||||||
providers: [
|
providers: [
|
||||||
Pleroma.Web.Preload.Providers.Instance,
|
Pleroma.Web.Preload.Providers.Instance
|
||||||
Pleroma.Web.Preload.Providers.StatusNet
|
|
||||||
]
|
]
|
||||||
|
|
||||||
config :pleroma, :http_security,
|
config :pleroma, :http_security,
|
||||||
|
|
|
@ -498,6 +498,7 @@
|
||||||
"dat",
|
"dat",
|
||||||
"dweb",
|
"dweb",
|
||||||
"gopher",
|
"gopher",
|
||||||
|
"hyper",
|
||||||
"ipfs",
|
"ipfs",
|
||||||
"ipns",
|
"ipns",
|
||||||
"irc",
|
"irc",
|
||||||
|
@ -699,8 +700,9 @@
|
||||||
key: :public,
|
key: :public,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
description:
|
||||||
"Makes the client API in authentificated mode-only except for user-profiles." <>
|
"Makes the client API in authenticated mode-only except for user-profiles." <>
|
||||||
" Useful for disabling the Local Timeline and The Whole Known Network."
|
" Useful for disabling the Local Timeline and The Whole Known Network. " <>
|
||||||
|
" Note: when setting to `false`, please also check `:restrict_unauthenticated` setting."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :quarantined_instances,
|
key: :quarantined_instances,
|
||||||
|
|
|
@ -182,10 +182,12 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||||
- `pleroma_background_image` - sets the background image of the user.
|
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
|
||||||
- `discoverable` - if true, discovery of this account in search results and other services is allowed.
|
- `discoverable` - if true, discovery of this account in search results and other services is allowed.
|
||||||
- `actor_type` - the type of this account.
|
- `actor_type` - the type of this account.
|
||||||
|
|
||||||
|
All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
|
||||||
|
|
||||||
### Pleroma Settings Store
|
### Pleroma Settings Store
|
||||||
|
|
||||||
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
||||||
|
|
|
@ -37,7 +37,7 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
||||||
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||||
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
|
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
|
||||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
|
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. See also: `restrict_unauthenticated`.
|
||||||
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
||||||
* `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``.
|
* `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``.
|
||||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||||
|
@ -971,11 +971,11 @@ config :pleroma, :database_config_whitelist, [
|
||||||
|
|
||||||
### :restrict_unauthenticated
|
### :restrict_unauthenticated
|
||||||
|
|
||||||
Restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
Restrict access for unauthenticated users to timelines (public and federated), user profiles and statuses.
|
||||||
|
|
||||||
* `timelines`: public and federated timelines
|
* `timelines`: public and federated timelines
|
||||||
* `local`: public timeline
|
* `local`: public timeline
|
||||||
* `federated`
|
* `federated`: federated timeline (includes public timeline)
|
||||||
* `profiles`: user profiles
|
* `profiles`: user profiles
|
||||||
* `local`
|
* `local`
|
||||||
* `remote`
|
* `remote`
|
||||||
|
@ -983,6 +983,7 @@ Restrict access for unauthenticated users to timelines (public and federate), us
|
||||||
* `local`
|
* `local`
|
||||||
* `remote`
|
* `remote`
|
||||||
|
|
||||||
|
Note: setting `restrict_unauthenticated/timelines/local` to `true` has no practical sense if `restrict_unauthenticated/timelines/federated` is set to `false` (since local public activities will still be delivered to unauthenticated users as part of federated timeline).
|
||||||
|
|
||||||
## Pleroma.Web.ApiSpec.CastAndValidate
|
## Pleroma.Web.ApiSpec.CastAndValidate
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,18 @@ def get(id, %{id: user_id} = _user) do
|
||||||
Repo.one(query)
|
Repo.one(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_filters(%User{id: user_id} = _user) do
|
def get_active(query) do
|
||||||
|
from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now())
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_irreversible(query) do
|
||||||
|
from(f in query, where: f.hide)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_filters(query \\ __MODULE__, %User{id: user_id}) do
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
f in Pleroma.Filter,
|
f in query,
|
||||||
where: f.user_id == ^user_id,
|
where: f.user_id == ^user_id,
|
||||||
order_by: [desc: :id]
|
order_by: [desc: :id]
|
||||||
)
|
)
|
||||||
|
@ -95,4 +103,34 @@ def update(%Pleroma.Filter{} = filter, params) do
|
||||||
|> validate_required([:phrase, :context])
|
|> validate_required([:phrase, :context])
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compose_regex(user_or_filters, format \\ :postgres)
|
||||||
|
|
||||||
|
def compose_regex(%User{} = user, format) do
|
||||||
|
__MODULE__
|
||||||
|
|> get_active()
|
||||||
|
|> get_irreversible()
|
||||||
|
|> get_filters(user)
|
||||||
|
|> compose_regex(format)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compose_regex([_ | _] = filters, format) do
|
||||||
|
phrases =
|
||||||
|
filters
|
||||||
|
|> Enum.map(& &1.phrase)
|
||||||
|
|> Enum.join("|")
|
||||||
|
|
||||||
|
case format do
|
||||||
|
:postgres ->
|
||||||
|
"\\y(#{phrases})\\y"
|
||||||
|
|
||||||
|
:re ->
|
||||||
|
~r/\b#{phrases}\b/i
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def compose_regex(_, _), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -130,6 +130,7 @@ def for_user_query(user, opts \\ %{}) do
|
||||||
|> preload([n, a, o], activity: {a, object: o})
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
|> exclude_notification_muted(user, exclude_notification_muted_opts)
|
|> exclude_notification_muted(user, exclude_notification_muted_opts)
|
||||||
|> exclude_blocked(user, exclude_blocked_opts)
|
|> exclude_blocked(user, exclude_blocked_opts)
|
||||||
|
|> exclude_filtered(user)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -158,6 +159,20 @@ defp exclude_notification_muted(query, user, opts) do
|
||||||
|> where([n, a, o, tm], is_nil(tm.user_id))
|
|> where([n, a, o, tm], is_nil(tm.user_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp exclude_filtered(query, user) do
|
||||||
|
case Pleroma.Filter.compose_regex(user) do
|
||||||
|
nil ->
|
||||||
|
query
|
||||||
|
|
||||||
|
regex ->
|
||||||
|
from([_n, a, o] in query,
|
||||||
|
where:
|
||||||
|
fragment("not(?->>'content' ~* ?)", o.data, ^regex) or
|
||||||
|
fragment("?->>'actor' = ?", o.data, ^user.ap_id)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@valid_visibilities ~w[direct unlisted public private]
|
@valid_visibilities ~w[direct unlisted public private]
|
||||||
|
|
||||||
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
|
@ -337,6 +352,7 @@ def dismiss(%{id: user_id} = _user, id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec create_notifications(Activity.t(), keyword()) :: {:ok, [Notification.t()] | []}
|
||||||
def create_notifications(activity, options \\ [])
|
def create_notifications(activity, options \\ [])
|
||||||
|
|
||||||
def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity, options) do
|
def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity, options) do
|
||||||
|
@ -555,7 +571,8 @@ def skip?(%Activity{} = activity, %User{} = user) do
|
||||||
:follows,
|
:follows,
|
||||||
:non_followers,
|
:non_followers,
|
||||||
:non_follows,
|
:non_follows,
|
||||||
:recently_followed
|
:recently_followed,
|
||||||
|
:filtered
|
||||||
]
|
]
|
||||||
|> Enum.find(&skip?(&1, activity, user))
|
|> Enum.find(&skip?(&1, activity, user))
|
||||||
end
|
end
|
||||||
|
@ -624,6 +641,26 @@ def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity,
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false
|
||||||
|
|
||||||
|
def skip?(:filtered, activity, user) do
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
is_nil(object) ->
|
||||||
|
false
|
||||||
|
|
||||||
|
object.data["actor"] == user.ap_id ->
|
||||||
|
false
|
||||||
|
|
||||||
|
not is_nil(regex = Pleroma.Filter.compose_regex(user, :re)) ->
|
||||||
|
Regex.match?(regex, object.data["content"])
|
||||||
|
|
||||||
|
true ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def skip?(_, _, _), do: false
|
def skip?(_, _, _), do: false
|
||||||
|
|
||||||
def for_user_and_activity(user, activity) do
|
def for_user_and_activity(user, activity) do
|
||||||
|
|
|
@ -89,7 +89,7 @@ defmodule Pleroma.User do
|
||||||
field(:keys, :string)
|
field(:keys, :string)
|
||||||
field(:public_key, :string)
|
field(:public_key, :string)
|
||||||
field(:ap_id, :string)
|
field(:ap_id, :string)
|
||||||
field(:avatar, :map)
|
field(:avatar, :map, default: %{})
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
field(:follower_address, :string)
|
field(:follower_address, :string)
|
||||||
field(:following_address, :string)
|
field(:following_address, :string)
|
||||||
|
@ -539,14 +539,11 @@ defp put_emoji(changeset) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_change_if_present(changeset, map_field, value_function) do
|
defp put_change_if_present(changeset, map_field, value_function) do
|
||||||
if value = get_change(changeset, map_field) do
|
with {:ok, value} <- fetch_change(changeset, map_field),
|
||||||
with {:ok, new_value} <- value_function.(value) do
|
{:ok, new_value} <- value_function.(value) do
|
||||||
put_change(changeset, map_field, new_value)
|
put_change(changeset, map_field, new_value)
|
||||||
else
|
|
||||||
_ -> changeset
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
changeset
|
_ -> changeset
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Constants
|
alias Pleroma.Constants
|
||||||
alias Pleroma.Conversation
|
alias Pleroma.Conversation
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Maps
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
@ -446,6 +447,7 @@ def fetch_activities_for_context_query(context, opts) do
|
||||||
|> maybe_set_thread_muted_field(opts)
|
|> maybe_set_thread_muted_field(opts)
|
||||||
|> restrict_blocked(opts)
|
|> restrict_blocked(opts)
|
||||||
|> restrict_recipients(recipients, opts[:user])
|
|> restrict_recipients(recipients, opts[:user])
|
||||||
|
|> restrict_filtered(opts)
|
||||||
|> where(
|
|> where(
|
||||||
[activity],
|
[activity],
|
||||||
fragment(
|
fragment(
|
||||||
|
@ -961,6 +963,26 @@ defp restrict_instance(query, %{instance: instance}) do
|
||||||
|
|
||||||
defp restrict_instance(query, _), do: query
|
defp restrict_instance(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_filtered(query, %{user: %User{} = user}) do
|
||||||
|
case Filter.compose_regex(user) do
|
||||||
|
nil ->
|
||||||
|
query
|
||||||
|
|
||||||
|
regex ->
|
||||||
|
from([activity, object] in query,
|
||||||
|
where:
|
||||||
|
fragment("not(?->>'content' ~* ?)", object.data, ^regex) or
|
||||||
|
activity.actor == ^user.ap_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_filtered(query, %{blocking_user: %User{} = user}) do
|
||||||
|
restrict_filtered(query, %{user: user})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_filtered(query, _), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
|
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, _) do
|
defp exclude_poll_votes(query, _) do
|
||||||
|
@ -1091,6 +1113,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_favorited_by(opts)
|
|> restrict_favorited_by(opts)
|
||||||
|> restrict_blocked(restrict_blocked_opts)
|
|> restrict_blocked(restrict_blocked_opts)
|
||||||
|> restrict_muted(restrict_muted_opts)
|
|> restrict_muted(restrict_muted_opts)
|
||||||
|
|> restrict_filtered(opts)
|
||||||
|> restrict_media(opts)
|
|> restrict_media(opts)
|
||||||
|> restrict_visibility(opts)
|
|> restrict_visibility(opts)
|
||||||
|> restrict_thread_visibility(opts, config)
|
|> restrict_thread_visibility(opts, config)
|
||||||
|
@ -1099,6 +1122,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_muted_reblogs(restrict_muted_reblogs_opts)
|
|> restrict_muted_reblogs(restrict_muted_reblogs_opts)
|
||||||
|> restrict_instance(opts)
|
|> restrict_instance(opts)
|
||||||
|> restrict_announce_object_actor(opts)
|
|> restrict_announce_object_actor(opts)
|
||||||
|
|> restrict_filtered(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|> exclude_chat_messages(opts)
|
|> exclude_chat_messages(opts)
|
||||||
|
|
|
@ -233,18 +233,24 @@ def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachm
|
||||||
is_map(url) && is_binary(url["href"]) -> url["href"]
|
is_map(url) && is_binary(url["href"]) -> url["href"]
|
||||||
is_binary(data["url"]) -> data["url"]
|
is_binary(data["url"]) -> data["url"]
|
||||||
is_binary(data["href"]) -> data["href"]
|
is_binary(data["href"]) -> data["href"]
|
||||||
|
true -> nil
|
||||||
end
|
end
|
||||||
|
|
||||||
attachment_url =
|
if href do
|
||||||
%{"href" => href}
|
attachment_url =
|
||||||
|> Maps.put_if_present("mediaType", media_type)
|
%{"href" => href}
|
||||||
|> Maps.put_if_present("type", Map.get(url || %{}, "type"))
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|
|> Maps.put_if_present("type", Map.get(url || %{}, "type"))
|
||||||
|
|
||||||
%{"url" => [attachment_url]}
|
%{"url" => [attachment_url]}
|
||||||
|> Maps.put_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|> Maps.put_if_present("type", data["type"])
|
|> Maps.put_if_present("type", data["type"])
|
||||||
|> Maps.put_if_present("name", data["name"])
|
|> Maps.put_if_present("name", data["name"])
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
Map.put(object, "attachment", attachments)
|
Map.put(object, "attachment", attachments)
|
||||||
end
|
end
|
||||||
|
@ -263,12 +269,18 @@ def fix_url(%{"url" => url} = object) when is_map(url) do
|
||||||
|
|
||||||
def fix_url(%{"type" => object_type, "url" => url} = object)
|
def fix_url(%{"type" => object_type, "url" => url} = object)
|
||||||
when object_type in ["Video", "Audio"] and is_list(url) do
|
when object_type in ["Video", "Audio"] and is_list(url) do
|
||||||
first_element = Enum.at(url, 0)
|
attachment =
|
||||||
|
Enum.find(url, fn x ->
|
||||||
|
media_type = x["mediaType"] || x["mimeType"] || ""
|
||||||
|
|
||||||
link_element = Enum.find(url, fn x -> is_map(x) and x["mimeType"] == "text/html" end)
|
is_map(x) and String.starts_with?(media_type, ["audio/", "video/"])
|
||||||
|
end)
|
||||||
|
|
||||||
|
link_element =
|
||||||
|
Enum.find(url, fn x -> is_map(x) and (x["mediaType"] || x["mimeType"]) == "text/html" end)
|
||||||
|
|
||||||
object
|
object
|
||||||
|> Map.put("attachment", [first_element])
|
|> Map.put("attachment", [attachment])
|
||||||
|> Map.put("url", link_element["href"])
|
|> Map.put("url", link_element["href"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
@ -40,48 +39,6 @@ def confirmation_resend_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_avatar_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Accounts"],
|
|
||||||
summary: "Set/clear user avatar image",
|
|
||||||
operationId: "PleromaAPI.AccountController.update_avatar",
|
|
||||||
requestBody:
|
|
||||||
request_body("Parameters", update_avatar_or_background_request(), required: true),
|
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
|
||||||
responses: %{
|
|
||||||
200 => update_response(),
|
|
||||||
403 => Operation.response("Forbidden", "application/json", ApiError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_banner_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Accounts"],
|
|
||||||
summary: "Set/clear user banner image",
|
|
||||||
operationId: "PleromaAPI.AccountController.update_banner",
|
|
||||||
requestBody: request_body("Parameters", update_banner_request(), required: true),
|
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
|
||||||
responses: %{
|
|
||||||
200 => update_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_background_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Accounts"],
|
|
||||||
summary: "Set/clear user background image",
|
|
||||||
operationId: "PleromaAPI.AccountController.update_background",
|
|
||||||
security: [%{"oAuth" => ["write:accounts"]}],
|
|
||||||
requestBody:
|
|
||||||
request_body("Parameters", update_avatar_or_background_request(), required: true),
|
|
||||||
responses: %{
|
|
||||||
200 => update_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def favourites_operation do
|
def favourites_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Accounts"],
|
tags: ["Accounts"],
|
||||||
|
@ -136,52 +93,4 @@ defp id_param do
|
||||||
required: true
|
required: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_avatar_or_background_request do
|
|
||||||
%Schema{
|
|
||||||
title: "PleromaAccountUpdateAvatarOrBackgroundRequest",
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
img: %Schema{
|
|
||||||
nullable: true,
|
|
||||||
type: :string,
|
|
||||||
format: :binary,
|
|
||||||
description: "Image encoded using `multipart/form-data` or an empty string to clear"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp update_banner_request do
|
|
||||||
%Schema{
|
|
||||||
title: "PleromaAccountUpdateBannerRequest",
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
banner: %Schema{
|
|
||||||
type: :string,
|
|
||||||
nullable: true,
|
|
||||||
format: :binary,
|
|
||||||
description: "Image encoded using `multipart/form-data` or an empty string to clear"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp update_response do
|
|
||||||
Operation.response("PleromaAccountUpdateResponse", "application/json", %Schema{
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
url: %Schema{
|
|
||||||
type: :string,
|
|
||||||
format: :uri,
|
|
||||||
nullable: true,
|
|
||||||
description: "Image URL"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
example: %{
|
|
||||||
"url" =>
|
|
||||||
"https://cofe.party/media/9d0add56-bcb6-4c0f-8225-cbbd0b6dd773/13eadb6972c9ccd3f4ffa3b8196f0e0d38b4d2f27594457c52e52946c054cd9a.gif"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,6 +148,13 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
||||||
|> Enum.into(%{})
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
# We use an empty string as a special value to reset
|
||||||
|
# avatars, banners, backgrounds
|
||||||
|
user_image_value = fn
|
||||||
|
"" -> {:ok, nil}
|
||||||
|
value -> {:ok, value}
|
||||||
|
end
|
||||||
|
|
||||||
user_params =
|
user_params =
|
||||||
[
|
[
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
|
@ -168,9 +175,9 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
|> Maps.put_if_present(:name, params[:display_name])
|
|> Maps.put_if_present(:name, params[:display_name])
|
||||||
|> Maps.put_if_present(:bio, params[:note])
|
|> Maps.put_if_present(:bio, params[:note])
|
||||||
|> Maps.put_if_present(:raw_bio, params[:note])
|
|> Maps.put_if_present(:raw_bio, params[:note])
|
||||||
|> Maps.put_if_present(:avatar, params[:avatar])
|
|> Maps.put_if_present(:avatar, params[:avatar], user_image_value)
|
||||||
|> Maps.put_if_present(:banner, params[:header])
|
|> Maps.put_if_present(:banner, params[:header], user_image_value)
|
||||||
|> Maps.put_if_present(:background, params[:pleroma_background_image])
|
|> Maps.put_if_present(:background, params[:pleroma_background_image], user_image_value)
|
||||||
|> Maps.put_if_present(
|
|> Maps.put_if_present(
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
params[:fields_attributes],
|
params[:fields_attributes],
|
||||||
|
|
|
@ -88,21 +88,20 @@ def direct(%{assigns: %{user: user}} = conn, params) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_unauthenticated?(true = _local_only) do
|
||||||
|
Pleroma.Config.get([:restrict_unauthenticated, :timelines, :local])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_unauthenticated?(_) do
|
||||||
|
Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated])
|
||||||
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/public
|
# GET /api/v1/timelines/public
|
||||||
def public(%{assigns: %{user: user}} = conn, params) do
|
def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
local_only = params[:local]
|
local_only = params[:local]
|
||||||
|
|
||||||
cfg_key =
|
if is_nil(user) and restrict_unauthenticated?(local_only) do
|
||||||
if local_only do
|
fail_on_bad_auth(conn)
|
||||||
:local
|
|
||||||
else
|
|
||||||
:federated
|
|
||||||
end
|
|
||||||
|
|
||||||
restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key])
|
|
||||||
|
|
||||||
if restrict? and is_nil(user) do
|
|
||||||
render_error(conn, :unauthorized, "authorization required for timeline view")
|
|
||||||
else
|
else
|
||||||
activities =
|
activities =
|
||||||
params
|
params
|
||||||
|
@ -123,6 +122,10 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fail_on_bad_auth(conn) do
|
||||||
|
render_error(conn, :unauthorized, "authorization required for timeline view")
|
||||||
|
end
|
||||||
|
|
||||||
defp hashtag_fetching(params, user, local_only) do
|
defp hashtag_fetching(params, user, local_only) do
|
||||||
tags =
|
tags =
|
||||||
[params[:tag], params[:any]]
|
[params[:tag], params[:any]]
|
||||||
|
@ -157,15 +160,20 @@ defp hashtag_fetching(params, user, local_only) do
|
||||||
# GET /api/v1/timelines/tag/:tag
|
# GET /api/v1/timelines/tag/:tag
|
||||||
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||||
local_only = params[:local]
|
local_only = params[:local]
|
||||||
activities = hashtag_fetching(params, user, local_only)
|
|
||||||
|
|
||||||
conn
|
if is_nil(user) and restrict_unauthenticated?(local_only) do
|
||||||
|> add_link_headers(activities, %{"local" => local_only})
|
fail_on_bad_auth(conn)
|
||||||
|> render("index.json",
|
else
|
||||||
activities: activities,
|
activities = hashtag_fetching(params, user, local_only)
|
||||||
for: user,
|
|
||||||
as: :activity
|
conn
|
||||||
)
|
|> add_link_headers(activities, %{"local" => local_only})
|
||||||
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/list/:list_id
|
# GET /api/v1/timelines/list/:list_id
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
||||||
|
|
||||||
alias Ecto.Changeset
|
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
|
@ -35,17 +34,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
%{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe]
|
%{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:accounts"]}
|
|
||||||
# Note: the following actions are not permission-secured in Mastodon:
|
|
||||||
when action in [
|
|
||||||
:update_avatar,
|
|
||||||
:update_banner,
|
|
||||||
:update_background
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||||
|
@ -68,56 +56,6 @@ def confirmation_resend(conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PATCH /api/v1/pleroma/accounts/update_avatar"
|
|
||||||
def update_avatar(%{assigns: %{user: user}, body_params: %{img: ""}} = conn, _) do
|
|
||||||
{:ok, _user} =
|
|
||||||
user
|
|
||||||
|> Changeset.change(%{avatar: nil})
|
|
||||||
|> User.update_and_set_cache()
|
|
||||||
|
|
||||||
json(conn, %{url: nil})
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_avatar(%{assigns: %{user: user}, body_params: params} = conn, _params) do
|
|
||||||
{:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar)
|
|
||||||
{:ok, _user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache()
|
|
||||||
%{"url" => [%{"href" => href} | _]} = data
|
|
||||||
|
|
||||||
json(conn, %{url: href})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "PATCH /api/v1/pleroma/accounts/update_banner"
|
|
||||||
def update_banner(%{assigns: %{user: user}, body_params: %{banner: ""}} = conn, _) do
|
|
||||||
with {:ok, _user} <- User.update_banner(user, %{}) do
|
|
||||||
json(conn, %{url: nil})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_banner(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
|
||||||
with {:ok, object} <- ActivityPub.upload(%{img: params[:banner]}, type: :banner),
|
|
||||||
{:ok, _user} <- User.update_banner(user, object.data) do
|
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
|
||||||
|
|
||||||
json(conn, %{url: href})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "PATCH /api/v1/pleroma/accounts/update_background"
|
|
||||||
def update_background(%{assigns: %{user: user}, body_params: %{img: ""}} = conn, _) do
|
|
||||||
with {:ok, _user} <- User.update_background(user, %{}) do
|
|
||||||
json(conn, %{url: nil})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_background(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
|
||||||
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
|
||||||
{:ok, _user} <- User.update_background(user, object.data) do
|
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
|
||||||
|
|
||||||
json(conn, %{url: href})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "GET /api/v1/pleroma/accounts/:id/favourites"
|
@doc "GET /api/v1/pleroma/accounts/:id/favourites"
|
||||||
def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params) do
|
def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params) do
|
||||||
render_error(conn, :forbidden, "Can't get favorites")
|
render_error(conn, :forbidden, "Can't get favorites")
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.Preload.Providers.StatusNet do
|
|
||||||
alias Pleroma.Web.Preload.Providers.Provider
|
|
||||||
alias Pleroma.Web.TwitterAPI.UtilController
|
|
||||||
|
|
||||||
@behaviour Provider
|
|
||||||
@config_url "/api/statusnet/config.json"
|
|
||||||
|
|
||||||
@impl Provider
|
|
||||||
def generate_terms(_params) do
|
|
||||||
%{}
|
|
||||||
|> build_config_tag()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp build_config_tag(acc) do
|
|
||||||
resp =
|
|
||||||
Plug.Test.conn(:get, @config_url |> to_string())
|
|
||||||
|> UtilController.config(nil)
|
|
||||||
|
|
||||||
Map.put(acc, @config_url, resp.resp_body)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -86,7 +86,10 @@ defp parse_url(url) do
|
||||||
end
|
end
|
||||||
|
|
||||||
try do
|
try do
|
||||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: opts)
|
rich_media_agent = Pleroma.Application.user_agent() <> "; Bot"
|
||||||
|
|
||||||
|
{:ok, %Tesla.Env{body: html}} =
|
||||||
|
Pleroma.HTTP.get(url, [{"user-agent", rich_media_agent}], adapter: opts)
|
||||||
|
|
||||||
html
|
html
|
||||||
|> parse_html()
|
|> parse_html()
|
||||||
|
|
|
@ -328,10 +328,6 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/statuses/:id/reactions/:emoji", EmojiReactionController, :delete)
|
delete("/statuses/:id/reactions/:emoji", EmojiReactionController, :delete)
|
||||||
post("/notifications/read", NotificationController, :mark_as_read)
|
post("/notifications/read", NotificationController, :mark_as_read)
|
||||||
|
|
||||||
patch("/accounts/update_avatar", AccountController, :update_avatar)
|
|
||||||
patch("/accounts/update_banner", AccountController, :update_banner)
|
|
||||||
patch("/accounts/update_background", AccountController, :update_background)
|
|
||||||
|
|
||||||
get("/mascot", MascotController, :show)
|
get("/mascot", MascotController, :show)
|
||||||
put("/mascot", MascotController, :update)
|
put("/mascot", MascotController, :update)
|
||||||
|
|
||||||
|
@ -516,10 +512,6 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
pipe_through(:config)
|
pipe_through(:config)
|
||||||
|
|
||||||
get("/help/test", TwitterAPI.UtilController, :help_test)
|
|
||||||
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)
|
get("/pleroma/frontend_configurations", TwitterAPI.UtilController, :frontend_configurations)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.TwitterAPI.UtilView
|
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
||||||
|
@ -42,12 +40,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
|
|
||||||
|
|
||||||
def help_test(conn, _params) do
|
|
||||||
json(conn, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nick),
|
with %User{} = user <- User.get_cached_by_nickname(nick),
|
||||||
avatar = User.avatar_url(user) do
|
avatar = User.avatar_url(user) do
|
||||||
|
@ -89,60 +81,6 @@ def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def config(%{assigns: %{format: "xml"}} = conn, _params) do
|
|
||||||
instance = Pleroma.Config.get(:instance)
|
|
||||||
response = UtilView.status_net_config(instance)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type("application/xml")
|
|
||||||
|> send_resp(200, response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(conn, _params) do
|
|
||||||
instance = Pleroma.Config.get(:instance)
|
|
||||||
|
|
||||||
vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
|
||||||
|
|
||||||
uploadlimit = %{
|
|
||||||
uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
|
|
||||||
avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
|
|
||||||
backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
|
|
||||||
bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
data = %{
|
|
||||||
name: Keyword.get(instance, :name),
|
|
||||||
description: Keyword.get(instance, :description),
|
|
||||||
server: Web.base_url(),
|
|
||||||
textlimit: to_string(Keyword.get(instance, :limit)),
|
|
||||||
uploadlimit: uploadlimit,
|
|
||||||
closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
|
|
||||||
private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
|
|
||||||
vapidPublicKey: vapid_public_key,
|
|
||||||
accountActivationRequired:
|
|
||||||
bool_to_val(Keyword.get(instance, :account_activation_required, false)),
|
|
||||||
invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
|
|
||||||
safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
|
|
||||||
}
|
|
||||||
|
|
||||||
managed_config = Keyword.get(instance, :managed_config)
|
|
||||||
|
|
||||||
data =
|
|
||||||
if managed_config do
|
|
||||||
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
|
||||||
Map.put(data, "pleromafe", pleroma_fe)
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, %{site: data})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp bool_to_val(true), do: "1"
|
|
||||||
defp bool_to_val(_), do: "0"
|
|
||||||
defp bool_to_val(true, val, _), do: val
|
|
||||||
defp bool_to_val(_, _, val), do: val
|
|
||||||
|
|
||||||
def frontend_configurations(conn, _params) do
|
def frontend_configurations(conn, _params) do
|
||||||
config =
|
config =
|
||||||
Pleroma.Config.get(:frontend_configurations, %{})
|
Pleroma.Config.get(:frontend_configurations, %{})
|
||||||
|
@ -151,18 +89,6 @@ def frontend_configurations(conn, _params) do
|
||||||
json(conn, config)
|
json(conn, config)
|
||||||
end
|
end
|
||||||
|
|
||||||
def version(%{assigns: %{format: "xml"}} = conn, _params) do
|
|
||||||
version = Pleroma.Application.named_version()
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_resp_content_type("application/xml")
|
|
||||||
|> send_resp(200, "<version>#{version}</version>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def version(conn, _params) do
|
|
||||||
json(conn, Pleroma.Application.named_version())
|
|
||||||
end
|
|
||||||
|
|
||||||
def emoji(conn, _params) do
|
def emoji(conn, _params) do
|
||||||
emoji =
|
emoji =
|
||||||
Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
|
Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.18fea621d430000acc27.css rel=stylesheet><link href=/static/css/app.613cef07981cd95ccceb.css rel=stylesheet><link href=/static/fontello.1589385935077.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.561a1c605d1dfb0e6f74.js></script><script type=text/javascript src=/static/js/app.838ffa9aecf210c7d744.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.493b9b5acee37ba97824.css rel=stylesheet><link href=/static/fontello.1594134783339.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.8837fb59589d1dd6acda.js></script><script type=text/javascript src=/static/js/app.53001fa190f37cf2743e.js></script></body></html>
|
|
@ -14,7 +14,6 @@
|
||||||
"logoMargin": ".1em",
|
"logoMargin": ".1em",
|
||||||
"logoMask": true,
|
"logoMask": true,
|
||||||
"minimalScopesMode": false,
|
"minimalScopesMode": false,
|
||||||
"noAttachmentLinks": false,
|
|
||||||
"nsfwCensorImage": "",
|
"nsfwCensorImage": "",
|
||||||
"postContentType": "text/plain",
|
"postContentType": "text/plain",
|
||||||
"redirectRootLogin": "/main/friends",
|
"redirectRootLogin": "/main/friends",
|
||||||
|
@ -22,6 +21,7 @@
|
||||||
"scopeCopy": true,
|
"scopeCopy": true,
|
||||||
"showFeaturesPanel": true,
|
"showFeaturesPanel": true,
|
||||||
"showInstanceSpecificPanel": false,
|
"showInstanceSpecificPanel": false,
|
||||||
|
"sidebarRight": false,
|
||||||
"subjectLineBehavior": "email",
|
"subjectLineBehavior": "email",
|
||||||
"theme": "pleroma-dark",
|
"theme": "pleroma-dark",
|
||||||
"webPushNotifications": false
|
"webPushNotifications": false
|
||||||
|
|
BIN
priv/static/static/css/2.0778a6a864a1307a6c41.css
Normal file
BIN
priv/static/static/css/2.0778a6a864a1307a6c41.css
Normal file
Binary file not shown.
1
priv/static/static/css/2.0778a6a864a1307a6c41.css.map
Normal file
1
priv/static/static/css/2.0778a6a864a1307a6c41.css.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":["webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/2.0778a6a864a1307a6c41.css","sourcesContent":[".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""}
|
Binary file not shown.
1
priv/static/static/css/3.b2603a50868c68a1c192.css.map
Normal file
1
priv/static/static/css/3.b2603a50868c68a1c192.css.map
Normal file
File diff suppressed because one or more lines are too long
BIN
priv/static/static/css/app.493b9b5acee37ba97824.css
Normal file
BIN
priv/static/static/css/app.493b9b5acee37ba97824.css
Normal file
Binary file not shown.
1
priv/static/static/css/app.493b9b5acee37ba97824.css.map
Normal file
1
priv/static/static/css/app.493b9b5acee37ba97824.css.map
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
{"version":3,"sources":["webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA,uBAAuB,aAAa,kBAAkB,qBAAqB,sBAAsB,qCAAqC,8BAA8B,e;ACApK,cAAc,oBAAoB,aAAa,0BAA0B,sBAAsB,wBAAwB,kBAAkB,cAAc,eAAe,gCAAgC,aAAa,wCAAwC,0BAA0B,aAAa,gBAAgB,oBAAoB,oBAAoB,aAAa,kBAAkB,WAAW,kBAAkB,gBAAgB,gBAAgB,sBAAsB,uDAAuD,cAAc,WAAW,kBAAkB,cAAc,wBAAwB,yBAAyB,wCAAwC,iCAAiC,YAAY,kBAAkB,oBAAoB,aAAa,kBAAkB,cAAc,sCAAsC,WAAW,cAAc,kBAAkB,4BAA4B,6BAA6B,gBAAgB,oBAAoB,oBAAoB,mBAAmB,cAAc,8BAA8B,yBAAyB,qCAAqC,mDAAmD,UAAU,yDAAyD,UAAU,6CAA6C,uBAAuB,UAAU,cAAc,oCAAoC,0CAA0C,gBAAgB,mBAAmB,gBAAgB,qDAAqD,WAAW,kBAAkB,OAAO,QAAQ,SAAS,UAAU,wBAAwB,yBAAyB,wC;ACAtlD,2BAA2B,aAAa,kBAAkB,kCAAkC,e","file":"static/css/app.613cef07981cd95ccceb.css","sourcesContent":[".with-load-more-footer{padding:10px;text-align:center;border-top:1px solid;border-top-color:#222;border-top-color:var(--border, #222)}.with-load-more-footer .error{font-size:14px}",".tab-switcher{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.tab-switcher .contents{-ms-flex:1 0 auto;flex:1 0 auto;min-height:0px}.tab-switcher .contents .hidden{display:none}.tab-switcher .contents.scrollable-tabs{-ms-flex-preferred-size:0;flex-basis:0;overflow-y:auto}.tab-switcher .tabs{display:-ms-flexbox;display:flex;position:relative;width:100%;overflow-y:hidden;overflow-x:auto;padding-top:5px;box-sizing:border-box}.tab-switcher .tabs::after,.tab-switcher .tabs::before{display:block;content:\"\";-ms-flex:1 1 auto;flex:1 1 auto;border-bottom:1px solid;border-bottom-color:#222;border-bottom-color:var(--border, #222)}.tab-switcher .tabs .tab-wrapper{height:28px;position:relative;display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto}.tab-switcher .tabs .tab-wrapper .tab{width:100%;min-width:1px;position:relative;border-bottom-left-radius:0;border-bottom-right-radius:0;padding:6px 1em;padding-bottom:99px;margin-bottom:-93px;white-space:nowrap;color:#b9b9ba;color:var(--tabText, #b9b9ba);background-color:#182230;background-color:var(--tab, #182230)}.tab-switcher .tabs .tab-wrapper .tab:not(.active){z-index:4}.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover{z-index:6}.tab-switcher .tabs .tab-wrapper .tab.active{background:transparent;z-index:5;color:#b9b9ba;color:var(--tabActiveText, #b9b9ba)}.tab-switcher .tabs .tab-wrapper .tab img{max-height:26px;vertical-align:top;margin-top:-5px}.tab-switcher .tabs .tab-wrapper:not(.active)::after{content:\"\";position:absolute;left:0;right:0;bottom:0;z-index:7;border-bottom:1px solid;border-bottom-color:#222;border-bottom-color:var(--border, #222)}",".with-subscription-loading{padding:10px;text-align:center}.with-subscription-loading .error{font-size:14px}"],"sourceRoot":""}
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -80,8 +80,16 @@
|
||||||
|
|
||||||
<glyph glyph-name="user" unicode="" d="M714 76q0-60-35-104t-84-44h-476q-49 0-84 44t-35 104q0 48 5 90t17 85 33 73 52 50 76 19q73-72 174-72t175 72q42 0 75-19t52-50 33-73 18-85 4-90z m-143 495q0-88-62-151t-152-63-151 63-63 151 63 152 151 63 152-63 62-152z" horiz-adv-x="714.3" />
|
<glyph glyph-name="user" unicode="" d="M714 76q0-60-35-104t-84-44h-476q-49 0-84 44t-35 104q0 48 5 90t17 85 33 73 52 50 76 19q73-72 174-72t175 72q42 0 75-19t52-50 33-73 18-85 4-90z m-143 495q0-88-62-151t-152-63-151 63-63 151 63 152 151 63 152-63 62-152z" horiz-adv-x="714.3" />
|
||||||
|
|
||||||
|
<glyph glyph-name="download" unicode="" d="M714 107q0 15-10 25t-25 11-25-11-11-25 11-25 25-11 25 11 10 25z m143 0q0 15-10 25t-26 11-25-11-10-25 10-25 25-11 26 11 10 25z m72 125v-179q0-22-16-37t-38-16h-821q-23 0-38 16t-16 37v179q0 22 16 38t38 16h259l75-76q33-32 76-32t76 32l76 76h259q22 0 38-16t16-38z m-182 318q10-23-8-39l-250-250q-10-11-25-11t-25 11l-250 250q-17 16-8 39 10 21 33 21h143v250q0 15 11 25t25 11h143q14 0 25-11t10-25v-250h143q24 0 33-21z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
|
<glyph glyph-name="bookmark" unicode="" d="M650 786q12 0 24-5 19-8 29-23t11-35v-719q0-19-11-35t-29-23q-10-4-24-4-27 0-47 18l-246 236-246-236q-20-19-46-19-13 0-25 5-18 7-29 23t-11 35v719q0 19 11 35t29 23q12 5 25 5h585z" horiz-adv-x="714.3" />
|
||||||
|
|
||||||
<glyph glyph-name="ok" unicode="" d="M933 541q0-22-16-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q16-15 16-38z" horiz-adv-x="1000" />
|
<glyph glyph-name="ok" unicode="" d="M933 541q0-22-16-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q16-15 16-38z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="music" unicode="" d="M857 732v-625q0-28-19-50t-48-33-58-18-53-6-54 6-58 18-48 33-19 50 19 50 48 33 58 18 54 6q58 0 107-22v300l-429-132v-396q0-28-19-50t-48-33-58-18-53-6-54 6-58 18-48 33-19 50 19 50 48 34 58 17 54 6q58 0 107-21v539q0 17 10 32t28 20l464 142q7 3 16 3 22 0 38-16t15-38z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="doc" unicode="" d="M819 645q16-16 27-42t11-50v-642q0-23-15-38t-38-16h-750q-23 0-38 16t-16 38v892q0 23 16 38t38 16h500q22 0 49-11t42-27z m-248 136v-210h210q-5 17-12 23l-175 175q-6 7-23 12z m215-853v572h-232q-23 0-38 16t-16 37v233h-429v-858h715z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<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="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" />
|
<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" />
|
||||||
|
@ -90,6 +98,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="link-ext-alt" unicode="" d="M714 339v268q0 15-10 25t-25 11h-268q-24 0-33-22-10-23 8-39l80-80-298-298q-11-11-11-26t11-25l57-57q11-10 25-10t25 10l298 298 81-80q10-11 25-11 6 0 14 3 21 10 21 33z m143 286v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" />
|
<glyph glyph-name="link-ext-alt" unicode="" d="M714 339v268q0 15-10 25t-25 11h-268q-24 0-33-22-10-23 8-39l80-80-298-298q-11-11-11-26t11-25l57-57q11-10 25-10t25 10l298 298 81-80q10-11 25-11 6 0 14 3 21 10 21 33z m143 286v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="bookmark-empty" unicode="" d="M643 714h-572v-693l237 227 49 47 50-47 236-227v693z m7 72q12 0 24-5 19-8 29-23t11-35v-719q0-19-11-35t-29-23q-10-4-24-4-27 0-47 18l-246 236-246-236q-20-19-46-19-13 0-25 5-18 7-29 23t-11 35v719q0 19 11 35t29 23q12 5 25 5h585z" horiz-adv-x="714.3" />
|
||||||
|
|
||||||
|
<glyph glyph-name="filter" unicode="" d="M783 692q9-22-8-39l-275-275v-414q0-23-22-33-7-3-14-3-15 0-25 11l-143 143q-10 11-10 25v271l-275 275q-18 17-8 39 9 22 33 22h714q23 0 33-22z" horiz-adv-x="785.7" />
|
||||||
|
|
||||||
<glyph glyph-name="menu" unicode="" d="M857 107v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-14-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
|
<glyph glyph-name="menu" unicode="" d="M857 107v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-14-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="mail-alt" unicode="" d="M1000 461v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-27 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-32-15-28-5h-2q-12 0-27 5t-32 15-30 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 63-26t26-63z" horiz-adv-x="1000" />
|
<glyph glyph-name="mail-alt" unicode="" d="M1000 461v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-27 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-32-15-28-5h-2q-12 0-27 5t-32 15-30 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 63-26t26-63z" horiz-adv-x="1000" />
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
Binary file not shown.
BIN
priv/static/static/font/fontello.1594134783339.woff
Normal file
BIN
priv/static/static/font/fontello.1594134783339.woff
Normal file
Binary file not shown.
BIN
priv/static/static/font/fontello.1594134783339.woff2
Normal file
BIN
priv/static/static/font/fontello.1594134783339.woff2
Normal file
Binary file not shown.
BIN
priv/static/static/fontello.1594030805019.css
vendored
Normal file
BIN
priv/static/static/fontello.1594030805019.css
vendored
Normal file
Binary file not shown.
BIN
priv/static/static/fontello.1594134783339.css
vendored
Normal file
BIN
priv/static/static/fontello.1594134783339.css
vendored
Normal file
Binary file not shown.
|
@ -363,6 +363,42 @@
|
||||||
"css": "ok",
|
"css": "ok",
|
||||||
"code": 59431,
|
"code": 59431,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "4109c474ff99cad28fd5a2c38af2ec6f",
|
||||||
|
"css": "filter",
|
||||||
|
"code": 61616,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "9a76bc135eac17d2c8b8ad4a5774fc87",
|
||||||
|
"css": "download",
|
||||||
|
"code": 59429,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "f04a5d24e9e659145b966739c4fde82a",
|
||||||
|
"css": "bookmark",
|
||||||
|
"code": 59430,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2f5ef6f6b7aaebc56458ab4e865beff5",
|
||||||
|
"css": "bookmark-empty",
|
||||||
|
"code": 61591,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "9ea0a737ccc45d6c510dcbae56058849",
|
||||||
|
"css": "music",
|
||||||
|
"code": 59432,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "1b5a5d7b7e3c71437f5a26befdd045ed",
|
||||||
|
"css": "doc",
|
||||||
|
"code": 59433,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
BIN
priv/static/static/js/10.4a22c77e34edcd678d2f.js
Normal file
BIN
priv/static/static/js/10.4a22c77e34edcd678d2f.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/10.4a22c77e34edcd678d2f.js.map
Normal file
BIN
priv/static/static/js/10.4a22c77e34edcd678d2f.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/11.787aa24e4fd5caef9adb.js
Normal file
BIN
priv/static/static/js/11.787aa24e4fd5caef9adb.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/11.787aa24e4fd5caef9adb.js.map
Normal file
BIN
priv/static/static/js/11.787aa24e4fd5caef9adb.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/12.35a510cf14233f0c6e1f.js
Normal file
BIN
priv/static/static/js/12.35a510cf14233f0c6e1f.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/12.35a510cf14233f0c6e1f.js.map
Normal file
BIN
priv/static/static/js/12.35a510cf14233f0c6e1f.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/13.7931a609d62a42678085.js
Normal file
BIN
priv/static/static/js/13.7931a609d62a42678085.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/13.7931a609d62a42678085.js.map
Normal file
BIN
priv/static/static/js/13.7931a609d62a42678085.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/14.cc092634462fd2a4cfbc.js
Normal file
BIN
priv/static/static/js/14.cc092634462fd2a4cfbc.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/14.cc092634462fd2a4cfbc.js.map
Normal file
BIN
priv/static/static/js/14.cc092634462fd2a4cfbc.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/15.e9ddc5dfd38426398e00.js
Normal file
BIN
priv/static/static/js/15.e9ddc5dfd38426398e00.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/15.e9ddc5dfd38426398e00.js.map
Normal file
BIN
priv/static/static/js/15.e9ddc5dfd38426398e00.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/16.476e7809b8593264469e.js
Normal file
BIN
priv/static/static/js/16.476e7809b8593264469e.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/16.476e7809b8593264469e.js.map
Normal file
BIN
priv/static/static/js/16.476e7809b8593264469e.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/17.acbe4c09f05ae56c76a2.js
Normal file
BIN
priv/static/static/js/17.acbe4c09f05ae56c76a2.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/17.acbe4c09f05ae56c76a2.js.map
Normal file
BIN
priv/static/static/js/17.acbe4c09f05ae56c76a2.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/18.a8ccd7f2a47c5c94b3b9.js
Normal file
BIN
priv/static/static/js/18.a8ccd7f2a47c5c94b3b9.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/18.a8ccd7f2a47c5c94b3b9.js.map
Normal file
BIN
priv/static/static/js/18.a8ccd7f2a47c5c94b3b9.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/19.5894e9c12b4fd5e45872.js
Normal file
BIN
priv/static/static/js/19.5894e9c12b4fd5e45872.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/19.5894e9c12b4fd5e45872.js.map
Normal file
BIN
priv/static/static/js/19.5894e9c12b4fd5e45872.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/2.f8dee9318a6f84ea92c3.js
Normal file
BIN
priv/static/static/js/2.f8dee9318a6f84ea92c3.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/2.f8dee9318a6f84ea92c3.js.map
Normal file
BIN
priv/static/static/js/2.f8dee9318a6f84ea92c3.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/20.43b5b27b0f68474f3b72.js
Normal file
BIN
priv/static/static/js/20.43b5b27b0f68474f3b72.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/20.43b5b27b0f68474f3b72.js.map
Normal file
BIN
priv/static/static/js/20.43b5b27b0f68474f3b72.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/21.72b45b01be9d0f4c62ce.js
Normal file
BIN
priv/static/static/js/21.72b45b01be9d0f4c62ce.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/21.72b45b01be9d0f4c62ce.js.map
Normal file
BIN
priv/static/static/js/21.72b45b01be9d0f4c62ce.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/22.26f13a22ad57a0d14670.js
Normal file
BIN
priv/static/static/js/22.26f13a22ad57a0d14670.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/22.26f13a22ad57a0d14670.js.map
Normal file
BIN
priv/static/static/js/22.26f13a22ad57a0d14670.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/23.91a60b775352a806f887.js
Normal file
BIN
priv/static/static/js/23.91a60b775352a806f887.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/23.91a60b775352a806f887.js.map
Normal file
BIN
priv/static/static/js/23.91a60b775352a806f887.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/24.c8d8438aac954d4707ac.js
Normal file
BIN
priv/static/static/js/24.c8d8438aac954d4707ac.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/24.c8d8438aac954d4707ac.js.map
Normal file
BIN
priv/static/static/js/24.c8d8438aac954d4707ac.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/25.79ac9e020d571b67f02a.js
Normal file
BIN
priv/static/static/js/25.79ac9e020d571b67f02a.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/25.79ac9e020d571b67f02a.js.map
Normal file
BIN
priv/static/static/js/25.79ac9e020d571b67f02a.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/26.3af8f54349f672f2c7c8.js
Normal file
BIN
priv/static/static/js/26.3af8f54349f672f2c7c8.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/26.3af8f54349f672f2c7c8.js.map
Normal file
BIN
priv/static/static/js/26.3af8f54349f672f2c7c8.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/27.51287d408313da67b0b8.js
Normal file
BIN
priv/static/static/js/27.51287d408313da67b0b8.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/27.51287d408313da67b0b8.js.map
Normal file
BIN
priv/static/static/js/27.51287d408313da67b0b8.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/28.be5118beb1098a81332d.js
Normal file
BIN
priv/static/static/js/28.be5118beb1098a81332d.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/28.be5118beb1098a81332d.js.map
Normal file
BIN
priv/static/static/js/28.be5118beb1098a81332d.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/29.084f6fb0987d3862d410.js
Normal file
BIN
priv/static/static/js/29.084f6fb0987d3862d410.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/29.084f6fb0987d3862d410.js.map
Normal file
BIN
priv/static/static/js/29.084f6fb0987d3862d410.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/3.e1f7d368d5840e12e850.js
Normal file
BIN
priv/static/static/js/3.e1f7d368d5840e12e850.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/3.e1f7d368d5840e12e850.js.map
Normal file
BIN
priv/static/static/js/3.e1f7d368d5840e12e850.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/30.6e6d63411def2e175d11.js
Normal file
BIN
priv/static/static/js/30.6e6d63411def2e175d11.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/30.6e6d63411def2e175d11.js.map
Normal file
BIN
priv/static/static/js/30.6e6d63411def2e175d11.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/4.c3f92d0b6ff90b36e3f5.js
Normal file
BIN
priv/static/static/js/4.c3f92d0b6ff90b36e3f5.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/4.c3f92d0b6ff90b36e3f5.js.map
Normal file
BIN
priv/static/static/js/4.c3f92d0b6ff90b36e3f5.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/5.d30e50cd5c52d54ffdc9.js
Normal file
BIN
priv/static/static/js/5.d30e50cd5c52d54ffdc9.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/5.d30e50cd5c52d54ffdc9.js.map
Normal file
BIN
priv/static/static/js/5.d30e50cd5c52d54ffdc9.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/6.fa6d5c2d85d44f0ba121.js
Normal file
BIN
priv/static/static/js/6.fa6d5c2d85d44f0ba121.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/6.fa6d5c2d85d44f0ba121.js.map
Normal file
BIN
priv/static/static/js/6.fa6d5c2d85d44f0ba121.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/7.d558a086622f668601a6.js
Normal file
BIN
priv/static/static/js/7.d558a086622f668601a6.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/7.d558a086622f668601a6.js.map
Normal file
BIN
priv/static/static/js/7.d558a086622f668601a6.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/8.615136ce6c34a6b96a29.js
Normal file
BIN
priv/static/static/js/8.615136ce6c34a6b96a29.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/8.615136ce6c34a6b96a29.js.map
Normal file
BIN
priv/static/static/js/8.615136ce6c34a6b96a29.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/9.ef4eb9703f9aee67515e.js
Normal file
BIN
priv/static/static/js/9.ef4eb9703f9aee67515e.js
Normal file
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