Merge remote-tracking branch 'remotes/origin/develop' into 1364-no-pushes-from-blocked-domains-users
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
bb5d0eafa4
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -12,19 +12,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
||||||
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
||||||
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
||||||
|
- Mix task to create trusted OAuth App.
|
||||||
|
- Notifications: Added `follow_request` notification type (configurable, see `[:notifications, :enable_follow_request_notifications]` setting).
|
||||||
|
- Added `:reject_deletes` group to SimplePolicy
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
- 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.
|
||||||
|
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Support pagination in conversations API
|
- Support pagination in conversations API
|
||||||
|
- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
|
||||||
- Filtering of push notifications on activities from blocked domains
|
- Filtering of push notifications on activities from blocked domains
|
||||||
|
|
||||||
## [unreleased-patch]
|
## [unreleased-patch]
|
||||||
### Fixed
|
### Fixed
|
||||||
- Logger configuration through AdminFE
|
- Logger configuration through AdminFE
|
||||||
|
- HTTP Basic Authentication permissions issue
|
||||||
|
- ObjectAgePolicy didn't filter out old messages
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- NodeInfo: ObjectAgePolicy settings to the `federation` list.
|
||||||
|
<details>
|
||||||
|
<summary>API Changes</summary>
|
||||||
|
- Admin API: `GET /api/pleroma/admin/need_reboot`.
|
||||||
|
</details>
|
||||||
|
|
||||||
## [2.0.2] - 2020-04-08
|
## [2.0.2] - 2020-04-08
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -336,7 +336,8 @@
|
||||||
reject: [],
|
reject: [],
|
||||||
accept: [],
|
accept: [],
|
||||||
avatar_removal: [],
|
avatar_removal: [],
|
||||||
banner_removal: []
|
banner_removal: [],
|
||||||
|
reject_deletes: []
|
||||||
|
|
||||||
config :pleroma, :mrf_keyword,
|
config :pleroma, :mrf_keyword,
|
||||||
reject: [],
|
reject: [],
|
||||||
|
@ -561,6 +562,8 @@
|
||||||
inactivity_threshold: 7
|
inactivity_threshold: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config :pleroma, :notifications, enable_follow_request_notifications: false
|
||||||
|
|
||||||
config :pleroma, :oauth2,
|
config :pleroma, :oauth2,
|
||||||
token_expires_in: 600,
|
token_expires_in: 600,
|
||||||
issue_new_refresh_token: true,
|
issue_new_refresh_token: true,
|
||||||
|
|
|
@ -1317,13 +1317,13 @@
|
||||||
%{
|
%{
|
||||||
key: :reject,
|
key: :reject,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to reject any activities from",
|
description: "List of instances to reject activities from (except deletes)",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :accept,
|
key: :accept,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to accept any activities from",
|
description: "List of instances to only accept activities from (except deletes)",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -1343,6 +1343,12 @@
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to strip banners from",
|
description: "List of instances to strip banners from",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :reject_deletes,
|
||||||
|
type: {:list, :string},
|
||||||
|
description: "List of instances to reject deletions from",
|
||||||
|
suggestions: ["example.com", "*.example.com"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2267,6 +2273,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: :notifications,
|
||||||
|
type: :group,
|
||||||
|
description: "Notification settings",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enable_follow_request_notifications,
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Enables notifications on new follow requests (causes issues with older PleromaFE versions)."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: Pleroma.Emails.UserEmail,
|
key: Pleroma.Emails.UserEmail,
|
||||||
|
|
|
@ -786,6 +786,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
### Restarts pleroma application
|
### Restarts pleroma application
|
||||||
|
|
||||||
|
**Only works when configuration from database is enabled.**
|
||||||
|
|
||||||
- Params: none
|
- Params: none
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
|
@ -795,11 +797,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
{}
|
{}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/need_reboot`
|
||||||
|
|
||||||
|
### Returns the flag whether the pleroma should be restarted
|
||||||
|
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
- `need_reboot` - boolean
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"need_reboot": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `GET /api/pleroma/admin/config`
|
## `GET /api/pleroma/admin/config`
|
||||||
|
|
||||||
### Get list of merged default settings with saved in database.
|
### Get list of merged default settings with saved in database.
|
||||||
|
|
||||||
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.*
|
*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
|
||||||
|
|
||||||
**Only works when configuration from database is enabled.**
|
**Only works when configuration from database is enabled.**
|
||||||
|
|
||||||
|
@ -821,13 +836,12 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
"need_reboot": true
|
"need_reboot": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
need_reboot - *optional*, if were changed reboot time settings.
|
|
||||||
|
|
||||||
## `POST /api/pleroma/admin/config`
|
## `POST /api/pleroma/admin/config`
|
||||||
|
|
||||||
### Update config settings
|
### Update config settings
|
||||||
|
|
||||||
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.*
|
*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
|
||||||
|
|
||||||
**Only works when configuration from database is enabled.**
|
**Only works when configuration from database is enabled.**
|
||||||
|
|
||||||
|
@ -971,7 +985,6 @@ config :quack,
|
||||||
"need_reboot": true
|
"need_reboot": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
need_reboot - *optional*, if were changed reboot time settings.
|
|
||||||
|
|
||||||
## ` GET /api/pleroma/admin/config/descriptions`
|
## ` GET /api/pleroma/admin/config/descriptions`
|
||||||
|
|
||||||
|
@ -1075,3 +1088,104 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/oauth_app`
|
||||||
|
|
||||||
|
### List OAuth app
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- *optional* `name`
|
||||||
|
- *optional* `client_id`
|
||||||
|
- *optional* `page`
|
||||||
|
- *optional* `page_size`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 17,
|
||||||
|
"page_size": 50
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/oauth_app`
|
||||||
|
|
||||||
|
### Create OAuth App
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `name`
|
||||||
|
- `redirect_uris`
|
||||||
|
- `scopes`
|
||||||
|
- *optional* `website`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- On failure:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"redirect_uris": "can't be blank",
|
||||||
|
"name": "can't be blank"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/oauth_app/:id`
|
||||||
|
|
||||||
|
### Update OAuth App
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- *optional* `name`
|
||||||
|
- *optional* `redirect_uris`
|
||||||
|
- *optional* `scopes`
|
||||||
|
- *optional* `website`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `DELETE /api/pleroma/admin/oauth_app/:id`
|
||||||
|
|
||||||
|
### Delete OAuth App
|
||||||
|
|
||||||
|
- Params: None
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
- On success: `204`, empty response
|
||||||
|
- On failure:
|
||||||
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Creating trusted OAuth App
|
||||||
|
|
||||||
|
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||||
|
|
||||||
|
## Create trusted OAuth App.
|
||||||
|
|
||||||
|
Optional params:
|
||||||
|
* `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`.
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.app create -n APP_NAME -r REDIRECT_URI
|
||||||
|
```
|
|
@ -41,11 +41,15 @@ config :pleroma, :instance,
|
||||||
|
|
||||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||||
|
|
||||||
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
|
||||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
|
||||||
* `reject`: Servers in this group will have their messages rejected.
|
* `reject`: Servers in this group will have their messages rejected.
|
||||||
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
* `accept`: If not empty, only messages from these instances will be accepted (whitelist federation).
|
||||||
|
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||||
|
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||||
|
* `avatar_removal`: Avatars from these servers will be stripped from incoming messages.
|
||||||
|
* `banner_removal`: Banner images from these servers will be stripped from incoming messages.
|
||||||
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
||||||
|
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
||||||
|
* `reject_deletes`: Deletion requests will be rejected from these servers.
|
||||||
|
|
||||||
Servers should be configured as lists.
|
Servers should be configured as lists.
|
||||||
|
|
||||||
|
@ -113,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def describe do
|
def describe do
|
||||||
{:ok, %{mrf_sample: %{content: "new message content"}}}`
|
{:ok, %{mrf_sample: %{content: "new message content"}}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.App do
|
||||||
|
@moduledoc File.read!("docs/administration/CLI_tasks/oauth_app.md")
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
import Mix.Pleroma
|
||||||
|
|
||||||
|
@shortdoc "Creates trusted OAuth App"
|
||||||
|
|
||||||
|
def run(["create" | options]) do
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
{opts, _} =
|
||||||
|
OptionParser.parse!(options,
|
||||||
|
strict: [name: :string, redirect_uri: :string, scopes: :string],
|
||||||
|
aliases: [n: :name, r: :redirect_uri, s: :scopes]
|
||||||
|
)
|
||||||
|
|
||||||
|
scopes =
|
||||||
|
if opts[:scopes] do
|
||||||
|
String.split(opts[:scopes], ",")
|
||||||
|
else
|
||||||
|
["read", "write", "follow", "push"]
|
||||||
|
end
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
client_name: opts[:name],
|
||||||
|
redirect_uris: opts[:redirect_uri],
|
||||||
|
trusted: true,
|
||||||
|
scopes: scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
with {:ok, app} <- Pleroma.Web.OAuth.App.create(params) do
|
||||||
|
shell_info("#{app.client_name} successfully created:")
|
||||||
|
shell_info("App client_id: " <> app.client_id)
|
||||||
|
shell_info("App client_secret: " <> app.client_secret)
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
shell_error("Creating failed:")
|
||||||
|
|
||||||
|
Enum.each(Pleroma.Web.OAuth.App.errors(changeset), fn {key, error} ->
|
||||||
|
shell_error("#{key}: #{error}")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,17 +27,13 @@ defmodule Pleroma.Activity do
|
||||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||||
@mastodon_notification_types %{
|
@mastodon_notification_types %{
|
||||||
"Create" => "mention",
|
"Create" => "mention",
|
||||||
"Follow" => "follow",
|
"Follow" => ["follow", "follow_request"],
|
||||||
"Announce" => "reblog",
|
"Announce" => "reblog",
|
||||||
"Like" => "favourite",
|
"Like" => "favourite",
|
||||||
"Move" => "move",
|
"Move" => "move",
|
||||||
"EmojiReact" => "pleroma:emoji_reaction"
|
"EmojiReact" => "pleroma:emoji_reaction"
|
||||||
}
|
}
|
||||||
|
|
||||||
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
|
||||||
into: %{},
|
|
||||||
do: {v, k}
|
|
||||||
|
|
||||||
schema "activities" do
|
schema "activities" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
|
@ -291,15 +287,43 @@ defp purge_web_resp_cache(%Activity{} = activity) do
|
||||||
|
|
||||||
defp purge_web_resp_cache(nil), do: nil
|
defp purge_web_resp_cache(nil), do: nil
|
||||||
|
|
||||||
for {ap_type, type} <- @mastodon_notification_types do
|
def follow_accepted?(
|
||||||
|
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
|
||||||
|
) do
|
||||||
|
with %User{} = follower <- Activity.user_actor(activity),
|
||||||
|
%User{} = followed <- User.get_cached_by_ap_id(followed_ap_id) do
|
||||||
|
Pleroma.FollowingRelationship.following?(follower, followed)
|
||||||
|
else
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_accepted?(_), do: false
|
||||||
|
|
||||||
|
@spec mastodon_notification_type(Activity.t()) :: String.t() | nil
|
||||||
|
|
||||||
|
for {ap_type, type} <- @mastodon_notification_types, not is_list(type) do
|
||||||
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
||||||
do: unquote(type)
|
do: unquote(type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mastodon_notification_type(%Activity{data: %{"type" => "Follow"}} = activity) do
|
||||||
|
if follow_accepted?(activity) do
|
||||||
|
"follow"
|
||||||
|
else
|
||||||
|
"follow_request"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def mastodon_notification_type(%Activity{}), do: nil
|
def mastodon_notification_type(%Activity{}), do: nil
|
||||||
|
|
||||||
|
@spec from_mastodon_notification_type(String.t()) :: String.t() | nil
|
||||||
|
@doc "Converts Mastodon notification type to AR activity type"
|
||||||
def from_mastodon_notification_type(type) do
|
def from_mastodon_notification_type(type) do
|
||||||
Map.get(@mastodon_to_ap_notification_types, type)
|
with {k, _v} <-
|
||||||
|
Enum.find(@mastodon_notification_types, fn {_k, v} -> type in List.wrap(v) end) do
|
||||||
|
k
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_by_actor_and_id(actor, status_ids \\ [])
|
def all_by_actor_and_id(actor, status_ids \\ [])
|
||||||
|
|
|
@ -122,7 +122,7 @@ defp configure({_, :backends, _, merged}) do
|
||||||
:ok = update_env(:logger, :backends, merged)
|
:ok = update_env(:logger, :backends, merged)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp configure({group, key, _, merged}) do
|
defp configure({_, key, _, merged}) when key in [:console, :ex_syslogger] do
|
||||||
merged =
|
merged =
|
||||||
if key == :console do
|
if key == :console do
|
||||||
put_in(merged[:format], merged[:format] <> "\n")
|
put_in(merged[:format], merged[:format] <> "\n")
|
||||||
|
@ -136,7 +136,12 @@ defp configure({group, key, _, merged}) do
|
||||||
else: key
|
else: key
|
||||||
|
|
||||||
Logger.configure_backend(backend, merged)
|
Logger.configure_backend(backend, merged)
|
||||||
:ok = update_env(:logger, group, merged)
|
:ok = update_env(:logger, key, merged)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp configure({_, key, _, merged}) do
|
||||||
|
Logger.configure([{key, merged}])
|
||||||
|
:ok = update_env(:logger, key, merged)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update({group, key, value, merged}) do
|
defp update({group, key, value, merged}) do
|
||||||
|
|
|
@ -38,22 +38,14 @@ def demojify(text) do
|
||||||
|
|
||||||
def demojify(text, nil), do: text
|
def demojify(text, nil), do: text
|
||||||
|
|
||||||
@doc "Outputs a list of the emoji-shortcodes in a text"
|
|
||||||
def get_emoji(text) when is_binary(text) do
|
|
||||||
Enum.filter(Emoji.get_all(), fn {emoji, %Emoji{}} ->
|
|
||||||
String.contains?(text, ":#{emoji}:")
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_emoji(_), do: []
|
|
||||||
|
|
||||||
@doc "Outputs a list of the emoji-Maps in a text"
|
@doc "Outputs a list of the emoji-Maps in a text"
|
||||||
def get_emoji_map(text) when is_binary(text) do
|
def get_emoji_map(text) when is_binary(text) do
|
||||||
get_emoji(text)
|
Emoji.get_all()
|
||||||
|
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|
||||||
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
|
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
|
||||||
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_emoji_map(_), do: []
|
def get_emoji_map(_), do: %{}
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,7 +31,7 @@ def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
|
||||||
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)
|
user_url = user.uri || user.ap_id
|
||||||
nickname_text = get_nickname_text(nickname, opts)
|
nickname_text = get_nickname_text(nickname, opts)
|
||||||
|
|
||||||
link =
|
link =
|
||||||
|
@ -42,7 +42,7 @@ def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||||
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
|
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
|
||||||
"data-user": id,
|
"data-user": id,
|
||||||
class: "u-url mention",
|
class: "u-url mention",
|
||||||
href: ap_id,
|
href: user_url,
|
||||||
rel: "ugc"
|
rel: "ugc"
|
||||||
),
|
),
|
||||||
class: "h-card"
|
class: "h-card"
|
||||||
|
@ -146,9 +146,6 @@ def truncate(text, max_length \\ 200, omission \\ "...") do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url
|
|
||||||
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)
|
||||||
end
|
end
|
||||||
|
|
|
@ -283,8 +283,17 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_notifications(%Activity{data: %{"type" => "Follow"}} = activity) do
|
||||||
|
if Pleroma.Config.get([:notifications, :enable_follow_request_notifications]) ||
|
||||||
|
Activity.follow_accepted?(activity) do
|
||||||
|
do_create_notifications(activity)
|
||||||
|
else
|
||||||
|
{:ok, []}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||||
when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do
|
when type in ["Like", "Announce", "Move", "EmojiReact"] do
|
||||||
do_create_notifications(activity)
|
do_create_notifications(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Plugs.AuthExpectedPlug do
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(options), do: options
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
put_private(conn, :auth_expected, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_expected?(conn) do
|
||||||
|
conn.private[:auth_expected]
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.AuthenticationPlug do
|
defmodule Pleroma.Plugs.AuthenticationPlug do
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
import Plug.Conn
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def init(options), do: options
|
def init(options), do: options
|
||||||
|
@ -37,6 +40,7 @@ def call(
|
||||||
if Pbkdf2.checkpw(password, password_hash) do
|
if Pbkdf2.checkpw(password, password_hash) do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, auth_user)
|
|> assign(:user, auth_user)
|
||||||
|
|> OAuthScopesPlug.skip_plug()
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.LegacyAuthenticationPlug do
|
defmodule Pleroma.Plugs.LegacyAuthenticationPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
|
@ -27,6 +29,7 @@ def call(
|
||||||
conn
|
conn
|
||||||
|> assign(:auth_user, user)
|
|> assign(:auth_user, user)
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|
|> OAuthScopesPlug.skip_plug()
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -8,12 +8,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
|
|
||||||
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
@behaviour Plug
|
@behaviour Plug
|
||||||
|
|
||||||
def init(%{scopes: _} = options), do: options
|
def init(%{scopes: _} = options), do: options
|
||||||
|
|
||||||
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
op = options[:op] || :|
|
op = options[:op] || :|
|
||||||
token = assigns[:token]
|
token = assigns[:token]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Plugs.PlugHelper do
|
||||||
|
@moduledoc "Pleroma Plug helper"
|
||||||
|
|
||||||
|
@called_plugs_list_id :called_plugs
|
||||||
|
def called_plugs_list_id, do: @called_plugs_list_id
|
||||||
|
|
||||||
|
@skipped_plugs_list_id :skipped_plugs
|
||||||
|
def skipped_plugs_list_id, do: @skipped_plugs_list_id
|
||||||
|
|
||||||
|
@doc "Returns `true` if specified plug was called."
|
||||||
|
def plug_called?(conn, plug_module) do
|
||||||
|
contained_in_private_list?(conn, @called_plugs_list_id, plug_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Returns `true` if specified plug was explicitly marked as skipped."
|
||||||
|
def plug_skipped?(conn, plug_module) do
|
||||||
|
contained_in_private_list?(conn, @skipped_plugs_list_id, plug_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Returns `true` if specified plug was either called or explicitly marked as skipped."
|
||||||
|
def plug_called_or_skipped?(conn, plug_module) do
|
||||||
|
plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Appends plug to known list (skipped, called). Intended to be used from within plug code only.
|
||||||
|
def append_to_private_list(conn, list_id, value) do
|
||||||
|
list = conn.private[list_id] || []
|
||||||
|
modified_list = Enum.uniq(list ++ [value])
|
||||||
|
Plug.Conn.put_private(conn, list_id, modified_list)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp contained_in_private_list?(conn, private_variable, value) do
|
||||||
|
list = conn.private[private_variable] || []
|
||||||
|
value in list
|
||||||
|
end
|
||||||
|
end
|
|
@ -110,20 +110,9 @@ defp handle(conn, action_settings) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def disabled?(conn) do
|
def disabled?(conn) do
|
||||||
localhost_or_socket =
|
|
||||||
case Config.get([Pleroma.Web.Endpoint, :http, :ip]) do
|
|
||||||
{127, 0, 0, 1} -> true
|
|
||||||
{0, 0, 0, 0, 0, 0, 0, 1} -> true
|
|
||||||
{:local, _} -> true
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
|
|
||||||
remote_ip_not_found =
|
|
||||||
if Map.has_key?(conn.assigns, :remote_ip_found),
|
if Map.has_key?(conn.assigns, :remote_ip_found),
|
||||||
do: !conn.assigns.remote_ip_found,
|
do: !conn.assigns.remote_ip_found,
|
||||||
else: false
|
else: false
|
||||||
|
|
||||||
localhost_or_socket and remote_ip_not_found
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@inspect_bucket_not_found {:error, :not_found}
|
@inspect_bucket_not_found {:error, :not_found}
|
||||||
|
|
|
@ -7,8 +7,6 @@ defmodule Pleroma.Plugs.RemoteIp do
|
||||||
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
|
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import Plug.Conn
|
|
||||||
|
|
||||||
@behaviour Plug
|
@behaviour Plug
|
||||||
|
|
||||||
@headers ~w[
|
@headers ~w[
|
||||||
|
@ -28,12 +26,11 @@ defmodule Pleroma.Plugs.RemoteIp do
|
||||||
|
|
||||||
def init(_), do: nil
|
def init(_), do: nil
|
||||||
|
|
||||||
def call(%{remote_ip: original_remote_ip} = conn, _) do
|
def call(conn, _) do
|
||||||
config = Pleroma.Config.get(__MODULE__, [])
|
config = Pleroma.Config.get(__MODULE__, [])
|
||||||
|
|
||||||
if Keyword.get(config, :enabled, false) do
|
if Keyword.get(config, :enabled, false) do
|
||||||
%{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts(config))
|
RemoteIp.call(conn, remote_ip_opts(config))
|
||||||
assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip)
|
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,6 +41,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
conn ->
|
conn ->
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|> merge_resp_headers([{"content-security-policy", "sandbox"}])
|
||||||
|
|
||||||
config = Pleroma.Config.get(Pleroma.Upload)
|
config = Pleroma.Config.get(Pleroma.Upload)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# A test controller reachable only in :test env.
|
||||||
|
# Serves to test OAuth scopes check skipping / enforcement.
|
||||||
|
defmodule Pleroma.Tests.OAuthTestController do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth)
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth)
|
||||||
|
|
||||||
|
def skipped_oauth(conn, _params) do
|
||||||
|
noop(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def performed_oauth(conn, _params) do
|
||||||
|
noop(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def missed_oauth(conn, _params) do
|
||||||
|
noop(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp noop(conn), do: json(conn, %{})
|
||||||
|
end
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Delivery
|
alias Pleroma.Delivery
|
||||||
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
@ -28,6 +29,7 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||||
|
@ -82,6 +84,7 @@ defmodule Pleroma.User do
|
||||||
field(:password, :string, virtual: true)
|
field(:password, :string, virtual: true)
|
||||||
field(:password_confirmation, :string, virtual: true)
|
field(:password_confirmation, :string, virtual: true)
|
||||||
field(:keys, :string)
|
field(:keys, :string)
|
||||||
|
field(:public_key, :string)
|
||||||
field(:ap_id, :string)
|
field(:ap_id, :string)
|
||||||
field(:avatar, :map)
|
field(:avatar, :map)
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
|
@ -94,7 +97,6 @@ defmodule Pleroma.User do
|
||||||
field(:last_digest_emailed_at, :naive_datetime)
|
field(:last_digest_emailed_at, :naive_datetime)
|
||||||
field(:banner, :map, default: %{})
|
field(:banner, :map, default: %{})
|
||||||
field(:background, :map, default: %{})
|
field(:background, :map, default: %{})
|
||||||
field(:source_data, :map, default: %{})
|
|
||||||
field(:note_count, :integer, default: 0)
|
field(:note_count, :integer, default: 0)
|
||||||
field(:follower_count, :integer, default: 0)
|
field(:follower_count, :integer, default: 0)
|
||||||
field(:following_count, :integer, default: 0)
|
field(:following_count, :integer, default: 0)
|
||||||
|
@ -112,7 +114,7 @@ defmodule Pleroma.User do
|
||||||
field(:show_role, :boolean, default: true)
|
field(:show_role, :boolean, default: true)
|
||||||
field(:settings, :map, default: nil)
|
field(:settings, :map, default: nil)
|
||||||
field(:magic_key, :string, default: nil)
|
field(:magic_key, :string, default: nil)
|
||||||
field(:uri, :string, default: nil)
|
field(:uri, Types.Uri, default: nil)
|
||||||
field(:hide_followers_count, :boolean, default: false)
|
field(:hide_followers_count, :boolean, default: false)
|
||||||
field(:hide_follows_count, :boolean, default: false)
|
field(:hide_follows_count, :boolean, default: false)
|
||||||
field(:hide_followers, :boolean, default: false)
|
field(:hide_followers, :boolean, default: false)
|
||||||
|
@ -122,7 +124,7 @@ defmodule Pleroma.User do
|
||||||
field(:pinned_activities, {:array, :string}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
field(:email_notifications, :map, default: %{"digest" => false})
|
field(:email_notifications, :map, default: %{"digest" => false})
|
||||||
field(:mascot, :map, default: nil)
|
field(:mascot, :map, default: nil)
|
||||||
field(:emoji, {:array, :map}, default: [])
|
field(:emoji, :map, default: %{})
|
||||||
field(:pleroma_settings_store, :map, default: %{})
|
field(:pleroma_settings_store, :map, default: %{})
|
||||||
field(:fields, {:array, :map}, default: [])
|
field(:fields, {:array, :map}, default: [])
|
||||||
field(:raw_fields, {:array, :map}, default: [])
|
field(:raw_fields, {:array, :map}, default: [])
|
||||||
|
@ -132,6 +134,8 @@ defmodule Pleroma.User do
|
||||||
field(:skip_thread_containment, :boolean, default: false)
|
field(:skip_thread_containment, :boolean, default: false)
|
||||||
field(:actor_type, :string, default: "Person")
|
field(:actor_type, :string, default: "Person")
|
||||||
field(:also_known_as, {:array, :string}, default: [])
|
field(:also_known_as, {:array, :string}, default: [])
|
||||||
|
field(:inbox, :string)
|
||||||
|
field(:shared_inbox, :string)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
|
@ -306,6 +310,7 @@ def banner_url(user, options \\ []) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Should probably be renamed or removed
|
||||||
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
|
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
|
||||||
|
|
||||||
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
||||||
|
@ -339,32 +344,53 @@ defp truncate_if_exists(params, key, max_length) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def remote_user_creation(params) do
|
defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params
|
||||||
|
|
||||||
|
defp fix_follower_address(%{nickname: nickname} = params),
|
||||||
|
do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname}))
|
||||||
|
|
||||||
|
defp fix_follower_address(params), do: params
|
||||||
|
|
||||||
|
def remote_user_changeset(struct \\ %User{local: false}, params) do
|
||||||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
||||||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
||||||
|
|
||||||
|
name =
|
||||||
|
case params[:name] do
|
||||||
|
name when is_binary(name) and byte_size(name) > 0 -> name
|
||||||
|
_ -> params[:nickname]
|
||||||
|
end
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|
|> Map.put(:name, name)
|
||||||
|
|> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
|
||||||
|> truncate_if_exists(:name, name_limit)
|
|> truncate_if_exists(:name, name_limit)
|
||||||
|> truncate_if_exists(:bio, bio_limit)
|
|> truncate_if_exists(:bio, bio_limit)
|
||||||
|> truncate_fields_param()
|
|> truncate_fields_param()
|
||||||
|
|> fix_follower_address()
|
||||||
|
|
||||||
changeset =
|
struct
|
||||||
%User{local: false}
|
|
||||||
|> cast(
|
|> cast(
|
||||||
params,
|
params,
|
||||||
[
|
[
|
||||||
:bio,
|
:bio,
|
||||||
:name,
|
:name,
|
||||||
|
:emoji,
|
||||||
:ap_id,
|
:ap_id,
|
||||||
|
:inbox,
|
||||||
|
:shared_inbox,
|
||||||
:nickname,
|
:nickname,
|
||||||
|
:public_key,
|
||||||
:avatar,
|
:avatar,
|
||||||
:ap_enabled,
|
:ap_enabled,
|
||||||
:source_data,
|
|
||||||
:banner,
|
:banner,
|
||||||
:locked,
|
:locked,
|
||||||
|
:last_refreshed_at,
|
||||||
:magic_key,
|
:magic_key,
|
||||||
:uri,
|
:uri,
|
||||||
|
:follower_address,
|
||||||
|
:following_address,
|
||||||
:hide_followers,
|
:hide_followers,
|
||||||
:hide_follows,
|
:hide_follows,
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
|
@ -384,17 +410,6 @@ def remote_user_creation(params) do
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, max: name_limit)
|
|> validate_length(:name, max: name_limit)
|
||||||
|> validate_fields(true)
|
|> validate_fields(true)
|
||||||
|
|
||||||
case params[:source_data] do
|
|
||||||
%{"followers" => followers, "following" => following} ->
|
|
||||||
changeset
|
|
||||||
|> put_change(:follower_address, followers)
|
|
||||||
|> put_change(:following_address, following)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
|
|
||||||
put_change(changeset, :follower_address, followers)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_changeset(struct, params \\ %{}) do
|
def update_changeset(struct, params \\ %{}) do
|
||||||
|
@ -407,7 +422,11 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
[
|
[
|
||||||
:bio,
|
:bio,
|
||||||
:name,
|
:name,
|
||||||
|
:emoji,
|
||||||
:avatar,
|
:avatar,
|
||||||
|
:public_key,
|
||||||
|
:inbox,
|
||||||
|
:shared_inbox,
|
||||||
:locked,
|
:locked,
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
:default_scope,
|
:default_scope,
|
||||||
|
@ -434,6 +453,7 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, min: 1, max: name_limit)
|
|> validate_length(:name, min: 1, max: name_limit)
|
||||||
|> put_fields()
|
|> put_fields()
|
||||||
|
|> put_emoji()
|
||||||
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||||
|> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|
|> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|
||||||
|> put_change_if_present(:banner, &put_upload(&1, :banner))
|
|> put_change_if_present(:banner, &put_upload(&1, :banner))
|
||||||
|
@ -469,6 +489,18 @@ defp parse_fields(value) do
|
||||||
|> elem(0)
|
|> elem(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_emoji(changeset) do
|
||||||
|
bio = get_change(changeset, :bio)
|
||||||
|
name = get_change(changeset, :name)
|
||||||
|
|
||||||
|
if bio || name do
|
||||||
|
emoji = Map.merge(Emoji.Formatter.get_emoji_map(bio), Emoji.Formatter.get_emoji_map(name))
|
||||||
|
put_change(changeset, :emoji, emoji)
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
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
|
if value = get_change(changeset, map_field) do
|
||||||
with {:ok, new_value} <- value_function.(value) do
|
with {:ok, new_value} <- value_function.(value) do
|
||||||
|
@ -488,49 +520,6 @@ defp put_upload(value, type) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
|
||||||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
|
||||||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
|
||||||
|
|
||||||
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
|
|
||||||
|
|
||||||
params = if remote?, do: truncate_fields_param(params), else: params
|
|
||||||
|
|
||||||
struct
|
|
||||||
|> cast(
|
|
||||||
params,
|
|
||||||
[
|
|
||||||
:bio,
|
|
||||||
:name,
|
|
||||||
:follower_address,
|
|
||||||
:following_address,
|
|
||||||
:avatar,
|
|
||||||
:last_refreshed_at,
|
|
||||||
:ap_enabled,
|
|
||||||
:source_data,
|
|
||||||
:banner,
|
|
||||||
:locked,
|
|
||||||
:magic_key,
|
|
||||||
:follower_count,
|
|
||||||
:following_count,
|
|
||||||
:hide_follows,
|
|
||||||
:fields,
|
|
||||||
:hide_followers,
|
|
||||||
:allow_following_move,
|
|
||||||
:discoverable,
|
|
||||||
:hide_followers_count,
|
|
||||||
:hide_follows_count,
|
|
||||||
:actor_type,
|
|
||||||
:also_known_as
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> unique_constraint(:nickname)
|
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|
||||||
|> validate_length(:name, max: name_limit)
|
|
||||||
|> validate_fields(remote?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_as_admin_changeset(struct, params) do
|
def update_as_admin_changeset(struct, params) do
|
||||||
struct
|
struct
|
||||||
|> update_changeset(params)
|
|> update_changeset(params)
|
||||||
|
@ -606,7 +595,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|> confirmation_changeset(need_confirmation: need_confirmation?)
|
|> confirmation_changeset(need_confirmation: need_confirmation?)
|
||||||
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
|
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation, :emoji])
|
||||||
|> validate_required([:name, :nickname, :password, :password_confirmation])
|
|> validate_required([:name, :nickname, :password, :password_confirmation])
|
||||||
|> validate_confirmation(:password)
|
|> validate_confirmation(:password)
|
||||||
|> unique_constraint(:email)
|
|> unique_constraint(:email)
|
||||||
|
@ -699,6 +688,8 @@ def needs_update?(%User{local: false} = user) do
|
||||||
def needs_update?(_), do: true
|
def needs_update?(_), do: true
|
||||||
|
|
||||||
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
|
|
||||||
|
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||||
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
|
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
|
||||||
follow(follower, followed, :follow_pending)
|
follow(follower, followed, :follow_pending)
|
||||||
end
|
end
|
||||||
|
@ -1621,8 +1612,7 @@ defp create_service_actor(uri, nickname) do
|
||||||
|> set_cache()
|
|> set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
# AP style
|
def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
|
||||||
def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do
|
|
||||||
key =
|
key =
|
||||||
public_key_pem
|
public_key_pem
|
||||||
|> :public_key.pem_decode()
|
|> :public_key.pem_decode()
|
||||||
|
@ -1632,7 +1622,7 @@ def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pe
|
||||||
{:ok, key}
|
{:ok, key}
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_key(_), do: {:error, "not found key"}
|
def public_key(_), do: {:error, "key not found"}
|
||||||
|
|
||||||
def get_public_key_for_ap_id(ap_id) do
|
def get_public_key_for_ap_id(ap_id) do
|
||||||
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
|
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
|
||||||
|
@ -1643,17 +1633,6 @@ def get_public_key_for_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp blank?(""), do: nil
|
|
||||||
defp blank?(n), do: n
|
|
||||||
|
|
||||||
def insert_or_update_user(data) do
|
|
||||||
data
|
|
||||||
|> Map.put(:name, blank?(data[:name]) || data[:nickname])
|
|
||||||
|> remote_user_creation()
|
|
||||||
|> Repo.insert(on_conflict: {:replace_all_except, [:id]}, conflict_target: :nickname)
|
|
||||||
|> set_cache()
|
|
||||||
end
|
|
||||||
|
|
||||||
def ap_enabled?(%User{local: true}), do: true
|
def ap_enabled?(%User{local: true}), do: true
|
||||||
def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
|
def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
|
||||||
def ap_enabled?(_), do: false
|
def ap_enabled?(_), do: false
|
||||||
|
@ -1962,12 +1941,6 @@ def update_background(user, background) do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_source_data(user, source_data) do
|
|
||||||
user
|
|
||||||
|> cast(%{source_data: source_data}, [:source_data])
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
|
||||||
|
|
||||||
def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
|
def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
|
||||||
%{
|
%{
|
||||||
admin: is_admin,
|
admin: is_admin,
|
||||||
|
@ -1975,21 +1948,6 @@ def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
|
||||||
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
|
||||||
def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
|
|
||||||
limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
|
|
||||||
|
|
||||||
attachment
|
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
|
||||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
|
||||||
|> Enum.take(limit)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fields(%{fields: nil}), do: []
|
|
||||||
|
|
||||||
def fields(%{fields: fields}), do: fields
|
|
||||||
|
|
||||||
def validate_fields(changeset, remote? \\ false) do
|
def validate_fields(changeset, remote? \\ false) do
|
||||||
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
||||||
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
||||||
|
@ -2177,9 +2135,7 @@ def sanitize_html(%User{} = user) do
|
||||||
# - display name
|
# - display name
|
||||||
def sanitize_html(%User{} = user, filter) do
|
def sanitize_html(%User{} = user, filter) do
|
||||||
fields =
|
fields =
|
||||||
user
|
Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
|
||||||
|> User.fields()
|
|
||||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
|
||||||
%{
|
%{
|
||||||
"name" => name,
|
"name" => name,
|
||||||
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||||
|
|
|
@ -1427,19 +1427,44 @@ defp object_to_user_data(data) do
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||||
|
|
||||||
|
emojis =
|
||||||
|
data
|
||||||
|
|> Map.get("tag", [])
|
||||||
|
|> Enum.filter(fn
|
||||||
|
%{"type" => "Emoji"} -> true
|
||||||
|
_ -> false
|
||||||
|
end)
|
||||||
|
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
|
||||||
|
Map.put(acc, String.trim(name, ":"), url)
|
||||||
|
end)
|
||||||
|
|
||||||
locked = data["manuallyApprovesFollowers"] || false
|
locked = data["manuallyApprovesFollowers"] || false
|
||||||
data = Transmogrifier.maybe_fix_user_object(data)
|
data = Transmogrifier.maybe_fix_user_object(data)
|
||||||
discoverable = data["discoverable"] || false
|
discoverable = data["discoverable"] || false
|
||||||
invisible = data["invisible"] || false
|
invisible = data["invisible"] || false
|
||||||
actor_type = data["type"] || "Person"
|
actor_type = data["type"] || "Person"
|
||||||
|
|
||||||
|
public_key =
|
||||||
|
if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
|
||||||
|
data["publicKey"]["publicKeyPem"]
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_inbox =
|
||||||
|
if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
|
||||||
|
data["endpoints"]["sharedInbox"]
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
user_data = %{
|
user_data = %{
|
||||||
ap_id: data["id"],
|
ap_id: data["id"],
|
||||||
uri: get_actor_url(data["url"]),
|
uri: get_actor_url(data["url"]),
|
||||||
ap_enabled: true,
|
ap_enabled: true,
|
||||||
source_data: data,
|
|
||||||
banner: banner,
|
banner: banner,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
|
emoji: emojis,
|
||||||
locked: locked,
|
locked: locked,
|
||||||
discoverable: discoverable,
|
discoverable: discoverable,
|
||||||
invisible: invisible,
|
invisible: invisible,
|
||||||
|
@ -1449,7 +1474,10 @@ defp object_to_user_data(data) do
|
||||||
following_address: data["following"],
|
following_address: data["following"],
|
||||||
bio: data["summary"],
|
bio: data["summary"],
|
||||||
actor_type: actor_type,
|
actor_type: actor_type,
|
||||||
also_known_as: Map.get(data, "alsoKnownAs", [])
|
also_known_as: Map.get(data, "alsoKnownAs", []),
|
||||||
|
public_key: public_key,
|
||||||
|
inbox: data["inbox"],
|
||||||
|
shared_inbox: shared_inbox
|
||||||
}
|
}
|
||||||
|
|
||||||
# nickname can be nil because of virtual actors
|
# nickname can be nil because of virtual actors
|
||||||
|
@ -1551,11 +1579,22 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_user_from_ap_id(ap_id) do
|
def make_user_from_ap_id(ap_id) do
|
||||||
if _user = User.get_cached_by_ap_id(ap_id) do
|
user = User.get_cached_by_ap_id(ap_id)
|
||||||
|
|
||||||
|
if user && !User.ap_enabled?(user) do
|
||||||
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||||
else
|
else
|
||||||
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
|
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
User.insert_or_update_user(data)
|
if user do
|
||||||
|
user
|
||||||
|
|> User.remote_user_changeset(data)
|
||||||
|
|> User.update_and_set_cache()
|
||||||
|
else
|
||||||
|
data
|
||||||
|
|> User.remote_user_changeset()
|
||||||
|
|> Repo.insert()
|
||||||
|
|> User.set_cache()
|
||||||
|
end
|
||||||
else
|
else
|
||||||
e -> {:error, e}
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||||
@moduledoc "Filter activities depending on their age"
|
@moduledoc "Filter activities depending on their age"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp check_date(%{"published" => published} = message) do
|
defp check_date(%{"object" => %{"published" => published}} = message) do
|
||||||
with %DateTime{} = now <- DateTime.utc_now(),
|
with %DateTime{} = now <- DateTime.utc_now(),
|
||||||
{:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published),
|
{:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published),
|
||||||
max_ttl <- Config.get([:mrf_object_age, :threshold]),
|
max_ttl <- Config.get([:mrf_object_age, :threshold]),
|
||||||
|
@ -96,5 +96,11 @@ def filter(%{"type" => "Create", "published" => _} = message) do
|
||||||
def filter(message), do: {:ok, message}
|
def filter(message), do: {:ok, message}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def describe, do: {:ok, %{}}
|
def describe do
|
||||||
|
mrf_object_age =
|
||||||
|
Pleroma.Config.get(:mrf_object_age)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
{:ok, %{mrf_object_age: mrf_object_age}}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,6 +148,21 @@ defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image
|
||||||
|
|
||||||
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(%{"type" => "Delete", "actor" => actor} = object) do
|
||||||
|
%{host: actor_host} = URI.parse(actor)
|
||||||
|
|
||||||
|
reject_deletes =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :reject_deletes])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(reject_deletes, actor_host) do
|
||||||
|
{:reject, nil}
|
||||||
|
else
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"actor" => actor} = object) do
|
def filter(%{"actor" => actor} = object) do
|
||||||
actor_info = URI.parse(actor)
|
actor_info = URI.parse(actor)
|
||||||
|
|
|
@ -35,6 +35,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
||||||
field(:like_count, :integer, default: 0)
|
field(:like_count, :integer, default: 0)
|
||||||
field(:announcement_count, :integer, default: 0)
|
field(:announcement_count, :integer, default: 0)
|
||||||
field(:inRepyTo, :string)
|
field(:inRepyTo, :string)
|
||||||
|
field(:uri, Types.Uri)
|
||||||
|
|
||||||
field(:likes, {:array, :string}, default: [])
|
field(:likes, {:array, :string}, default: [])
|
||||||
field(:announcements, {:array, :string}, default: [])
|
field(:announcements, {:array, :string}, default: [])
|
||||||
|
|
|
@ -15,15 +15,9 @@ def cast(object) when is_binary(object) do
|
||||||
|
|
||||||
def cast(%{"id" => object}), do: cast(object)
|
def cast(%{"id" => object}), do: cast(object)
|
||||||
|
|
||||||
def cast(_) do
|
def cast(_), do: :error
|
||||||
:error
|
|
||||||
end
|
|
||||||
|
|
||||||
def dump(data) do
|
def dump(data), do: {:ok, data}
|
||||||
{:ok, data}
|
|
||||||
end
|
|
||||||
|
|
||||||
def load(data) do
|
def load(data), do: {:ok, data}
|
||||||
{:ok, data}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.Uri do
|
||||||
|
use Ecto.Type
|
||||||
|
|
||||||
|
def type, do: :string
|
||||||
|
|
||||||
|
def cast(uri) when is_binary(uri) do
|
||||||
|
case URI.parse(uri) do
|
||||||
|
%URI{host: nil} -> :error
|
||||||
|
%URI{host: ""} -> :error
|
||||||
|
%URI{scheme: scheme} when scheme in ["https", "http"] -> {:ok, uri}
|
||||||
|
_ -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast(_), do: :error
|
||||||
|
|
||||||
|
def dump(data), do: {:ok, data}
|
||||||
|
|
||||||
|
def load(data), do: {:ok, data}
|
||||||
|
end
|
|
@ -141,8 +141,8 @@ defp get_cc_ap_ids(ap_id, recipients) do
|
||||||
|> Enum.map(& &1.ap_id)
|
|> Enum.map(& &1.ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_use_sharedinbox(%User{source_data: data}),
|
defp maybe_use_sharedinbox(%User{shared_inbox: nil, inbox: inbox}), do: inbox
|
||||||
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
defp maybe_use_sharedinbox(%User{shared_inbox: shared_inbox}), do: shared_inbox
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Determine a user inbox to use based on heuristics. These heuristics
|
Determine a user inbox to use based on heuristics. These heuristics
|
||||||
|
@ -157,7 +157,7 @@ defp maybe_use_sharedinbox(%User{source_data: data}),
|
||||||
"""
|
"""
|
||||||
def determine_inbox(
|
def determine_inbox(
|
||||||
%Activity{data: activity_data},
|
%Activity{data: activity_data},
|
||||||
%User{source_data: data} = user
|
%User{inbox: inbox} = user
|
||||||
) do
|
) do
|
||||||
to = activity_data["to"] || []
|
to = activity_data["to"] || []
|
||||||
cc = activity_data["cc"] || []
|
cc = activity_data["cc"] || []
|
||||||
|
@ -174,7 +174,7 @@ def determine_inbox(
|
||||||
maybe_use_sharedinbox(user)
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
data["inbox"]
|
inbox
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -192,14 +192,13 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
||||||
inboxes =
|
inboxes =
|
||||||
recipients
|
recipients
|
||||||
|> Enum.filter(&User.ap_enabled?/1)
|
|> Enum.filter(&User.ap_enabled?/1)
|
||||||
|> Enum.map(fn %{source_data: data} -> data["inbox"] end)
|
|> Enum.map(fn actor -> actor.inbox end)
|
||||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||||
|> Instances.filter_reachable()
|
|> Instances.filter_reachable()
|
||||||
|
|
||||||
Repo.checkout(fn ->
|
Repo.checkout(fn ->
|
||||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||||
%User{ap_id: ap_id} =
|
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
|
||||||
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
|
|
||||||
|
|
||||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||||
|
|
|
@ -17,7 +17,9 @@ def handle(object, meta \\ [])
|
||||||
def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
||||||
liked_object = Object.get_by_ap_id(object.data["object"])
|
liked_object = Object.get_by_ap_id(object.data["object"])
|
||||||
Utils.add_like_to_object(object, liked_object)
|
Utils.add_like_to_object(object, liked_object)
|
||||||
|
|
||||||
Notification.create_notifications(object)
|
Notification.create_notifications(object)
|
||||||
|
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -711,7 +711,7 @@ def handle_incoming(
|
||||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||||
|
|
||||||
actor
|
actor
|
||||||
|> User.upgrade_changeset(new_user_data, true)
|
|> User.remote_user_changeset(new_user_data)
|
||||||
|> User.update_and_set_cache()
|
|> User.update_and_set_cache()
|
||||||
|
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
|
@ -1160,7 +1160,7 @@ defp build_mention_tag(%{ap_id: ap_id, nickname: nickname} = _) do
|
||||||
|
|
||||||
def take_emoji_tags(%User{emoji: emoji}) do
|
def take_emoji_tags(%User{emoji: emoji}) do
|
||||||
emoji
|
emoji
|
||||||
|> Enum.flat_map(&Map.to_list/1)
|
|> Map.to_list()
|
||||||
|> Enum.map(&build_emoji_tag/1)
|
|> Enum.map(&build_emoji_tag/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1254,12 +1254,8 @@ def perform(:user_upgrade, user) do
|
||||||
def upgrade_user_from_ap_id(ap_id) do
|
def upgrade_user_from_ap_id(ap_id) do
|
||||||
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
|
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
|
||||||
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
||||||
already_ap <- User.ap_enabled?(user),
|
{:ok, user} <- update_user(user, data) do
|
||||||
{:ok, user} <- upgrade_user(user, data) do
|
|
||||||
if not already_ap do
|
|
||||||
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
|
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
%User{} = user -> {:ok, user}
|
%User{} = user -> {:ok, user}
|
||||||
|
@ -1267,9 +1263,9 @@ def upgrade_user_from_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp upgrade_user(user, data) do
|
defp update_user(user, data) do
|
||||||
user
|
user
|
||||||
|> User.upgrade_changeset(data, true)
|
|> User.remote_user_changeset(data)
|
||||||
|> User.update_and_set_cache()
|
|> User.update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,7 @@ def render("user.json", %{user: user}) do
|
||||||
|
|
||||||
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
||||||
|
|
||||||
fields =
|
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||||
user
|
|
||||||
|> User.fields()
|
|
||||||
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
|
@ -103,7 +100,7 @@ def render("user.json", %{user: user}) do
|
||||||
},
|
},
|
||||||
"endpoints" => endpoints,
|
"endpoints" => endpoints,
|
||||||
"attachment" => fields,
|
"attachment" => fields,
|
||||||
"tag" => (user.source_data["tag"] || []) ++ emoji_tags,
|
"tag" => emoji_tags,
|
||||||
"discoverable" => user.discoverable
|
"discoverable" => user.discoverable
|
||||||
}
|
}
|
||||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||||
|
|
|
@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.MastodonAPI.AppView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -914,16 +916,7 @@ def config_show(conn, _params) do
|
||||||
end)
|
end)
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|
||||||
response = %{configs: merged}
|
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
|
|
||||||
response =
|
|
||||||
if Restarter.Pleroma.need_reboot?() do
|
|
||||||
Map.put(response, :need_reboot, true)
|
|
||||||
else
|
|
||||||
response
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, response)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -950,28 +943,22 @@ def config_update(conn, %{"configs" => configs}) do
|
||||||
|
|
||||||
Config.TransferTask.load_and_update_env(deleted, false)
|
Config.TransferTask.load_and_update_env(deleted, false)
|
||||||
|
|
||||||
need_reboot? =
|
if !Restarter.Pleroma.need_reboot?() do
|
||||||
Restarter.Pleroma.need_reboot?() ||
|
changed_reboot_settings? =
|
||||||
Enum.any?(updated, fn config ->
|
(updated ++ deleted)
|
||||||
|
|> Enum.any?(fn config ->
|
||||||
group = ConfigDB.from_string(config.group)
|
group = ConfigDB.from_string(config.group)
|
||||||
key = ConfigDB.from_string(config.key)
|
key = ConfigDB.from_string(config.key)
|
||||||
value = ConfigDB.from_binary(config.value)
|
value = ConfigDB.from_binary(config.value)
|
||||||
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
response = %{configs: updated}
|
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
||||||
|
|
||||||
response =
|
|
||||||
if need_reboot? do
|
|
||||||
Restarter.Pleroma.need_reboot()
|
|
||||||
Map.put(response, :need_reboot, need_reboot?)
|
|
||||||
else
|
|
||||||
response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ConfigView)
|
|> put_view(ConfigView)
|
||||||
|> render("index.json", response)
|
|> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -983,6 +970,10 @@ def restart(conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def need_reboot(conn, _params) do
|
||||||
|
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
|
end
|
||||||
|
|
||||||
defp configurable_from_database(conn) do
|
defp configurable_from_database(conn) do
|
||||||
if Config.get(:configurable_from_database) do
|
if Config.get(:configurable_from_database) do
|
||||||
:ok
|
:ok
|
||||||
|
@ -1028,6 +1019,83 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
conn |> json("")
|
conn |> json("")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def oauth_app_create(conn, params) do
|
||||||
|
params =
|
||||||
|
if params["name"] do
|
||||||
|
Map.put(params, "client_name", params["name"])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
result =
|
||||||
|
case App.create(params) do
|
||||||
|
{:ok, app} ->
|
||||||
|
AppView.render("show.json", %{app: app, admin: true})
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
App.errors(changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
json(conn, result)
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_update(conn, params) do
|
||||||
|
params =
|
||||||
|
if params["name"] do
|
||||||
|
Map.put(params, "client_name", params["name"])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, app} <- App.update(params) do
|
||||||
|
json(conn, AppView.render("show.json", %{app: app, admin: true}))
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_list(conn, params) do
|
||||||
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
search_params = %{
|
||||||
|
client_name: params["name"],
|
||||||
|
client_id: params["client_id"],
|
||||||
|
page: page,
|
||||||
|
page_size: page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
search_params =
|
||||||
|
if Map.has_key?(params, "trusted") do
|
||||||
|
Map.put(search_params, :trusted, params["trusted"])
|
||||||
|
else
|
||||||
|
search_params
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, apps, count} <- App.search(search_params) do
|
||||||
|
json(
|
||||||
|
conn,
|
||||||
|
AppView.render("index.json",
|
||||||
|
apps: apps,
|
||||||
|
count: count,
|
||||||
|
page_size: page_size,
|
||||||
|
admin: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_delete(conn, params) do
|
||||||
|
with {:ok, _app} <- App.destroy(params["id"]) do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def stats(conn, _) do
|
def stats(conn, _) do
|
||||||
count = Stats.get_status_visibility_count()
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ApiSpec.Helpers do
|
defmodule Pleroma.Web.ApiSpec.Helpers do
|
||||||
def request_body(description, schema_ref, opts \\ []) do
|
def request_body(description, schema_ref, opts \\ []) do
|
||||||
media_types = ["application/json", "multipart/form-data"]
|
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
|
||||||
|
|
||||||
content =
|
content =
|
||||||
media_types
|
media_types
|
||||||
|
|
|
@ -332,26 +332,6 @@ defp maybe_create_activity_expiration({:ok, activity}, %NaiveDateTime{} = expire
|
||||||
|
|
||||||
defp maybe_create_activity_expiration(result, _), do: result
|
defp maybe_create_activity_expiration(result, _), do: result
|
||||||
|
|
||||||
# Updates the emojis for a user based on their profile
|
|
||||||
def update(user) do
|
|
||||||
emoji = emoji_from_profile(user)
|
|
||||||
source_data = Map.put(user.source_data, "tag", emoji)
|
|
||||||
|
|
||||||
user =
|
|
||||||
case User.update_source_data(user, source_data) do
|
|
||||||
{:ok, user} -> user
|
|
||||||
_ -> user
|
|
||||||
end
|
|
||||||
|
|
||||||
ActivityPub.update(%{
|
|
||||||
local: true,
|
|
||||||
to: [Pleroma.Constants.as_public(), user.follower_address],
|
|
||||||
cc: [],
|
|
||||||
actor: user.ap_id,
|
|
||||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
||||||
with %Activity{
|
with %Activity{
|
||||||
actor: ^user_ap_id,
|
actor: ^user_ap_id,
|
||||||
|
|
|
@ -10,7 +10,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Emoji
|
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Plugs.AuthenticationPlug
|
alias Pleroma.Plugs.AuthenticationPlug
|
||||||
|
@ -18,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Endpoint
|
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -175,7 +173,7 @@ def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_i
|
||||||
"replies" => %{"type" => "Collection", "totalItems" => 0}
|
"replies" => %{"type" => "Collection", "totalItems" => 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
{note, Map.merge(emoji, Emoji.Formatter.get_emoji_map(option))}
|
{note, Map.merge(emoji, Pleroma.Emoji.Formatter.get_emoji_map(option))}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end_time =
|
end_time =
|
||||||
|
@ -431,19 +429,6 @@ def confirm_current_password(user, password) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def emoji_from_profile(%User{bio: bio, name: name}) do
|
|
||||||
[bio, name]
|
|
||||||
|> Enum.map(&Emoji.Formatter.get_emoji/1)
|
|
||||||
|> Enum.concat()
|
|
||||||
|> Enum.map(fn {shortcode, %Emoji{file: path}} ->
|
|
||||||
%{
|
|
||||||
"type" => "Emoji",
|
|
||||||
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{path}"},
|
|
||||||
"name" => ":#{shortcode}:"
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_notify_to_recipients(
|
def maybe_notify_to_recipients(
|
||||||
recipients,
|
recipients,
|
||||||
%Activity{data: %{"to" => to, "type" => _type}} = _activity
|
%Activity{data: %{"to" => to, "type" => _type}} = _activity
|
||||||
|
|
|
@ -23,7 +23,7 @@ def pub_date(date) when is_binary(date) do
|
||||||
def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
|
def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
|
||||||
|
|
||||||
def prepare_activity(activity, opts \\ []) do
|
def prepare_activity(activity, opts \\ []) do
|
||||||
object = activity_object(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
actor =
|
actor =
|
||||||
if opts[:actor] do
|
if opts[:actor] do
|
||||||
|
@ -33,7 +33,6 @@ def prepare_activity(activity, opts \\ []) do
|
||||||
%{
|
%{
|
||||||
activity: activity,
|
activity: activity,
|
||||||
data: Map.get(object, :data),
|
data: Map.get(object, :data),
|
||||||
object: object,
|
|
||||||
actor: actor
|
actor: actor
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -68,9 +67,7 @@ def logo(user) do
|
||||||
|
|
||||||
def last_activity(activities), do: List.last(activities)
|
def last_activity(activities), do: List.last(activities)
|
||||||
|
|
||||||
def activity_object(activity), do: Object.normalize(activity)
|
def activity_title(%{"content" => content}, opts \\ %{}) do
|
||||||
|
|
||||||
def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
|
|
||||||
content
|
content
|
||||||
|> Pleroma.Web.Metadata.Utils.scrub_html()
|
|> Pleroma.Web.Metadata.Utils.scrub_html()
|
||||||
|> Pleroma.Emoji.Formatter.demojify()
|
|> Pleroma.Emoji.Formatter.demojify()
|
||||||
|
@ -78,7 +75,7 @@ def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
|
||||||
|> escape()
|
|> escape()
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity_content(%{data: %{"content" => content}}) do
|
def activity_content(%{"content" => content}) do
|
||||||
content
|
content
|
||||||
|> String.replace(~r/[\n\r]/, "")
|
|> String.replace(~r/[\n\r]/, "")
|
||||||
|> escape()
|
|> escape()
|
||||||
|
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastoFEController do
|
||||||
when action == :index
|
when action == :index
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index)
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest])
|
||||||
|
|
||||||
@doc "GET /web/*path"
|
@doc "GET /web/*path"
|
||||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||||
|
|
|
@ -21,10 +21,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI.MastodonAPIController
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
|
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
||||||
|
@ -101,6 +104,7 @@ def create(
|
||||||
|> Map.put("fullname", params["fullname"] || nickname)
|
|> Map.put("fullname", params["fullname"] || nickname)
|
||||||
|> Map.put("bio", params["bio"] || "")
|
|> Map.put("bio", params["bio"] || "")
|
||||||
|> Map.put("confirm", params["password"])
|
|> Map.put("confirm", params["password"])
|
||||||
|
|> Map.put("trusted_app", app.trusted)
|
||||||
|
|
||||||
with :ok <- validate_email_param(params),
|
with :ok <- validate_email_param(params),
|
||||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||||
|
@ -146,9 +150,7 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||||
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
user = original_user
|
|
||||||
|
|
||||||
user_params =
|
user_params =
|
||||||
[
|
[
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
|
@ -184,8 +186,6 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
|
|
||||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
if original_user != user, do: CommonAPI.update(user)
|
|
||||||
|
|
||||||
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :forbidden, "Invalid request")
|
_e -> render_error(conn, :forbidden, "Invalid request")
|
||||||
|
@ -380,6 +380,8 @@ def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/endorsements"
|
@doc "GET /api/v1/endorsements"
|
||||||
def endorsements(conn, params),
|
def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
|
||||||
|
@doc "GET /api/v1/identity_proofs"
|
||||||
|
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,21 +3,31 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|
@moduledoc """
|
||||||
|
Contains stubs for unimplemented Mastodon API endpoints.
|
||||||
|
|
||||||
|
Note: instead of routing directly to this controller's action,
|
||||||
|
it's preferable to define an action in relevant (non-generic) controller,
|
||||||
|
set up OAuth rules for it and call this controller's function from it.
|
||||||
|
"""
|
||||||
|
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
|
||||||
|
|
||||||
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
# Stubs for unimplemented mastodon api
|
|
||||||
#
|
|
||||||
def empty_array(conn, _) do
|
def empty_array(conn, _) do
|
||||||
Logger.debug("Unimplemented, returning an empty array")
|
Logger.debug("Unimplemented, returning an empty array (list)")
|
||||||
json(conn, [])
|
json(conn, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty_object(conn, _) do
|
def empty_object(conn, _) do
|
||||||
Logger.debug("Unimplemented, returning an empty object")
|
Logger.debug("Unimplemented, returning an empty object (map)")
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,25 +6,22 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
@moduledoc "The module represents functions to manage user subscriptions."
|
@moduledoc "The module represents functions to manage user subscriptions."
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
plug(:restrict_push_enabled)
|
||||||
|
|
||||||
# Creates PushSubscription
|
# Creates PushSubscription
|
||||||
# POST /api/v1/push/subscription
|
# POST /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, _} <- Subscription.delete_if_exists(user, token),
|
||||||
{:ok, _} <- Subscription.delete_if_exists(user, token),
|
|
||||||
{:ok, subscription} <- Subscription.create(user, token, params) do
|
{:ok, subscription} <- Subscription.create(user, token, params) do
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
render(conn, "show.json", subscription: subscription)
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,10 +29,8 @@ def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
# GET /api/v1/push/subscription
|
# GET /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, subscription} <- Subscription.get(user, token) do
|
||||||
{:ok, subscription} <- Subscription.get(user, token) do
|
render(conn, "show.json", subscription: subscription)
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,10 +38,8 @@ def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
# PUT /api/v1/push/subscription
|
# PUT /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, subscription} <- Subscription.update(user, token, params) do
|
||||||
{:ok, subscription} <- Subscription.update(user, token, params) do
|
render(conn, "show.json", subscription: subscription)
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,11 +47,20 @@ def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
# DELETE /api/v1/push/subscription
|
# DELETE /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, _response} <- Subscription.delete(user, token),
|
||||||
{:ok, _response} <- Subscription.delete(user, token),
|
|
||||||
do: json(conn, %{})
|
do: json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_push_enabled(conn, _) do
|
||||||
|
if Push.enabled() do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> render_error(:forbidden, "Web push subscription is disabled on this Pleroma instance")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# fallback action
|
# fallback action
|
||||||
#
|
#
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
|
|
|
@ -5,10 +5,13 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
|
||||||
|
|
||||||
@doc "GET /api/v1/suggestions"
|
@doc "GET /api/v1/suggestions"
|
||||||
def index(conn, _) do
|
def index(conn, params),
|
||||||
json(conn, [])
|
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -181,13 +181,11 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
bot = user.actor_type in ["Application", "Service"]
|
bot = user.actor_type in ["Application", "Service"]
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
(user.source_data["tag"] || [])
|
Enum.map(user.emoji, fn {shortcode, url} ->
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
|
||||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
|
||||||
%{
|
%{
|
||||||
"shortcode" => String.trim(name, ":"),
|
"shortcode" => shortcode,
|
||||||
"url" => MediaProxy.url(url),
|
"url" => url,
|
||||||
"static_url" => MediaProxy.url(url),
|
"static_url" => url,
|
||||||
"visible_in_picker" => false
|
"visible_in_picker" => false
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do
|
||||||
|
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
|
|
||||||
|
def render("index.json", %{apps: apps, count: count, page_size: page_size, admin: true}) do
|
||||||
|
%{
|
||||||
|
apps: render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json", %{admin: true}),
|
||||||
|
count: count,
|
||||||
|
page_size: page_size
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{admin: true, app: %App{} = app} = assigns) do
|
||||||
|
"show.json"
|
||||||
|
|> render(Map.delete(assigns, :admin))
|
||||||
|
|> Map.put(:trusted, app.trusted)
|
||||||
|
|> Map.put(:id, app.id)
|
||||||
|
end
|
||||||
|
|
||||||
def render("show.json", %{app: %App{} = app}) do
|
def render("show.json", %{app: %App{} = app}) do
|
||||||
%{
|
%{
|
||||||
id: app.id |> to_string,
|
id: app.id |> to_string,
|
||||||
|
|
|
@ -117,14 +117,14 @@ def render(
|
||||||
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
||||||
put_target(response, activity, reading_user, render_opts)
|
put_target(response, activity, reading_user, render_opts)
|
||||||
|
|
||||||
"follow" ->
|
|
||||||
response
|
|
||||||
|
|
||||||
"pleroma:emoji_reaction" ->
|
"pleroma:emoji_reaction" ->
|
||||||
response
|
response
|
||||||
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
||||||
|> put_emoji(activity)
|
|> put_emoji(activity)
|
||||||
|
|
||||||
|
type when type in ["follow", "follow_request"] ->
|
||||||
|
response
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
defmodule Pleroma.Web.MastodonAPI.SubscriptionView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
|
|
||||||
def render("push_subscription.json", %{subscription: subscription}) do
|
def render("show.json", %{subscription: subscription}) do
|
||||||
%{
|
%{
|
||||||
id: to_string(subscription.id),
|
id: to_string(subscription.id),
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.OAuth.App do
|
defmodule Pleroma.Web.OAuth.App do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
field(:website, :string)
|
field(:website, :string)
|
||||||
field(:client_id, :string)
|
field(:client_id, :string)
|
||||||
field(:client_secret, :string)
|
field(:client_secret, :string)
|
||||||
|
field(:trusted, :boolean, default: false)
|
||||||
|
|
||||||
|
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
||||||
|
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
|
||||||
|
def changeset(struct, params) do
|
||||||
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
|
||||||
def register_changeset(struct, params \\ %{}) do
|
def register_changeset(struct, params \\ %{}) do
|
||||||
changeset =
|
changeset =
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:client_name, :redirect_uris, :scopes, :website])
|
|> changeset(params)
|
||||||
|> validate_required([:client_name, :redirect_uris, :scopes])
|
|> validate_required([:client_name, :redirect_uris, :scopes])
|
||||||
|
|
||||||
if changeset.valid? do
|
if changeset.valid? do
|
||||||
|
@ -41,6 +52,21 @@ def register_changeset(struct, params \\ %{}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def create(params) do
|
||||||
|
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
|
||||||
|
Repo.insert(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def update(params) do
|
||||||
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
|
||||||
|
changeset <- changeset(app, params) do
|
||||||
|
Repo.update(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets app by attrs or create new with attrs.
|
Gets app by attrs or create new with attrs.
|
||||||
And updates the scopes if need.
|
And updates the scopes if need.
|
||||||
|
@ -65,4 +91,58 @@ defp update_scopes(%__MODULE__{} = app, scopes) do
|
||||||
|> change(%{scopes: scopes})
|
|> change(%{scopes: scopes})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
|
||||||
|
def search(params) do
|
||||||
|
query = from(a in __MODULE__)
|
||||||
|
|
||||||
|
query =
|
||||||
|
if params[:client_name] do
|
||||||
|
from(a in query, where: a.client_name == ^params[:client_name])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
if params[:client_id] do
|
||||||
|
from(a in query, where: a.client_id == ^params[:client_id])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
if Map.has_key?(params, :trusted) do
|
||||||
|
from(a in query, where: a.trusted == ^params[:trusted])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
from(u in query,
|
||||||
|
limit: ^params[:page_size],
|
||||||
|
offset: ^((params[:page] - 1) * params[:page_size])
|
||||||
|
)
|
||||||
|
|
||||||
|
count = Repo.aggregate(__MODULE__, :count, :id)
|
||||||
|
|
||||||
|
{:ok, Repo.all(query), count}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def destroy(id) do
|
||||||
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
|
Repo.delete(app)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec errors(Ecto.Changeset.t()) :: map()
|
||||||
|
def errors(changeset) do
|
||||||
|
Enum.reduce(changeset.errors, %{}, fn
|
||||||
|
{:client_name, {error, _}}, acc ->
|
||||||
|
Map.put(acc, :name, error)
|
||||||
|
|
||||||
|
{key, {error, _}}, acc ->
|
||||||
|
Map.put(acc, key, error)
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
plug(:fetch_flash)
|
plug(:fetch_flash)
|
||||||
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
||||||
|
|
||||||
|
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
||||||
|
|
||||||
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
||||||
|
|
|
@ -13,7 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
@ -58,38 +57,32 @@ def confirmation_resend(conn, params) do
|
||||||
|
|
||||||
@doc "PATCH /api/v1/pleroma/accounts/update_avatar"
|
@doc "PATCH /api/v1/pleroma/accounts/update_avatar"
|
||||||
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||||
{:ok, user} =
|
{:ok, _user} =
|
||||||
user
|
user
|
||||||
|> Changeset.change(%{avatar: nil})
|
|> Changeset.change(%{avatar: nil})
|
||||||
|> User.update_and_set_cache()
|
|> User.update_and_set_cache()
|
||||||
|
|
||||||
CommonAPI.update(user)
|
|
||||||
|
|
||||||
json(conn, %{url: nil})
|
json(conn, %{url: nil})
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
||||||
{:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar)
|
{:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar)
|
||||||
{:ok, user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache()
|
{:ok, _user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache()
|
||||||
%{"url" => [%{"href" => href} | _]} = data
|
%{"url" => [%{"href" => href} | _]} = data
|
||||||
|
|
||||||
CommonAPI.update(user)
|
|
||||||
|
|
||||||
json(conn, %{url: href})
|
json(conn, %{url: href})
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PATCH /api/v1/pleroma/accounts/update_banner"
|
@doc "PATCH /api/v1/pleroma/accounts/update_banner"
|
||||||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||||
with {:ok, user} <- User.update_banner(user, %{}) do
|
with {:ok, _user} <- User.update_banner(user, %{}) do
|
||||||
CommonAPI.update(user)
|
|
||||||
json(conn, %{url: nil})
|
json(conn, %{url: nil})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||||
{:ok, user} <- User.update_banner(user, object.data) do
|
{:ok, _user} <- User.update_banner(user, object.data) do
|
||||||
CommonAPI.update(user)
|
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
%{"url" => [%{"href" => href} | _]} = object.data
|
||||||
|
|
||||||
json(conn, %{url: href})
|
json(conn, %{url: href})
|
||||||
|
|
|
@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:conversations"]} when action == :update_conversation
|
%{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
|
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.Push.Impl do
|
||||||
require Logger
|
require Logger
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
defdelegate mastodon_notification_type(activity), to: Activity
|
||||||
|
|
||||||
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
||||||
|
|
||||||
@doc "Performs sending notifications for user subscriptions"
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
|
@ -24,32 +26,32 @@ def perform(
|
||||||
%{
|
%{
|
||||||
activity: %{data: %{"type" => activity_type}} = activity,
|
activity: %{data: %{"type" => activity_type}} = activity,
|
||||||
user: %User{id: user_id}
|
user: %User{id: user_id}
|
||||||
} = notif
|
} = notification
|
||||||
)
|
)
|
||||||
when activity_type in @types do
|
when activity_type in @types do
|
||||||
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
||||||
|
|
||||||
type = Activity.mastodon_notification_type(notif.activity)
|
mastodon_type = mastodon_notification_type(notification.activity)
|
||||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||||
avatar_url = User.avatar_url(actor)
|
avatar_url = User.avatar_url(actor)
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
user = User.get_cached_by_id(user_id)
|
user = User.get_cached_by_id(user_id)
|
||||||
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
||||||
|
|
||||||
for subscription <- fetch_subsriptions(user_id),
|
for subscription <- fetch_subscriptions(user_id),
|
||||||
get_in(subscription.data, ["alerts", type]) do
|
Subscription.enabled?(subscription, mastodon_type) do
|
||||||
%{
|
%{
|
||||||
access_token: subscription.token.token,
|
access_token: subscription.token.token,
|
||||||
notification_id: notif.id,
|
notification_id: notification.id,
|
||||||
notification_type: type,
|
notification_type: mastodon_type,
|
||||||
icon: avatar_url,
|
icon: avatar_url,
|
||||||
preferred_locale: "en",
|
preferred_locale: "en",
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
activity_id: notif.activity.id,
|
activity_id: notification.activity.id,
|
||||||
direct_conversation_id: direct_conversation_id
|
direct_conversation_id: direct_conversation_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> Map.merge(build_content(notif, actor, object))
|
|> Map.merge(build_content(notification, actor, object, mastodon_type))
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
||||||
end
|
end
|
||||||
|
@ -82,7 +84,7 @@ def push_message(body, sub, api_key, subscription) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Gets user subscriptions"
|
@doc "Gets user subscriptions"
|
||||||
def fetch_subsriptions(user_id) do
|
def fetch_subscriptions(user_id) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(user_id: ^user_id)
|
|> where(user_id: ^user_id)
|
||||||
|> preload(:token)
|
|> preload(:token)
|
||||||
|
@ -99,28 +101,36 @@ def build_sub(subscription) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_content(notification, actor, object, mastodon_type \\ nil)
|
||||||
|
|
||||||
def build_content(
|
def build_content(
|
||||||
%{
|
%{
|
||||||
activity: %{data: %{"directMessage" => true}},
|
activity: %{data: %{"directMessage" => true}},
|
||||||
user: %{notification_settings: %{privacy_option: true}}
|
user: %{notification_settings: %{privacy_option: true}}
|
||||||
},
|
},
|
||||||
actor,
|
actor,
|
||||||
_
|
_object,
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
%{title: "New Direct Message", body: "@#{actor.nickname}"}
|
%{title: "New Direct Message", body: "@#{actor.nickname}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_content(notif, actor, object) do
|
def build_content(notification, actor, object, mastodon_type) do
|
||||||
|
mastodon_type = mastodon_type || mastodon_notification_type(notification.activity)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
title: format_title(notif),
|
title: format_title(notification, mastodon_type),
|
||||||
body: format_body(notif, actor, object)
|
body: format_body(notification, actor, object, mastodon_type)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_body(activity, actor, object, mastodon_type \\ nil)
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Create"}}},
|
%{activity: %{data: %{"type" => "Create"}}},
|
||||||
actor,
|
actor,
|
||||||
%{data: %{"content" => content}}
|
%{data: %{"content" => content}},
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
@ -128,33 +138,44 @@ def format_body(
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Announce"}}},
|
%{activity: %{data: %{"type" => "Announce"}}},
|
||||||
actor,
|
actor,
|
||||||
%{data: %{"content" => content}}
|
%{data: %{"content" => content}},
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => type}}},
|
%{activity: %{data: %{"type" => type}}} = notification,
|
||||||
actor,
|
actor,
|
||||||
_object
|
_object,
|
||||||
|
mastodon_type
|
||||||
)
|
)
|
||||||
when type in ["Follow", "Like"] do
|
when type in ["Follow", "Like"] do
|
||||||
case type do
|
mastodon_type = mastodon_type || mastodon_notification_type(notification.activity)
|
||||||
"Follow" -> "@#{actor.nickname} has followed you"
|
|
||||||
"Like" -> "@#{actor.nickname} has favorited your post"
|
case mastodon_type do
|
||||||
|
"follow" -> "@#{actor.nickname} has followed you"
|
||||||
|
"follow_request" -> "@#{actor.nickname} has requested to follow you"
|
||||||
|
"favourite" -> "@#{actor.nickname} has favorited your post"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_title(%{activity: %{data: %{"directMessage" => true}}}) do
|
def format_title(activity, mastodon_type \\ nil)
|
||||||
|
|
||||||
|
def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do
|
||||||
"New Direct Message"
|
"New Direct Message"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
def format_title(%{activity: activity}, mastodon_type) do
|
||||||
case type do
|
mastodon_type = mastodon_type || mastodon_notification_type(activity)
|
||||||
"Create" -> "New Mention"
|
|
||||||
"Follow" -> "New Follower"
|
case mastodon_type do
|
||||||
"Announce" -> "New Repeat"
|
"mention" -> "New Mention"
|
||||||
"Like" -> "New Favorite"
|
"follow" -> "New Follower"
|
||||||
|
"follow_request" -> "New Follow Request"
|
||||||
|
"reblog" -> "New Repeat"
|
||||||
|
"favourite" -> "New Favorite"
|
||||||
|
type -> "New #{String.capitalize(type || "event")}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,14 @@ defp alerts(%{"data" => %{"alerts" => alerts}}) do
|
||||||
%{"alerts" => alerts}
|
%{"alerts" => alerts}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def enabled?(subscription, "follow_request") do
|
||||||
|
enabled?(subscription, "follow")
|
||||||
|
end
|
||||||
|
|
||||||
|
def enabled?(subscription, alert_type) do
|
||||||
|
get_in(subscription.data, ["alerts", alert_type])
|
||||||
|
end
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
%User{} = user,
|
%User{} = user,
|
||||||
%Token{} = token,
|
%Token{} = token,
|
||||||
|
|
|
@ -16,78 +16,60 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :authenticate do
|
||||||
plug(:accepts, ["json"])
|
|
||||||
plug(:fetch_session)
|
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :after_auth do
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
end
|
||||||
|
|
||||||
|
pipeline :base_api do
|
||||||
|
plug(:accepts, ["json"])
|
||||||
|
plug(:fetch_session)
|
||||||
|
plug(:authenticate)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :api do
|
||||||
|
plug(:base_api)
|
||||||
|
plug(:after_auth)
|
||||||
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :authenticated_api do
|
pipeline :authenticated_api do
|
||||||
plug(:accepts, ["json"])
|
plug(:base_api)
|
||||||
plug(:fetch_session)
|
plug(Pleroma.Plugs.AuthExpectedPlug)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :admin_api do
|
pipeline :admin_api do
|
||||||
plug(:accepts, ["json"])
|
plug(:base_api)
|
||||||
plug(:fetch_session)
|
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Plugs.UserIsAdminPlug)
|
plug(Pleroma.Plugs.UserIsAdminPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :mastodon_html do
|
pipeline :mastodon_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:browser)
|
||||||
plug(:fetch_session)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :pleroma_html do
|
pipeline :pleroma_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:browser)
|
||||||
plug(:fetch_session)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -203,12 +185,18 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/config", AdminAPIController, :config_show)
|
get("/config", AdminAPIController, :config_show)
|
||||||
post("/config", AdminAPIController, :config_update)
|
post("/config", AdminAPIController, :config_update)
|
||||||
get("/config/descriptions", AdminAPIController, :config_descriptions)
|
get("/config/descriptions", AdminAPIController, :config_descriptions)
|
||||||
|
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||||
get("/restart", AdminAPIController, :restart)
|
get("/restart", AdminAPIController, :restart)
|
||||||
|
|
||||||
get("/moderation_log", AdminAPIController, :list_log)
|
get("/moderation_log", AdminAPIController, :list_log)
|
||||||
|
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
get("/stats", AdminAPIController, :stats)
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
|
get("/oauth_app", AdminAPIController, :oauth_app_list)
|
||||||
|
post("/oauth_app", AdminAPIController, :oauth_app_create)
|
||||||
|
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
|
||||||
|
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -338,7 +326,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/accounts/relationships", AccountController, :relationships)
|
get("/accounts/relationships", AccountController, :relationships)
|
||||||
|
|
||||||
get("/accounts/:id/lists", AccountController, :lists)
|
get("/accounts/:id/lists", AccountController, :lists)
|
||||||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
|
get("/accounts/:id/identity_proofs", AccountController, :identity_proofs)
|
||||||
|
|
||||||
get("/follow_requests", FollowRequestController, :index)
|
get("/follow_requests", FollowRequestController, :index)
|
||||||
get("/blocks", AccountController, :blocks)
|
get("/blocks", AccountController, :blocks)
|
||||||
|
@ -508,7 +496,7 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api" do
|
scope "/api" do
|
||||||
pipe_through(:api)
|
pipe_through(:base_api)
|
||||||
|
|
||||||
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
||||||
end
|
end
|
||||||
|
@ -522,10 +510,6 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
|
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :ap_service_actor do
|
|
||||||
plug(:accepts, ["activity+json", "json"])
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :ostatus do
|
pipeline :ostatus do
|
||||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||||
plug(Pleroma.Plugs.StaticFEPlug)
|
plug(Pleroma.Plugs.StaticFEPlug)
|
||||||
|
@ -536,8 +520,7 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through(:ostatus)
|
pipe_through([:ostatus, :http_signature])
|
||||||
pipe_through(:http_signature)
|
|
||||||
|
|
||||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||||
|
@ -555,13 +538,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Server to Server (S2S) AP interactions
|
|
||||||
pipeline :activitypub do
|
|
||||||
plug(:accepts, ["activity+json", "json"])
|
|
||||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
|
||||||
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
# XXX: not really ostatus
|
# XXX: not really ostatus
|
||||||
pipe_through(:ostatus)
|
pipe_through(:ostatus)
|
||||||
|
@ -569,19 +545,22 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :ap_service_actor do
|
||||||
|
plug(:accepts, ["activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Server to Server (S2S) AP interactions
|
||||||
|
pipeline :activitypub do
|
||||||
|
plug(:ap_service_actor)
|
||||||
|
plug(:http_signature)
|
||||||
|
end
|
||||||
|
|
||||||
# Client to Server (C2S) AP interactions
|
# Client to Server (C2S) AP interactions
|
||||||
pipeline :activitypub_client do
|
pipeline :activitypub_client do
|
||||||
plug(:accepts, ["activity+json", "json"])
|
plug(:ap_service_actor)
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
|
@ -653,12 +632,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/web/*path", MastoFEController, :index)
|
get("/web/*path", MastoFEController, :index)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :remote_media do
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web.MediaProxy do
|
scope "/proxy/", Pleroma.Web.MediaProxy do
|
||||||
pipe_through(:remote_media)
|
|
||||||
|
|
||||||
get("/:sig/:url", MediaProxyController, :remote)
|
get("/:sig/:url", MediaProxyController, :remote)
|
||||||
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
||||||
end
|
end
|
||||||
|
@ -671,6 +645,17 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Test-only routes needed to test action dispatching and plug chain execution
|
||||||
|
if Pleroma.Config.get(:env) == :test do
|
||||||
|
scope "/test/authenticated_api", Pleroma.Tests do
|
||||||
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do
|
||||||
|
get("/#{action}", OAuthTestController, action)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.MongooseIM do
|
scope "/", Pleroma.Web.MongooseIM do
|
||||||
get("/user_exists", MongooseIMController, :user_exists)
|
get("/user_exists", MongooseIMController, :user_exists)
|
||||||
get("/check_password", MongooseIMController, :check_password)
|
get("/check_password", MongooseIMController, :check_password)
|
||||||
|
|
|
@ -18,15 +18,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEView do
|
||||||
|
|
||||||
@media_types ["image", "audio", "video"]
|
@media_types ["image", "audio", "video"]
|
||||||
|
|
||||||
def emoji_for_user(%User{} = user) do
|
|
||||||
user.source_data
|
|
||||||
|> Map.get("tag", [])
|
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
|
||||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
|
||||||
{String.trim(name, ":"), url}
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_media_type(%{"mediaType" => mediaType}) do
|
def fetch_media_type(%{"mediaType" => mediaType}) do
|
||||||
Utils.fetch_media_type(@media_types, mediaType)
|
Utils.fetch_media_type(@media_types, mediaType)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
||||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||||
<id><%= @data["id"] %></id>
|
<id><%= @data["id"] %></id>
|
||||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||||
<content type="html"><%= activity_content(@object) %></content>
|
<content type="html"><%= activity_content(@data) %></content>
|
||||||
<published><%= @data["published"] %></published>
|
<published><%= @activity.data["published"] %></published>
|
||||||
<updated><%= @data["published"] %></updated>
|
<updated><%= @activity.data["published"] %></updated>
|
||||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||||
<%= activity_context(@activity) %>
|
<%= activity_context(@activity) %>
|
||||||
</ostatus:conversation>
|
</ostatus:conversation>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
||||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||||
<guid><%= @data["id"] %></guid>
|
<guid><%= @data["id"] %></guid>
|
||||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||||
<description><%= activity_content(@object) %></description>
|
<description><%= activity_content(@data) %></description>
|
||||||
<pubDate><%= @data["published"] %></pubDate>
|
<pubDate><%= @activity.data["published"] %></pubDate>
|
||||||
<updated><%= @data["published"] %></updated>
|
<updated><%= @activity.data["published"] %></updated>
|
||||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||||
<%= activity_context(@activity) %>
|
<%= activity_context(@activity) %>
|
||||||
</ostatus:conversation>
|
</ostatus:conversation>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<%= render @view_module, "_tag_author.atom", assigns %>
|
<%= render @view_module, "_tag_author.atom", assigns %>
|
||||||
|
|
||||||
<id><%= @data["id"] %></id>
|
<id><%= @data["id"] %></id>
|
||||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||||
<content type="html"><%= activity_content(@object) %></content>
|
<content type="html"><%= activity_content(@data) %></content>
|
||||||
|
|
||||||
<%= if @activity.local do %>
|
<%= if @activity.local do %>
|
||||||
<link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
|
<link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
<link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
|
<link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<published><%= @data["published"] %></published>
|
<published><%= @activity.data["published"] %></published>
|
||||||
<updated><%= @data["published"] %></updated>
|
<updated><%= @activity.data["published"] %></updated>
|
||||||
|
|
||||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||||
<%= activity_context(@activity) %>
|
<%= activity_context(@activity) %>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
<item>
|
<item>
|
||||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||||
|
|
||||||
|
|
||||||
<guid isPermalink="true"><%= activity_context(@activity) %></guid>
|
<guid isPermalink="true"><%= activity_context(@activity) %></guid>
|
||||||
<link><%= activity_context(@activity) %></link>
|
<link><%= activity_context(@activity) %></link>
|
||||||
<pubDate><%= pub_date(@data["published"]) %></pubDate>
|
<pubDate><%= pub_date(@activity.data["published"]) %></pubDate>
|
||||||
|
|
||||||
<description><%= activity_content(@object) %></description>
|
<description><%= activity_content(@data) %></description>
|
||||||
<%= for attachment <- @data["attachment"] || [] do %>
|
<%= for attachment <- @data["attachment"] || [] do %>
|
||||||
<enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
|
<enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt="">
|
<img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt="">
|
||||||
</div>
|
</div>
|
||||||
<span class="display-name">
|
<span class="display-name">
|
||||||
<bdi><%= raw (@user.name |> Formatter.emojify(emoji_for_user(@user))) %></bdi>
|
<bdi><%= raw Formatter.emojify(@user.name, @user.emoji) %></bdi>
|
||||||
<span class="nickname"><%= @user.nickname %></span>
|
<span class="nickname"><%= @user.nickname %></span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<input type="hidden" name="profile" value="">
|
<input type="hidden" name="profile" value="">
|
||||||
<button type="submit" class="collapse">Remote follow</button>
|
<button type="submit" class="collapse">Remote follow</button>
|
||||||
</form>
|
</form>
|
||||||
<%= raw Formatter.emojify(@user.name, emoji_for_user(@user)) %> |
|
<%= raw Formatter.emojify(@user.name, @user.emoji) %> |
|
||||||
<%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>
|
<%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>
|
||||||
</h3>
|
</h3>
|
||||||
<p><%= raw @user.bio %></p>
|
<p><%= raw @user.bio %></p>
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||||
|
|
||||||
def register_user(params, opts \\ []) do
|
def register_user(params, opts \\ []) do
|
||||||
token = params["token"]
|
token = params["token"]
|
||||||
|
trusted_app? = params["trusted_app"]
|
||||||
|
|
||||||
params = %{
|
params = %{
|
||||||
nickname: params["nickname"],
|
nickname: params["nickname"],
|
||||||
|
@ -29,7 +30,7 @@ def register_user(params, opts \\ []) do
|
||||||
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
||||||
# true if captcha is disabled or enabled and valid, false otherwise
|
# true if captcha is disabled or enabled and valid, false otherwise
|
||||||
captcha_ok =
|
captcha_ok =
|
||||||
if not captcha_enabled do
|
if trusted_app? || not captcha_enabled do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
Pleroma.Captcha.validate(
|
Pleroma.Captcha.validate(
|
||||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
||||||
|
|
||||||
|
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
|
@ -29,11 +29,45 @@ def controller do
|
||||||
import Pleroma.Web.Router.Helpers
|
import Pleroma.Web.Router.Helpers
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
|
|
||||||
plug(:set_put_layout)
|
plug(:set_put_layout)
|
||||||
|
|
||||||
defp set_put_layout(conn, _) do
|
defp set_put_layout(conn, _) do
|
||||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain
|
||||||
|
defp skip_plug(conn, plug_module) do
|
||||||
|
try do
|
||||||
|
plug_module.skip_plug(conn)
|
||||||
|
rescue
|
||||||
|
UndefinedFunctionError ->
|
||||||
|
raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
||||||
|
defp action(conn, params) do
|
||||||
|
with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||||
|
super(conn, params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
|
||||||
|
defp maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||||
|
if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
|
||||||
|
not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
|
||||||
|
conn
|
||||||
|
|> render_error(
|
||||||
|
:forbidden,
|
||||||
|
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||||
|
)
|
||||||
|
|> halt()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,6 +130,35 @@ def channel do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def plug do
|
||||||
|
quote do
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
|
||||||
|
"""
|
||||||
|
def skip_plug(conn) do
|
||||||
|
PlugHelper.append_to_private_list(
|
||||||
|
conn,
|
||||||
|
PlugHelper.skipped_plugs_list_id(),
|
||||||
|
__MODULE__
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
@doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise."
|
||||||
|
def call(%Plug.Conn{} = conn, options) do
|
||||||
|
if PlugHelper.plug_skipped?(conn, __MODULE__) do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__)
|
||||||
|
|> perform(options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
When used, dispatch to the appropriate controller/view/etc.
|
When used, dispatch to the appropriate controller/view/etc.
|
||||||
"""
|
"""
|
||||||
|
|
23
mix.exs
23
mix.exs
|
@ -183,7 +183,7 @@ defp deps do
|
||||||
{:flake_id, "~> 0.1.0"},
|
{:flake_id, "~> 0.1.0"},
|
||||||
{:remote_ip,
|
{:remote_ip,
|
||||||
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
||||||
ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"},
|
ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
|
||||||
{:captcha,
|
{:captcha,
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
|
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
|
||||||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||||
|
@ -221,19 +221,26 @@ defp version(version) do
|
||||||
identifier_filter = ~r/[^0-9a-z\-]+/i
|
identifier_filter = ~r/[^0-9a-z\-]+/i
|
||||||
|
|
||||||
# Pre-release version, denoted from patch version with a hyphen
|
# Pre-release version, denoted from patch version with a hyphen
|
||||||
|
{tag, tag_err} =
|
||||||
|
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
|
||||||
|
|
||||||
|
{describe, describe_err} = System.cmd("git", ["describe", "--tags", "--abbrev=8"])
|
||||||
|
{commit_hash, commit_hash_err} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
|
|
||||||
git_pre_release =
|
git_pre_release =
|
||||||
with {tag, 0} <-
|
cond do
|
||||||
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
|
tag_err == 0 and describe_err == 0 ->
|
||||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]) do
|
|
||||||
describe
|
describe
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|> String.replace(String.trim(tag), "")
|
|> String.replace(String.trim(tag), "")
|
||||||
|> String.trim_leading("-")
|
|> String.trim_leading("-")
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
else
|
|
||||||
_ ->
|
commit_hash_err == 0 ->
|
||||||
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
|
||||||
"0-g" <> String.trim(commit_hash)
|
"0-g" <> String.trim(commit_hash)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
""
|
||||||
end
|
end
|
||||||
|
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
|
@ -251,6 +258,8 @@ defp version(version) do
|
||||||
|> String.replace(identifier_filter, "-")
|
|> String.replace(identifier_filter, "-")
|
||||||
|
|
||||||
branch_name
|
branch_name
|
||||||
|
else
|
||||||
|
_ -> "stable"
|
||||||
end
|
end
|
||||||
|
|
||||||
build_name =
|
build_name =
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -97,7 +97,7 @@
|
||||||
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "d736bfa7444112eb840027bb887832a0e403a4a3437f48028c3b29a2dbbd2543"},
|
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "d736bfa7444112eb840027bb887832a0e403a4a3437f48028c3b29a2dbbd2543"},
|
||||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||||
"recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"},
|
"recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"},
|
||||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "825dc00aaba5a1b7c4202a532b696b595dd3bcb3", [ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"]},
|
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
||||||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
||||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddTrustedToApps do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:apps) do
|
||||||
|
add(:trusted, :boolean, default: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.UsersAddPublicKey do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
add_if_not_exists(:public_key, :text)
|
||||||
|
end
|
||||||
|
|
||||||
|
execute("UPDATE users SET public_key = source_data->'publicKey'->>'publicKeyPem'")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
remove_if_exists(:public_key, :text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.UsersAddInboxes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
add_if_not_exists(:inbox, :text)
|
||||||
|
add_if_not_exists(:shared_inbox, :text)
|
||||||
|
end
|
||||||
|
|
||||||
|
execute("UPDATE users SET inbox = source_data->>'inbox'")
|
||||||
|
execute("UPDATE users SET shared_inbox = source_data->'endpoints'->>'sharedInbox'")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
remove_if_exists(:inbox, :text)
|
||||||
|
remove_if_exists(:shared_inbox, :text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,38 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.UsersPopulateEmoji do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
def up do
|
||||||
|
execute("ALTER TABLE users ALTER COLUMN emoji SET DEFAULT '{}'::jsonb")
|
||||||
|
execute("UPDATE users SET emoji = DEFAULT WHERE emoji = '[]'::jsonb")
|
||||||
|
|
||||||
|
from(u in User)
|
||||||
|
|> select([u], struct(u, [:id, :ap_id, :source_data]))
|
||||||
|
|> Repo.stream()
|
||||||
|
|> Enum.each(fn user ->
|
||||||
|
emoji =
|
||||||
|
user.source_data
|
||||||
|
|> Map.get("tag", [])
|
||||||
|
|> Enum.filter(fn
|
||||||
|
%{"type" => "Emoji"} -> true
|
||||||
|
_ -> false
|
||||||
|
end)
|
||||||
|
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
|
||||||
|
Map.put(acc, String.trim(name, ":"), url)
|
||||||
|
end)
|
||||||
|
|
||||||
|
user
|
||||||
|
|> Ecto.Changeset.cast(%{emoji: emoji}, [:emoji])
|
||||||
|
|> Repo.update()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute("ALTER TABLE users ALTER COLUMN emoji SET DEFAULT '[]'::jsonb")
|
||||||
|
execute("UPDATE users SET emoji = DEFAULT WHERE emoji = '{}'::jsonb")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.UsersRemoveSourceData do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
remove_if_exists(:source_data, :map)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
add_if_not_exists(:source_data, :map, default: %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Binary file not shown.
|
@ -1,126 +0,0 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="fontello" horiz-adv-x="1000" >
|
|
||||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="857" descent="-143" />
|
|
||||||
<missing-glyph horiz-adv-x="1000" />
|
|
||||||
<glyph glyph-name="cancel" unicode="" d="M724 119q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="upload" unicode="" d="M714 36q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m143 0q0 14-10 25t-26 10-25-10-10-25 10-25 25-11 26 11 10 25z m72 125v-179q0-22-16-38t-38-16h-821q-23 0-38 16t-16 38v179q0 22 16 38t38 15h238q12-31 39-51t62-20h143q34 0 61 20t40 51h238q22 0 38-15t16-38z m-182 361q-9-22-33-22h-143v-250q0-15-10-25t-25-11h-143q-15 0-25 11t-11 25v250h-143q-23 0-33 22-9 22 8 39l250 250q10 10 25 10t25-10l250-250q18-17 8-39z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="star" unicode="" d="M929 496q0-12-15-27l-202-197 48-279q0-4 0-12 0-11-6-19t-17-9q-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="star-empty" unicode="" d="M635 297l170 166-235 34-106 213-105-213-236-34 171-166-41-235 211 111 211-111z m294 199q0-12-15-27l-202-197 48-279q0-4 0-12 0-28-23-28-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="retweet" unicode="" d="M714 18q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="eye-off" unicode="" d="M310 112l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="search" unicode="" d="M643 393q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="cog" unicode="" d="M571 357q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="logout" unicode="" d="M357 53q0-2 1-11t0-14-2-14-5-10-12-4h-178q-67 0-114 47t-47 114v392q0 67 47 114t114 47h178q8 0 13-5t5-13q0-2 1-11t0-15-2-13-5-11-12-3h-178q-37 0-63-26t-27-64v-392q0-37 27-63t63-27h174t6 0 7-2 4-3 4-5 1-8z m518 304q0-14-11-25l-303-304q-11-10-25-10t-25 10-11 25v161h-250q-14 0-25 11t-11 25v214q0 15 11 25t25 11h250v161q0 14 11 25t25 10 25-10l303-304q11-10 11-25z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="down-open" unicode="" d="M939 406l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="attach" unicode="" d="M244-133q-102 0-170 72-72 70-74 166t84 190l496 496q80 80 174 54 44-12 79-47t47-79q26-96-54-176l-474-474q-40-40-88-46-48-4-80 28-30 24-27 74t47 92l332 334q24 26 50 0t0-50l-332-332q-44-44-20-70 12-8 24-6 24 4 46 26l474 474q50 50 34 108-16 60-76 76-54 14-108-36l-494-494q-66-76-64-143t52-117q50-48 117-50t141 62l496 494q24 24 50 0 26-22 0-48l-496-496q-82-82-186-82z" horiz-adv-x="939" />
|
|
||||||
|
|
||||||
<glyph glyph-name="picture" unicode="" d="M357 536q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="video" unicode="" d="M214-36v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="right-open" unicode="" d="M618 368l-414-415q-11-10-25-10t-25 10l-93 93q-11 11-11 25t11 25l296 297-296 296q-11 11-11 25t11 25l93 93q10 11 25 11t25-11l414-414q10-11 10-25t-10-25z" horiz-adv-x="714.3" />
|
|
||||||
|
|
||||||
<glyph glyph-name="left-open" unicode="" d="M654 689l-297-296 297-297q10-10 10-25t-10-25l-93-93q-11-10-25-10t-25 10l-414 415q-11 10-11 25t11 25l414 414q10 11 25 11t25-11l93-93q10-10 10-25t-10-25z" horiz-adv-x="714.3" />
|
|
||||||
|
|
||||||
<glyph glyph-name="up-open" unicode="" d="M939 114l-92-92q-11-10-26-10t-25 10l-296 297-296-297q-11-10-25-10t-25 10l-93 92q-11 11-11 26t11 25l414 414q11 10 25 10t25-10l414-414q11-11 11-25t-11-26z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="bell-ringing-o" unicode="" d="M498 857c-30 0-54-24-54-53 0-8 2-15 5-22-147-22-236-138-236-245 0-268-95-393-177-462 0-39 32-71 71-71h249c0-79 63-143 142-143s142 64 142 143h249c39 0 71 32 71 71-82 69-178 194-178 462 0 107-88 223-235 245 2 7 4 14 4 22 0 29-24 53-53 53z m-309-45c-81-74-118-170-118-275l71 0c0 89 28 162 95 223l-48 52z m617 0l-48-52c67-61 96-134 95-223l71 0c1 105-37 201-118 275z m-397-799c5 0 9-4 9-9 0-44 36-80 80-80 5 0 9-4 9-9s-4-9-9-9c-54 0-98 44-98 98 0 5 4 9 9 9z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="lock" unicode="" d="M179 428h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="globe" unicode="" d="M429 786q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m153-291q-2-1-6-5t-7-6q1 0 2 3t3 6 2 4q3 4 12 8 8 4 29 7 19 5 29-6-1 1 5 7t8 7q2 1 8 3t9 4l1 12q-7-1-10 4t-3 12q0-2-4-5 0 4-2 5t-7-1-5-1q-5 2-8 5t-5 9-2 8q-1 3-5 6t-5 6q-1 1-2 3t-1 4-3 3-3 1-4-3-4-5-2-3q-2 1-4 1t-2-1-3-1-3-2q-1-2-4-2t-5-1q8 3-1 6-5 2-9 2 6 2 5 6t-5 8h3q-1 2-5 5t-10 5-7 3q-5 3-19 5t-18 1q-3-4-3-6t2-8 2-7q1-3-3-7t-3-7q0-4 7-9t6-12q-2-4-9-9t-9-6q-3-5-1-11t6-9q1-1 1-2t-2-3-3-2-4-2l-1-1q-7-3-12 3t-7 15q-4 14-9 17-13 4-16-1-3 7-23 15-14 5-33 2 4 0 0 8-4 9-10 7 1 3 2 10t0 7q2 8 7 13 1 1 4 5t5 7 1 4q19-3 28 6 2 3 6 9t6 10q5 3 8 3t8-3 8-3q8-1 8 6t-4 11q7 0 2 10-2 4-5 5-6 2-15-3-4-2 2-4-1 0-6-6t-9-10-9 3q0 0-3 7t-5 8q-5 0-9-9 1 5-6 9t-14 4q11 7-4 15-4 3-12 3t-11-2q-2-4-3-7t3-4 6-3 6-2 5-2q8-6 5-8-1 0-5-2t-6-2-4-2q-1-3 0-8t-1-8q-3 3-5 10t-4 9q4-5-14-3l-5 0q-3 0-9-1t-12-1-7 5q-3 4 0 11 0 2 2 1-2 2-6 5t-6 5q-25-8-52-23 3 0 6 1 3 1 8 4t5 3q19 7 24 4l3 2q7-9 11-14-4 3-17 1-11-3-12-7 4-6 2-10-2 2-6 6t-8 6-8 3q-9 0-13-1-81-45-131-124 4-4 7-4 2-1 3-5t1-6 6 1q5-4 2-10 1 0 25-15 10-10 11-12 2-6-5-10-1 1-5 5t-5 2q-2-3 0-10t6-7q-4 0-5-9t-2-20 0-13l1-1q-2-6 3-19t12-11q-7-1 11-24 3-4 4-5 2-1 7-4t9-6 5-5q2-3 6-13t8-13q-2-3 5-11t6-13q-1 0-2-1t-1 0q2-4 9-8t8-7q1-2 1-6t2-6 4-1q2 11-13 35-8 13-9 16-2 2-4 8t-2 8q1 0 3 0t5-2 4-3 1-1q-1-4 1-10t7-10 10-11 6-7q4-4 8-11t0-8q5 0 11-5t10-11q3-5 4-15t3-13q1-4 5-8t7-5l9-5t7-3q3-2 10-6t12-7q6-2 9-2t8 1 8 2q8 1 16-8t12-12q20-10 30-6-1 0 1-4t4-9 5-8 3-5q3-3 10-8t10-8q4 2 4 5-1-5 4-11t10-6q8 2 8 18-17-8-27 10 0 0-2 3t-2 5-1 4 0 5 2 1q5 0 6 2t-1 7-2 8q-1 4-6 11t-7 8q-3-5-9-4t-9 5q0-1-1-3t-1-4q-7 0-8 0 1 2 1 10t2 13q1 2 3 6t5 9 2 7-3 5-9 1q-11 0-15-11-1-2-2-6t-2-6-5-4q-4-2-14-1t-13 3q-8 4-13 16t-5 20q0 6 1 15t2 14-3 14q2 1 5 5t5 6q2 1 3 1t3 0 2 1 1 3q0 1-2 2-1 1-2 1 4-1 16 1t15-1q9-6 12 1 0 1-1 6t0 7q3-15 16-5 2-1 9-3t9-2q2-1 4-3t3-3 3 0 5 4q5-8 7-13 6-23 10-25 4-2 6-1t3 5 0 8-1 7l-1 5v10l0 4q-8 2-10 7t0 10 9 10q0 1 4 2t9 4 7 4q12 11 8 20 4 0 6 5 0 0-2 2t-5 2-2 2q5 2 1 8 3 2 4 7t4 5q5-6 12-1 5 5 1 9 2 4 11 6t10 5q4-1 5 1t0 7 2 7q2 2 9 5t7 2l9 7q2 2 0 2 10-1 18 6 5 6-4 11 2 4-1 5t-9 4q2 0 7 0t5 1q9 5-3 9-10 2-24-7z m-91-490q115 21 195 106-1 2-7 2t-7 2q-10 4-13 5 1 4-1 7t-5 5-7 5-6 4q-1 1-4 3t-4 3-4 2-5 2-5-1l-2-1q-2 0-3-1t-3-2-2-1 0-2q-12 10-20 13-3 0-6 3t-6 4-6 0-6-3q-3-3-4-9t-1-7q-4 3 0 10t1 10q-1 3-6 2t-6-2-7-5-5-3-4-3-5-5q-2-2-4-6t-2-6q-1 2-7 3t-5 3q1-5 2-19t3-22q4-17-7-26-15-14-16-23-2-12 7-14 0-4-5-12t-4-12q0-3 2-9z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="brush" unicode="" d="M464 209q0-124-87-212t-210-87q-81 0-149 40 68 39 109 108t40 151q0 61 44 105t105 44 105-44 43-105z m415 562q32-32 32-79t-33-79l-318-318q-20 55-61 97t-97 62l318 318q32 32 79 32t80-33z" horiz-adv-x="928" />
|
|
||||||
|
|
||||||
<glyph glyph-name="attention" unicode="" d="M571 90v106q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-106q0-8 5-13t12-6h108q7 0 12 6t5 13z m-1 208l10 257q0 6-5 10-7 6-14 6h-122q-6 0-14-6-5-4-5-12l9-255q0-5 6-9t13-3h103q8 0 14 3t5 9z m-7 522l428-786q20-35-1-70-9-17-26-26t-35-10h-858q-18 0-35 10t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 27-27z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="plus" unicode="" d="M786 446v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="adjust" unicode="" d="M429 53v608q-83 0-153-41t-110-111-41-152 41-152 110-111 153-41z m428 304q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="pin" unicode="" d="M268 375v250q0 8-5 13t-13 5-13-5-5-13v-250q0-8 5-13t13-5 13 5 5 13z m375-197q0-14-11-25t-25-10h-239l-29-270q-1-7-6-11t-11-5h-1q-15 0-17 15l-43 271h-225q-15 0-25 10t-11 25q0 69 44 124t99 55v286q-29 0-50 21t-22 50 22 50 50 22h357q29 0 50-22t21-50-21-50-50-21v-286q55 0 99-55t44-124z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="wrench" unicode="" d="M214 36q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m360 234l-381-381q-21-20-50-20-29 0-51 20l-59 61q-21 20-21 50 0 29 21 51l380 380q22-55 64-97t97-64z m354 243q0-22-13-59-27-75-92-122t-144-46q-104 0-177 73t-73 177 73 176 177 74q32 0 67-10t60-26q9-6 9-15t-9-16l-163-94v-125l108-60q2 2 44 27t75 45 40 20q8 0 13-5t5-14z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="verified" unicode="" d="M926 453l-19 13c-21 14-30 41-23 65l6 22c10 34-13 69-48 75l-23 4c-25 4-45 23-49 48l-4 23c-6 35-41 57-75 47l-22-7c-24-7-51 2-65 22l-14 20c-21 29-62 33-88 9l-17-16c-19-17-46-21-69-8l-20 11c-31 17-70 3-84-30l-9-22c-9-24-33-39-58-37l-23 1c-36 2-65-28-62-63l2-23c2-25-13-49-36-59l-21-9c-33-14-46-53-29-84l12-20c13-22 10-50-7-69l-15-17c-24-27-19-68 11-88l19-13c21-14 30-41 23-65l-9-23c-10-34 13-69 48-75l23-4c25-4 45-23 49-48l4-23c6-35 41-57 75-47l22 7c24 7 51-2 65-22l14-19c21-29 62-33 88-9l17 16c19 17 46 21 69 8l20-11c31-17 70-3 84 30l9 22c9 24 33 39 58 37l23-1c36-2 65 28 62 63l-1 23c-2 25 13 49 36 59l21 9c33 14 46 53 29 84l-12 20c-13 22-10 50 7 69l15 17c25 26 20 68-9 88z m-399-189l-82-81-81 82-78 79 82 81 78-79 187 186 81-82-187-186z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="chart-bar" unicode="" d="M357 357v-286h-143v286h143z m214 286v-572h-142v572h142z m572-643v-72h-1143v858h71v-786h1072z m-357 500v-429h-143v429h143z m214 214v-643h-143v643h143z" horiz-adv-x="1142.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="users" unicode="" d="M331 357q-90-3-148-71h-75q-45 0-77 22t-31 66q0 197 69 197 4 0 25-11t54-24 66-12q38 0 75 13-3-21-3-37 0-78 45-143z m598-355q0-67-41-106t-108-39h-488q-68 0-108 39t-41 106q0 29 2 57t8 61 14 61 24 54 35 45 48 30 62 11q6 0 24-12t41-26 59-27 76-12 75 12 60 27 41 26 24 12q34 0 62-11t47-30 35-45 24-54 15-61 8-61 2-57z m-572 712q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m393-214q0-89-63-152t-151-62-152 62-63 152 63 151 152 63 151-63 63-151z m321-126q0-43-31-66t-77-22h-75q-57 68-147 71 45 65 45 143 0 16-3 37 37-13 74-13 33 0 67 12t54 24 24 11q69 0 69-197z m-71 340q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="address-book" unicode="" d="M0-143l0 1000 1000 0 0-178-109 0 0-95 109 0 0-178-109 0 0-98 109 0 0-177-109 0 0-96 109 0 0-178-1000 0z m193 285l504 0 0 155-187 111q37 19 58 54t22 77q0 58-42 101t-103 43-102-43-42-101q0-43 22-77t58-54l-188-111 0-155z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="cog-alt" unicode="" d="M500 357q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m429-286q0 29-22 51t-50 21-50-21-21-51q0-29 21-50t50-21 51 21 21 50z m0 572q0 29-22 50t-50 21-50-21-21-50q0-30 21-51t50-21 51 21 21 51z m-215-235v-103q0-6-4-11t-8-6l-87-14q-6-19-18-42 19-27 50-64 4-6 4-11 0-7-4-11-12-17-46-50t-43-33q-7 0-12 4l-64 50q-21-11-43-17-6-60-13-87-4-13-17-13h-104q-6 0-11 4t-5 10l-13 85q-19 6-42 18l-66-50q-4-4-11-4-6 0-12 4-80 75-80 90 0 5 4 10 5 8 23 30t26 34q-13 24-20 46l-85 13q-5 1-9 5t-4 11v104q0 5 4 10t9 6l86 14q7 19 18 42-19 27-50 64-4 6-4 11 0 7 4 12 12 16 46 49t44 33q6 0 12-4l64-50q19 10 43 18 6 60 13 86 3 13 16 13h104q6 0 11-4t6-10l13-85q19-6 42-17l65 49q5 4 12 4 6 0 11-4 81-75 81-90 0-4-4-10-7-9-24-30t-25-34q13-27 19-46l85-12q6-2 9-6t4-11z m357-298v-78q0-9-83-17-6-15-16-29 28-63 28-77 0-2-2-4-68-40-69-40-5 0-26 27t-29 37q-11-1-17-1t-17 1q-7-11-29-37t-25-27q-1 0-69 40-3 2-3 4 0 14 29 77-10 14-17 29-83 8-83 17v78q0 9 83 18 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 1 17 1t17-1q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-9 83-18z m0 572v-78q0-9-83-18-6-15-16-29 28-63 28-77 0-2-2-4-68-39-69-39-5 0-26 26t-29 38q-11-1-17-1t-17 1q-7-12-29-38t-25-26q-1 0-69 39-3 2-3 4 0 14 29 77-10 14-17 29-83 9-83 18v78q0 9 83 17 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-37q12 1 17 1t17-1q28 39 51 62l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-8 83-17z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="zoom-in" unicode="" d="M571 411v-36q0-7-5-13t-12-5h-125v-125q0-7-6-13t-12-5h-36q-7 0-13 5t-5 13v125h-125q-7 0-12 5t-6 13v36q0 7 6 12t12 5h125v125q0 8 5 13t13 5h36q7 0 12-5t6-13v-125h125q7 0 12-5t5-12z m72-18q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="home-2" unicode="" d="M521 826q322-279 500-429 20-16 20-40 0-21-15-37t-36-15l-105 0 0-364q0-21-15-37t-36-16l-156 0q-22 0-37 16t-16 37l0 208-209 0 0-208q0-21-15-37t-36-16l-156 0q-21 0-37 16t-16 37l0 364-103 0q-22 0-37 15t-16 37 19 40z" horiz-adv-x="1041" />
|
|
||||||
|
|
||||||
<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="link-ext" unicode="" d="M786 339v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<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="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="gauge" unicode="" d="M214 214q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="bell-alt" unicode="" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m455 160q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-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="laptop" unicode="" d="M232 143q-37 0-63 26t-26 63v393q0 37 26 63t63 26h607q37 0 63-26t27-63v-393q0-37-27-63t-63-26h-607z m-18 482v-393q0-7 6-13t12-5h607q8 0 13 5t5 13v393q0 7-5 12t-13 6h-607q-7 0-12-6t-6-12z m768-518h89v-54q0-22-26-37t-63-16h-893q-36 0-63 16t-26 37v54h982z m-402-54q9 0 9 9t-9 9h-89q-9 0-9-9t9-9h89z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="quote-right" unicode="" d="M429 678v-392q0-58-23-111t-61-91-91-61-111-23h-36q-14 0-25 11t-11 25v71q0 15 11 25t25 11h36q59 0 101 42t42 101v17q0 23-16 38t-38 16h-125q-44 0-76 31t-31 76v214q0 45 31 76t76 32h214q45 0 76-32t32-76z m500 0v-392q0-58-23-111t-61-91-91-61-111-23h-36q-14 0-25 11t-11 25v71q0 15 11 25t25 11h36q59 0 101 42t42 101v17q0 23-16 38t-38 16h-125q-44 0-76 31t-31 76v214q0 45 31 76t76 32h214q45 0 76-32t32-76z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="reply" unicode="" d="M1000 232q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="smile" unicode="" d="M633 257q-21-67-77-109t-127-41-128 41-77 109q-4 14 3 27t21 18q14 4 27-2t17-22q14-44 52-72t85-28 84 28 52 72q4 15 18 22t27 2 21-18 2-27z m-276 243q0-30-21-51t-50-21-51 21-21 51 21 50 51 21 50-21 21-50z m286 0q0-30-21-51t-51-21-50 21-21 51 21 50 50 21 51-21 21-50z m143-143q0 73-29 139t-76 114-114 76-138 28-139-28-114-76-76-114-29-139 29-139 76-113 114-77 139-28 138 28 114 77 76 113 29 139z m71 0q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="lock-open-alt" unicode="" d="M589 428q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="ellipsis" unicode="" d="M214 446v-107q0-22-15-38t-38-15h-107q-23 0-38 15t-16 38v107q0 23 16 38t38 16h107q22 0 38-16t15-38z m286 0v-107q0-22-16-38t-38-15h-107q-22 0-38 15t-15 38v107q0 23 15 38t38 16h107q23 0 38-16t16-38z m286 0v-107q0-22-16-38t-38-15h-107q-22 0-38 15t-16 38v107q0 23 16 38t38 16h107q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="play-circled" unicode="" d="M429 786q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m214-460q18 10 18 31t-18 31l-304 178q-17 11-35 1-18-11-18-31v-358q0-20 18-31 9-4 17-4 10 0 18 5z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="thumbs-up-alt" unicode="" d="M143 107q0 15-11 25t-25 11q-15 0-25-11t-11-25q0-15 11-25t25-11q15 0 25 11t11 25z m89 286v-357q0-15-10-25t-26-11h-160q-15 0-25 11t-11 25v357q0 14 11 25t25 10h160q15 0 26-10t10-25z m661 0q0-48-31-83 9-25 9-43 1-42-24-76 9-31 0-66-9-31-31-52 5-62-27-101-36-43-110-44h-72q-37 0-80 9t-68 16-67 22q-69 24-88 25-15 0-25 11t-11 25v357q0 14 10 25t24 11q13 1 42 33t57 67q38 49 56 67 10 10 17 27t10 27 8 34q4 22 7 34t11 29 19 28q10 11 25 11 25 0 46-6t33-15 22-22 14-25 7-28 2-25 1-22q0-21-6-43t-10-33-16-31q-1-4-5-10t-6-13-5-13h155q43 0 75-32t32-75z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="apple" unicode="" d="M777 179q-21-70-68-139-72-110-144-110-27 0-78 18-48 18-84 18-34 0-79-19-45-19-74-19-85 0-168 145-82 146-82 281 0 127 63 208 63 81 159 81 40 0 98-17 58-17 77-17 25 0 80 19 57 19 97 19 66 0 119-36 29-20 58-56-44-37-64-66-36-52-36-115 0-69 38-125t88-70z m-209 655q0-34-17-76-16-42-52-77-30-30-60-40-20-7-58-10 2 83 44 143 41 60 139 83 1-2 2-6t1-6q0-2 0-6t1-5z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="android" unicode="" d="M275 588q9 0 16 6t6 15-6 16-16 6-15-6-6-16 6-15 15-6z m236 0q9 0 15 6t6 15-6 16-15 6-16-6-6-16 6-15 16-6z m-453-103q23 0 40-17t16-40v-240q0-24-16-41t-40-17-41 17-17 41v240q0 23 17 40t41 17z m591-11v-371q0-26-18-44t-43-18h-42v-127q0-24-16-40t-41-17-41 17-17 40v127h-77v-127q0-24-16-40t-41-17q-24 0-40 17t-17 40l-1 127h-41q-26 0-43 18t-18 44v371h512z m-129 226q59-30 95-85t36-121h-516q0 66 35 121t96 85l-39 73q-4 8 2 12 8 3 12-4l40-74q53 24 112 24t112-24l40 74q4 7 11 4 7-4 3-12z m266-272v-240q0-24-17-41t-41-17q-23 0-40 17t-17 41v240q0 24 17 40t40 17q24 0 41-17t17-40z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="paper-plane-empty" unicode="" d="M984 851q19-13 15-36l-142-857q-3-16-18-25-8-5-18-5-6 0-13 3l-294 120-166-182q-10-12-27-12-7 0-12 2-11 4-17 13t-6 21v252l-264 108q-20 8-22 30-2 22 18 33l928 536q20 12 38-1z m-190-837l123 739-800-462 187-76 482 356-267-444z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="binoculars" unicode="" d="M393 678v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="user-plus" unicode="" d="M393 357q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="hashtag" unicode="" d="M553 286l36 142h-142l-36-142h142z m429 281l-32-125q-4-14-17-14h-182l-36-142h173q9 0 14-7 6-8 4-16l-32-125q-2-13-17-13h-182l-45-183q-4-14-18-14h-125q-9 0-14 7-5 7-4 16l44 174h-142l-45-183q-4-14-17-14h-126q-8 0-14 7-5 7-3 16l43 174h-173q-9 0-14 7-5 6-4 15l32 125q4 14 17 14h182l36 142h-173q-9 0-14 7-6 8-4 16l32 125q2 13 17 13h182l46 183q3 14 17 14h125q9 0 14-7 5-7 4-16l-44-174h142l45 183q4 14 18 14h125q8 0 14-7 5-7 3-16l-43-174h173q9 0 14-7 5-6 4-15z" horiz-adv-x="1000" />
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,126 +0,0 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="fontello" horiz-adv-x="1000" >
|
|
||||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="857" descent="-143" />
|
|
||||||
<missing-glyph horiz-adv-x="1000" />
|
|
||||||
<glyph glyph-name="cancel" unicode="" d="M724 119q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="upload" unicode="" d="M714 36q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m143 0q0 14-10 25t-26 10-25-10-10-25 10-25 25-11 26 11 10 25z m72 125v-179q0-22-16-38t-38-16h-821q-23 0-38 16t-16 38v179q0 22 16 38t38 15h238q12-31 39-51t62-20h143q34 0 61 20t40 51h238q22 0 38-15t16-38z m-182 361q-9-22-33-22h-143v-250q0-15-10-25t-25-11h-143q-15 0-25 11t-11 25v250h-143q-23 0-33 22-9 22 8 39l250 250q10 10 25 10t25-10l250-250q18-17 8-39z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="star" unicode="" d="M929 496q0-12-15-27l-202-197 48-279q0-4 0-12 0-11-6-19t-17-9q-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="star-empty" unicode="" d="M635 297l170 166-235 34-106 213-105-213-236-34 171-166-41-235 211 111 211-111z m294 199q0-12-15-27l-202-197 48-279q0-4 0-12 0-28-23-28-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="retweet" unicode="" d="M714 18q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="eye-off" unicode="" d="M310 112l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="search" unicode="" d="M643 393q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="cog" unicode="" d="M571 357q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="logout" unicode="" d="M357 53q0-2 1-11t0-14-2-14-5-10-12-4h-178q-67 0-114 47t-47 114v392q0 67 47 114t114 47h178q8 0 13-5t5-13q0-2 1-11t0-15-2-13-5-11-12-3h-178q-37 0-63-26t-27-64v-392q0-37 27-63t63-27h174t6 0 7-2 4-3 4-5 1-8z m518 304q0-14-11-25l-303-304q-11-10-25-10t-25 10-11 25v161h-250q-14 0-25 11t-11 25v214q0 15 11 25t25 11h250v161q0 14 11 25t25 10 25-10l303-304q11-10 11-25z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="down-open" unicode="" d="M939 406l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="attach" unicode="" d="M244-133q-102 0-170 72-72 70-74 166t84 190l496 496q80 80 174 54 44-12 79-47t47-79q26-96-54-176l-474-474q-40-40-88-46-48-4-80 28-30 24-27 74t47 92l332 334q24 26 50 0t0-50l-332-332q-44-44-20-70 12-8 24-6 24 4 46 26l474 474q50 50 34 108-16 60-76 76-54 14-108-36l-494-494q-66-76-64-143t52-117q50-48 117-50t141 62l496 494q24 24 50 0 26-22 0-48l-496-496q-82-82-186-82z" horiz-adv-x="939" />
|
|
||||||
|
|
||||||
<glyph glyph-name="picture" unicode="" d="M357 536q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="video" unicode="" d="M214-36v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="right-open" unicode="" d="M618 368l-414-415q-11-10-25-10t-25 10l-93 93q-11 11-11 25t11 25l296 297-296 296q-11 11-11 25t11 25l93 93q10 11 25 11t25-11l414-414q10-11 10-25t-10-25z" horiz-adv-x="714.3" />
|
|
||||||
|
|
||||||
<glyph glyph-name="left-open" unicode="" d="M654 689l-297-296 297-297q10-10 10-25t-10-25l-93-93q-11-10-25-10t-25 10l-414 415q-11 10-11 25t11 25l414 414q10 11 25 11t25-11l93-93q10-10 10-25t-10-25z" horiz-adv-x="714.3" />
|
|
||||||
|
|
||||||
<glyph glyph-name="up-open" unicode="" d="M939 114l-92-92q-11-10-26-10t-25 10l-296 297-296-297q-11-10-25-10t-25 10l-93 92q-11 11-11 26t11 25l414 414q11 10 25 10t25-10l414-414q11-11 11-25t-11-26z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="bell-ringing-o" unicode="" d="M498 857c-30 0-54-24-54-53 0-8 2-15 5-22-147-22-236-138-236-245 0-268-95-393-177-462 0-39 32-71 71-71h249c0-79 63-143 142-143s142 64 142 143h249c39 0 71 32 71 71-82 69-178 194-178 462 0 107-88 223-235 245 2 7 4 14 4 22 0 29-24 53-53 53z m-309-45c-81-74-118-170-118-275l71 0c0 89 28 162 95 223l-48 52z m617 0l-48-52c67-61 96-134 95-223l71 0c1 105-37 201-118 275z m-397-799c5 0 9-4 9-9 0-44 36-80 80-80 5 0 9-4 9-9s-4-9-9-9c-54 0-98 44-98 98 0 5 4 9 9 9z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="lock" unicode="" d="M179 428h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="globe" unicode="" d="M429 786q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m153-291q-2-1-6-5t-7-6q1 0 2 3t3 6 2 4q3 4 12 8 8 4 29 7 19 5 29-6-1 1 5 7t8 7q2 1 8 3t9 4l1 12q-7-1-10 4t-3 12q0-2-4-5 0 4-2 5t-7-1-5-1q-5 2-8 5t-5 9-2 8q-1 3-5 6t-5 6q-1 1-2 3t-1 4-3 3-3 1-4-3-4-5-2-3q-2 1-4 1t-2-1-3-1-3-2q-1-2-4-2t-5-1q8 3-1 6-5 2-9 2 6 2 5 6t-5 8h3q-1 2-5 5t-10 5-7 3q-5 3-19 5t-18 1q-3-4-3-6t2-8 2-7q1-3-3-7t-3-7q0-4 7-9t6-12q-2-4-9-9t-9-6q-3-5-1-11t6-9q1-1 1-2t-2-3-3-2-4-2l-1-1q-7-3-12 3t-7 15q-4 14-9 17-13 4-16-1-3 7-23 15-14 5-33 2 4 0 0 8-4 9-10 7 1 3 2 10t0 7q2 8 7 13 1 1 4 5t5 7 1 4q19-3 28 6 2 3 6 9t6 10q5 3 8 3t8-3 8-3q8-1 8 6t-4 11q7 0 2 10-2 4-5 5-6 2-15-3-4-2 2-4-1 0-6-6t-9-10-9 3q0 0-3 7t-5 8q-5 0-9-9 1 5-6 9t-14 4q11 7-4 15-4 3-12 3t-11-2q-2-4-3-7t3-4 6-3 6-2 5-2q8-6 5-8-1 0-5-2t-6-2-4-2q-1-3 0-8t-1-8q-3 3-5 10t-4 9q4-5-14-3l-5 0q-3 0-9-1t-12-1-7 5q-3 4 0 11 0 2 2 1-2 2-6 5t-6 5q-25-8-52-23 3 0 6 1 3 1 8 4t5 3q19 7 24 4l3 2q7-9 11-14-4 3-17 1-11-3-12-7 4-6 2-10-2 2-6 6t-8 6-8 3q-9 0-13-1-81-45-131-124 4-4 7-4 2-1 3-5t1-6 6 1q5-4 2-10 1 0 25-15 10-10 11-12 2-6-5-10-1 1-5 5t-5 2q-2-3 0-10t6-7q-4 0-5-9t-2-20 0-13l1-1q-2-6 3-19t12-11q-7-1 11-24 3-4 4-5 2-1 7-4t9-6 5-5q2-3 6-13t8-13q-2-3 5-11t6-13q-1 0-2-1t-1 0q2-4 9-8t8-7q1-2 1-6t2-6 4-1q2 11-13 35-8 13-9 16-2 2-4 8t-2 8q1 0 3 0t5-2 4-3 1-1q-1-4 1-10t7-10 10-11 6-7q4-4 8-11t0-8q5 0 11-5t10-11q3-5 4-15t3-13q1-4 5-8t7-5l9-5t7-3q3-2 10-6t12-7q6-2 9-2t8 1 8 2q8 1 16-8t12-12q20-10 30-6-1 0 1-4t4-9 5-8 3-5q3-3 10-8t10-8q4 2 4 5-1-5 4-11t10-6q8 2 8 18-17-8-27 10 0 0-2 3t-2 5-1 4 0 5 2 1q5 0 6 2t-1 7-2 8q-1 4-6 11t-7 8q-3-5-9-4t-9 5q0-1-1-3t-1-4q-7 0-8 0 1 2 1 10t2 13q1 2 3 6t5 9 2 7-3 5-9 1q-11 0-15-11-1-2-2-6t-2-6-5-4q-4-2-14-1t-13 3q-8 4-13 16t-5 20q0 6 1 15t2 14-3 14q2 1 5 5t5 6q2 1 3 1t3 0 2 1 1 3q0 1-2 2-1 1-2 1 4-1 16 1t15-1q9-6 12 1 0 1-1 6t0 7q3-15 16-5 2-1 9-3t9-2q2-1 4-3t3-3 3 0 5 4q5-8 7-13 6-23 10-25 4-2 6-1t3 5 0 8-1 7l-1 5v10l0 4q-8 2-10 7t0 10 9 10q0 1 4 2t9 4 7 4q12 11 8 20 4 0 6 5 0 0-2 2t-5 2-2 2q5 2 1 8 3 2 4 7t4 5q5-6 12-1 5 5 1 9 2 4 11 6t10 5q4-1 5 1t0 7 2 7q2 2 9 5t7 2l9 7q2 2 0 2 10-1 18 6 5 6-4 11 2 4-1 5t-9 4q2 0 7 0t5 1q9 5-3 9-10 2-24-7z m-91-490q115 21 195 106-1 2-7 2t-7 2q-10 4-13 5 1 4-1 7t-5 5-7 5-6 4q-1 1-4 3t-4 3-4 2-5 2-5-1l-2-1q-2 0-3-1t-3-2-2-1 0-2q-12 10-20 13-3 0-6 3t-6 4-6 0-6-3q-3-3-4-9t-1-7q-4 3 0 10t1 10q-1 3-6 2t-6-2-7-5-5-3-4-3-5-5q-2-2-4-6t-2-6q-1 2-7 3t-5 3q1-5 2-19t3-22q4-17-7-26-15-14-16-23-2-12 7-14 0-4-5-12t-4-12q0-3 2-9z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="brush" unicode="" d="M464 209q0-124-87-212t-210-87q-81 0-149 40 68 39 109 108t40 151q0 61 44 105t105 44 105-44 43-105z m415 562q32-32 32-79t-33-79l-318-318q-20 55-61 97t-97 62l318 318q32 32 79 32t80-33z" horiz-adv-x="928" />
|
|
||||||
|
|
||||||
<glyph glyph-name="attention" unicode="" d="M571 90v106q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-106q0-8 5-13t12-6h108q7 0 12 6t5 13z m-1 208l10 257q0 6-5 10-7 6-14 6h-122q-6 0-14-6-5-4-5-12l9-255q0-5 6-9t13-3h103q8 0 14 3t5 9z m-7 522l428-786q20-35-1-70-9-17-26-26t-35-10h-858q-18 0-35 10t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 27-27z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="plus" unicode="" d="M786 446v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="adjust" unicode="" d="M429 53v608q-83 0-153-41t-110-111-41-152 41-152 110-111 153-41z m428 304q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="pin" unicode="" d="M268 375v250q0 8-5 13t-13 5-13-5-5-13v-250q0-8 5-13t13-5 13 5 5 13z m375-197q0-14-11-25t-25-10h-239l-29-270q-1-7-6-11t-11-5h-1q-15 0-17 15l-43 271h-225q-15 0-25 10t-11 25q0 69 44 124t99 55v286q-29 0-50 21t-22 50 22 50 50 22h357q29 0 50-22t21-50-21-50-50-21v-286q55 0 99-55t44-124z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="wrench" unicode="" d="M214 36q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m360 234l-381-381q-21-20-50-20-29 0-51 20l-59 61q-21 20-21 50 0 29 21 51l380 380q22-55 64-97t97-64z m354 243q0-22-13-59-27-75-92-122t-144-46q-104 0-177 73t-73 177 73 176 177 74q32 0 67-10t60-26q9-6 9-15t-9-16l-163-94v-125l108-60q2 2 44 27t75 45 40 20q8 0 13-5t5-14z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="verified" unicode="" d="M926 453l-19 13c-21 14-30 41-23 65l6 22c10 34-13 69-48 75l-23 4c-25 4-45 23-49 48l-4 23c-6 35-41 57-75 47l-22-7c-24-7-51 2-65 22l-14 20c-21 29-62 33-88 9l-17-16c-19-17-46-21-69-8l-20 11c-31 17-70 3-84-30l-9-22c-9-24-33-39-58-37l-23 1c-36 2-65-28-62-63l2-23c2-25-13-49-36-59l-21-9c-33-14-46-53-29-84l12-20c13-22 10-50-7-69l-15-17c-24-27-19-68 11-88l19-13c21-14 30-41 23-65l-9-23c-10-34 13-69 48-75l23-4c25-4 45-23 49-48l4-23c6-35 41-57 75-47l22 7c24 7 51-2 65-22l14-19c21-29 62-33 88-9l17 16c19 17 46 21 69 8l20-11c31-17 70-3 84 30l9 22c9 24 33 39 58 37l23-1c36-2 65 28 62 63l-1 23c-2 25 13 49 36 59l21 9c33 14 46 53 29 84l-12 20c-13 22-10 50 7 69l15 17c25 26 20 68-9 88z m-399-189l-82-81-81 82-78 79 82 81 78-79 187 186 81-82-187-186z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="chart-bar" unicode="" d="M357 357v-286h-143v286h143z m214 286v-572h-142v572h142z m572-643v-72h-1143v858h71v-786h1072z m-357 500v-429h-143v429h143z m214 214v-643h-143v643h143z" horiz-adv-x="1142.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="users" unicode="" d="M331 357q-90-3-148-71h-75q-45 0-77 22t-31 66q0 197 69 197 4 0 25-11t54-24 66-12q38 0 75 13-3-21-3-37 0-78 45-143z m598-355q0-67-41-106t-108-39h-488q-68 0-108 39t-41 106q0 29 2 57t8 61 14 61 24 54 35 45 48 30 62 11q6 0 24-12t41-26 59-27 76-12 75 12 60 27 41 26 24 12q34 0 62-11t47-30 35-45 24-54 15-61 8-61 2-57z m-572 712q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m393-214q0-89-63-152t-151-62-152 62-63 152 63 151 152 63 151-63 63-151z m321-126q0-43-31-66t-77-22h-75q-57 68-147 71 45 65 45 143 0 16-3 37 37-13 74-13 33 0 67 12t54 24 24 11q69 0 69-197z m-71 340q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="address-book" unicode="" d="M0-143l0 1000 1000 0 0-178-109 0 0-95 109 0 0-178-109 0 0-98 109 0 0-177-109 0 0-96 109 0 0-178-1000 0z m193 285l504 0 0 155-187 111q37 19 58 54t22 77q0 58-42 101t-103 43-102-43-42-101q0-43 22-77t58-54l-188-111 0-155z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="cog-alt" unicode="" d="M500 357q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m429-286q0 29-22 51t-50 21-50-21-21-51q0-29 21-50t50-21 51 21 21 50z m0 572q0 29-22 50t-50 21-50-21-21-50q0-30 21-51t50-21 51 21 21 51z m-215-235v-103q0-6-4-11t-8-6l-87-14q-6-19-18-42 19-27 50-64 4-6 4-11 0-7-4-11-12-17-46-50t-43-33q-7 0-12 4l-64 50q-21-11-43-17-6-60-13-87-4-13-17-13h-104q-6 0-11 4t-5 10l-13 85q-19 6-42 18l-66-50q-4-4-11-4-6 0-12 4-80 75-80 90 0 5 4 10 5 8 23 30t26 34q-13 24-20 46l-85 13q-5 1-9 5t-4 11v104q0 5 4 10t9 6l86 14q7 19 18 42-19 27-50 64-4 6-4 11 0 7 4 12 12 16 46 49t44 33q6 0 12-4l64-50q19 10 43 18 6 60 13 86 3 13 16 13h104q6 0 11-4t6-10l13-85q19-6 42-17l65 49q5 4 12 4 6 0 11-4 81-75 81-90 0-4-4-10-7-9-24-30t-25-34q13-27 19-46l85-12q6-2 9-6t4-11z m357-298v-78q0-9-83-17-6-15-16-29 28-63 28-77 0-2-2-4-68-40-69-40-5 0-26 27t-29 37q-11-1-17-1t-17 1q-7-11-29-37t-25-27q-1 0-69 40-3 2-3 4 0 14 29 77-10 14-17 29-83 8-83 17v78q0 9 83 18 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 1 17 1t17-1q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-9 83-18z m0 572v-78q0-9-83-18-6-15-16-29 28-63 28-77 0-2-2-4-68-39-69-39-5 0-26 26t-29 38q-11-1-17-1t-17 1q-7-12-29-38t-25-26q-1 0-69 39-3 2-3 4 0 14 29 77-10 14-17 29-83 9-83 18v78q0 9 83 17 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-37q12 1 17 1t17-1q28 39 51 62l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-8 83-17z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="zoom-in" unicode="" d="M571 411v-36q0-7-5-13t-12-5h-125v-125q0-7-6-13t-12-5h-36q-7 0-13 5t-5 13v125h-125q-7 0-12 5t-6 13v36q0 7 6 12t12 5h125v125q0 8 5 13t13 5h36q7 0 12-5t6-13v-125h125q7 0 12-5t5-12z m72-18q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="home-2" unicode="" d="M521 826q322-279 500-429 20-16 20-40 0-21-15-37t-36-15l-105 0 0-364q0-21-15-37t-36-16l-156 0q-22 0-37 16t-16 37l0 208-209 0 0-208q0-21-15-37t-36-16l-156 0q-21 0-37 16t-16 37l0 364-103 0q-22 0-37 15t-16 37 19 40z" horiz-adv-x="1041" />
|
|
||||||
|
|
||||||
<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="link-ext" unicode="" d="M786 339v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<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="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="gauge" unicode="" d="M214 214q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="bell-alt" unicode="" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m455 160q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-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="laptop" unicode="" d="M232 143q-37 0-63 26t-26 63v393q0 37 26 63t63 26h607q37 0 63-26t27-63v-393q0-37-27-63t-63-26h-607z m-18 482v-393q0-7 6-13t12-5h607q8 0 13 5t5 13v393q0 7-5 12t-13 6h-607q-7 0-12-6t-6-12z m768-518h89v-54q0-22-26-37t-63-16h-893q-36 0-63 16t-26 37v54h982z m-402-54q9 0 9 9t-9 9h-89q-9 0-9-9t9-9h89z" horiz-adv-x="1071.4" />
|
|
||||||
|
|
||||||
<glyph glyph-name="quote-right" unicode="" d="M429 678v-392q0-58-23-111t-61-91-91-61-111-23h-36q-14 0-25 11t-11 25v71q0 15 11 25t25 11h36q59 0 101 42t42 101v17q0 23-16 38t-38 16h-125q-44 0-76 31t-31 76v214q0 45 31 76t76 32h214q45 0 76-32t32-76z m500 0v-392q0-58-23-111t-61-91-91-61-111-23h-36q-14 0-25 11t-11 25v71q0 15 11 25t25 11h36q59 0 101 42t42 101v17q0 23-16 38t-38 16h-125q-44 0-76 31t-31 76v214q0 45 31 76t76 32h214q45 0 76-32t32-76z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="reply" unicode="" d="M1000 232q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="smile" unicode="" d="M633 257q-21-67-77-109t-127-41-128 41-77 109q-4 14 3 27t21 18q14 4 27-2t17-22q14-44 52-72t85-28 84 28 52 72q4 15 18 22t27 2 21-18 2-27z m-276 243q0-30-21-51t-50-21-51 21-21 51 21 50 51 21 50-21 21-50z m286 0q0-30-21-51t-51-21-50 21-21 51 21 50 50 21 51-21 21-50z m143-143q0 73-29 139t-76 114-114 76-138 28-139-28-114-76-76-114-29-139 29-139 76-113 114-77 139-28 138 28 114 77 76 113 29 139z m71 0q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="lock-open-alt" unicode="" d="M589 428q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="ellipsis" unicode="" d="M214 446v-107q0-22-15-38t-38-15h-107q-23 0-38 15t-16 38v107q0 23 16 38t38 16h107q22 0 38-16t15-38z m286 0v-107q0-22-16-38t-38-15h-107q-22 0-38 15t-15 38v107q0 23 15 38t38 16h107q23 0 38-16t16-38z m286 0v-107q0-22-16-38t-38-15h-107q-22 0-38 15t-16 38v107q0 23 16 38t38 16h107q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="play-circled" unicode="" d="M429 786q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m214-460q18 10 18 31t-18 31l-304 178q-17 11-35 1-18-11-18-31v-358q0-20 18-31 9-4 17-4 10 0 18 5z" horiz-adv-x="857.1" />
|
|
||||||
|
|
||||||
<glyph glyph-name="thumbs-up-alt" unicode="" d="M143 107q0 15-11 25t-25 11q-15 0-25-11t-11-25q0-15 11-25t25-11q15 0 25 11t11 25z m89 286v-357q0-15-10-25t-26-11h-160q-15 0-25 11t-11 25v357q0 14 11 25t25 10h160q15 0 26-10t10-25z m661 0q0-48-31-83 9-25 9-43 1-42-24-76 9-31 0-66-9-31-31-52 5-62-27-101-36-43-110-44h-72q-37 0-80 9t-68 16-67 22q-69 24-88 25-15 0-25 11t-11 25v357q0 14 10 25t24 11q13 1 42 33t57 67q38 49 56 67 10 10 17 27t10 27 8 34q4 22 7 34t11 29 19 28q10 11 25 11 25 0 46-6t33-15 22-22 14-25 7-28 2-25 1-22q0-21-6-43t-10-33-16-31q-1-4-5-10t-6-13-5-13h155q43 0 75-32t32-75z" horiz-adv-x="928.6" />
|
|
||||||
|
|
||||||
<glyph glyph-name="apple" unicode="" d="M777 179q-21-70-68-139-72-110-144-110-27 0-78 18-48 18-84 18-34 0-79-19-45-19-74-19-85 0-168 145-82 146-82 281 0 127 63 208 63 81 159 81 40 0 98-17 58-17 77-17 25 0 80 19 57 19 97 19 66 0 119-36 29-20 58-56-44-37-64-66-36-52-36-115 0-69 38-125t88-70z m-209 655q0-34-17-76-16-42-52-77-30-30-60-40-20-7-58-10 2 83 44 143 41 60 139 83 1-2 2-6t1-6q0-2 0-6t1-5z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="android" unicode="" d="M275 588q9 0 16 6t6 15-6 16-16 6-15-6-6-16 6-15 15-6z m236 0q9 0 15 6t6 15-6 16-15 6-16-6-6-16 6-15 16-6z m-453-103q23 0 40-17t16-40v-240q0-24-16-41t-40-17-41 17-17 41v240q0 23 17 40t41 17z m591-11v-371q0-26-18-44t-43-18h-42v-127q0-24-16-40t-41-17-41 17-17 40v127h-77v-127q0-24-16-40t-41-17q-24 0-40 17t-17 40l-1 127h-41q-26 0-43 18t-18 44v371h512z m-129 226q59-30 95-85t36-121h-516q0 66 35 121t96 85l-39 73q-4 8 2 12 8 3 12-4l40-74q53 24 112 24t112-24l40 74q4 7 11 4 7-4 3-12z m266-272v-240q0-24-17-41t-41-17q-23 0-40 17t-17 41v240q0 24 17 40t40 17q24 0 41-17t17-40z" horiz-adv-x="785.7" />
|
|
||||||
|
|
||||||
<glyph glyph-name="paper-plane-empty" unicode="" d="M984 851q19-13 15-36l-142-857q-3-16-18-25-8-5-18-5-6 0-13 3l-294 120-166-182q-10-12-27-12-7 0-12 2-11 4-17 13t-6 21v252l-264 108q-20 8-22 30-2 22 18 33l928 536q20 12 38-1z m-190-837l123 739-800-462 187-76 482 356-267-444z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="binoculars" unicode="" d="M393 678v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="user-plus" unicode="" d="M393 357q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
|
|
||||||
|
|
||||||
<glyph glyph-name="hashtag" unicode="" d="M553 286l36 142h-142l-36-142h142z m429 281l-32-125q-4-14-17-14h-182l-36-142h173q9 0 14-7 6-8 4-16l-32-125q-2-13-17-13h-182l-45-183q-4-14-18-14h-125q-9 0-14 7-5 7-4 16l44 174h-142l-45-183q-4-14-17-14h-126q-8 0-14 7-5 7-3 16l43 174h-173q-9 0-14 7-5 6-4 15l32 125q4 14 17 14h182l36 142h-173q-9 0-14 7-6 8-4 16l32 125q2 13 17 13h182l46 183q3 14 17 14h125q9 0 14-7 5-7 4-16l-44-174h142l45 183q4 14 18 14h125q8 0 14-7 5-7 3-16l-43-174h173q9 0 14-7 5-6 4-15z" horiz-adv-x="1000" />
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.1055039ce3f2fe4dd110.css rel=stylesheet><link href=/static/fontello.1583594169021.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.c5bbd3734647f0cc7eef.js></script><script type=text/javascript src=/static/js/app.5c94bdec79a7d0f3cfcb.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/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.1055039ce3f2fe4dd110.css rel=stylesheet><link href=/static/fontello.1587147224637.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.c5bbd3734647f0cc7eef.js></script><script type=text/javascript src=/static/js/app.def6476e8bc9b214218b.js></script></body></html>
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
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.
|
@ -16,6 +16,7 @@ test "transfer config values from db to env" do
|
||||||
refute Application.get_env(:pleroma, :test_key)
|
refute Application.get_env(:pleroma, :test_key)
|
||||||
refute Application.get_env(:idna, :test_key)
|
refute Application.get_env(:idna, :test_key)
|
||||||
refute Application.get_env(:quack, :test_key)
|
refute Application.get_env(:quack, :test_key)
|
||||||
|
initial = Application.get_env(:logger, :level)
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
@ -35,16 +36,20 @@ test "transfer config values from db to env" do
|
||||||
value: [:test_value1, :test_value2]
|
value: [:test_value1, :test_value2]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ConfigDB.create(%{group: ":logger", key: ":level", value: :debug})
|
||||||
|
|
||||||
TransferTask.start_link([])
|
TransferTask.start_link([])
|
||||||
|
|
||||||
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
|
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
|
||||||
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
|
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
|
||||||
assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2]
|
assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2]
|
||||||
|
assert Application.get_env(:logger, :level) == :debug
|
||||||
|
|
||||||
on_exit(fn ->
|
on_exit(fn ->
|
||||||
Application.delete_env(:pleroma, :test_key)
|
Application.delete_env(:pleroma, :test_key)
|
||||||
Application.delete_env(:idna, :test_key)
|
Application.delete_env(:idna, :test_key)
|
||||||
Application.delete_env(:quack, :test_key)
|
Application.delete_env(:quack, :test_key)
|
||||||
|
Application.put_env(:logger, :level, initial)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,8 +83,8 @@ test "transfer config values for 1 group and some keys" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "transfer config values with full subkey update" do
|
test "transfer config values with full subkey update" do
|
||||||
emoji = Application.get_env(:pleroma, :emoji)
|
clear_config(:emoji)
|
||||||
assets = Application.get_env(:pleroma, :assets)
|
clear_config(:assets)
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
@ -99,11 +104,6 @@ test "transfer config values with full subkey update" do
|
||||||
assert emoji_env[:groups] == [a: 1, b: 2]
|
assert emoji_env[:groups] == [a: 1, b: 2]
|
||||||
assets_env = Application.get_env(:pleroma, :assets)
|
assets_env = Application.get_env(:pleroma, :assets)
|
||||||
assert assets_env[:mascots] == [a: 1, b: 2]
|
assert assets_env[:mascots] == [a: 1, b: 2]
|
||||||
|
|
||||||
on_exit(fn ->
|
|
||||||
Application.put_env(:pleroma, :emoji, emoji)
|
|
||||||
Application.put_env(:pleroma, :assets, assets)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "pleroma restart" do
|
describe "pleroma restart" do
|
||||||
|
@ -112,8 +112,7 @@ test "transfer config values with full subkey update" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "don't restart if no reboot time settings were changed" do
|
test "don't restart if no reboot time settings were changed" do
|
||||||
emoji = Application.get_env(:pleroma, :emoji)
|
clear_config(:emoji)
|
||||||
on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end)
|
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
@ -128,8 +127,7 @@ test "don't restart if no reboot time settings were changed" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "on reboot time key" do
|
test "on reboot time key" do
|
||||||
chat = Application.get_env(:pleroma, :chat)
|
clear_config(:chat)
|
||||||
on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end)
|
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
@ -141,8 +139,7 @@ test "on reboot time key" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "on reboot time subkey" do
|
test "on reboot time subkey" do
|
||||||
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
|
clear_config(Pleroma.Captcha)
|
||||||
on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end)
|
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
@ -154,13 +151,8 @@ test "on reboot time subkey" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
||||||
chat = Application.get_env(:pleroma, :chat)
|
clear_config(:chat)
|
||||||
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
|
clear_config(Pleroma.Captcha)
|
||||||
|
|
||||||
on_exit(fn ->
|
|
||||||
Application.put_env(:pleroma, :chat, chat)
|
|
||||||
Application.put_env(:pleroma, Pleroma.Captcha, captcha)
|
|
||||||
end)
|
|
||||||
|
|
||||||
ConfigDB.create(%{
|
ConfigDB.create(%{
|
||||||
group: ":pleroma",
|
group: ":pleroma",
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Emoji.FormatterTest do
|
defmodule Pleroma.Emoji.FormatterTest do
|
||||||
alias Pleroma.Emoji
|
|
||||||
alias Pleroma.Emoji.Formatter
|
alias Pleroma.Emoji.Formatter
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
@ -32,30 +31,19 @@ test "it does not add XSS emoji" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "get_emoji" do
|
describe "get_emoji_map" do
|
||||||
test "it returns the emoji used in the text" do
|
test "it returns the emoji used in the text" do
|
||||||
text = "I love :firefox:"
|
assert Formatter.get_emoji_map("I love :firefox:") == %{
|
||||||
|
"firefox" => "http://localhost:4001/emoji/Firefox.gif"
|
||||||
assert Formatter.get_emoji(text) == [
|
}
|
||||||
{"firefox",
|
|
||||||
%Emoji{
|
|
||||||
code: "firefox",
|
|
||||||
file: "/emoji/Firefox.gif",
|
|
||||||
tags: ["Gif", "Fun"],
|
|
||||||
safe_code: "firefox",
|
|
||||||
safe_file: "/emoji/Firefox.gif"
|
|
||||||
}}
|
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns a nice empty result when no emojis are present" do
|
test "it returns a nice empty result when no emojis are present" do
|
||||||
text = "I love moominamma"
|
assert Formatter.get_emoji_map("I love moominamma") == %{}
|
||||||
assert Formatter.get_emoji(text) == []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't die when text is absent" do
|
test "it doesn't die when text is absent" do
|
||||||
text = nil
|
assert Formatter.get_emoji_map(nil) == %{}
|
||||||
assert Formatter.get_emoji(text) == []
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,7 +140,7 @@ test "gives a replacement for user links, using local nicknames in user links te
|
||||||
archaeme =
|
archaeme =
|
||||||
insert(:user,
|
insert(:user,
|
||||||
nickname: "archa_eme_",
|
nickname: "archa_eme_",
|
||||||
source_data: %{"url" => "https://archeme/@archa_eme_"}
|
uri: "https://archeme/@archa_eme_"
|
||||||
)
|
)
|
||||||
|
|
||||||
archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
|
archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
|
||||||
|
|
|
@ -8,11 +8,13 @@ defmodule Pleroma.NotificationTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Mock
|
import Mock
|
||||||
|
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
|
|
||||||
|
@ -272,16 +274,6 @@ test "it doesn't create a notification for user if he is the activity author" do
|
||||||
refute Notification.create_notification(activity, author)
|
refute Notification.create_notification(activity, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
|
||||||
user = insert(:user)
|
|
||||||
followed_user = insert(:user)
|
|
||||||
{:ok, _, _, activity} = CommonAPI.follow(user, followed_user)
|
|
||||||
Notification.create_notification(activity, followed_user)
|
|
||||||
CommonAPI.unfollow(user, followed_user)
|
|
||||||
{:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user)
|
|
||||||
refute Notification.create_notification(activity_dupe, followed_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
subscriber = insert(:user)
|
subscriber = insert(:user)
|
||||||
|
@ -304,6 +296,74 @@ test "it doesn't create subscription notifications if the recipient cannot see t
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "follow / follow_request notifications" do
|
||||||
|
test "it creates `follow` notification for approved Follow activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: false)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
assert FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
assert %{type: "follow"} =
|
||||||
|
NotificationView.render("show.json", %{
|
||||||
|
notification: notification,
|
||||||
|
for: followed_user
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if `follow_request` notifications are enabled, " <>
|
||||||
|
"it creates `follow_request` notification for pending Follow activity" do
|
||||||
|
clear_config([:notifications, :enable_follow_request_notifications], true)
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: true)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
refute FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
render_opts = %{notification: notification, for: followed_user}
|
||||||
|
assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
|
||||||
|
|
||||||
|
# After request is accepted, the same notification is rendered with type "follow":
|
||||||
|
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
||||||
|
|
||||||
|
notification_id = notification.id
|
||||||
|
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
||||||
|
assert %{type: "follow"} = NotificationView.render("show.json", render_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if `follow_request` notifications are disabled, " <>
|
||||||
|
"it does NOT create `follow*` notification for pending Follow activity" do
|
||||||
|
clear_config([:notifications, :enable_follow_request_notifications], false)
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: true)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
refute FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
# After request is accepted, no new notifications are generated:
|
||||||
|
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
||||||
|
assert [] = Notification.for_user(followed_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: false)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
assert FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
CommonAPI.unfollow(user, followed_user)
|
||||||
|
{:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
|
||||||
|
|
||||||
|
notification_id = notification.id
|
||||||
|
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "get notification" do
|
describe "get notification" do
|
||||||
test "it gets a notification that belongs to the user" do
|
test "it gets a notification that belongs to the user" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
alias Pleroma.Plugs.AuthenticationPlug
|
alias Pleroma.Plugs.AuthenticationPlug
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
|
@ -36,13 +38,16 @@ test "it does nothing if a user is assigned", %{conn: conn} do
|
||||||
assert ret_conn == conn
|
assert ret_conn == conn
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with a correct password in the credentials, it assigns the auth_user", %{conn: conn} do
|
test "with a correct password in the credentials, " <>
|
||||||
|
"it assigns the auth_user and marks OAuthScopesPlug as skipped",
|
||||||
|
%{conn: conn} do
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:auth_credentials, %{password: "guy"})
|
|> assign(:auth_credentials, %{password: "guy"})
|
||||||
|> AuthenticationPlug.call(%{})
|
|> AuthenticationPlug.call(%{})
|
||||||
|
|
||||||
assert conn.assigns.user == conn.assigns.auth_user
|
assert conn.assigns.user == conn.assigns.auth_user
|
||||||
|
assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with a wrong password in the credentials, it does nothing", %{conn: conn} do
|
test "with a wrong password in the credentials, it does nothing", %{conn: conn} do
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue