Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
4
.gitignore
vendored
|
@ -38,3 +38,7 @@ erl_crash.dump
|
||||||
|
|
||||||
# Prevent committing docs files
|
# Prevent committing docs files
|
||||||
/priv/static/doc/*
|
/priv/static/doc/*
|
||||||
|
|
||||||
|
# Code test coverage
|
||||||
|
/cover
|
||||||
|
/Elixir.*.coverdata
|
||||||
|
|
|
@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Pleroma API: Healthcheck endpoint
|
- Pleroma API: Healthcheck endpoint
|
||||||
- Admin API: Endpoints for listing/revoking invite tokens
|
- Admin API: Endpoints for listing/revoking invite tokens
|
||||||
- Admin API: Endpoints for making users follow/unfollow each other
|
- Admin API: Endpoints for making users follow/unfollow each other
|
||||||
|
- Admin API: added filters (role, tags, email, name) for users endpoint
|
||||||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||||
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
||||||
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
|
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
|
||||||
|
@ -60,6 +61,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Deps: Updated Ecto to 3.0.7
|
- Deps: Updated Ecto to 3.0.7
|
||||||
- Don't ship finmoji by default, they can be installed as an emoji pack
|
- Don't ship finmoji by default, they can be installed as an emoji pack
|
||||||
- Mastodon API: Added support max_id & since_id for bookmark timeline endpoints.
|
- Mastodon API: Added support max_id & since_id for bookmark timeline endpoints.
|
||||||
|
- Admin API: Move the user related API to `api/pleroma/admin/users`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
|
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
|
||||||
|
|
|
@ -12,7 +12,7 @@ For clients it supports both the [GNU Social API with Qvitter extensions](https:
|
||||||
|
|
||||||
- [Client Applications for Pleroma](https://docs-develop.pleroma.social/clients.html)
|
- [Client Applications for Pleroma](https://docs-develop.pleroma.social/clients.html)
|
||||||
|
|
||||||
No release has been made yet, but several servers have been online for months already. If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>.
|
If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
@ -232,8 +232,7 @@
|
||||||
welcome_message: nil,
|
welcome_message: nil,
|
||||||
max_report_comment_size: 1000,
|
max_report_comment_size: 1000,
|
||||||
safe_dm_mentions: false,
|
safe_dm_mentions: false,
|
||||||
healthcheck: false,
|
healthcheck: false
|
||||||
repo_batch_size: 500
|
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
|
|
@ -8,15 +8,20 @@ Authentication is required and the user must be an admin.
|
||||||
|
|
||||||
- Method `GET`
|
- Method `GET`
|
||||||
- Query Params:
|
- Query Params:
|
||||||
- *optional* `query`: **string** search term
|
- *optional* `query`: **string** search term (e.g. nickname, domain, nickname@domain)
|
||||||
- *optional* `filters`: **string** comma-separated string of filters:
|
- *optional* `filters`: **string** comma-separated string of filters:
|
||||||
- `local`: only local users
|
- `local`: only local users
|
||||||
- `external`: only external users
|
- `external`: only external users
|
||||||
- `active`: only active users
|
- `active`: only active users
|
||||||
- `deactivated`: only deactivated users
|
- `deactivated`: only deactivated users
|
||||||
|
- `is_admin`: users with admin role
|
||||||
|
- `is_moderator`: users with moderator role
|
||||||
- *optional* `page`: **integer** page number
|
- *optional* `page`: **integer** page number
|
||||||
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
||||||
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10`
|
- *optional* `tags`: **[string]** tags list
|
||||||
|
- *optional* `name`: **string** user display name
|
||||||
|
- *optional* `email`: **string** user email
|
||||||
|
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
|
||||||
- Response:
|
- Response:
|
||||||
|
|
||||||
```JSON
|
```JSON
|
||||||
|
@ -40,7 +45,7 @@ Authentication is required and the user must be an admin.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/pleroma/admin/user`
|
## `/api/pleroma/admin/users`
|
||||||
|
|
||||||
### Remove a user
|
### Remove a user
|
||||||
|
|
||||||
|
@ -58,7 +63,7 @@ Authentication is required and the user must be an admin.
|
||||||
- `password`
|
- `password`
|
||||||
- Response: User’s nickname
|
- Response: User’s nickname
|
||||||
|
|
||||||
## `/api/pleroma/admin/user/follow`
|
## `/api/pleroma/admin/users/follow`
|
||||||
### Make a user follow another user
|
### Make a user follow another user
|
||||||
|
|
||||||
- Methods: `POST`
|
- Methods: `POST`
|
||||||
|
@ -68,7 +73,7 @@ Authentication is required and the user must be an admin.
|
||||||
- Response:
|
- Response:
|
||||||
- "ok"
|
- "ok"
|
||||||
|
|
||||||
## `/api/pleroma/admin/user/unfollow`
|
## `/api/pleroma/admin/users/unfollow`
|
||||||
### Make a user unfollow another user
|
### Make a user unfollow another user
|
||||||
|
|
||||||
- Methods: `POST`
|
- Methods: `POST`
|
||||||
|
@ -111,7 +116,7 @@ Authentication is required and the user must be an admin.
|
||||||
- `nickname`
|
- `nickname`
|
||||||
- `tags`
|
- `tags`
|
||||||
|
|
||||||
## `/api/pleroma/admin/permission_group/:nickname`
|
## `/api/pleroma/admin/users/:nickname/permission_group`
|
||||||
|
|
||||||
### Get user user permission groups membership
|
### Get user user permission groups membership
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ Authentication is required and the user must be an admin.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/pleroma/admin/permission_group/:nickname/:permission_group`
|
## `/api/pleroma/admin/users/:nickname/permission_group/:permission_group`
|
||||||
|
|
||||||
Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist.
|
Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist.
|
||||||
|
|
||||||
|
@ -160,7 +165,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- On success: JSON of the `user.info`
|
- On success: JSON of the `user.info`
|
||||||
- Note: An admin cannot revoke their own admin status.
|
- Note: An admin cannot revoke their own admin status.
|
||||||
|
|
||||||
## `/api/pleroma/admin/activation_status/:nickname`
|
## `/api/pleroma/admin/users/:nickname/activation_status`
|
||||||
|
|
||||||
### Active or deactivate a user
|
### Active or deactivate a user
|
||||||
|
|
||||||
|
@ -198,7 +203,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Response:
|
- Response:
|
||||||
- On success: URL of the unfollowed relay
|
- On success: URL of the unfollowed relay
|
||||||
|
|
||||||
## `/api/pleroma/admin/invite_token`
|
## `/api/pleroma/admin/users/invite_token`
|
||||||
|
|
||||||
### Get an account registration invite token
|
### Get an account registration invite token
|
||||||
|
|
||||||
|
@ -210,7 +215,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
]
|
]
|
||||||
- Response: invite token (base64 string)
|
- Response: invite token (base64 string)
|
||||||
|
|
||||||
## `/api/pleroma/admin/invites`
|
## `/api/pleroma/admin/users/invites`
|
||||||
|
|
||||||
### Get a list of generated invites
|
### Get a list of generated invites
|
||||||
|
|
||||||
|
@ -236,7 +241,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/pleroma/admin/revoke_invite`
|
## `/api/pleroma/admin/users/revoke_invite`
|
||||||
|
|
||||||
### Revoke invite by token
|
### Revoke invite by token
|
||||||
|
|
||||||
|
@ -259,7 +264,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## `/api/pleroma/admin/email_invite`
|
## `/api/pleroma/admin/users/email_invite`
|
||||||
|
|
||||||
### Sends registration invite via email
|
### Sends registration invite via email
|
||||||
|
|
||||||
|
@ -268,7 +273,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- `email`
|
- `email`
|
||||||
- `name`, optional
|
- `name`, optional
|
||||||
|
|
||||||
## `/api/pleroma/admin/password_reset`
|
## `/api/pleroma/admin/users/:nickname/password_reset`
|
||||||
|
|
||||||
### Get a password reset token for a given nickname
|
### Get a password reset token for a given nickname
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,6 @@ config :pleroma, Pleroma.Emails.Mailer,
|
||||||
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
|
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
|
||||||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
||||||
* `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``.
|
* `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``.
|
||||||
* `repo_batch_size`: Repo batch size. The number of loaded rows from the database to the memory for processing chunks. E.g. deleting user statuses.
|
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
||||||
|
|
|
@ -109,7 +109,7 @@ def run(["get-packs" | args]) do
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
binary_archive = Tesla.get!(src_url).body
|
binary_archive = Tesla.get!(client(), src_url).body
|
||||||
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
||||||
|
|
||||||
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
|
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
|
||||||
|
@ -137,7 +137,7 @@ def run(["get-packs" | args]) do
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
files = Tesla.get!(files_url).body |> Poison.decode!()
|
files = Tesla.get!(client(), files_url).body |> Poison.decode!()
|
||||||
|
|
||||||
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
|
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ def run(["gen-pack", src]) do
|
||||||
|
|
||||||
IO.puts("Downloading the pack and generating SHA256")
|
IO.puts("Downloading the pack and generating SHA256")
|
||||||
|
|
||||||
binary_archive = Tesla.get!(src).body
|
binary_archive = Tesla.get!(client(), src).body
|
||||||
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
||||||
|
|
||||||
IO.puts("SHA256 is #{archive_sha}")
|
IO.puts("SHA256 is #{archive_sha}")
|
||||||
|
@ -272,7 +272,7 @@ def run(["gen-pack", src]) do
|
||||||
defp fetch_manifest(from) do
|
defp fetch_manifest(from) do
|
||||||
Poison.decode!(
|
Poison.decode!(
|
||||||
if String.starts_with?(from, "http") do
|
if String.starts_with?(from, "http") do
|
||||||
Tesla.get!(from).body
|
Tesla.get!(client(), from).body
|
||||||
else
|
else
|
||||||
File.read!(from)
|
File.read!(from)
|
||||||
end
|
end
|
||||||
|
@ -290,4 +290,12 @@ defp parse_global_opts(args) do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp client do
|
||||||
|
middleware = [
|
||||||
|
{Tesla.Middleware.FollowRedirects, [max_redirects: 3]}
|
||||||
|
]
|
||||||
|
|
||||||
|
Tesla.client(middleware)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,11 @@ defmodule Pleroma.Activity do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -35,6 +37,8 @@ defmodule Pleroma.Activity do
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
field(:actor, :string)
|
field(:actor, :string)
|
||||||
field(:recipients, {:array, :string}, default: [])
|
field(:recipients, {:array, :string}, default: [])
|
||||||
|
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
|
||||||
|
has_one(:bookmark, Bookmark)
|
||||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||||
|
|
||||||
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||||
|
@ -73,6 +77,16 @@ def with_preloaded_object(query) do
|
||||||
|> preload([activity, object], object: object)
|
|> preload([activity, object], object: object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_preloaded_bookmark(query, %User{} = user) do
|
||||||
|
from([a] in query,
|
||||||
|
left_join: b in Bookmark,
|
||||||
|
on: b.user_id == ^user.id and b.activity_id == a.id,
|
||||||
|
preload: [bookmark: b]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_preloaded_bookmark(query, _), do: query
|
||||||
|
|
||||||
def get_by_ap_id(ap_id) do
|
def get_by_ap_id(ap_id) do
|
||||||
Repo.one(
|
Repo.one(
|
||||||
from(
|
from(
|
||||||
|
@ -82,6 +96,16 @@ def get_by_ap_id(ap_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_bookmark(%Activity{} = activity, %User{} = user) do
|
||||||
|
if Ecto.assoc_loaded?(activity.bookmark) do
|
||||||
|
activity.bookmark
|
||||||
|
else
|
||||||
|
Bookmark.get(user.id, activity.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_bookmark(_, _), do: nil
|
||||||
|
|
||||||
def change(struct, params \\ %{}) do
|
def change(struct, params \\ %{}) do
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:data])
|
|> cast(params, [:data])
|
||||||
|
@ -263,6 +287,29 @@ def all_by_actor_and_id(actor, status_ids) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
|
||||||
|
from(
|
||||||
|
a in Activity,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"? ->> 'type' = 'Follow'",
|
||||||
|
a.data
|
||||||
|
),
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"? ->> 'state' = 'pending'",
|
||||||
|
a.data
|
||||||
|
),
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
a.data,
|
||||||
|
a.data,
|
||||||
|
^ap_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@spec query_by_actor(actor()) :: Ecto.Query.t()
|
@spec query_by_actor(actor()) :: Ecto.Query.t()
|
||||||
def query_by_actor(actor) do
|
def query_by_actor(actor) do
|
||||||
from(a in Activity, where: a.actor == ^actor)
|
from(a in Activity, where: a.actor == ^actor)
|
||||||
|
|
75
lib/pleroma/conversation.ex
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Conversation do
|
||||||
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
schema "conversations" do
|
||||||
|
# This is the context ap id.
|
||||||
|
field(:ap_id, :string)
|
||||||
|
has_many(:participations, Participation)
|
||||||
|
has_many(:users, through: [:participations, :user])
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def creation_cng(struct, params) do
|
||||||
|
struct
|
||||||
|
|> cast(params, [:ap_id])
|
||||||
|
|> validate_required([:ap_id])
|
||||||
|
|> unique_constraint(:ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_for_ap_id(ap_id) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> creation_cng(%{ap_id: ap_id})
|
||||||
|
|> Repo.insert(
|
||||||
|
on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]],
|
||||||
|
returning: true,
|
||||||
|
conflict_target: :ap_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_for_ap_id(ap_id) do
|
||||||
|
Repo.get_by(__MODULE__, ap_id: ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
This will
|
||||||
|
1. Create a conversation if there isn't one already
|
||||||
|
2. Create a participation for all the people involved who don't have one already
|
||||||
|
3. Bump all relevant participations to 'unread'
|
||||||
|
"""
|
||||||
|
def create_or_bump_for(activity) do
|
||||||
|
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
|
||||||
|
"Create" <- activity.data["type"],
|
||||||
|
object <- Pleroma.Object.normalize(activity),
|
||||||
|
"Note" <- object.data["type"],
|
||||||
|
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
|
||||||
|
{:ok, conversation} = create_for_ap_id(ap_id)
|
||||||
|
|
||||||
|
users = User.get_users_from_set(activity.recipients, false)
|
||||||
|
|
||||||
|
participations =
|
||||||
|
Enum.map(users, fn user ->
|
||||||
|
{:ok, participation} =
|
||||||
|
Participation.create_for_user_and_conversation(user, conversation)
|
||||||
|
|
||||||
|
participation
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
conversation
|
||||||
|
| participations: participations
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
e -> {:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
81
lib/pleroma/conversation/participation.ex
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Conversation.Participation do
|
||||||
|
use Ecto.Schema
|
||||||
|
alias Pleroma.Conversation
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
schema "conversation_participations" do
|
||||||
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
belongs_to(:conversation, Conversation)
|
||||||
|
field(:read, :boolean, default: false)
|
||||||
|
field(:last_activity_id, Pleroma.FlakeId, virtual: true)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def creation_cng(struct, params) do
|
||||||
|
struct
|
||||||
|
|> cast(params, [:user_id, :conversation_id])
|
||||||
|
|> validate_required([:user_id, :conversation_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_for_user_and_conversation(user, conversation) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> creation_cng(%{user_id: user.id, conversation_id: conversation.id})
|
||||||
|
|> Repo.insert(
|
||||||
|
on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]],
|
||||||
|
returning: true,
|
||||||
|
conflict_target: [:user_id, :conversation_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_cng(struct, params) do
|
||||||
|
struct
|
||||||
|
|> cast(params, [:read])
|
||||||
|
|> validate_required([:read])
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_as_read(participation) do
|
||||||
|
participation
|
||||||
|
|> read_cng(%{read: true})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_as_unread(participation) do
|
||||||
|
participation
|
||||||
|
|> read_cng(%{read: false})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_user(user, params \\ %{}) do
|
||||||
|
from(p in __MODULE__,
|
||||||
|
where: p.user_id == ^user.id,
|
||||||
|
order_by: [desc: p.updated_at]
|
||||||
|
)
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(params)
|
||||||
|
|> Repo.preload(conversation: [:users])
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_user_with_last_activity_id(user, params \\ %{}) do
|
||||||
|
for_user(user, params)
|
||||||
|
|> Enum.map(fn participation ->
|
||||||
|
activity_id =
|
||||||
|
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
||||||
|
"user" => user,
|
||||||
|
"blocking_user" => user
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
participation
|
||||||
|
| last_activity_id: activity_id
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,5 @@
|
||||||
defmodule Pleroma.Object.Containment do
|
defmodule Pleroma.Object.Containment do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
# Object Containment
|
|
||||||
|
|
||||||
This module contains some useful functions for containing objects to specific
|
This module contains some useful functions for containing objects to specific
|
||||||
origins and determining those origins. They previously lived in the
|
origins and determining those origins. They previously lived in the
|
||||||
ActivityPub `Transmogrifier` module.
|
ActivityPub `Transmogrifier` module.
|
||||||
|
|
|
@ -34,7 +34,7 @@ def schedule_update do
|
||||||
def update_stats do
|
def update_stats do
|
||||||
peers =
|
peers =
|
||||||
from(
|
from(
|
||||||
u in Pleroma.User,
|
u in User,
|
||||||
select: fragment("distinct split_part(?, '@', 2)", u.nickname),
|
select: fragment("distinct split_part(?, '@', 2)", u.nickname),
|
||||||
where: u.local != ^true
|
where: u.local != ^true
|
||||||
)
|
)
|
||||||
|
@ -44,10 +44,13 @@ def update_stats do
|
||||||
domain_count = Enum.count(peers)
|
domain_count = Enum.count(peers)
|
||||||
|
|
||||||
status_query =
|
status_query =
|
||||||
from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info))
|
from(u in User.Query.build(%{local: true}),
|
||||||
|
select: fragment("sum((?->>'note_count')::int)", u.info)
|
||||||
|
)
|
||||||
|
|
||||||
status_count = Repo.one(status_query)
|
status_count = Repo.one(status_query)
|
||||||
user_count = Repo.aggregate(User.active_local_user_query(), :count, :id)
|
|
||||||
|
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)
|
||||||
|
|
||||||
Agent.update(__MODULE__, fn _ ->
|
Agent.update(__MODULE__, fn _ ->
|
||||||
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}
|
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Upload do
|
defmodule Pleroma.Upload do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
# Upload
|
Manage user uploads
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
* `:type`: presets for activity type (defaults to Document) and size limits from app configuration
|
* `:type`: presets for activity type (defaults to Document) and size limits from app configuration
|
||||||
|
|
|
@ -10,7 +10,6 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Bookmark
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
|
@ -54,7 +53,6 @@ defmodule Pleroma.User do
|
||||||
field(:search_type, :integer, virtual: true)
|
field(:search_type, :integer, virtual: true)
|
||||||
field(:tags, {:array, :string}, default: [])
|
field(:tags, {:array, :string}, default: [])
|
||||||
field(:last_refreshed_at, :naive_datetime_usec)
|
field(:last_refreshed_at, :naive_datetime_usec)
|
||||||
has_many(:bookmarks, Bookmark)
|
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
has_many(:registrations, Registration)
|
has_many(:registrations, Registration)
|
||||||
embeds_one(:info, Pleroma.User.Info)
|
embeds_one(:info, Pleroma.User.Info)
|
||||||
|
@ -256,10 +254,7 @@ defp autofollow_users(user) do
|
||||||
candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames])
|
candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames])
|
||||||
|
|
||||||
autofollowed_users =
|
autofollowed_users =
|
||||||
from(u in User,
|
User.Query.build(%{nickname: candidates, local: true})
|
||||||
where: u.local == true,
|
|
||||||
where: u.nickname in ^candidates
|
|
||||||
)
|
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
follow_all(user, autofollowed_users)
|
follow_all(user, autofollowed_users)
|
||||||
|
@ -578,19 +573,17 @@ def fetch_initial_posts(user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do
|
@spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
|
||||||
from(
|
def get_followers_query(%User{} = user, nil) do
|
||||||
u in User,
|
User.Query.build(%{followers: user})
|
||||||
where: fragment("? <@ ?", ^[follower_address], u.following),
|
|
||||||
where: u.id != ^id
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_followers_query(user, page) do
|
def get_followers_query(user, page) do
|
||||||
from(u in get_followers_query(user, nil))
|
from(u in get_followers_query(user, nil))
|
||||||
|> paginate(page, 20)
|
|> User.Query.paginate(page, 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_followers_query(User.t()) :: Ecto.Query.t()
|
||||||
def get_followers_query(user), do: get_followers_query(user, nil)
|
def get_followers_query(user), do: get_followers_query(user, nil)
|
||||||
|
|
||||||
def get_followers(user, page \\ nil) do
|
def get_followers(user, page \\ nil) do
|
||||||
|
@ -605,19 +598,17 @@ def get_followers_ids(user, page \\ nil) do
|
||||||
Repo.all(from(u in q, select: u.id))
|
Repo.all(from(u in q, select: u.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_friends_query(%User{id: id, following: following}, nil) do
|
@spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
|
||||||
from(
|
def get_friends_query(%User{} = user, nil) do
|
||||||
u in User,
|
User.Query.build(%{friends: user})
|
||||||
where: u.follower_address in ^following,
|
|
||||||
where: u.id != ^id
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_friends_query(user, page) do
|
def get_friends_query(user, page) do
|
||||||
from(u in get_friends_query(user, nil))
|
from(u in get_friends_query(user, nil))
|
||||||
|> paginate(page, 20)
|
|> User.Query.paginate(page, 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_friends_query(User.t()) :: Ecto.Query.t()
|
||||||
def get_friends_query(user), do: get_friends_query(user, nil)
|
def get_friends_query(user), do: get_friends_query(user, nil)
|
||||||
|
|
||||||
def get_friends(user, page \\ nil) do
|
def get_friends(user, page \\ nil) do
|
||||||
|
@ -632,33 +623,10 @@ def get_friends_ids(user, page \\ nil) do
|
||||||
Repo.all(from(u in q, select: u.id))
|
Repo.all(from(u in q, select: u.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_follow_requests_query(%User{} = user) do
|
@spec get_follow_requests(User.t()) :: {:ok, [User.t()]}
|
||||||
from(
|
|
||||||
a in Activity,
|
|
||||||
where:
|
|
||||||
fragment(
|
|
||||||
"? ->> 'type' = 'Follow'",
|
|
||||||
a.data
|
|
||||||
),
|
|
||||||
where:
|
|
||||||
fragment(
|
|
||||||
"? ->> 'state' = 'pending'",
|
|
||||||
a.data
|
|
||||||
),
|
|
||||||
where:
|
|
||||||
fragment(
|
|
||||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
|
||||||
a.data,
|
|
||||||
a.data,
|
|
||||||
^user.ap_id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_follow_requests(%User{} = user) do
|
def get_follow_requests(%User{} = user) do
|
||||||
users =
|
users =
|
||||||
user
|
Activity.follow_requests_for_actor(user)
|
||||||
|> User.get_follow_requests_query()
|
|
||||||
|> join(:inner, [a], u in User, on: a.actor == u.ap_id)
|
|> join(:inner, [a], u in User, on: a.actor == u.ap_id)
|
||||||
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
|
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
|
||||||
|> group_by([a, u], u.id)
|
|> group_by([a, u], u.id)
|
||||||
|
@ -731,10 +699,7 @@ def update_note_count(%User{} = user) do
|
||||||
|
|
||||||
def update_follower_count(%User{} = user) do
|
def update_follower_count(%User{} = user) do
|
||||||
follower_count_query =
|
follower_count_query =
|
||||||
User
|
User.Query.build(%{followers: user}) |> select([u], %{count: count(u.id)})
|
||||||
|> where([u], ^user.follower_address in u.following)
|
|
||||||
|> where([u], u.id != ^user.id)
|
|
||||||
|> select([u], %{count: count(u.id)})
|
|
||||||
|
|
||||||
User
|
User
|
||||||
|> where(id: ^user.id)
|
|> where(id: ^user.id)
|
||||||
|
@ -757,38 +722,19 @@ def update_follower_count(%User{} = user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_users_from_set_query(ap_ids, false) do
|
@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
|
||||||
from(
|
|
||||||
u in User,
|
|
||||||
where: u.ap_id in ^ap_ids
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_users_from_set_query(ap_ids, true) do
|
|
||||||
query = get_users_from_set_query(ap_ids, false)
|
|
||||||
|
|
||||||
from(
|
|
||||||
u in query,
|
|
||||||
where: u.local == true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_users_from_set(ap_ids, local_only \\ true) do
|
def get_users_from_set(ap_ids, local_only \\ true) do
|
||||||
get_users_from_set_query(ap_ids, local_only)
|
criteria = %{ap_id: ap_ids}
|
||||||
|
criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
|
||||||
|
|
||||||
|
User.Query.build(criteria)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_recipients_from_activity(Activity.t()) :: [User.t()]
|
||||||
def get_recipients_from_activity(%Activity{recipients: to}) do
|
def get_recipients_from_activity(%Activity{recipients: to}) do
|
||||||
query =
|
User.Query.build(%{recipients_from_activity: to, local: true})
|
||||||
from(
|
|> Repo.all()
|
||||||
u in User,
|
|
||||||
where: u.ap_id in ^to,
|
|
||||||
or_where: fragment("? && ?", u.following, ^to)
|
|
||||||
)
|
|
||||||
|
|
||||||
query = from(u in query, where: u.local == true)
|
|
||||||
|
|
||||||
Repo.all(query)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def search(query, resolve \\ false, for_user \\ nil) do
|
def search(query, resolve \\ false, for_user \\ nil) do
|
||||||
|
@ -1050,14 +996,23 @@ def subscribed_to?(user, %{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def muted_users(user),
|
@spec muted_users(User.t()) :: [User.t()]
|
||||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes))
|
def muted_users(user) do
|
||||||
|
User.Query.build(%{ap_id: user.info.mutes})
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
def blocked_users(user),
|
@spec blocked_users(User.t()) :: [User.t()]
|
||||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
|
def blocked_users(user) do
|
||||||
|
User.Query.build(%{ap_id: user.info.blocks})
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
def subscribers(user),
|
@spec subscribers(User.t()) :: [User.t()]
|
||||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers))
|
def subscribers(user) do
|
||||||
|
User.Query.build(%{ap_id: user.info.subscribers})
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
def block_domain(user, domain) do
|
def block_domain(user, domain) do
|
||||||
info_cng =
|
info_cng =
|
||||||
|
@ -1083,69 +1038,6 @@ def unblock_domain(user, domain) do
|
||||||
update_and_set_cache(cng)
|
update_and_set_cache(cng)
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_local_user_query(query, local) do
|
|
||||||
if local, do: local_user_query(query), else: query
|
|
||||||
end
|
|
||||||
|
|
||||||
def local_user_query(query \\ User) do
|
|
||||||
from(
|
|
||||||
u in query,
|
|
||||||
where: u.local == true,
|
|
||||||
where: not is_nil(u.nickname)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_external_user_query(query, external) do
|
|
||||||
if external, do: external_user_query(query), else: query
|
|
||||||
end
|
|
||||||
|
|
||||||
def external_user_query(query \\ User) do
|
|
||||||
from(
|
|
||||||
u in query,
|
|
||||||
where: u.local == false,
|
|
||||||
where: not is_nil(u.nickname)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_active_user_query(query, active) do
|
|
||||||
if active, do: active_user_query(query), else: query
|
|
||||||
end
|
|
||||||
|
|
||||||
def active_user_query(query \\ User) do
|
|
||||||
from(
|
|
||||||
u in query,
|
|
||||||
where: fragment("not (?->'deactivated' @> 'true')", u.info),
|
|
||||||
where: not is_nil(u.nickname)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_deactivated_user_query(query, deactivated) do
|
|
||||||
if deactivated, do: deactivated_user_query(query), else: query
|
|
||||||
end
|
|
||||||
|
|
||||||
def deactivated_user_query(query \\ User) do
|
|
||||||
from(
|
|
||||||
u in query,
|
|
||||||
where: fragment("(?->'deactivated' @> 'true')", u.info),
|
|
||||||
where: not is_nil(u.nickname)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def active_local_user_query do
|
|
||||||
from(
|
|
||||||
u in local_user_query(),
|
|
||||||
where: fragment("not (?->'deactivated' @> 'true')", u.info)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def moderator_user_query do
|
|
||||||
from(
|
|
||||||
u in User,
|
|
||||||
where: u.local == true,
|
|
||||||
where: fragment("?->'is_moderator' @> 'true'", u.info)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def deactivate(%User{} = user, status \\ true) do
|
def deactivate(%User{} = user, status \\ true) do
|
||||||
info_cng = User.Info.set_activation_status(user.info, status)
|
info_cng = User.Info.set_activation_status(user.info, status)
|
||||||
|
|
||||||
|
@ -1308,7 +1200,7 @@ def ap_enabled?(%User{info: info}), do: info.ap_enabled
|
||||||
def ap_enabled?(_), do: false
|
def ap_enabled?(_), do: false
|
||||||
|
|
||||||
@doc "Gets or fetch a user by uri or nickname."
|
@doc "Gets or fetch a user by uri or nickname."
|
||||||
@spec get_or_fetch(String.t()) :: User.t()
|
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
||||||
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
||||||
|
|
||||||
|
@ -1425,22 +1317,12 @@ def error_user(ap_id) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec all_superusers() :: [User.t()]
|
||||||
def all_superusers do
|
def all_superusers do
|
||||||
from(
|
User.Query.build(%{super_users: true, local: true})
|
||||||
u in User,
|
|
||||||
where: u.local == true,
|
|
||||||
where: fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info)
|
|
||||||
)
|
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp paginate(query, page, page_size) do
|
|
||||||
from(u in query,
|
|
||||||
limit: ^page_size,
|
|
||||||
offset: ^((page - 1) * page_size)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def showing_reblogs?(%User{} = user, %User{} = target) do
|
def showing_reblogs?(%User{} = user, %User{} = target) do
|
||||||
target.ap_id not in user.info.muted_reblogs
|
target.ap_id not in user.info.muted_reblogs
|
||||||
end
|
end
|
||||||
|
|
150
lib/pleroma/user/query.ex
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.Query do
|
||||||
|
@moduledoc """
|
||||||
|
User query builder module. Builds query from new query or another user query.
|
||||||
|
|
||||||
|
## Example:
|
||||||
|
query = Pleroma.User.Query(%{nickname: "nickname"})
|
||||||
|
another_query = Pleroma.User.Query.build(query, %{email: "email@example.com"})
|
||||||
|
Pleroma.Repo.all(query)
|
||||||
|
Pleroma.Repo.all(another_query)
|
||||||
|
|
||||||
|
Adding new rules:
|
||||||
|
- *ilike criteria*
|
||||||
|
- add field to @ilike_criteria list
|
||||||
|
- pass non empty string
|
||||||
|
- e.g. Pleroma.User.Query.build(%{nickname: "nickname"})
|
||||||
|
- *equal criteria*
|
||||||
|
- add field to @equal_criteria list
|
||||||
|
- pass non empty string
|
||||||
|
- e.g. Pleroma.User.Query.build(%{email: "email@example.com"})
|
||||||
|
- *contains criteria*
|
||||||
|
- add field to @containns_criteria list
|
||||||
|
- pass values list
|
||||||
|
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]})
|
||||||
|
"""
|
||||||
|
import Ecto.Query
|
||||||
|
import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1]
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@type criteria ::
|
||||||
|
%{
|
||||||
|
query: String.t(),
|
||||||
|
tags: [String.t()],
|
||||||
|
name: String.t(),
|
||||||
|
email: String.t(),
|
||||||
|
local: boolean(),
|
||||||
|
external: boolean(),
|
||||||
|
active: boolean(),
|
||||||
|
deactivated: boolean(),
|
||||||
|
is_admin: boolean(),
|
||||||
|
is_moderator: boolean(),
|
||||||
|
super_users: boolean(),
|
||||||
|
followers: User.t(),
|
||||||
|
friends: User.t(),
|
||||||
|
recipients_from_activity: [String.t()],
|
||||||
|
nickname: [String.t()],
|
||||||
|
ap_id: [String.t()]
|
||||||
|
}
|
||||||
|
| %{}
|
||||||
|
|
||||||
|
@ilike_criteria [:nickname, :name, :query]
|
||||||
|
@equal_criteria [:email]
|
||||||
|
@role_criteria [:is_admin, :is_moderator]
|
||||||
|
@contains_criteria [:ap_id, :nickname]
|
||||||
|
|
||||||
|
@spec build(criteria()) :: Query.t()
|
||||||
|
def build(query \\ base_query(), criteria) do
|
||||||
|
prepare_query(query, criteria)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec paginate(Ecto.Query.t(), pos_integer(), pos_integer()) :: Ecto.Query.t()
|
||||||
|
def paginate(query, page, page_size) do
|
||||||
|
from(u in query,
|
||||||
|
limit: ^page_size,
|
||||||
|
offset: ^((page - 1) * page_size)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp base_query do
|
||||||
|
from(u in User)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_query(query, criteria) do
|
||||||
|
Enum.reduce(criteria, query, &compose_query/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({key, value}, query)
|
||||||
|
when key in @ilike_criteria and not_empty_string(value) do
|
||||||
|
# hack for :query key
|
||||||
|
key = if key == :query, do: :nickname, else: key
|
||||||
|
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({key, value}, query)
|
||||||
|
when key in @equal_criteria and not_empty_string(value) do
|
||||||
|
where(query, [u], ^[{key, value}])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({key, values}, query) when key in @contains_criteria and is_list(values) do
|
||||||
|
where(query, [u], field(u, ^key) in ^values)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 do
|
||||||
|
Enum.reduce(tags, query, &prepare_tag_criteria/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({key, _}, query) when key in @role_criteria do
|
||||||
|
where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:super_users, _}, query) do
|
||||||
|
where(
|
||||||
|
query,
|
||||||
|
[u],
|
||||||
|
fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:local, _}, query), do: location_query(query, true)
|
||||||
|
|
||||||
|
defp compose_query({:external, _}, query), do: location_query(query, false)
|
||||||
|
|
||||||
|
defp compose_query({:active, _}, query) do
|
||||||
|
where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info))
|
||||||
|
|> where([u], not is_nil(u.nickname))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:deactivated, _}, query) do
|
||||||
|
where(query, [u], fragment("?->'deactivated' @> 'true'", u.info))
|
||||||
|
|> where([u], not is_nil(u.nickname))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do
|
||||||
|
where(query, [u], fragment("? <@ ?", ^[follower_address], u.following))
|
||||||
|
|> where([u], u.id != ^id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:friends, %User{id: id, following: following}}, query) do
|
||||||
|
where(query, [u], u.follower_address in ^following)
|
||||||
|
|> where([u], u.id != ^id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:recipients_from_activity, to}, query) do
|
||||||
|
where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query(_unsupported_param, query), do: query
|
||||||
|
|
||||||
|
defp prepare_tag_criteria(tag, query) do
|
||||||
|
or_where(query, [u], fragment("? = any(?)", ^tag, u.tags))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp location_query(query, local) do
|
||||||
|
where(query, [u], u.local == ^local)
|
||||||
|
|> where([u], not is_nil(u.nickname))
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Conversation
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
@ -141,7 +142,14 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Notification.create_notifications(activity)
|
Notification.create_notifications(activity)
|
||||||
|
|
||||||
|
participations =
|
||||||
|
activity
|
||||||
|
|> Conversation.create_or_bump_for()
|
||||||
|
|> get_participations()
|
||||||
|
|
||||||
stream_out(activity)
|
stream_out(activity)
|
||||||
|
stream_out_participations(participations)
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%Activity{} = activity ->
|
%Activity{} = activity ->
|
||||||
|
@ -164,6 +172,19 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_participations({:ok, %{participations: participations}}), do: participations
|
||||||
|
defp get_participations(_), do: []
|
||||||
|
|
||||||
|
def stream_out_participations(participations) do
|
||||||
|
participations =
|
||||||
|
participations
|
||||||
|
|> Repo.preload(:user)
|
||||||
|
|
||||||
|
Enum.each(participations, fn participation ->
|
||||||
|
Pleroma.Web.Streamer.stream("participation", participation)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
def stream_out(activity) do
|
def stream_out(activity) do
|
||||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
@ -195,6 +216,7 @@ def stream_out(activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
# TODO: Write test, replace with visibility test
|
||||||
if !Enum.member?(activity.data["cc"] || [], public) &&
|
if !Enum.member?(activity.data["cc"] || [], public) &&
|
||||||
!Enum.member?(
|
!Enum.member?(
|
||||||
activity.data["to"],
|
activity.data["to"],
|
||||||
|
@ -457,35 +479,44 @@ def flag(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities_for_context(context, opts \\ %{}) do
|
defp fetch_activities_for_context_query(context, opts) do
|
||||||
public = ["https://www.w3.org/ns/activitystreams#Public"]
|
public = ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
|
if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
|
||||||
|
|
||||||
query = from(activity in Activity)
|
from(activity in Activity)
|
||||||
|
|> restrict_blocked(opts)
|
||||||
query =
|
|> restrict_recipients(recipients, opts["user"])
|
||||||
query
|
|> where(
|
||||||
|> restrict_blocked(opts)
|
[activity],
|
||||||
|> restrict_recipients(recipients, opts["user"])
|
fragment(
|
||||||
|
"?->>'type' = ? and ?->>'context' = ?",
|
||||||
query =
|
activity.data,
|
||||||
from(
|
"Create",
|
||||||
activity in query,
|
activity.data,
|
||||||
where:
|
^context
|
||||||
fragment(
|
|
||||||
"?->>'type' = ? and ?->>'context' = ?",
|
|
||||||
activity.data,
|
|
||||||
"Create",
|
|
||||||
activity.data,
|
|
||||||
^context
|
|
||||||
),
|
|
||||||
order_by: [desc: :id]
|
|
||||||
)
|
)
|
||||||
|> Activity.with_preloaded_object()
|
)
|
||||||
|
|> order_by([activity], desc: activity.id)
|
||||||
|
end
|
||||||
|
|
||||||
Repo.all(query)
|
@spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
|
||||||
|
def fetch_activities_for_context(context, opts \\ %{}) do
|
||||||
|
context
|
||||||
|
|> fetch_activities_for_context_query(opts)
|
||||||
|
|> Activity.with_preloaded_object()
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
|
||||||
|
Pleroma.FlakeId.t() | nil
|
||||||
|
def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
||||||
|
context
|
||||||
|
|> fetch_activities_for_context_query(opts)
|
||||||
|
|> limit(1)
|
||||||
|
|> select([a], a.id)
|
||||||
|
|> Repo.one()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_public_activities(opts \\ %{}) do
|
def fetch_public_activities(opts \\ %{}) do
|
||||||
|
@ -784,11 +815,32 @@ defp maybe_preload_objects(query, _) do
|
||||||
|> Activity.with_preloaded_object()
|
|> Activity.with_preloaded_object()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
|
||||||
|
|
||||||
|
defp maybe_preload_bookmarks(query, opts) do
|
||||||
|
query
|
||||||
|
|> Activity.with_preloaded_bookmark(opts["user"])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_order(query, %{order: :desc}) do
|
||||||
|
query
|
||||||
|
|> order_by(desc: :id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_order(query, %{order: :asc}) do
|
||||||
|
query
|
||||||
|
|> order_by(asc: :id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_order(query, _), do: query
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query = from(activity in Activity)
|
base_query = from(activity in Activity)
|
||||||
|
|
||||||
base_query
|
base_query
|
||||||
|> maybe_preload_objects(opts)
|
|> maybe_preload_objects(opts)
|
||||||
|
|> maybe_preload_bookmarks(opts)
|
||||||
|
|> maybe_order(opts)
|
||||||
|> restrict_recipients(recipients, opts["user"])
|
|> restrict_recipients(recipients, opts["user"])
|
||||||
|> restrict_tag(opts)
|
|> restrict_tag(opts)
|
||||||
|> restrict_tag_reject(opts)
|
|> restrict_tag_reject(opts)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@moduledoc "Prevent followbots from following with a bit of heuristic"
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
# XXX: this should become User.normalize_by_ap_id() or similar, really.
|
# XXX: this should become User.normalize_by_ap_id() or similar, really.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
||||||
require Logger
|
require Logger
|
||||||
|
@moduledoc "Drop and log everything received"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
|
||||||
|
@moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
|
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@moduledoc "Block messages with too much mentions (configurable)"
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp delist_message(message, threshold) when threshold > 0 do
|
defp delist_message(message, threshold) when threshold > 0 do
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
||||||
|
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
defp string_matches?(string, _) when not is_binary(string) do
|
defp string_matches?(string, _) when not is_binary(string) do
|
||||||
false
|
false
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
|
||||||
|
@moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||||
|
@moduledoc "Does nothing (lets the messages go through unmodified)"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
||||||
|
@moduledoc "Scrub configured hypertext markup"
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@moduledoc "Rejects non-public (followers-only, direct) activities"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@moduledoc "Filter activities depending on their origin instance"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp check_accept(%{host: actor_host} = _actor_info, object) do
|
defp check_accept(%{host: actor_host} = _actor_info, object) do
|
||||||
|
|
|
@ -5,6 +5,19 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
@moduledoc """
|
||||||
|
Apply policies based on user tags
|
||||||
|
|
||||||
|
This policy applies policies on a user activities depending on their tags
|
||||||
|
on your instance.
|
||||||
|
|
||||||
|
- `mrf_tag:media-force-nsfw`: Mark as sensitive on presence of attachments
|
||||||
|
- `mrf_tag:media-strip`: Remove attachments
|
||||||
|
- `mrf_tag:force-unlisted`: Mark as unlisted (removes from the federated timeline)
|
||||||
|
- `mrf_tag:sandbox`: Remove from public (local and federated) timelines
|
||||||
|
- `mrf_tag:disable-remote-subscription`: Reject non-local follow requests
|
||||||
|
- `mrf_tag:disable-any-subscription`: Reject any follow requests
|
||||||
|
"""
|
||||||
|
|
||||||
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
|
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
|
||||||
defp get_tags(_), do: []
|
defp get_tags(_), do: []
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
@moduledoc "Accept-list of users from specified instances"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp filter_by_list(object, []), do: {:ok, object}
|
defp filter_by_list(object, []), do: {:ok, object}
|
||||||
|
|
|
@ -101,7 +101,10 @@ def list_users(conn, params) do
|
||||||
search_params = %{
|
search_params = %{
|
||||||
query: params["query"],
|
query: params["query"],
|
||||||
page: page,
|
page: page,
|
||||||
page_size: page_size
|
page_size: page_size,
|
||||||
|
tags: params["tags"],
|
||||||
|
name: params["name"],
|
||||||
|
email: params["email"]
|
||||||
}
|
}
|
||||||
|
|
||||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
||||||
|
@ -116,11 +119,11 @@ def list_users(conn, params) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@filters ~w(local external active deactivated)
|
@filters ~w(local external active deactivated is_admin is_moderator)
|
||||||
|
|
||||||
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
|
||||||
|
|
||||||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
||||||
|
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
||||||
|
|
||||||
defp maybe_parse_filters(filters) do
|
defp maybe_parse_filters(filters) do
|
||||||
filters
|
filters
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|
|
|
@ -10,45 +10,23 @@ defmodule Pleroma.Web.AdminAPI.Search do
|
||||||
|
|
||||||
@page_size 50
|
@page_size 50
|
||||||
|
|
||||||
def user(%{query: term} = params) when is_nil(term) or term == "" do
|
defmacro not_empty_string(string) do
|
||||||
query = maybe_filtered_query(params)
|
quote do
|
||||||
|
is_binary(unquote(string)) and unquote(string) != ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
|
||||||
|
def user(params \\ %{}) do
|
||||||
|
query = User.Query.build(params) |> order_by([u], u.nickname)
|
||||||
|
|
||||||
paginated_query =
|
paginated_query =
|
||||||
maybe_filtered_query(params)
|
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
|
||||||
|> paginate(params[:page] || 1, params[:page_size] || @page_size)
|
|
||||||
|
|
||||||
count = query |> Repo.aggregate(:count, :id)
|
count = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
results = Repo.all(paginated_query)
|
results = Repo.all(paginated_query)
|
||||||
|
|
||||||
{:ok, results, count}
|
{:ok, results, count}
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(%{query: term} = params) when is_binary(term) do
|
|
||||||
search_query = from(u in maybe_filtered_query(params), where: ilike(u.nickname, ^"%#{term}%"))
|
|
||||||
|
|
||||||
count = search_query |> Repo.aggregate(:count, :id)
|
|
||||||
|
|
||||||
results =
|
|
||||||
search_query
|
|
||||||
|> paginate(params[:page] || 1, params[:page_size] || @page_size)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
{:ok, results, count}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp maybe_filtered_query(params) do
|
|
||||||
from(u in User, order_by: u.nickname)
|
|
||||||
|> User.maybe_local_user_query(params[:local])
|
|
||||||
|> User.maybe_external_user_query(params[:external])
|
|
||||||
|> User.maybe_active_user_query(params[:active])
|
|
||||||
|> User.maybe_deactivated_user_query(params[:deactivated])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp paginate(query, page, page_size) do
|
|
||||||
from(u in query,
|
|
||||||
limit: ^page_size,
|
|
||||||
offset: ^((page - 1) * page_size)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,12 +10,6 @@ defmodule Pleroma.Web.ControllerHelper do
|
||||||
def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil
|
def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil
|
||||||
def truthy_param?(value), do: value not in @falsy_param_values
|
def truthy_param?(value), do: value not in @falsy_param_values
|
||||||
|
|
||||||
def oauth_scopes(params, default) do
|
|
||||||
# Note: `scopes` is used by Mastodon — supporting it but sticking to
|
|
||||||
# OAuth's standard `scope` wherever we control it
|
|
||||||
Pleroma.Web.OAuth.parse_scopes(params["scope"] || params["scopes"], default)
|
|
||||||
end
|
|
||||||
|
|
||||||
def json_response(conn, status, json) do
|
def json_response(conn, status, json) do
|
||||||
conn
|
conn
|
||||||
|> put_status(status)
|
|> put_status(status)
|
||||||
|
|
|
@ -29,6 +29,13 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
|
||||||
|
|
||||||
|
plug(Plug.Static,
|
||||||
|
at: "/pleroma/admin/",
|
||||||
|
from: {:pleroma, "priv/static/adminfe/"}
|
||||||
|
)
|
||||||
|
|
||||||
# Code reloading can be explicitly enabled under the
|
# Code reloading can be explicitly enabled under the
|
||||||
# :code_reloader configuration of your endpoint.
|
# :code_reloader configuration of your endpoint.
|
||||||
if code_reloading? do
|
if code_reloading? do
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Bookmark
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -24,6 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.AppView
|
alias Pleroma.Web.MastodonAPI.AppView
|
||||||
|
alias Pleroma.Web.MastodonAPI.ConversationView
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||||
|
@ -35,6 +37,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
alias Pleroma.Web.OAuth.Scopes
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
alias Pleroma.Web.ControllerHelper
|
alias Pleroma.Web.ControllerHelper
|
||||||
|
@ -48,7 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def create_app(conn, params) do
|
def create_app(conn, params) do
|
||||||
scopes = ControllerHelper.oauth_scopes(params, ["read"])
|
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||||
|
|
||||||
app_attrs =
|
app_attrs =
|
||||||
params
|
params
|
||||||
|
@ -165,7 +168,7 @@ def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@mastodon_api_level "2.5.0"
|
@mastodon_api_level "2.6.5"
|
||||||
|
|
||||||
def masto_instance(conn, _params) do
|
def masto_instance(conn, _params) do
|
||||||
instance = Config.get(:instance)
|
instance = Config.get(:instance)
|
||||||
|
@ -293,8 +296,6 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.contain_timeline(user)
|
|> ActivityPub.contain_timeline(user)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:home_timeline, activities)
|
|> add_link_headers(:home_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -313,8 +314,6 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -322,8 +321,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- User.get_cached_by_id(params["id"]),
|
with %User{} = user <- User.get_cached_by_id(params["id"]) do
|
||||||
reading_user <- Repo.preload(reading_user, :bookmarks) do
|
|
||||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -350,8 +348,6 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.fetch_activities_query(params)
|
|> ActivityPub.fetch_activities_query(params)
|
||||||
|> Pagination.fetch_paginated(params)
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:dm_timeline, activities)
|
|> add_link_headers(:dm_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -361,8 +357,6 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||||
true <- Visibility.visible_for_user?(activity, user) do
|
true <- Visibility.visible_for_user?(activity, user) do
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user})
|
|> try_render("status.json", %{activity: activity, for: user})
|
||||||
|
@ -512,8 +506,6 @@ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
||||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
|
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
|
||||||
|
@ -523,8 +515,6 @@ def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -575,8 +565,6 @@ def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -588,8 +576,6 @@ def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -1110,8 +1096,6 @@ def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
ActivityPub.fetch_activities([], params)
|
ActivityPub.fetch_activities([], params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:favourites, activities)
|
|> add_link_headers(:favourites, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -1157,7 +1141,6 @@ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params
|
||||||
|
|
||||||
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
bookmarks =
|
bookmarks =
|
||||||
Bookmark.for_user_query(user.id)
|
Bookmark.for_user_query(user.id)
|
||||||
|
@ -1165,7 +1148,7 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
bookmarks
|
bookmarks
|
||||||
|> Enum.map(fn b -> b.activity end)
|
|> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:bookmarks, bookmarks)
|
|> add_link_headers(:bookmarks, bookmarks)
|
||||||
|
@ -1274,8 +1257,6 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|
||||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
user = Repo.preload(user, bookmarks: :activity)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
|
@ -1712,6 +1693,31 @@ def reports(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conversations(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
participations = Participation.for_user_with_last_activity_id(user, params)
|
||||||
|
|
||||||
|
conversations =
|
||||||
|
Enum.map(participations, fn participation ->
|
||||||
|
ConversationView.render("participation.json", %{participation: participation, user: user})
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(:conversations, participations)
|
||||||
|
|> json(conversations)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
||||||
|
with %Participation{} = participation <-
|
||||||
|
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
||||||
|
{:ok, participation} <- Participation.mark_as_read(participation) do
|
||||||
|
participation_view =
|
||||||
|
ConversationView.render("participation.json", %{participation: participation, user: user})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(participation_view)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def try_render(conn, target, params)
|
def try_render(conn, target, params)
|
||||||
when is_binary(target) do
|
when is_binary(target) do
|
||||||
res = render(conn, target, params)
|
res = render(conn, target, params)
|
||||||
|
|
38
lib/pleroma/web/mastodon_api/views/conversation_view.ex
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
def render("participation.json", %{participation: participation, user: user}) do
|
||||||
|
participation = Repo.preload(participation, conversation: :users)
|
||||||
|
|
||||||
|
last_activity_id =
|
||||||
|
with nil <- participation.last_activity_id do
|
||||||
|
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
||||||
|
"user" => user,
|
||||||
|
"blocking_user" => user
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
activity = Activity.get_by_id_with_object(last_activity_id)
|
||||||
|
|
||||||
|
last_status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
accounts =
|
||||||
|
AccountView.render("accounts.json", %{
|
||||||
|
users: participation.conversation.users,
|
||||||
|
as: :user
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: participation.id |> to_string(),
|
||||||
|
accounts: accounts,
|
||||||
|
unread: !participation.read,
|
||||||
|
last_status: last_status
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -75,18 +75,22 @@ def render("index.json", opts) do
|
||||||
|
|
||||||
def render(
|
def render(
|
||||||
"status.json",
|
"status.json",
|
||||||
%{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts
|
%{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts
|
||||||
) do
|
) do
|
||||||
user = get_user(activity.data["actor"])
|
user = get_user(activity.data["actor"])
|
||||||
created_at = Utils.to_masto_date(activity.data["published"])
|
created_at = Utils.to_masto_date(activity.data["published"])
|
||||||
|
activity_object = Object.normalize(activity)
|
||||||
|
|
||||||
|
reblogged_activity =
|
||||||
|
Activity.create_by_object_ap_id(activity_object.data["id"])
|
||||||
|
|> Activity.with_preloaded_bookmark(opts[:for])
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
reblogged_activity = Activity.get_create_by_object_ap_id(object)
|
|
||||||
reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
|
reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
|
||||||
|
|
||||||
activity_object = Object.normalize(activity)
|
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
||||||
|
|
||||||
bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], reblogged_activity)
|
bookmarked = Activity.get_bookmark(reblogged_activity, opts[:for]) != nil
|
||||||
|
|
||||||
mentions =
|
mentions =
|
||||||
activity.recipients
|
activity.recipients
|
||||||
|
@ -96,8 +100,8 @@ def render(
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(activity.id),
|
id: to_string(activity.id),
|
||||||
uri: object,
|
uri: activity_object.data["id"],
|
||||||
url: object,
|
url: activity_object.data["id"],
|
||||||
account: AccountView.render("account.json", %{user: user}),
|
account: AccountView.render("account.json", %{user: user}),
|
||||||
in_reply_to_id: nil,
|
in_reply_to_id: nil,
|
||||||
in_reply_to_account_id: nil,
|
in_reply_to_account_id: nil,
|
||||||
|
@ -149,7 +153,7 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
|
||||||
|
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
||||||
|
|
||||||
bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], activity)
|
bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil
|
||||||
|
|
||||||
attachment_data = object.data["attachment"] || []
|
attachment_data = object.data["attachment"] || []
|
||||||
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
||||||
|
|
|
@ -3,18 +3,4 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OAuth do
|
defmodule Pleroma.Web.OAuth do
|
||||||
def parse_scopes(scopes, _default) when is_list(scopes) do
|
|
||||||
Enum.filter(scopes, &(&1 not in [nil, ""]))
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_scopes(scopes, default) when is_binary(scopes) do
|
|
||||||
scopes
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split(~r/[\s,]+/)
|
|
||||||
|> parse_scopes(default)
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_scopes(_, default) do
|
|
||||||
default
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,8 +15,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
||||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||||
|
alias Pleroma.Web.OAuth.Scopes
|
||||||
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
|
||||||
|
|
||||||
if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth)
|
if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth)
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ def authorize(conn, params), do: do_authorize(conn, params)
|
||||||
defp do_authorize(conn, params) do
|
defp do_authorize(conn, params) do
|
||||||
app = Repo.get_by(App, client_id: params["client_id"])
|
app = Repo.get_by(App, client_id: params["client_id"])
|
||||||
available_scopes = (app && app.scopes) || []
|
available_scopes = (app && app.scopes) || []
|
||||||
scopes = oauth_scopes(params, nil) || available_scopes
|
scopes = Scopes.fetch_scopes(params, available_scopes)
|
||||||
|
|
||||||
# Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
|
# Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
|
||||||
render(conn, Authenticator.auth_template(), %{
|
render(conn, Authenticator.auth_template(), %{
|
||||||
|
@ -113,7 +112,7 @@ def after_create_authorization(conn, auth, %{
|
||||||
|
|
||||||
defp handle_create_authorization_error(
|
defp handle_create_authorization_error(
|
||||||
conn,
|
conn,
|
||||||
{scopes_issue, _},
|
{:error, scopes_issue},
|
||||||
%{"authorization" => _} = params
|
%{"authorization" => _} = params
|
||||||
)
|
)
|
||||||
when scopes_issue in [:unsupported_scopes, :missing_scopes] do
|
when scopes_issue in [:unsupported_scopes, :missing_scopes] do
|
||||||
|
@ -184,9 +183,7 @@ def token_exchange(
|
||||||
%App{} = app <- get_app_from_request(conn, params),
|
%App{} = app <- get_app_from_request(conn, params),
|
||||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
|
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
|
||||||
{:user_active, true} <- {:user_active, !user.info.deactivated},
|
{:user_active, true} <- {:user_active, !user.info.deactivated},
|
||||||
scopes <- oauth_scopes(params, app.scopes),
|
{:ok, scopes} <- validate_scopes(app, params),
|
||||||
[] <- scopes -- app.scopes,
|
|
||||||
true <- Enum.any?(scopes),
|
|
||||||
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
||||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||||
json(conn, response_token(user, token))
|
json(conn, response_token(user, token))
|
||||||
|
@ -247,8 +244,9 @@ defp bad_request(conn, _) do
|
||||||
@doc "Prepares OAuth request to provider for Ueberauth"
|
@doc "Prepares OAuth request to provider for Ueberauth"
|
||||||
def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do
|
def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do
|
||||||
scope =
|
scope =
|
||||||
oauth_scopes(auth_attrs, [])
|
auth_attrs
|
||||||
|> Enum.join(" ")
|
|> Scopes.fetch_scopes([])
|
||||||
|
|> Scopes.to_string()
|
||||||
|
|
||||||
state =
|
state =
|
||||||
auth_attrs
|
auth_attrs
|
||||||
|
@ -326,7 +324,7 @@ def registration_details(conn, %{"authorization" => auth_attrs}) do
|
||||||
client_id: auth_attrs["client_id"],
|
client_id: auth_attrs["client_id"],
|
||||||
redirect_uri: auth_attrs["redirect_uri"],
|
redirect_uri: auth_attrs["redirect_uri"],
|
||||||
state: auth_attrs["state"],
|
state: auth_attrs["state"],
|
||||||
scopes: oauth_scopes(auth_attrs, []),
|
scopes: Scopes.fetch_scopes(auth_attrs, []),
|
||||||
nickname: auth_attrs["nickname"],
|
nickname: auth_attrs["nickname"],
|
||||||
email: auth_attrs["email"]
|
email: auth_attrs["email"]
|
||||||
})
|
})
|
||||||
|
@ -401,10 +399,7 @@ defp do_create_authorization(
|
||||||
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
||||||
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
||||||
true <- redirect_uri in String.split(app.redirect_uris),
|
true <- redirect_uri in String.split(app.redirect_uris),
|
||||||
scopes <- oauth_scopes(auth_attrs, []),
|
{:ok, scopes} <- validate_scopes(app, auth_attrs),
|
||||||
{:unsupported_scopes, []} <- {:unsupported_scopes, scopes -- app.scopes},
|
|
||||||
# Note: `scope` param is intentionally not optional in this context
|
|
||||||
{:missing_scopes, false} <- {:missing_scopes, scopes == []},
|
|
||||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
||||||
Authorization.create_authorization(app, user, scopes)
|
Authorization.create_authorization(app, user, scopes)
|
||||||
end
|
end
|
||||||
|
@ -458,4 +453,12 @@ defp response_token(%User{} = user, token, opts \\ %{}) do
|
||||||
}
|
}
|
||||||
|> Map.merge(opts)
|
|> Map.merge(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec validate_scopes(App.t(), map()) ::
|
||||||
|
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||||
|
defp validate_scopes(app, params) do
|
||||||
|
params
|
||||||
|
|> Scopes.fetch_scopes(app.scopes)
|
||||||
|
|> Scopes.validates(app.scopes)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
67
lib/pleroma/web/oauth/scopes.ex
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.OAuth.Scopes do
|
||||||
|
@moduledoc """
|
||||||
|
Functions for dealing with scopes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Fetch scopes from requiest params.
|
||||||
|
|
||||||
|
Note: `scopes` is used by Mastodon — supporting it but sticking to
|
||||||
|
OAuth's standard `scope` wherever we control it
|
||||||
|
"""
|
||||||
|
@spec fetch_scopes(map(), list()) :: list()
|
||||||
|
def fetch_scopes(params, default) do
|
||||||
|
parse_scopes(params["scope"] || params["scopes"], default)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_scopes(scopes, _default) when is_list(scopes) do
|
||||||
|
Enum.filter(scopes, &(&1 not in [nil, ""]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_scopes(scopes, default) when is_binary(scopes) do
|
||||||
|
scopes
|
||||||
|
|> to_list
|
||||||
|
|> parse_scopes(default)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_scopes(_, default) do
|
||||||
|
default
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Convert scopes string to list
|
||||||
|
"""
|
||||||
|
@spec to_list(binary()) :: [binary()]
|
||||||
|
def to_list(nil), do: []
|
||||||
|
|
||||||
|
def to_list(str) do
|
||||||
|
str
|
||||||
|
|> String.trim()
|
||||||
|
|> String.split(~r/[\s,]+/)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Convert scopes list to string
|
||||||
|
"""
|
||||||
|
@spec to_string(list()) :: binary()
|
||||||
|
def to_string(scopes), do: Enum.join(scopes, " ")
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Validates scopes.
|
||||||
|
"""
|
||||||
|
@spec validates(list() | nil, list()) ::
|
||||||
|
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||||
|
def validates([], _app_scopes), do: {:error, :missing_scopes}
|
||||||
|
def validates(nil, _app_scopes), do: {:error, :missing_scopes}
|
||||||
|
|
||||||
|
def validates(scopes, app_scopes) do
|
||||||
|
case scopes -- app_scopes do
|
||||||
|
[] -> {:ok, scopes}
|
||||||
|
_ -> {:error, :unsupported_scopes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,15 +18,18 @@ defp get_href(id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_in_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}}) do
|
defp get_in_reply_to(activity) do
|
||||||
[
|
with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do
|
||||||
{:"thr:in-reply-to",
|
[
|
||||||
[ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
|
{:"thr:in-reply-to",
|
||||||
]
|
[ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
|
||||||
|
]
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_in_reply_to(_), do: []
|
|
||||||
|
|
||||||
defp get_mentions(to) do
|
defp get_mentions(to) do
|
||||||
Enum.map(to, fn id ->
|
Enum.map(to, fn id ->
|
||||||
cond do
|
cond do
|
||||||
|
@ -98,7 +101,7 @@ def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author)
|
||||||
[]}
|
[]}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
in_reply_to = get_in_reply_to(activity.data)
|
in_reply_to = get_in_reply_to(activity)
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||||
mentions = activity.recipients |> get_mentions
|
mentions = activity.recipients |> get_mentions
|
||||||
|
|
||||||
|
@ -146,7 +149,6 @@ def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) d
|
||||||
updated_at = activity.data["published"]
|
updated_at = activity.data["published"]
|
||||||
inserted_at = activity.data["published"]
|
inserted_at = activity.data["published"]
|
||||||
|
|
||||||
_in_reply_to = get_in_reply_to(activity.data)
|
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||||
mentions = activity.recipients |> get_mentions
|
mentions = activity.recipients |> get_mentions
|
||||||
|
|
||||||
|
@ -177,7 +179,6 @@ def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_autho
|
||||||
updated_at = activity.data["published"]
|
updated_at = activity.data["published"]
|
||||||
inserted_at = activity.data["published"]
|
inserted_at = activity.data["published"]
|
||||||
|
|
||||||
_in_reply_to = get_in_reply_to(activity.data)
|
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||||
|
|
||||||
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
|
|
|
@ -146,34 +146,52 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through([:admin_api, :oauth_write])
|
pipe_through([:admin_api, :oauth_write])
|
||||||
|
|
||||||
post("/user/follow", AdminAPIController, :user_follow)
|
post("/users/follow", AdminAPIController, :user_follow)
|
||||||
post("/user/unfollow", AdminAPIController, :user_unfollow)
|
post("/users/unfollow", AdminAPIController, :user_unfollow)
|
||||||
|
|
||||||
get("/users", AdminAPIController, :list_users)
|
|
||||||
get("/users/:nickname", AdminAPIController, :user_show)
|
|
||||||
|
|
||||||
|
# TODO: to be removed at version 1.0
|
||||||
delete("/user", AdminAPIController, :user_delete)
|
delete("/user", AdminAPIController, :user_delete)
|
||||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
|
||||||
post("/user", AdminAPIController, :user_create)
|
post("/user", AdminAPIController, :user_create)
|
||||||
|
|
||||||
|
delete("/users", AdminAPIController, :user_delete)
|
||||||
|
post("/users", AdminAPIController, :user_create)
|
||||||
|
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||||
put("/users/tag", AdminAPIController, :tag_users)
|
put("/users/tag", AdminAPIController, :tag_users)
|
||||||
delete("/users/tag", AdminAPIController, :untag_users)
|
delete("/users/tag", AdminAPIController, :untag_users)
|
||||||
|
|
||||||
|
# TODO: to be removed at version 1.0
|
||||||
get("/permission_group/:nickname", AdminAPIController, :right_get)
|
get("/permission_group/:nickname", AdminAPIController, :right_get)
|
||||||
get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
|
get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
|
||||||
post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
|
post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
|
||||||
delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
|
delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
|
||||||
|
|
||||||
put("/activation_status/:nickname", AdminAPIController, :set_activation_status)
|
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
||||||
|
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
||||||
|
post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
|
||||||
|
|
||||||
|
delete(
|
||||||
|
"/users/:nickname/permission_group/:permission_group",
|
||||||
|
AdminAPIController,
|
||||||
|
:right_delete
|
||||||
|
)
|
||||||
|
|
||||||
|
put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status)
|
||||||
|
|
||||||
post("/relay", AdminAPIController, :relay_follow)
|
post("/relay", AdminAPIController, :relay_follow)
|
||||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||||
|
|
||||||
get("/invite_token", AdminAPIController, :get_invite_token)
|
get("/users/invite_token", AdminAPIController, :get_invite_token)
|
||||||
get("/invites", AdminAPIController, :invites)
|
get("/users/invites", AdminAPIController, :invites)
|
||||||
post("/revoke_invite", AdminAPIController, :revoke_invite)
|
post("/users/revoke_invite", AdminAPIController, :revoke_invite)
|
||||||
post("/email_invite", AdminAPIController, :email_invite)
|
post("/users/email_invite", AdminAPIController, :email_invite)
|
||||||
|
|
||||||
|
# TODO: to be removed at version 1.0
|
||||||
get("/password_reset", AdminAPIController, :get_password_reset)
|
get("/password_reset", AdminAPIController, :get_password_reset)
|
||||||
|
|
||||||
|
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||||
|
|
||||||
|
get("/users", AdminAPIController, :list_users)
|
||||||
|
get("/users/:nickname", AdminAPIController, :user_show)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.TwitterAPI do
|
scope "/", Pleroma.Web.TwitterAPI do
|
||||||
|
@ -276,6 +294,9 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/suggestions", MastodonAPIController, :suggestions)
|
get("/suggestions", MastodonAPIController, :suggestions)
|
||||||
|
|
||||||
|
get("/conversations", MastodonAPIController, :conversations)
|
||||||
|
post("/conversations/:id/read", MastodonAPIController, :conversation_read)
|
||||||
|
|
||||||
get("/endorsements", MastodonAPIController, :empty_array)
|
get("/endorsements", MastodonAPIController, :empty_array)
|
||||||
|
|
||||||
get("/pleroma/flavour", MastodonAPIController, :get_flavour)
|
get("/pleroma/flavour", MastodonAPIController, :get_flavour)
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.Streamer do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -71,6 +72,15 @@ def handle_cast(%{action: :stream, topic: "direct", item: item}, topics) do
|
||||||
{:noreply, topics}
|
{:noreply, topics}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_cast(%{action: :stream, topic: "participation", item: participation}, topics) do
|
||||||
|
user_topic = "direct:#{participation.user_id}"
|
||||||
|
Logger.debug("Trying to push a conversation participation to #{user_topic}\n\n")
|
||||||
|
|
||||||
|
push_to_socket(topics, user_topic, participation)
|
||||||
|
|
||||||
|
{:noreply, topics}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
|
def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
|
||||||
# filter the recipient list if the activity is not public, see #270.
|
# filter the recipient list if the activity is not public, see #270.
|
||||||
recipient_lists =
|
recipient_lists =
|
||||||
|
@ -192,6 +202,19 @@ defp represent_update(%Activity{} = activity) do
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def represent_conversation(%Participation{} = participation) do
|
||||||
|
%{
|
||||||
|
event: "conversation",
|
||||||
|
payload:
|
||||||
|
Pleroma.Web.MastodonAPI.ConversationView.render("participation.json", %{
|
||||||
|
participation: participation,
|
||||||
|
user: participation.user
|
||||||
|
})
|
||||||
|
|> Jason.encode!()
|
||||||
|
}
|
||||||
|
|> Jason.encode!()
|
||||||
|
end
|
||||||
|
|
||||||
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
|
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
# Get the current user so we have up-to-date blocks etc.
|
# Get the current user so we have up-to-date blocks etc.
|
||||||
|
@ -214,6 +237,12 @@ def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = ite
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def push_to_socket(topics, topic, %Participation{} = participation) do
|
||||||
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
|
send(socket.transport_pid, {:text, represent_conversation(participation)})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
def push_to_socket(topics, topic, %Activity{
|
def push_to_socket(topics, topic, %Activity{
|
||||||
data: %{"type" => "Delete", "deleted_activity_id" => deleted_activity_id}
|
data: %{"type" => "Delete", "deleted_activity_id" => deleted_activity_id}
|
||||||
}) do
|
}) do
|
||||||
|
|
|
@ -182,6 +182,7 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put("blocking_user", user)
|
||||||
|> Map.put("user", user)
|
|> Map.put("user", user)
|
||||||
|> Map.put(:visibility, "direct")
|
|> Map.put(:visibility, "direct")
|
||||||
|
|> Map.put(:order, :desc)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities_query([user.ap_id], params)
|
ActivityPub.fetch_activities_query([user.ap_id], params)
|
||||||
|
|
|
@ -170,7 +170,7 @@ def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activ
|
||||||
created_at = activity.data["published"] |> Utils.date_to_asctime()
|
created_at = activity.data["published"] |> Utils.date_to_asctime()
|
||||||
announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
|
|
||||||
text = "#{user.nickname} retweeted a status."
|
text = "#{user.nickname} repeated a status."
|
||||||
|
|
||||||
retweeted_status = render("activity.json", Map.merge(opts, %{activity: announced_activity}))
|
retweeted_status = render("activity.json", Map.merge(opts, %{activity: announced_activity}))
|
||||||
|
|
||||||
|
|
11
mix.exs
|
@ -16,11 +16,11 @@ def project do
|
||||||
|
|
||||||
# Docs
|
# Docs
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
source_url: "https://git.pleroma.social/pleroma/pleroma",
|
|
||||||
source_url_pattern:
|
|
||||||
"https://git.pleroma.social/pleroma/pleroma/blob/develop/%{path}#L%{line}",
|
|
||||||
homepage_url: "https://pleroma.social/",
|
homepage_url: "https://pleroma.social/",
|
||||||
|
source_url: "https://git.pleroma.social/pleroma/pleroma",
|
||||||
docs: [
|
docs: [
|
||||||
|
source_url_pattern:
|
||||||
|
"https://git.pleroma.social/pleroma/pleroma/blob/develop/%{path}#L%{line}",
|
||||||
logo: "priv/static/static/logo.png",
|
logo: "priv/static/static/logo.png",
|
||||||
extras: ["README.md", "CHANGELOG.md"] ++ Path.wildcard("docs/**/*.md"),
|
extras: ["README.md", "CHANGELOG.md"] ++ Path.wildcard("docs/**/*.md"),
|
||||||
groups_for_extras: [
|
groups_for_extras: [
|
||||||
|
@ -87,7 +87,7 @@ defp deps do
|
||||||
{:bbcode, "~> 0.1"},
|
{:bbcode, "~> 0.1"},
|
||||||
{:ex_machina, "~> 2.3", only: :test},
|
{:ex_machina, "~> 2.3", only: :test},
|
||||||
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
||||||
{:mock, "~> 0.3.1", only: :test},
|
{:mock, "~> 0.3.3", only: :test},
|
||||||
{:crypt,
|
{:crypt,
|
||||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||||
{:cors_plug, "~> 1.5"},
|
{:cors_plug, "~> 1.5"},
|
||||||
|
@ -113,7 +113,8 @@ defp deps do
|
||||||
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
||||||
{:quack, "~> 0.1.1"},
|
{:quack, "~> 0.1.1"},
|
||||||
{:benchee, "~> 1.0"},
|
{:benchee, "~> 1.0"},
|
||||||
{:esshd, "~> 0.1.0"}
|
{:esshd, "~> 0.1.0"},
|
||||||
|
{:plug_static_index_html, "~> 1.0.0"}
|
||||||
] ++ oauth_deps
|
] ++ oauth_deps
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
3
mix.lock
|
@ -46,7 +46,7 @@
|
||||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||||
"mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"},
|
"mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"},
|
||||||
"mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
|
"mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"},
|
"mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||||
|
@ -59,6 +59,7 @@
|
||||||
"plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
|
"plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||||
|
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
|
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
|
||||||
"postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
"postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
|
26
priv/repo/migrations/20190408123347_create_conversations.exs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateConversations do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:conversations) do
|
||||||
|
add(:ap_id, :string, null: false)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create table(:conversation_participations) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:conversation_id, references(:conversations, on_delete: :delete_all))
|
||||||
|
add(:read, :boolean, default: false)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create index(:conversation_participations, [:conversation_id])
|
||||||
|
create unique_index(:conversation_participations, [:user_id, :conversation_id])
|
||||||
|
create unique_index(:conversations, [:ap_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddParticipationUpdatedAtIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create index(:conversation_participations, ["updated_at desc"])
|
||||||
|
end
|
||||||
|
end
|
BIN
priv/static/adminfe/favicon.ico
Normal file
After Width: | Height: | Size: 66 KiB |
1
priv/static/adminfe/index.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=static/css/chunk-elementUI.4296cedf.css rel=stylesheet><link href=static/css/chunk-libs.bd17d456.css rel=stylesheet><link href=static/css/app.cea15678.css rel=stylesheet></head><body><script src=/pleroma/admin/static/tinymce4.7.5/tinymce.min.js></script><div id=app></div><script type=text/javascript src=static/js/runtime.7144b2cf.js></script><script type=text/javascript src=static/js/chunk-elementUI.d388c21d.js></script><script type=text/javascript src=static/js/chunk-libs.48e79a9e.js></script><script type=text/javascript src=static/js/app.25699e3d.js></script></body></html>
|
BIN
priv/static/adminfe/static/css/app.cea15678.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-18e1.6aaab273.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-50cf.1db1ed5b.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-8b70.9ba0945c.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-elementUI.4296cedf.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-f018.0d22684d.css
Normal file
BIN
priv/static/adminfe/static/css/chunk-libs.bd17d456.css
Normal file
BIN
priv/static/adminfe/static/fonts/element-icons.2fad952.woff
Normal file
BIN
priv/static/adminfe/static/fonts/element-icons.6f0a763.ttf
Normal file
BIN
priv/static/adminfe/static/img/401.089007e.gif
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
priv/static/adminfe/static/img/404.a57b6f3.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
priv/static/adminfe/static/js/7zzA.e1ae1c94.js
Normal file
BIN
priv/static/adminfe/static/js/JEtC.f9ba4594.js
Normal file
BIN
priv/static/adminfe/static/js/app.25699e3d.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-18e1.7f9c377c.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-50cf.b9b1df43.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-8b70.46525646.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-elementUI.d388c21d.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-f018.e1a7a454.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-libs.48e79a9e.js
Normal file
BIN
priv/static/adminfe/static/js/runtime.7144b2cf.js
Normal file
BIN
priv/static/adminfe/static/tinymce4.7.5/langs/zh_CN.js
Normal file
After Width: | Height: | Size: 354 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 331 B |
After Width: | Height: | Size: 342 B |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 336 B |
After Width: | Height: | Size: 338 B |
After Width: | Height: | Size: 343 B |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 323 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 338 B |
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 336 B |
BIN
priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.inline.min.css
vendored
Normal file
BIN
priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.min.css
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?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>Generated by IcoMoon</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="tinymce-small" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||||
|
<glyph unicode="" glyph-name="save" d="M960 80v591.938l-223.938 224.062h-592.062c-44.182 0-80-35.816-80-80v-736c0-44.184 35.818-80 80-80h736c44.184 0 80 35.816 80 80zM576 768h64v-192h-64v192zM704 128h-384v255.882c0.034 0.042 0.076 0.082 0.116 0.118h383.77c0.040-0.036 0.082-0.076 0.116-0.118l-0.002-255.882zM832 128h-64v256c0 35.2-28.8 64-64 64h-384c-35.2 0-64-28.8-64-64v-256h-64v640h64v-192c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v171.010l128-128.072v-490.938z" />
|
||||||
|
<glyph unicode="" glyph-name="newdocument" d="M850.746 717.254l-133.492 133.49c-24.888 24.892-74.054 45.256-109.254 45.256h-416c-35.2 0-64-28.8-64-64v-768c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v544c0 35.2-20.366 84.364-45.254 109.254zM805.49 672.002c6.792-6.796 13.792-19.162 18.894-32.002h-184.384v184.386c12.84-5.1 25.204-12.1 32-18.896l133.49-133.488zM831.884 64h-639.77c-0.040 0.034-0.082 0.076-0.114 0.116v767.77c0.034 0.040 0.076 0.082 0.114 0.114h383.886v-256h256v-511.884c-0.034-0.040-0.076-0.082-0.116-0.116z" />
|
||||||
|
<glyph unicode="" glyph-name="fullpage" d="M1024 367.542v160.916l-159.144 15.914c-8.186 30.042-20.088 58.548-35.21 84.98l104.596 127.838-113.052 113.050-127.836-104.596c-26.434 15.124-54.942 27.026-84.982 35.208l-15.914 159.148h-160.916l-15.914-159.146c-30.042-8.186-58.548-20.086-84.98-35.208l-127.838 104.594-113.050-113.050 104.596-127.836c-15.124-26.432-27.026-54.94-35.21-84.98l-159.146-15.916v-160.916l159.146-15.914c8.186-30.042 20.086-58.548 35.21-84.982l-104.596-127.836 113.048-113.048 127.838 104.596c26.432-15.124 54.94-27.028 84.98-35.21l15.916-159.148h160.916l15.914 159.144c30.042 8.186 58.548 20.088 84.982 35.21l127.836-104.596 113.048 113.048-104.596 127.836c15.124 26.434 27.028 54.942 35.21 84.98l159.148 15.92zM704 384l-128-128h-128l-128 128v128l128 128h128l128-128v-128z" />
|
||||||
|
<glyph unicode="" glyph-name="alignleft" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM64 576h576v-128h-576zM64 192h576v-128h-576z" />
|
||||||
|
<glyph unicode="" glyph-name="aligncenter" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM256 576h512v-128h-512zM256 192h512v-128h-512z" />
|
||||||
|
<glyph unicode="" glyph-name="alignright" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM384 576h576v-128h-576zM384 192h576v-128h-576z" />
|
||||||
|
<glyph unicode="" glyph-name="alignjustify" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM64 576h896v-128h-896zM64 192h896v-128h-896z" />
|
||||||
|
<glyph unicode="" glyph-name="cut" d="M864.408 289.868c-46.47 46.47-106.938 68.004-161.082 62.806l-63.326 63.326 192 192c0 0 128 128 0 256l-320-320-320 320c-128-128 0-256 0-256l192-192-63.326-63.326c-54.144 5.198-114.61-16.338-161.080-62.806-74.98-74.98-85.112-186.418-22.626-248.9 62.482-62.482 173.92-52.354 248.9 22.626 46.47 46.468 68.002 106.938 62.806 161.080l63.326 63.326 63.328-63.328c-5.196-54.144 16.336-114.61 62.806-161.078 74.978-74.98 186.418-85.112 248.898-22.626 62.488 62.482 52.356 173.918-22.624 248.9zM353.124 201.422c-2.212-24.332-15.020-49.826-35.14-69.946-22.212-22.214-51.080-35.476-77.218-35.476-10.524 0-25.298 2.228-35.916 12.848-21.406 21.404-17.376 73.132 22.626 113.136 22.212 22.214 51.080 35.476 77.218 35.476 10.524 0 25.298-2.228 35.916-12.848 13.112-13.11 13.47-32.688 12.514-43.19zM512 352c-35.346 0-64 28.654-64 64s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zM819.152 108.848c-10.62-10.62-25.392-12.848-35.916-12.848-26.138 0-55.006 13.262-77.218 35.476-20.122 20.12-32.928 45.614-35.138 69.946-0.958 10.502-0.6 30.080 12.514 43.192 10.618 10.622 25.39 12.848 35.916 12.848 26.136 0 55.006-13.262 77.216-35.474 40.004-40.008 44.032-91.736 22.626-113.14z" />
|
||||||
|
<glyph unicode="" glyph-name="paste" d="M704 576v160c0 17.6-14.4 32-32 32h-160v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-160c-17.602 0-32-14.4-32-32v-512c0-17.6 14.398-32 32-32h224v-192h384l192 192v384h-192zM320 831.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 640v64h384v-64h-384zM704 90.51v101.49h101.49l-101.49-101.49zM832 256h-192v-192h-256v448h448v-256z" />
|
||||||
|
<glyph unicode="" glyph-name="searchreplace" d="M888 576h-56v256h64v64h-320v-64h64v-256h-256v256h64v64h-320v-64h64v-256h-56c-39.6 0-72-32.4-72-72v-432c0-39.6 32.4-72 72-72h240c39.6 0 72 32.4 72 72v312h128v-312c0-39.6 32.4-72 72-72h240c39.6 0 72 32.4 72 72v432c0 39.6-32.4 72-72 72zM348 64h-184c-19.8 0-36 14.4-36 32s16.2 32 36 32h184c19.8 0 36-14.4 36-32s-16.2-32-36-32zM544 448h-64c-17.6 0-32 14.4-32 32s14.4 32 32 32h64c17.6 0 32-14.4 32-32s-14.4-32-32-32zM860 64h-184c-19.8 0-36 14.4-36 32s16.2 32 36 32h184c19.8 0 36-14.4 36-32s-16.2-32-36-32z" />
|
||||||
|
<glyph unicode="" glyph-name="bullist" d="M384 832h576v-128h-576zM384 512h576v-128h-576zM384 192h576v-128h-576zM128 768c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM128 448c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM128 128c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64z" />
|
||||||
|
<glyph unicode="" glyph-name="numlist" d="M384 832h576v-128h-576zM384 512h576v-128h-576zM384 192h576v-128h-576zM320 430v146h-64v320h-128v-64h64v-256h-64v-64h128v-50l-128-60v-146h128v-64h-128v-64h128v-64h-128v-64h192v320h-128v50z" />
|
||||||
|
<glyph unicode="" glyph-name="indent" d="M64 768h896v-128h-896zM384 384h576v-128h-576zM384 576h576v-128h-576zM64 192h896v-128h-896zM64 576l224-160-224-160z" />
|
||||||
|
<glyph unicode="" glyph-name="outdent" d="M64 768h896v-128h-896zM64 384h576v-128h-576zM64 576h576v-128h-576zM64 192h896v-128h-896zM960 576l-224-160 224-160z" />
|
||||||
|
<glyph unicode="" glyph-name="blockquote" d="M256.428 535.274c105.8 0 191.572-91.17 191.572-203.638 0-112.464-85.772-203.636-191.572-203.636-105.802 0-191.572 91.17-191.572 203.636l-0.856 29.092c0 224.93 171.54 407.272 383.144 407.272v-116.364c-73.1 0-141.826-30.26-193.516-85.204-9.954-10.578-19.034-21.834-27.224-33.656 9.784 1.64 19.806 2.498 30.024 2.498zM768.428 535.274c105.8 0 191.572-91.17 191.572-203.638 0-112.464-85.772-203.636-191.572-203.636-105.802 0-191.572 91.17-191.572 203.636l-0.856 29.092c0 224.93 171.54 407.272 383.144 407.272v-116.364c-73.1 0-141.826-30.26-193.516-85.204-9.956-10.578-19.036-21.834-27.224-33.656 9.784 1.64 19.806 2.498 30.024 2.498z" />
|
||||||
|
<glyph unicode="" glyph-name="undo" d="M704 0c59 199 134.906 455.266-256 446.096v-222.096l-336.002 336 336.002 336v-217.326c468.092 12.2 544-358.674 256-678.674z" />
|
||||||
|
<glyph unicode="" glyph-name="redo" d="M576 678.674v217.326l336.002-336-336.002-336v222.096c-390.906 9.17-315-247.096-256-446.096-288 320-212.092 690.874 256 678.674z" />
|
||||||
|
<glyph unicode="" glyph-name="unlink" d="M927.274 729.784l-133.49 133.488c-21.104 21.104-49.232 32.728-79.198 32.728s-58.094-11.624-79.196-32.726l-165.492-165.49c-43.668-43.668-43.668-114.724 0-158.392l2.746-2.746 67.882 67.882-2.746 2.746c-6.132 6.132-6.132 16.494 0 22.626l165.492 165.492c4.010 4.008 8.808 4.608 11.312 4.608s7.302-0.598 11.312-4.61l133.49-133.488c6.132-6.134 6.132-16.498 0.002-22.628l-165.494-165.494c-4.008-4.008-8.806-4.608-11.31-4.608s-7.302 0.6-11.312 4.612l-2.746 2.746-67.88-67.884 2.742-2.742c21.106-21.108 49.23-32.728 79.2-32.728s58.094 11.624 79.196 32.726l165.494 165.492c43.662 43.666 43.662 114.72-0.004 158.39zM551.356 359.356l-67.882-67.882 2.746-2.746c4.008-4.008 4.61-8.806 4.61-11.31 0-2.506-0.598-7.302-4.606-11.314l-165.494-165.49c-4.010-4.010-8.81-4.61-11.314-4.61s-7.304 0.6-11.314 4.61l-133.492 133.486c-4.010 4.010-4.61 8.81-4.61 11.314s0.598 7.3 4.61 11.312l165.49 165.488c4.010 4.012 8.81 4.612 11.314 4.612s7.304-0.6 11.314-4.612l2.746-2.742 67.882 67.88-2.746 2.746c-21.104 21.104-49.23 32.726-79.196 32.726s-58.092-11.624-79.196-32.726l-165.488-165.486c-21.106-21.104-32.73-49.234-32.73-79.198s11.624-58.094 32.726-79.198l133.49-133.49c21.106-21.102 49.232-32.726 79.198-32.726s58.092 11.624 79.196 32.726l165.494 165.492c21.104 21.104 32.722 49.23 32.722 79.196s-11.624 58.094-32.726 79.196l-2.744 2.746zM352 250c-9.724 0-19.45 3.71-26.87 11.128-14.84 14.84-14.84 38.898 0 53.738l320 320c14.84 14.84 38.896 14.84 53.736 0 14.844-14.84 14.844-38.9 0-53.74l-320-320c-7.416-7.416-17.142-11.126-26.866-11.126z" />
|
||||||
|
<glyph unicode="" glyph-name="link" d="M927.274 729.784l-133.49 133.488c-21.104 21.104-49.232 32.728-79.198 32.728s-58.094-11.624-79.196-32.726l-165.492-165.49c-43.668-43.668-43.668-114.724 0-158.392l2.746-2.746 67.882 67.882-2.746 2.746c-6.132 6.132-6.132 16.494 0 22.626l165.492 165.492c4.010 4.008 8.808 4.608 11.312 4.608s7.302-0.598 11.312-4.61l133.49-133.488c6.132-6.134 6.132-16.498 0.002-22.628l-165.494-165.494c-4.008-4.008-8.806-4.608-11.31-4.608s-7.302 0.6-11.312 4.612l-2.746 2.746-67.88-67.884 2.742-2.742c21.106-21.108 49.23-32.728 79.2-32.728s58.094 11.624 79.196 32.726l165.494 165.492c43.662 43.666 43.662 114.72-0.004 158.39zM551.356 359.356l-67.882-67.882 2.746-2.746c4.008-4.008 4.61-8.806 4.61-11.31 0-2.506-0.598-7.302-4.606-11.314l-165.494-165.49c-4.010-4.010-8.81-4.61-11.314-4.61s-7.304 0.6-11.314 4.61l-133.492 133.486c-4.010 4.010-4.61 8.81-4.61 11.314s0.598 7.3 4.61 11.312l165.49 165.488c4.010 4.012 8.81 4.612 11.314 4.612s7.304-0.6 11.314-4.612l2.746-2.742 67.882 67.88-2.746 2.746c-21.104 21.104-49.23 32.726-79.196 32.726s-58.092-11.624-79.196-32.726l-165.488-165.486c-21.106-21.104-32.73-49.234-32.73-79.198s11.624-58.094 32.726-79.198l133.49-133.49c21.106-21.102 49.232-32.726 79.198-32.726s58.092 11.624 79.196 32.726l165.494 165.492c21.104 21.104 32.722 49.23 32.722 79.196s-11.624 58.094-32.726 79.196l-2.744 2.746zM800 122c-9.724 0-19.45 3.708-26.87 11.13l-128 127.998c-14.844 14.84-14.844 38.898 0 53.738 14.84 14.844 38.896 14.844 53.736 0l128-128c14.844-14.84 14.844-38.896 0-53.736-7.416-7.422-17.142-11.13-26.866-11.13zM608 0c-17.674 0-32 14.326-32 32v128c0 17.674 14.326 32 32 32s32-14.326 32-32v-128c0-17.674-14.326-32-32-32zM928 320h-128c-17.674 0-32 14.326-32 32s14.326 32 32 32h128c17.674 0 32-14.326 32-32s-14.326-32-32-32zM224 774c9.724 0 19.45-3.708 26.87-11.13l128-128c14.842-14.84 14.842-38.898 0-53.738-14.84-14.844-38.898-14.844-53.738 0l-128 128c-14.842 14.84-14.842 38.898 0 53.738 7.418 7.422 17.144 11.13 26.868 11.13zM416 896c17.674 0 32-14.326 32-32v-128c0-17.674-14.326-32-32-32s-32 14.326-32 32v128c0 17.674 14.326 32 32 32zM96 576h128c17.674 0 32-14.326 32-32s-14.326-32-32-32h-128c-17.674 0-32 14.326-32 32s14.326 32 32 32z" />
|
||||||
|
<glyph unicode="" glyph-name="bookmark" d="M256 896v-896l256 256 256-256v896h-512zM704 170.51l-192 192-192-192v661.49h384v-661.49z" />
|
||||||
|
<glyph unicode="" glyph-name="image" d="M896 832h-768c-35.2 0-64-28.8-64-64v-640c0-35.2 28.8-64 64-64h768c35.2 0 64 28.8 64 64v640c0 35.2-28.8 64-64 64zM896 128.116c-0.012-0.014-0.030-0.028-0.042-0.042l-191.958 319.926-160-128-224 288-191.968-479.916c-0.010 0.010-0.022 0.022-0.032 0.032v639.77c0.034 0.040 0.076 0.082 0.114 0.114h767.77c0.040-0.034 0.082-0.076 0.116-0.116v-639.768zM640 608c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96s-96-42.981-96-96z" />
|
||||||
|
<glyph unicode="" glyph-name="media" d="M896 832h-768c-35.2 0-64-28.8-64-64v-640c0-35.2 28.8-64 64-64h768c35.2 0 64 28.8 64 64v640c0 35.2-28.8 64-64 64zM256 128h-128v128h128v-128zM256 384h-128v128h128v-128zM256 640h-128v128h128v-128zM704 128h-384v640h384v-640zM896 128h-128v128h128v-128zM896 384h-128v128h128v-128zM896 640h-128v128h128v-128zM384 640v-384l288 192z" />
|
||||||
|
<glyph unicode="" glyph-name="help" d="M448 256h128v-128h-128v128zM704 704c35.346 0 64-28.654 64-64v-166l-228-154h-92v64l192 128v64h-320v128h384zM512 896c-119.666 0-232.166-46.6-316.784-131.216-84.614-84.618-131.216-197.118-131.216-316.784 0-119.664 46.602-232.168 131.216-316.784 84.618-84.616 197.118-131.216 316.784-131.216 119.664 0 232.168 46.6 316.784 131.216s131.216 197.12 131.216 316.784c0 119.666-46.6 232.166-131.216 316.784-84.616 84.616-197.12 131.216-316.784 131.216z" />
|
||||||
|
<glyph unicode="" glyph-name="code" d="M416 256l-192 192 192 192-64 64-256-256 256-256zM672 704l-64-64 192-192-192-192 64-64 256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="insertdatetime" d="M77.798 655.376l81.414-50.882c50.802 81.114 128.788 143.454 221.208 174.246l-30.366 91.094c-113.748-37.898-209.728-114.626-272.256-214.458zM673.946 869.834l-30.366-91.094c92.422-30.792 170.404-93.132 221.208-174.248l81.412 50.882c-62.526 99.834-158.506 176.562-272.254 214.46zM607.974 255.992c-4.808 0-9.692 1.090-14.286 3.386l-145.688 72.844v211.778c0 17.672 14.328 32 32 32s32-14.328 32-32v-172.222l110.31-55.156c15.806-7.902 22.214-27.124 14.31-42.932-5.604-11.214-16.908-17.696-28.646-17.698zM512 768c-212.078 0-384-171.922-384-384s171.922-384 384-384c212.078 0 384 171.922 384 384s-171.922 384-384 384zM512 96c-159.058 0-288 128.942-288 288s128.942 288 288 288c159.058 0 288-128.942 288-288s-128.942-288-288-288z" />
|
||||||
|
<glyph unicode="" glyph-name="preview" d="M64 504.254c45.318 49.92 97.162 92.36 153.272 125.124 90.332 52.744 192.246 80.622 294.728 80.622 102.48 0 204.396-27.878 294.726-80.624 56.112-32.764 107.956-75.204 153.274-125.124v117.432c-33.010 28.118-68.124 53.14-104.868 74.594-105.006 61.314-223.658 93.722-343.132 93.722s-238.128-32.408-343.134-93.72c-36.742-21.454-71.856-46.478-104.866-74.596v-117.43zM512 640c-183.196 0-345.838-100.556-448-256 102.162-155.448 264.804-256 448-256s345.838 100.552 448 256c-102.162 155.444-264.804 256-448 256zM512 448c0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.348 28.654 64 64 64s64-28.652 64-64zM728.066 263.338c-67.434-39.374-140.128-59.338-216.066-59.338s-148.632 19.964-216.066 59.338c-51.554 30.104-98.616 71.31-138.114 120.662 39.498 49.35 86.56 90.558 138.116 120.66 13.276 7.752 26.758 14.74 40.426 20.982-10.512-23.742-16.362-50.008-16.362-77.642 0-106.040 85.962-192 192-192 106.040 0 192 85.96 192 192 0 27.634-5.85 53.9-16.36 77.642 13.668-6.244 27.15-13.23 40.426-20.982 51.554-30.102 98.616-71.31 138.116-120.66-39.498-49.352-86.56-90.558-138.116-120.662z" />
|
||||||
|
<glyph unicode="" glyph-name="forecolor" d="M651.168 676.166c-24.612 81.962-28.876 91.834-107.168 91.834h-64c-79.618 0-82.664-10.152-108.418-96 0-0.002 0-0.002-0.002-0.004l-143.998-479.996h113.636l57.6 192h226.366l57.6-192h113.63l-145.246 484.166zM437.218 512l38.4 136c10.086 33.618 36.38 30 36.38 30s26.294 3.618 36.38-30h0.004l38.4-136h-149.564z" />
|
||||||
|
<glyph unicode="" glyph-name="table" d="M64 768v-704h896v704h-896zM384 320v128h256v-128h-256zM640 256v-128h-256v128h256zM640 640v-128h-256v128h256zM320 640v-128h-192v128h192zM128 448h192v-128h-192v128zM704 448h192v-128h-192v128zM704 512v128h192v-128h-192zM128 256h192v-128h-192v128zM704 128v128h192v-128h-192z" />
|
||||||
|
<glyph unicode="" glyph-name="hr" d="M64 512h896v-128h-896z" />
|
||||||
|
<glyph unicode="" glyph-name="removeformat" d="M64 192h512v-128h-512v128zM768 768h-220.558l-183.766-512h-132.288l183.762 512h-223.15v128h576v-128zM929.774 64l-129.774 129.774-129.774-129.774-62.226 62.226 129.774 129.774-129.774 129.774 62.226 62.226 129.774-129.774 129.774 129.774 62.226-62.226-129.774-129.774 129.774-129.774-62.226-62.226z" />
|
||||||
|
<glyph unicode="" glyph-name="subscript" d="M768 50v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="superscript" d="M768 754v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="charmap" d="M704 128v37.004c151.348 61.628 256 193.82 256 346.996 0 212.078-200.576 384-448 384s-448-171.922-448-384c0-153.176 104.654-285.368 256-346.996v-37.004h-192l-64 96v-224h320v222.812c-100.9 51.362-170.666 161.54-170.666 289.188 0 176.732 133.718 320 298.666 320s298.666-143.268 298.666-320c0-127.648-69.766-237.826-170.666-289.188v-222.812h320v224l-64-96h-192z" />
|
||||||
|
<glyph unicode="" glyph-name="emoticons" d="M512 820c99.366 0 192.782-38.694 263.042-108.956s108.958-163.678 108.958-263.044-38.696-192.782-108.958-263.042-163.676-108.958-263.042-108.958-192.782 38.696-263.044 108.958-108.956 163.676-108.956 263.042 38.694 192.782 108.956 263.044 163.678 108.956 263.044 108.956zM512 896c-247.424 0-448-200.576-448-448s200.576-448 448-448 448 200.576 448 448-200.576 448-448 448v0zM320 576c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM576 576c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM512 304c-101.84 0-192.56 36.874-251.166 94.328 23.126-117.608 126.778-206.328 251.166-206.328s228.040 88.72 251.168 206.328c-58.608-57.454-149.328-94.328-251.168-94.328z" />
|
||||||
|
<glyph unicode="" glyph-name="print" d="M256 832h512v-128h-512v128zM896 640h-768c-35.2 0-64-28.8-64-64v-256c0-35.2 28.796-64 64-64h128v-192h512v192h128c35.2 0 64 28.8 64 64v256c0 35.2-28.8 64-64 64zM704 128h-384v256h384v-256zM910.4 544c0-25.626-20.774-46.4-46.398-46.4s-46.402 20.774-46.402 46.4 20.778 46.4 46.402 46.4c25.626 0 46.398-20.774 46.398-46.4z" />
|
||||||
|
<glyph unicode="" glyph-name="fullscreen" d="M480 576l-192 192 128 128h-352v-352l128 128 192-192zM640 480l192 192 128-128v352h-352l128-128-192-192zM544 320l192-192-128-128h352v352l-128-128-192 192zM384 416l-192-192-128 128v-352h352l-128 128 192 192z" />
|
||||||
|
<glyph unicode="" glyph-name="spellcheck" d="M960 832v64h-192c-35.202 0-64-28.8-64-64v-320c0-15.856 5.858-30.402 15.496-41.614l-303.496-260.386-142 148-82-70 224-288 416 448h128v64h-192v320h192zM256 448h64v384c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-384h64v192h128v-192zM128 704v128h128v-128h-128zM640 512v96c0 35.2-8.8 64-44 64 35.2 0 44 28.8 44 64v96c0 35.2-28.8 64-64 64h-192v-448h192c35.2 0 64 28.8 64 64zM448 832h128v-128h-128v128zM448 640h128v-128h-128v128z" />
|
||||||
|
<glyph unicode="" glyph-name="nonbreaking" d="M448 448h-128v128h128v128h128v-128h128v-128h-128v-128h-128v128zM960 384v-320h-896v320h128v-192h640v192h128z" />
|
||||||
|
<glyph unicode="" glyph-name="template" d="M512 576h128v-64h-128zM512 192h128v-64h-128zM576 384h128v-64h-128zM768 384v-192h-64v-64h128v256zM384 384h128v-64h-128zM320 192h128v-64h-128zM320 576h128v-64h-128zM192 768v-256h64v192h64v64zM704 512h128v256h-64v-192h-64zM64 896v-896h896v896h-896zM896 64h-768v768h768v-768zM192 384v-256h64v192h64v64zM576 768h128v-64h-128zM384 768h128v-64h-128z" />
|
||||||
|
<glyph unicode="" glyph-name="pagebreak" d="M816 896l16-384h-640l16 384h32l16-320h512l16 320h32zM208 0l-16 320h640l-16-320h-32l-16 256h-512l-16-256h-32zM64 448h128v-64h-128zM256 448h128v-64h-128zM448 448h128v-64h-128zM640 448h128v-64h-128zM832 448h128v-64h-128z" />
|
||||||
|
<glyph unicode="" glyph-name="restoredraft" d="M576 896c247.424 0 448-200.576 448-448s-200.576-448-448-448v96c94.024 0 182.418 36.614 248.902 103.098s103.098 154.878 103.098 248.902c0 94.022-36.614 182.418-103.098 248.902s-154.878 103.098-248.902 103.098c-94.022 0-182.418-36.614-248.902-103.098-51.14-51.138-84.582-115.246-97.306-184.902h186.208l-224-256-224 256h164.57c31.060 217.102 217.738 384 443.43 384zM768 512v-128h-256v320h128v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="bold" d="M625.442 465.818c48.074 38.15 78.558 94.856 78.558 158.182 0 114.876-100.29 208-224 208h-224v-768h288c123.712 0 224 93.124 224 208 0 88.196-59.118 163.562-142.558 193.818zM384 656c0 26.51 21.49 48 48 48h67.204c42.414 0 76.796-42.98 76.796-96s-34.382-96-76.796-96h-115.204v144zM547.2 192h-115.2c-26.51 0-48 21.49-48 48v144h163.2c42.418 0 76.8-42.98 76.8-96s-34.382-96-76.8-96z" />
|
||||||
|
<glyph unicode="" glyph-name="italic" d="M832 832v-64h-144l-256-640h144v-64h-448v64h144l256 640h-144v64h448z" />
|
||||||
|
<glyph unicode="" glyph-name="underline" d="M192 128h576v-64h-576v64zM640 832v-384c0-31.312-14.7-61.624-41.39-85.352-30.942-27.502-73.068-42.648-118.61-42.648-45.544 0-87.668 15.146-118.608 42.648-26.692 23.728-41.392 54.040-41.392 85.352v384h-128v-384c0-141.382 128.942-256 288-256s288 114.618 288 256v384h-128z" />
|
||||||
|
<glyph unicode="" glyph-name="strikethrough" d="M960 448h-265.876c-50.078 35.42-114.43 54.86-182.124 54.86-89.206 0-164.572 50.242-164.572 109.712s75.366 109.714 164.572 109.714c75.058 0 140.308-35.576 159.12-82.286h113.016c-7.93 50.644-37.58 97.968-84.058 132.826-50.88 38.16-117.676 59.174-188.078 59.174-70.404 0-137.196-21.014-188.074-59.174-54.788-41.090-86.212-99.502-86.212-160.254s31.424-119.164 86.212-160.254c1.956-1.466 3.942-2.898 5.946-4.316h-265.872v-64h512.532c58.208-17.106 100.042-56.27 100.042-100.572 0-59.468-75.368-109.71-164.572-109.71-75.060 0-140.308 35.574-159.118 82.286h-113.016c7.93-50.64 37.582-97.968 84.060-132.826 50.876-38.164 117.668-59.18 188.072-59.18 70.402 0 137.198 21.016 188.074 59.174 54.79 41.090 86.208 99.502 86.208 160.254 0 35.298-10.654 69.792-30.294 100.572h204.012v64z" />
|
||||||
|
<glyph unicode="" glyph-name="visualchars" d="M384 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448z" />
|
||||||
|
<glyph unicode="" glyph-name="ltr" d="M448 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448zM64 64l224 192-224 192z" />
|
||||||
|
<glyph unicode="" glyph-name="rtl" d="M320 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448zM960 448l-224-192 224-192z" />
|
||||||
|
<glyph unicode="" glyph-name="copy" d="M832 640h-192v64l-192 192h-384v-704h384v-192h576v448l-192 192zM832 549.49l101.49-101.49h-101.49v101.49zM448 805.49l101.49-101.49h-101.49v101.49zM128 832h256v-192h192v-384h-448v576zM960 64h-448v128h128v384h128v-192h192v-320z" />
|
||||||
|
<glyph unicode="" glyph-name="resize" d="M768 704h64v-64h-64zM640 576h64v-64h-64zM640 448h64v-64h-64zM640 320h64v-64h-64zM512 448h64v-64h-64zM512 320h64v-64h-64zM384 320h64v-64h-64zM768 576h64v-64h-64zM768 448h64v-64h-64zM768 320h64v-64h-64zM768 192h64v-64h-64zM640 192h64v-64h-64zM512 192h64v-64h-64zM384 192h64v-64h-64zM256 192h64v-64h-64z" />
|
||||||
|
<glyph unicode="" glyph-name="browse" d="M928 832h-416l-32 64h-352l-64-128h896zM840.34 256h87.66l32 448h-896l64-640h356.080c-104.882 37.776-180.080 138.266-180.080 256 0 149.982 122.018 272 272 272 149.98 0 272-122.018 272-272 0-21.678-2.622-43.15-7.66-64zM874.996 110.25l-134.496 110.692c17.454 28.922 27.5 62.814 27.5 99.058 0 106.040-85.96 192-192 192s-192-85.96-192-192 85.96-192 192-192c36.244 0 70.138 10.046 99.058 27.5l110.692-134.496c22.962-26.678 62.118-28.14 87.006-3.252l5.492 5.492c24.888 24.888 23.426 64.044-3.252 87.006zM576 196c-68.484 0-124 55.516-124 124s55.516 124 124 124 124-55.516 124-124-55.516-124-124-124z" />
|
||||||
|
<glyph unicode="" glyph-name="pastetext" d="M704 576v160c0 17.6-14.4 32-32 32h-160v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-160c-17.602 0-32-14.4-32-32v-512c0-17.6 14.398-32 32-32h224v-192h576v576h-192zM320 831.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 640v64h384v-64h-384zM832 64h-448v448h448v-448zM448 448v-128h32l32 64h64v-192h-48v-64h160v64h-48v192h64l32-64h32v128z" />
|
||||||
|
<glyph unicode="" glyph-name="codesample" d="M200.015 577.994v103.994c0 43.077 34.919 77.997 77.997 77.997h26v103.994h-26c-100.51 0-181.991-81.481-181.991-181.991v-103.994c0-43.077-34.919-77.997-77.997-77.997h-26v-103.994h26c43.077 0 77.997-34.919 77.997-77.997v-103.994c0-100.509 81.481-181.991 181.991-181.991h26v103.994h-26c-43.077 0-77.997 34.919-77.997 77.997v103.994c0 50.927-20.928 96.961-54.642 129.994 33.714 33.032 54.642 79.065 54.642 129.994zM823.985 577.994v103.994c0 43.077-34.919 77.997-77.997 77.997h-26v103.994h26c100.509 0 181.991-81.481 181.991-181.991v-103.994c0-43.077 34.919-77.997 77.997-77.997h26v-103.994h-26c-43.077 0-77.997-34.919-77.997-77.997v-103.994c0-100.509-81.482-181.991-181.991-181.991h-26v103.994h26c43.077 0 77.997 34.919 77.997 77.997v103.994c0 50.927 20.928 96.961 54.642 129.994-33.714 33.032-54.642 79.065-54.642 129.994zM615.997 603.277c0-57.435-46.56-103.994-103.994-103.994s-103.994 46.56-103.994 103.994c0 57.435 46.56 103.994 103.994 103.994s103.994-46.56 103.994-103.994zM512 448.717c-57.435 0-103.994-46.56-103.994-103.994 0-55.841 26-100.107 105.747-103.875-23.715-33.413-59.437-46.608-105.747-50.94v-61.747c0 0 207.991-18.144 207.991 216.561-0.202 57.437-46.56 103.996-103.994 103.996z" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,131 @@
|
||||||
|
<?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>Generated by IcoMoon</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="tinymce" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||||
|
<glyph unicode="" glyph-name="save" d="M896 960h-896v-1024h1024v896l-128 128zM512 832h128v-256h-128v256zM896 64h-768v768h64v-320h576v320h74.978l53.022-53.018v-714.982z" />
|
||||||
|
<glyph unicode="" glyph-name="newdocument" d="M903.432 760.57l-142.864 142.862c-31.112 31.112-92.568 56.568-136.568 56.568h-480c-44 0-80-36-80-80v-864c0-44 36-80 80-80h736c44 0 80 36 80 80v608c0 44-25.456 105.458-56.568 136.57zM858.178 715.314c3.13-3.13 6.25-6.974 9.28-11.314h-163.458v163.456c4.34-3.030 8.184-6.15 11.314-9.28l142.864-142.862zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16h480c4.832 0 10.254-0.61 16-1.704v-254.296h254.296c1.094-5.746 1.704-11.166 1.704-16v-608z" />
|
||||||
|
<glyph unicode="" glyph-name="fullpage" d="M1024 367.542v160.916l-159.144 15.914c-8.186 30.042-20.088 58.548-35.21 84.98l104.596 127.838-113.052 113.050-127.836-104.596c-26.434 15.124-54.942 27.026-84.982 35.208l-15.914 159.148h-160.916l-15.914-159.146c-30.042-8.186-58.548-20.086-84.98-35.208l-127.838 104.594-113.050-113.050 104.596-127.836c-15.124-26.432-27.026-54.94-35.21-84.98l-159.146-15.916v-160.916l159.146-15.914c8.186-30.042 20.086-58.548 35.21-84.982l-104.596-127.836 113.048-113.048 127.838 104.596c26.432-15.124 54.94-27.028 84.98-35.21l15.916-159.148h160.916l15.914 159.144c30.042 8.186 58.548 20.088 84.982 35.21l127.836-104.596 113.048 113.048-104.596 127.836c15.124 26.434 27.028 54.942 35.21 84.98l159.148 15.92zM704 384l-128-128h-128l-128 128v128l128 128h128l128-128v-128z" />
|
||||||
|
<glyph unicode="" glyph-name="alignleft" d="M0 896h1024v-128h-1024zM0 704h640v-128h-640zM0 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="aligncenter" d="M0 896h1024v-128h-1024zM192 704h640v-128h-640zM192 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="alignright" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="alignjustify" d="M0 896h1024v-128h-1024zM0 704h1024v-128h-1024zM0 512h1024v-128h-1024zM0 320h1024v-128h-1024zM0 128h1024v-128h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="cut" d="M890.774 250.846c-45.654 45.556-103.728 69.072-157.946 69.072h-29.112l-63.904 64.008 255.62 256.038c63.904 64.010 63.904 192.028 0 256.038l-383.43-384.056-383.432 384.054c-63.904-64.008-63.904-192.028 0-256.038l255.622-256.034-63.906-64.008h-29.114c-54.22 0-112.292-23.518-157.948-69.076-81.622-81.442-92.65-202.484-24.63-270.35 29.97-29.902 70.288-44.494 112.996-44.494 54.216 0 112.29 23.514 157.946 69.072 53.584 53.464 76.742 124 67.084 185.348l65.384 65.488 65.376-65.488c-9.656-61.348 13.506-131.882 67.084-185.348 45.662-45.558 103.732-69.072 157.948-69.072 42.708 0 83.024 14.592 112.994 44.496 68.020 67.866 56.988 188.908-24.632 270.35zM353.024 114.462c-7.698-17.882-19.010-34.346-33.626-48.926-14.636-14.604-31.172-25.918-49.148-33.624-16.132-6.916-32.96-10.568-48.662-10.568-15.146 0-36.612 3.402-52.862 19.612-16.136 16.104-19.52 37.318-19.52 52.288 0 15.542 3.642 32.21 10.526 48.212 7.7 17.884 19.014 34.346 33.626 48.926 14.634 14.606 31.172 25.914 49.15 33.624 16.134 6.914 32.96 10.568 48.664 10.568 15.146 0 36.612-3.4 52.858-19.614 16.134-16.098 19.522-37.316 19.522-52.284 0.002-15.542-3.638-32.216-10.528-48.214zM512.004 293.404c-49.914 0-90.376 40.532-90.376 90.526 0 49.992 40.462 90.52 90.376 90.52s90.372-40.528 90.372-90.52c0-49.998-40.46-90.526-90.372-90.526zM855.272 40.958c-16.248-16.208-37.712-19.612-52.86-19.612-15.704 0-32.53 3.652-48.666 10.568-17.972 7.706-34.508 19.020-49.142 33.624-14.614 14.58-25.926 31.042-33.626 48.926-6.886 15.998-10.526 32.672-10.526 48.212 0 14.966 3.384 36.188 19.52 52.286 16.246 16.208 37.712 19.614 52.86 19.614 15.7 0 32.53-3.654 48.66-10.568 17.978-7.708 34.516-19.018 49.15-33.624 14.61-14.58 25.924-31.042 33.626-48.926 6.884-15.998 10.526-32.67 10.526-48.212-0.002-14.97-3.39-36.186-19.522-52.288z" />
|
||||||
|
<glyph unicode="" glyph-name="paste" d="M832 640v160c0 17.6-14.4 32-32 32h-224v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-224c-17.602 0-32-14.4-32-32v-640c0-17.6 14.398-32 32-32h288v-192h448l192 192v512h-192zM384 895.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 704v64h512v-64h-512zM832 26.51v101.49h101.49l-101.49-101.49zM960 192h-192v-192h-320v576h512v-384z" />
|
||||||
|
<glyph unicode="" glyph-name="searchreplace" d="M64 960h384v-64h-384zM576 960h384v-64h-384zM952 640h-56v256h-256v-256h-256v256h-256v-256h-56c-39.6 0-72-32.4-72-72v-560c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v376h128v-376c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v560c0 39.6-32.4 72-72 72zM348 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32zM544 448h-64c-17.6 0-32 14.4-32 32s14.4 32 32 32h64c17.6 0 32-14.4 32-32s-14.4-32-32-32zM924 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32z" />
|
||||||
|
<glyph unicode="" glyph-name="bullist" d="M384 896h640v-128h-640v128zM384 512h640v-128h-640v128zM384 128h640v-128h-640v128zM0 832c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM0 448c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM0 64c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128z" />
|
||||||
|
<glyph unicode="" glyph-name="numlist" d="M384 128h640v-128h-640zM384 512h640v-128h-640zM384 896h640v-128h-640zM192 960v-256h-64v192h-64v64zM128 434v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM256 256v-320h-192v64h128v64h-128v64h128v64h-128v64z" />
|
||||||
|
<glyph unicode="" glyph-name="indent" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 512h640v-128h-640zM384 320h640v-128h-640zM0 128h1024v-128h-1024zM0 256v384l256-192z" />
|
||||||
|
<glyph unicode="" glyph-name="outdent" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 512h640v-128h-640zM384 320h640v-128h-640zM0 128h1024v-128h-1024zM256 640v-384l-256 192z" />
|
||||||
|
<glyph unicode="" glyph-name="blockquote" d="M225 512c123.712 0 224-100.29 224-224 0-123.712-100.288-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.634-11.636-22.252-24.016-31.83-37.020 11.438 1.8 23.16 2.746 35.104 2.746zM801 512c123.71 0 224-100.29 224-224 0-123.712-100.29-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.636-11.636-22.254-24.016-31.832-37.020 11.44 1.8 23.16 2.746 35.106 2.746z" />
|
||||||
|
<glyph unicode="" glyph-name="undo" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
|
||||||
|
<glyph unicode="" glyph-name="redo" d="M576 711.628v248.372l384-384-384-384v253.824c-446.75 10.482-427.588-303.792-313.86-509.824-280.712 303.414-221.1 789.57 313.86 775.628z" />
|
||||||
|
<glyph unicode="" glyph-name="link" d="M320 256c17.6-17.6 47.274-16.726 65.942 1.942l316.118 316.116c18.668 18.668 19.54 48.342 1.94 65.942s-47.274 16.726-65.942-1.942l-316.116-316.116c-18.668-18.668-19.542-48.342-1.942-65.942zM476.888 284.888c4.56-9.050 6.99-19.16 6.99-29.696 0-17.616-6.744-34.060-18.992-46.308l-163.382-163.382c-12.248-12.248-28.694-18.992-46.308-18.992s-34.060 6.744-46.308 18.992l-99.382 99.382c-12.248 12.248-18.992 28.694-18.992 46.308s6.744 34.060 18.992 46.308l163.382 163.382c12.248 12.248 28.694 18.994 46.308 18.994 10.536 0 20.644-2.43 29.696-6.99l65.338 65.338c-27.87 21.41-61.44 32.16-95.034 32.16-39.986 0-79.972-15.166-110.308-45.502l-163.382-163.382c-60.67-60.67-60.67-159.948 0-220.618l99.382-99.382c30.334-30.332 70.32-45.5 110.306-45.5 39.988 0 79.974 15.168 110.308 45.502l163.382 163.382c55.82 55.82 60.238 144.298 13.344 205.344l-65.34-65.34zM978.498 815.116l-99.382 99.382c-30.334 30.336-70.32 45.502-110.308 45.502-39.986 0-79.972-15.166-110.308-45.502l-163.382-163.382c-55.82-55.82-60.238-144.298-13.342-205.342l65.338 65.34c-4.558 9.050-6.988 19.16-6.988 29.694 0 17.616 6.744 34.060 18.992 46.308l163.382 163.382c12.248 12.248 28.694 18.994 46.308 18.994s34.060-6.746 46.308-18.994l99.382-99.382c12.248-12.248 18.992-28.694 18.992-46.308s-6.744-34.060-18.992-46.308l-163.382-163.382c-12.248-12.248-28.694-18.992-46.308-18.992-10.536 0-20.644 2.43-29.696 6.99l-65.338-65.338c27.872-21.41 61.44-32.16 95.034-32.16 39.988 0 79.974 15.168 110.308 45.502l163.382 163.382c60.67 60.666 60.67 159.944 0 220.614z" />
|
||||||
|
<glyph unicode="" glyph-name="unlink" d="M476.888 284.886c4.56-9.048 6.99-19.158 6.99-29.696 0-17.616-6.744-34.058-18.992-46.308l-163.38-163.38c-12.248-12.248-28.696-18.992-46.308-18.992s-34.060 6.744-46.308 18.992l-99.38 99.38c-12.248 12.25-18.992 28.696-18.992 46.308s6.744 34.060 18.992 46.308l163.38 163.382c12.248 12.246 28.696 18.992 46.308 18.992 10.538 0 20.644-2.43 29.696-6.988l65.338 65.336c-27.87 21.41-61.44 32.16-95.034 32.16-39.986 0-79.972-15.166-110.308-45.502l-163.38-163.382c-60.67-60.67-60.67-159.95 0-220.618l99.38-99.382c30.334-30.332 70.32-45.5 110.306-45.5 39.988 0 79.974 15.168 110.308 45.502l163.38 163.38c55.82 55.82 60.238 144.298 13.344 205.346l-65.34-65.338zM978.496 815.116l-99.38 99.382c-30.334 30.336-70.32 45.502-110.308 45.502-39.986 0-79.97-15.166-110.306-45.502l-163.382-163.382c-55.82-55.82-60.238-144.298-13.342-205.342l65.338 65.34c-4.558 9.050-6.988 19.16-6.988 29.694 0 17.616 6.744 34.060 18.992 46.308l163.382 163.382c12.246 12.248 28.694 18.994 46.306 18.994 17.616 0 34.060-6.746 46.308-18.994l99.38-99.382c12.248-12.248 18.992-28.694 18.992-46.308s-6.744-34.060-18.992-46.308l-163.38-163.382c-12.248-12.248-28.694-18.992-46.308-18.992-10.536 0-20.644 2.43-29.696 6.99l-65.338-65.338c27.872-21.41 61.44-32.16 95.034-32.16 39.988 0 79.974 15.168 110.308 45.504l163.38 163.38c60.672 60.666 60.672 159.944 0 220.614zM233.368 681.376l-191.994 191.994 45.256 45.256 191.994-191.994zM384 960h64v-192h-64zM0 576h192v-64h-192zM790.632 214.624l191.996-191.996-45.256-45.256-191.996 191.996zM576 128h64v-192h-64zM832 384h192v-64h-192z" />
|
||||||
|
<glyph unicode="" glyph-name="anchor" d="M192 960v-1024l320 320 320-320v1024h-640zM768 90.51l-256 256-256-256v805.49h512v-805.49z" />
|
||||||
|
<glyph unicode="" glyph-name="image" d="M0 832v-832h1024v832h-1024zM960 64h-896v704h896v-704zM704 608c0 53.019 42.981 96 96 96s96-42.981 96-96c0-53.019-42.981-96-96-96s-96 42.981-96 96zM896 128h-768l192 512 256-320 128 96z" />
|
||||||
|
<glyph unicode="" glyph-name="media" d="M0 832v-768h1024v768h-1024zM192 128h-128v128h128v-128zM192 384h-128v128h128v-128zM192 640h-128v128h128v-128zM768 128h-512v640h512v-640zM960 128h-128v128h128v-128zM960 384h-128v128h128v-128zM960 640h-128v128h128v-128zM384 640v-384l256 192z" />
|
||||||
|
<glyph unicode="" glyph-name="help" d="M448 256h128v-128h-128zM704 704c35.346 0 64-28.654 64-64v-192l-192-128h-128v64l192 128v64h-320v128h384zM512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512z" />
|
||||||
|
<glyph unicode="" glyph-name="code" d="M320 704l-256-256 256-256h128l-256 256 256 256zM704 704h-128l256-256-256-256h128l256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="inserttime" d="M512 768c-212.076 0-384-171.922-384-384s171.922-384 384-384c212.074 0 384 171.922 384 384s-171.926 384-384 384zM715.644 180.354c-54.392-54.396-126.716-84.354-203.644-84.354s-149.25 29.958-203.646 84.354c-54.396 54.394-84.354 126.718-84.354 203.646s29.958 149.25 84.354 203.646c54.396 54.396 126.718 84.354 203.646 84.354s149.252-29.958 203.642-84.354c54.402-54.396 84.358-126.718 84.358-203.646s-29.958-149.252-84.356-203.646zM325.93 756.138l-42.94 85.878c-98.874-49.536-179.47-130.132-229.006-229.008l85.876-42.94c40.248 80.336 105.732 145.822 186.070 186.070zM884.134 570.070l85.878 42.938c-49.532 98.876-130.126 179.472-229.004 229.008l-42.944-85.878c80.338-40.248 145.824-105.732 186.070-186.068zM512 576h-64v-192c0-10.11 4.7-19.11 12.022-24.972l-0.012-0.016 160-128 39.976 49.976-147.986 118.39v176.622z" />
|
||||||
|
<glyph unicode="" glyph-name="preview" d="M512 640c-209.368 0-395.244-100.556-512-256 116.756-155.446 302.632-256 512-256s395.244 100.554 512 256c-116.756 155.444-302.632 256-512 256zM448 512c35.346 0 64-28.654 64-64s-28.654-64-64-64-64 28.654-64 64 28.654 64 64 64zM773.616 254.704c-39.648-20.258-81.652-35.862-124.846-46.376-44.488-10.836-90.502-16.328-136.77-16.328-46.266 0-92.282 5.492-136.768 16.324-43.194 10.518-85.198 26.122-124.846 46.376-63.020 32.202-120.222 76.41-167.64 129.298 47.418 52.888 104.62 97.1 167.64 129.298 32.336 16.522 66.242 29.946 101.082 40.040-19.888-30.242-31.468-66.434-31.468-105.336 0-106.040 85.962-192 192-192s192 85.96 192 192c0 38.902-11.582 75.094-31.466 105.34 34.838-10.096 68.744-23.52 101.082-40.042 63.022-32.198 120.218-76.408 167.638-129.298-47.42-52.886-104.618-97.1-167.638-129.296zM860.918 716.278c-108.72 55.554-226.112 83.722-348.918 83.722s-240.198-28.168-348.918-83.722c-58.772-30.032-113.732-67.904-163.082-112.076v-109.206c55.338 58.566 120.694 107.754 192.194 144.29 99.62 50.904 207.218 76.714 319.806 76.714s220.186-25.81 319.804-76.716c71.502-36.536 136.858-85.724 192.196-144.29v109.206c-49.35 44.174-104.308 82.046-163.082 112.078z" />
|
||||||
|
<glyph unicode="" glyph-name="forecolor" d="M322.018 128l57.6 192h264.764l57.6-192h113.632l-191.996 640h-223.236l-192-640h113.636zM475.618 640h72.764l57.6-192h-187.964l57.6 192z" />
|
||||||
|
<glyph unicode="" glyph-name="table" d="M0 896v-896h1024v896h-1024zM384 320v192h256v-192h-256zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
|
||||||
|
<glyph unicode="" glyph-name="hr" d="M0 512h1024v-128h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="removeformat" d="M0 64h576v-128h-576zM192 960h704v-128h-704zM277.388 128l204.688 784.164 123.85-32.328-196.25-751.836zM929.774-64l-129.774 129.774-129.774-129.774-62.226 62.226 129.774 129.774-129.774 129.774 62.226 62.226 129.774-129.774 129.774 129.774 62.226-62.226-129.774-129.774 129.774-129.774z" />
|
||||||
|
<glyph unicode="" glyph-name="sub" d="M768 50v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="sup" d="M768 754v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="charmap" d="M704 64h256l64 128v-256h-384v214.214c131.112 56.484 224 197.162 224 361.786 0 214.432-157.598 382.266-352 382.266-194.406 0-352-167.832-352-382.266 0-164.624 92.886-305.302 224-361.786v-214.214h-384v256l64-128h256v32.59c-187.63 66.46-320 227.402-320 415.41 0 247.424 229.23 448 512 448s512-200.576 512-448c0-188.008-132.37-348.95-320-415.41v-32.59z" />
|
||||||
|
<glyph unicode="" glyph-name="emoticons" d="M512 960c-282.77 0-512-229.228-512-512 0-282.77 229.228-512 512-512 282.77 0 512 229.23 512 512 0 282.772-229.23 512-512 512zM512 16c-238.586 0-432 193.412-432 432 0 238.586 193.414 432 432 432 238.59 0 432-193.414 432-432 0-238.588-193.41-432-432-432zM384 640c0-35.346-28.654-64-64-64s-64 28.654-64 64 28.654 64 64 64 64-28.654 64-64zM768 640c0-35.346-28.652-64-64-64s-64 28.654-64 64 28.652 64 64 64 64-28.654 64-64zM512 308c141.074 0 262.688 57.532 318.462 123.192-20.872-171.22-156.288-303.192-318.462-303.192-162.118 0-297.498 132.026-318.444 303.168 55.786-65.646 177.386-123.168 318.444-123.168z" />
|
||||||
|
<glyph unicode="" glyph-name="print" d="M256 896h512v-128h-512zM960 704h-896c-35.2 0-64-28.8-64-64v-320c0-35.2 28.796-64 64-64h192v-256h512v256h192c35.2 0 64 28.8 64 64v320c0 35.2-28.8 64-64 64zM704 64h-384v320h384v-320zM974.4 608c0-25.626-20.774-46.4-46.398-46.4-25.626 0-46.402 20.774-46.402 46.4s20.776 46.4 46.402 46.4c25.626 0 46.398-20.774 46.398-46.4z" />
|
||||||
|
<glyph unicode="" glyph-name="fullscreen" d="M1024 960v-384l-138.26 138.26-212-212-107.48 107.48 212 212-138.26 138.26zM245.74 821.74l212-212-107.48-107.48-212 212-138.26-138.26v384h384zM885.74 181.74l138.26 138.26v-384h-384l138.26 138.26-212 212 107.48 107.48zM457.74 286.26l-212-212 138.26-138.26h-384v384l138.26-138.26 212 212z" />
|
||||||
|
<glyph unicode="" glyph-name="spellchecker" d="M128 704h128v-192h64v384c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-384h64v192zM128 896h128v-128h-128v128zM960 896v64h-192c-35.202 0-64-28.8-64-64v-320c0-35.2 28.798-64 64-64h192v64h-192v320h192zM640 800v96c0 35.2-28.8 64-64 64h-192v-448h192c35.2 0 64 28.8 64 64v96c0 35.2-8.8 64-44 64 35.2 0 44 28.8 44 64zM576 576h-128v128h128v-128zM576 768h-128v128h128v-128zM832 384l-416-448-224 288 82 70 142-148 352 302z" />
|
||||||
|
<glyph unicode="" glyph-name="nonbreaking" d="M448 384h-192v128h192v192h128v-192h192v-128h-192v-192h-128zM1024 320v-384h-1024v384h128v-256h768v256z" />
|
||||||
|
<glyph unicode="" glyph-name="template" d="M384 768h128v-64h-128zM576 768h128v-64h-128zM896 768v-256h-192v64h128v128h-64v64zM320 576h128v-64h-128zM512 576h128v-64h-128zM192 704v-128h64v-64h-128v256h192v-64zM384 384h128v-64h-128zM576 384h128v-64h-128zM896 384v-256h-192v64h128v128h-64v64zM320 192h128v-64h-128zM512 192h128v-64h-128zM192 320v-128h64v-64h-128v256h192v-64zM960 896h-896v-896h896v896zM1024 960v0-1024h-1024v1024h1024z" />
|
||||||
|
<glyph unicode="" glyph-name="pagebreak" d="M0 448h128v-64h-128zM192 448h192v-64h-192zM448 448h128v-64h-128zM640 448h192v-64h-192zM896 448h128v-64h-128zM880 960l16-448h-768l16 448h32l16-384h640l16 384zM144-64l-16 384h768l-16-384h-32l-16 320h-640l-16-320z" />
|
||||||
|
<glyph unicode="" glyph-name="restoredraft" d="M576 896c247.424 0 448-200.576 448-448s-200.576-448-448-448v96c94.024 0 182.418 36.614 248.902 103.098s103.098 154.878 103.098 248.902c0 94.022-36.614 182.418-103.098 248.902s-154.878 103.098-248.902 103.098c-94.022 0-182.418-36.614-248.902-103.098-51.14-51.138-84.582-115.246-97.306-184.902h186.208l-224-256-224 256h164.57c31.060 217.102 217.738 384 443.43 384zM768 512v-128h-256v320h128v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="bold" d="M707.88 475.348c37.498 44.542 60.12 102.008 60.12 164.652 0 141.16-114.842 256-256 256h-320v-896h384c141.158 0 256 114.842 256 256 0 92.956-49.798 174.496-124.12 219.348zM384 768h101.5c55.968 0 101.5-57.42 101.5-128s-45.532-128-101.5-128h-101.5v256zM543 128h-159v256h159c58.45 0 106-57.42 106-128s-47.55-128-106-128z" />
|
||||||
|
<glyph unicode="" glyph-name="italic" d="M896 896v-64h-128l-320-768h128v-64h-448v64h128l320 768h-128v64z" />
|
||||||
|
<glyph unicode="" glyph-name="underline" d="M704 896h128v-416c0-159.058-143.268-288-320-288-176.73 0-320 128.942-320 288v416h128v-416c0-40.166 18.238-78.704 51.354-108.506 36.896-33.204 86.846-51.494 140.646-51.494s103.75 18.29 140.646 51.494c33.116 29.802 51.354 68.34 51.354 108.506v416zM192 128h640v-128h-640z" />
|
||||||
|
<glyph unicode="" glyph-name="strikethrough" d="M731.42 442.964c63.92-47.938 100.58-116.086 100.58-186.964s-36.66-139.026-100.58-186.964c-59.358-44.518-137.284-69.036-219.42-69.036-82.138 0-160.062 24.518-219.42 69.036-63.92 47.938-100.58 116.086-100.58 186.964h128c0-69.382 87.926-128 192-128s192 58.618 192 128c0 69.382-87.926 128-192 128-82.138 0-160.062 24.518-219.42 69.036-63.92 47.94-100.58 116.086-100.58 186.964s36.66 139.024 100.58 186.964c59.358 44.518 137.282 69.036 219.42 69.036 82.136 0 160.062-24.518 219.42-69.036 63.92-47.94 100.58-116.086 100.58-186.964h-128c0 69.382-87.926 128-192 128s-192-58.618-192-128c0-69.382 87.926-128 192-128 82.136 0 160.062-24.518 219.42-69.036zM0 448h1024v-64h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="visualchars" d="M384 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224z" />
|
||||||
|
<glyph unicode="" glyph-name="ltr" d="M448 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224zM64 512l256-224-256-224z" />
|
||||||
|
<glyph unicode="" glyph-name="rtl" d="M256 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224zM960 64l-256 224 256 224z" />
|
||||||
|
<glyph unicode="" glyph-name="copy" d="M832 704h-192v64l-192 192h-448v-768h384v-256h640v576l-192 192zM832 613.49l101.49-101.49h-101.49v101.49zM448 869.49l101.49-101.49h-101.49v101.49zM64 896h320v-192h192v-448h-512v640zM960 0h-512v192h192v448h128v-192h192v-448z" />
|
||||||
|
<glyph unicode="" glyph-name="resize" d="M768 704h64v-64h-64zM640 576h64v-64h-64zM640 448h64v-64h-64zM640 320h64v-64h-64zM512 448h64v-64h-64zM512 320h64v-64h-64zM384 320h64v-64h-64zM768 576h64v-64h-64zM768 448h64v-64h-64zM768 320h64v-64h-64zM768 192h64v-64h-64zM640 192h64v-64h-64zM512 192h64v-64h-64zM384 192h64v-64h-64zM256 192h64v-64h-64z" />
|
||||||
|
<glyph unicode="" glyph-name="checkbox" d="M128 416l288-288 480 480-128 128-352-352-160 160z" />
|
||||||
|
<glyph unicode="" glyph-name="browse" d="M928 832h-416l-32 64h-352l-64-128h896zM904.34 256h74.86l44.8 448h-1024l64-640h484.080c-104.882 37.776-180.080 138.266-180.080 256 0 149.982 122.018 272 272 272 149.98 0 272-122.018 272-272 0-21.678-2.622-43.15-7.66-64zM1002.996 46.25l-198.496 174.692c17.454 28.92 27.5 62.814 27.5 99.058 0 106.040-85.96 192-192 192s-192-85.96-192-192 85.96-192 192-192c36.244 0 70.138 10.046 99.058 27.5l174.692-198.496c22.962-26.678 62.118-28.14 87.006-3.252l5.492 5.492c24.888 24.888 23.426 64.044-3.252 87.006zM640 196c-68.484 0-124 55.516-124 124s55.516 124 124 124 124-55.516 124-124-55.516-124-124-124z" />
|
||||||
|
<glyph unicode="" glyph-name="pastetext" d="M512 448v-128h32l32 64h64v-256h-48v-64h224v64h-48v256h64l32-64h32v128zM832 640v160c0 17.6-14.4 32-32 32h-224v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-224c-17.602 0-32-14.4-32-32v-640c0-17.6 14.398-32 32-32h288v-192h640v704h-192zM384 895.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 704v64h512v-64h-512zM960 0h-512v576h512v-576z" />
|
||||||
|
<glyph unicode="" glyph-name="gamma" d="M483.2 320l-147.2 336c-9.6 25.6-19.2 44.8-25.6 54.4s-16 12.8-25.6 12.8c-16 0-25.6-3.2-28.8-3.2v70.4c9.6 6.4 25.6 6.4 38.4 9.6 32 0 57.6-6.4 73.6-22.4 6.4-6.4 12.8-16 19.2-25.6 6.4-12.8 12.8-25.6 16-41.6l121.6-291.2 150.4 371.2h92.8l-198.4-470.4v-224h-86.4v224zM0 960v-1024h1024v1024h-1024zM960 0h-896v896h896v-896z" />
|
||||||
|
<glyph unicode="" glyph-name="orientation" d="M627.2 80h-579.2v396.8h579.2v-396.8zM553.6 406.4h-435.2v-256h435.2v256zM259.2 732.8c176 176 457.6 176 633.6 0s176-457.6 0-633.6c-121.6-121.6-297.6-160-454.4-108.8 121.6-28.8 262.4 9.6 361.6 108.8 150.4 150.4 160 384 22.4 521.6-121.6 121.6-320 128-470.4 19.2l86.4-86.4-294.4-22.4 22.4 294.4 92.8-92.8z" />
|
||||||
|
<glyph unicode="" glyph-name="invert" d="M892.8-22.4l-89.6 89.6c-70.4-80-172.8-131.2-288-131.2-208 0-380.8 166.4-384 377.6 0 0 0 0 0 0 0 3.2 0 3.2 0 6.4s0 3.2 0 6.4v0c0 0 0 0 0 3.2 0 0 0 3.2 0 3.2 3.2 105.6 48 211.2 105.6 304l-192 192 44.8 44.8 182.4-182.4c0 0 0 0 0 0l569.6-569.6c0 0 0 0 0 0l99.2-99.2-48-44.8zM896 326.4c0 0 0 0 0 0 0 3.2 0 6.4 0 6.4-9.6 316.8-384 627.2-384 627.2s-108.8-89.6-208-220.8l70.4-70.4c6.4 9.6 16 22.4 22.4 32 41.6 51.2 83.2 96 115.2 128v0c32-32 73.6-76.8 115.2-128 108.8-137.6 169.6-265.6 172.8-371.2 0 0 0-3.2 0-3.2v0 0c0-3.2 0-3.2 0-6.4s0-3.2 0-3.2v0 0c0-22.4-3.2-41.6-9.6-64l76.8-76.8c16 41.6 28.8 89.6 28.8 137.6 0 0 0 0 0 0 0 3.2 0 3.2 0 6.4s0 3.2 0 6.4z" />
|
||||||
|
<glyph unicode="" glyph-name="codesample" d="M199.995 578.002v104.002c0 43.078 34.923 78.001 78.001 78.001h26v104.002h-26c-100.518 0-182.003-81.485-182.003-182.003v-104.002c0-43.078-34.923-78.001-78.001-78.001h-26v-104.002h26c43.078 0 78.001-34.923 78.001-78.001v-104.002c0-100.515 81.485-182.003 182.003-182.003h26v104.002h-26c-43.078 0-78.001 34.923-78.001 78.001v104.002c0 50.931-20.928 96.966-54.646 130.002 33.716 33.036 54.646 79.072 54.646 130.002zM824.005 578.002v104.002c0 43.078-34.923 78.001-78.001 78.001h-26v104.002h26c100.515 0 182.003-81.485 182.003-182.003v-104.002c0-43.078 34.923-78.001 78.001-78.001h26v-104.002h-26c-43.078 0-78.001-34.923-78.001-78.001v-104.002c0-100.515-81.488-182.003-182.003-182.003h-26v104.002h26c43.078 0 78.001 34.923 78.001 78.001v104.002c0 50.931 20.928 96.966 54.646 130.002-33.716 33.036-54.646 79.072-54.646 130.002zM616.002 603.285c0-57.439-46.562-104.002-104.002-104.002s-104.002 46.562-104.002 104.002c0 57.439 46.562 104.002 104.002 104.002s104.002-46.562 104.002-104.002zM512 448.717c-57.439 0-104.002-46.562-104.002-104.002 0-55.845 26-100.115 105.752-103.88-23.719-33.417-59.441-46.612-105.752-50.944v-61.751c0 0 208.003-18.144 208.003 216.577-0.202 57.441-46.56 104.004-104.002 104.004z" />
|
||||||
|
<glyph unicode="" glyph-name="tablerowprops" d="M0 896v-896h1024v896h-1024zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
|
||||||
|
<glyph unicode="" glyph-name="tablecellprops" d="M0 896v-896h1024v896h-1024zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
|
||||||
|
<glyph unicode="" glyph-name="table2" d="M0 896v-832h1024v832h-1024zM320 128h-256v192h256v-192zM320 384h-256v192h256v-192zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192zM960 640h-896v192h896v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tablemergecells" d="M0 896v-896h1024v896h-1024zM384 64v448h576v-448h-576zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192z" />
|
||||||
|
<glyph unicode="" glyph-name="tableinsertcolbefore" d="M320 188.8v182.4h-182.4v89.6h182.4v182.4h86.4v-182.4h185.6v-89.6h-185.6v-182.4zM0 896v-896h1024v896h-1024zM640 64h-576v704h576v-704zM960 64h-256v192h256v-192zM960 320h-256v192h256v-192zM960 576h-256v192h256v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tableinsertcolafter" d="M704 643.2v-182.4h182.4v-89.6h-182.4v-182.4h-86.4v182.4h-185.6v89.6h185.6v182.4zM0 896v-896h1024v896h-1024zM320 64h-256v192h256v-192zM320 320h-256v192h256v-192zM320 576h-256v192h256v-192zM960 64h-576v704h576v-704z" />
|
||||||
|
<glyph unicode="" glyph-name="tableinsertrowbefore" d="M691.2 508.8h-144v-144h-70.4v144h-144v67.2h144v144h70.4v-144h144zM0 896v-896h1024v896h-1024zM320 64h-256v192h256v-192zM640 64h-256v192h256v-192zM960 64h-256v192h256v-192zM960 316.8h-896v451.2h896v-451.2z" />
|
||||||
|
<glyph unicode="" glyph-name="tableinsertrowafter" d="M332.8 323.2h144v144h70.4v-144h144v-67.2h-144v-144h-70.4v144h-144zM0 896v-896h1024v896h-1024zM384 768h256v-192h-256v192zM64 768h256v-192h-256v192zM960 64h-896v451.2h896v-451.2zM960 576h-256v192h256v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tablesplitcells" d="M0 896v-896h1024v896h-1024zM384 768h256v-192h-256v192zM320 64h-256v192h256v-192zM320 320h-256v192h256v-192zM320 576h-256v192h256v-192zM960 64h-576v448h576v-448zM960 576h-256v192h256v-192zM864 156.8l-60.8-60.8-131.2 131.2-131.2-131.2-60.8 60.8 131.2 131.2-131.2 131.2 60.8 60.8 131.2-131.2 131.2 131.2 60.8-60.8-131.2-131.2z" />
|
||||||
|
<glyph unicode="" glyph-name="tabledelete" d="M0 896h1024v-896h-1024v896zM60.8 768v-704h899.2v704h-899.2zM809.6 211.2l-96-96-204.8 204.8-204.8-204.8-96 96 204.8 204.8-204.8 204.8 96 96 204.8-204.8 204.8 204.8 96-96-204.8-204.8z" />
|
||||||
|
<glyph unicode="" glyph-name="tableleftheader" d="M0 896v-832h1024v832h-1024zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM640 640h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192zM960 640h-256v192h256v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tabletopheader" d="M0 896v-832h1024v832h-1024zM320 128h-256v192h256v-192zM320 384h-256v192h256v-192zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tabledeleterow" d="M886.4 572.8l-156.8-156.8 160-160-76.8-76.8-160 160-156.8-156.8-76.8 73.6 160 160-163.2 163.2 76.8 76.8 163.2-163.2 156.8 156.8 73.6-76.8zM0 896v-896h1024v896h-1024zM960 576h-22.4l-64-64h86.4v-192h-89.6l64-64h25.6v-192h-896v192h310.4l64 64h-374.4v192h371.2l-64 64h-307.2v192h896v-192z" />
|
||||||
|
<glyph unicode="" glyph-name="tabledeletecol" d="M320 499.2l64-64v-12.8l-64-64v140.8zM640 422.4l64-64v137.6l-64-64v-9.6zM1024 896v-896h-1024v896h1024zM960 768h-256v-51.2l-12.8 12.8-51.2-51.2v89.6h-256v-89.6l-51.2 51.2-12.8-12.8v51.2h-256v-704h256v118.4l35.2-35.2 28.8 28.8v-115.2h256v115.2l48-48 16 16v-83.2h256v707.2zM672 662.4l-156.8-156.8-163.2 163.2-76.8-76.8 163.2-163.2-156.8-156.8 76.8-76.8 156.8 156.8 160-160 76.8 76.8-160 160 156.8 156.8-76.8 76.8z" />
|
||||||
|
<glyph unicode="" glyph-name="a11y" d="M960 704v64l-448-128-448 128v-64l320-128v-256l-128-448h64l192 448 192-448h64l-128 448v256zM416 800q0 40 28 68t68 28 68-28 28-68-28-68-68-28-68 28-28 68z" />
|
||||||
|
<glyph unicode="" glyph-name="toc" d="M0 896h128v-128h-128v128zM192 896h832v-128h-832v128zM0 512h128v-128h-128v128zM192 512h832v-128h-832v128zM0 128h128v-128h-128v128zM192 128h832v-128h-832v128zM192 704h128v-128h-128v128zM384 704h640v-128h-640v128zM192 320h128v-128h-128v128zM384 320h640v-128h-640v128z" />
|
||||||
|
<glyph unicode="" glyph-name="fill" d="M521.6 915.2l-67.2-67.2-86.4 86.4-86.4-86.4 86.4-86.4-368-368 432-432 518.4 518.4-428.8 435.2zM435.2 134.4l-262.4 262.4 35.2 35.2 576 51.2-348.8-348.8zM953.6 409.6c-6.4-6.4-16-16-28.8-32-28.8-32-41.6-64-41.6-89.6v0 0 0 0 0 0 0c0-16 6.4-35.2 22.4-48 12.8-12.8 32-22.4 48-22.4s35.2 6.4 48 22.4 22.4 32 22.4 48v0 0 0 0 0 0 0c0 25.6-12.8 54.4-41.6 89.6-9.6 16-22.4 25.6-28.8 32v0z" />
|
||||||
|
<glyph unicode="" glyph-name="borderwidth" d="M0 265.6h1024v-128h-1024v128zM0 32h1024v-64h-1024v64zM0 566.4h1024v-192h-1024v192zM0 928h1024v-256h-1024v256z" />
|
||||||
|
<glyph unicode="" glyph-name="line" d="M739.2 627.2l-502.4-502.4h-185.6v185.6l502.4 502.4 185.6-185.6zM803.2 688l-185.6 185.6 67.2 67.2c22.4 22.4 54.4 22.4 76.8 0l108.8-108.8c22.4-22.4 22.4-54.4 0-76.8l-67.2-67.2zM41.6 48h940.8v-112h-940.8v112z" />
|
||||||
|
<glyph unicode="" glyph-name="count" d="M0 480h1024v-64h-1024v64zM304 912v-339.2h-67.2v272h-67.2v67.2zM444.8 694.4v-54.4h134.4v-67.2h-201.6v153.6l134.4 64v54.4h-134.4v67.2h201.6v-153.6zM854.4 912v-339.2h-204.8v67.2h137.6v67.2h-137.6v70.4h137.6v67.2h-137.6v67.2zM115.2 166.4c3.2 57.6 38.4 83.2 108.8 83.2 38.4 0 67.2-9.6 86.4-25.6s25.6-35.2 25.6-70.4v-112c0-25.6 0-28.8 9.6-41.6h-73.6c-3.2 9.6-3.2 9.6-6.4 19.2-22.4-19.2-41.6-25.6-70.4-25.6-54.4 0-89.6 32-89.6 76.8s28.8 70.4 99.2 80l38.4 6.4c16 3.2 22.4 6.4 22.4 16 0 12.8-12.8 22.4-38.4 22.4s-41.6-9.6-44.8-28.8h-67.2zM262.4 115.2c-6.4-3.2-12.8-6.4-25.6-6.4l-25.6-6.4c-25.6-6.4-38.4-16-38.4-28.8 0-16 12.8-25.6 35.2-25.6s41.6 9.6 54.4 32v35.2zM390.4 336h73.6v-112c22.4 16 41.6 22.4 67.2 22.4 64 0 105.6-51.2 105.6-124.8 0-76.8-44.8-134.4-108.8-134.4-32 0-48 9.6-67.2 35.2v-28.8h-70.4v342.4zM460.8 121.6c0-41.6 22.4-70.4 51.2-70.4s51.2 28.8 51.2 70.4c0 44.8-19.2 70.4-51.2 70.4-28.8 0-51.2-28.8-51.2-70.4zM851.2 153.6c-3.2 22.4-19.2 35.2-44.8 35.2-32 0-51.2-25.6-51.2-70.4 0-48 19.2-73.6 51.2-73.6 25.6 0 41.6 12.8 44.8 41.6l70.4-3.2c-9.6-60.8-54.4-96-118.4-96-73.6 0-121.6 51.2-121.6 128 0 80 48 131.2 124.8 131.2 64 0 108.8-35.2 112-96h-67.2z" />
|
||||||
|
<glyph unicode="" glyph-name="reload" d="M889.68 793.68c-93.608 102.216-228.154 166.32-377.68 166.32-282.77 0-512-229.23-512-512h96c0 229.75 186.25 416 416 416 123.020 0 233.542-53.418 309.696-138.306l-149.696-149.694h352v352l-134.32-134.32zM928 448c0-229.75-186.25-416-416-416-123.020 0-233.542 53.418-309.694 138.306l149.694 149.694h-352v-352l134.32 134.32c93.608-102.216 228.154-166.32 377.68-166.32 282.77 0 512 229.23 512 512h-96z" />
|
||||||
|
<glyph unicode="" glyph-name="translate" d="M553.6 304l-118.4 118.4c80 89.6 137.6 195.2 172.8 304h137.6v92.8h-326.4v92.8h-92.8v-92.8h-326.4v-92.8h518.4c-32-89.6-80-176-147.2-249.6-44.8 48-80 99.2-108.8 156.8h-92.8c35.2-76.8 80-147.2 137.6-211.2l-236.8-233.6 67.2-67.2 233.6 233.6 144-144c3.2 0 38.4 92.8 38.4 92.8zM816 540.8h-92.8l-208-560h92.8l51.2 140.8h220.8l51.2-140.8h92.8l-208 560zM691.2 214.4l76.8 201.6 76.8-201.6h-153.6z" />
|
||||||
|
<glyph unicode="" glyph-name="drag" d="M576 896h128v-128h-128v128zM576 640h128v-128h-128v128zM320 640h128v-128h-128v128zM576 384h128v-128h-128v128zM320 384h128v-128h-128v128zM320 128h128v-128h-128v128zM576 128h128v-128h-128v128zM320 896h128v-128h-128v128z" />
|
||||||
|
<glyph unicode="" glyph-name="home" d="M1024 369.556l-512 397.426-512-397.428v162.038l512 397.426 512-397.428zM896 384v-384h-256v256h-256v-256h-256v384l384 288z" />
|
||||||
|
<glyph unicode="" glyph-name="books" d="M576.234 670.73l242.712 81.432 203.584-606.784-242.712-81.432zM0 64h256v704h-256v-704zM64 640h128v-64h-128v64zM320 64h256v704h-256v-704zM384 640h128v-64h-128v64z" />
|
||||||
|
<glyph unicode="" glyph-name="upload" d="M839.432 760.57c27.492-27.492 50.554-78.672 55.552-120.57h-318.984v318.984c41.898-4.998 93.076-28.060 120.568-55.552l142.864-142.862zM512 576v384h-368c-44 0-80-36-80-80v-864c0-44 36-80 80-80h672c44 0 80 36 80 80v560h-384zM576 192v-192h-192v192h-160l256 256 256-256h-160z" />
|
||||||
|
<glyph unicode="" glyph-name="editimage" d="M768 416v-352h-640v640h352l128 128h-512c-52.8 0-96-43.2-96-96v-704c0-52.8 43.2-96 96-96h704c52.798 0 96 43.2 96 96v512l-128-128zM864 960l-608-608v-160h160l608 608c0 96-64 160-160 160zM416 320l-48 48 480 480 48-48-480-480z" />
|
||||||
|
<glyph unicode="" glyph-name="bubble" d="M928 896h-832c-52.8 0-96-43.2-96-96v-512c0-52.8 43.2-96 96-96h160v-256l307.2 256h364.8c52.8 0 96 43.2 96 96v512c0 52.8-43.2 96-96 96zM896 320h-379.142l-196.858-174.714v174.714h-192v448h768v-448z" />
|
||||||
|
<glyph unicode="" glyph-name="user" d="M622.826 257.264c-22.11 3.518-22.614 64.314-22.614 64.314s64.968 64.316 79.128 150.802c38.090 0 61.618 91.946 23.522 124.296 1.59 34.054 48.96 267.324-190.862 267.324s-192.45-233.27-190.864-267.324c-38.094-32.35-14.57-124.296 23.522-124.296 14.158-86.486 79.128-150.802 79.128-150.802s-0.504-60.796-22.614-64.314c-71.22-11.332-337.172-128.634-337.172-257.264h896c0 128.63-265.952 245.932-337.174 257.264z" />
|
||||||
|
<glyph unicode="" glyph-name="lock" d="M592 512h-16v192c0 105.87-86.13 192-192 192h-128c-105.87 0-192-86.13-192-192v-192h-16c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h544c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48zM192 704c0 35.29 28.71 64 64 64h128c35.29 0 64-28.71 64-64v-192h-256v192z" />
|
||||||
|
<glyph unicode="" glyph-name="unlock" d="M768 896c105.87 0 192-86.13 192-192v-192h-128v192c0 35.29-28.71 64-64 64h-128c-35.29 0-64-28.71-64-64v-192h16c26.4 0 48-21.6 48-48v-480c0-26.4-21.6-48-48-48h-544c-26.4 0-48 21.6-48 48v480c0 26.4 21.6 48 48 48h400v192c0 105.87 86.13 192 192 192h128z" />
|
||||||
|
<glyph unicode="" glyph-name="settings" d="M448 832v16c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576zM256 704v128h128v-128h-128zM832 528c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-576v-128h576v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h192v128h-192v16zM640 384v128h128v-128h-128zM448 208c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576v16zM256 64v128h128v-128h-128z" />
|
||||||
|
<glyph unicode="" glyph-name="remove2" d="M192-64h640l64 704h-768zM640 832v128h-256v-128h-320v-192l64 64h768l64-64v192h-320zM576 832h-128v64h128v-64z" />
|
||||||
|
<glyph unicode="" glyph-name="menu" d="M384 896h256v-256h-256zM384 576h256v-256h-256zM384 256h256v-256h-256z" />
|
||||||
|
<glyph unicode="" glyph-name="warning" d="M1009.956 44.24l-437.074 871.112c-16.742 29.766-38.812 44.648-60.882 44.648s-44.14-14.882-60.884-44.648l-437.074-871.112c-33.486-59.532-5-108.24 63.304-108.24h869.308c68.302 0 96.792 48.708 63.302 108.24zM512 64c-35.346 0-64 28.654-64 64 0 35.348 28.654 64 64 64 35.348 0 64-28.652 64-64 0-35.346-28.652-64-64-64zM556 256h-88l-20 256c0 35.346 28.654 64 64 64s64-28.654 64-64l-20-256z" />
|
||||||
|
<glyph unicode="" glyph-name="question" d="M448 256h128v-128h-128zM704 704c35.346 0 64-28.654 64-64v-192l-192-128h-128v64l192 128v64h-320v128h384zM512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512z" />
|
||||||
|
<glyph unicode="" glyph-name="pluscircle" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384s-171.922-384-384-384zM768 384h-192v-192h-128v192h-192v128h192v192h128v-192h192z" />
|
||||||
|
<glyph unicode="" glyph-name="info" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM448 768h128v-128h-128v128zM640 128h-256v64h64v256h-64v64h192v-320h64v-64z" />
|
||||||
|
<glyph unicode="" glyph-name="notice" d="M1024 224l-288 736h-448l-288-288v-448l288-288h448l288 288v448l-288 288zM576 128h-128v128h128v-128zM576 384h-128v384h128v-384z" />
|
||||||
|
<glyph unicode="" glyph-name="drop" d="M864.626 486.838c-65.754 183.44-205.11 348.15-352.626 473.162-147.516-125.012-286.87-289.722-352.626-473.162-40.664-113.436-44.682-236.562 12.584-345.4 65.846-125.14 198.632-205.438 340.042-205.438s274.196 80.298 340.040 205.44c57.27 108.838 53.25 231.962 12.586 345.398zM738.764 201.044c-43.802-83.252-132.812-137.044-226.764-137.044-55.12 0-108.524 18.536-152.112 50.652 13.242-1.724 26.632-2.652 40.112-2.652 117.426 0 228.668 67.214 283.402 171.242 44.878 85.292 40.978 173.848 23.882 244.338 14.558-28.15 26.906-56.198 36.848-83.932 22.606-63.062 40.024-156.34-5.368-242.604z" />
|
||||||
|
<glyph unicode="" glyph-name="minus" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
|
||||||
|
<glyph unicode="" glyph-name="plus" d="M992 576h-352v352c0 17.672-14.328 32-32 32h-192c-17.672 0-32-14.328-32-32v-352h-352c-17.672 0-32-14.328-32-32v-192c0-17.672 14.328-32 32-32h352v-352c0-17.672 14.328-32 32-32h192c17.672 0 32 14.328 32 32v352h352c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowup" d="M0 320l192-192 320 320 320-320 192 192-511.998 512z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowright" d="M384 960l-192-192 320-320-320-320 192-192 512 512z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowdown" d="M1024 576l-192 192-320-320-320 320-192-192 512-511.998z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowup2" d="M768 320l-256 256-256-256z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowdown2" d="M256 576l256-256 256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="menu2" d="M256 704l256-256 256 256zM255.996 384.004l256-256 256 256z" />
|
||||||
|
<glyph unicode="" glyph-name="newtab" d="M704 384l128 128v-512h-768v768h512l-128-128h-256v-512h512zM960 896v-352l-130.744 130.744-354.746-354.744h-90.51v90.512l354.744 354.744-130.744 130.744z" />
|
||||||
|
<glyph unicode="" glyph-name="rotateleft" d="M607.998 831.986c-212.070 0-383.986-171.916-383.986-383.986h-191.994l246.848-246.848 246.848 246.848h-191.994c0 151.478 122.798 274.276 274.276 274.276 151.48 0 274.276-122.798 274.276-274.276 0-151.48-122.796-274.276-274.276-274.276v-109.71c212.070 0 383.986 171.916 383.986 383.986s-171.916 383.986-383.986 383.986z" />
|
||||||
|
<glyph unicode="" glyph-name="rotateright" d="M416.002 831.986c212.070 0 383.986-171.916 383.986-383.986h191.994l-246.848-246.848-246.848 246.848h191.994c0 151.478-122.798 274.276-274.276 274.276-151.48 0-274.276-122.798-274.276-274.276 0-151.48 122.796-274.276 274.276-274.276v-109.71c-212.070 0-383.986 171.916-383.986 383.986s171.916 383.986 383.986 383.986z" />
|
||||||
|
<glyph unicode="" glyph-name="flipv" d="M0 576h1024v384zM1024 0v384h-1024z" />
|
||||||
|
<glyph unicode="" glyph-name="fliph" d="M576 960v-1024h384zM0-64h384v1024z" />
|
||||||
|
<glyph unicode="" glyph-name="zoomin" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM448 768h-128v-128h-128v-128h128v-128h128v128h128v128h-128z" />
|
||||||
|
<glyph unicode="" glyph-name="zoomout" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM192 640h384v-128h-384z" />
|
||||||
|
<glyph unicode="" glyph-name="sharpen" d="M768 832h-512l-256-256 512-576 512 576-256 256zM512 181.334v2.666h-2.37l-14.222 16h16.592v16h-30.814l-14.222 16h45.036v16h-59.258l-14.222 16h73.48v16h-87.704l-14.222 16h101.926v16h-116.148l-14.222 16h130.37v16h-144.592l-14.222 16h158.814v16h-173.038l-14.222 16h187.26v16h-201.482l-14.222 16h215.704v16h-229.926l-14.222 16h244.148v16h-258.372l-14.222 16h272.594v16h-286.816l-14.222 16h301.038v16h-315.26l-14.222 16h329.482v16h-343.706l-7.344 8.262 139.072 139.072h211.978v-3.334h215.314l16-16h-231.314v-16h247.314l16-16h-263.314v-16h279.314l16-16h-295.314v-16h311.314l16-16h-327.314v-16h343.312l7.738-7.738-351.050-394.928z" />
|
||||||
|
<glyph unicode="" glyph-name="options" d="M64 768h896v-192h-896zM64 512h896v-192h-896zM64 256h896v-192h-896z" />
|
||||||
|
<glyph unicode="" glyph-name="sun" d="M512 128c35.346 0 64-28.654 64-64v-64c0-35.346-28.654-64-64-64s-64 28.654-64 64v64c0 35.346 28.654 64 64 64zM512 768c-35.346 0-64 28.654-64 64v64c0 35.346 28.654 64 64 64s64-28.654 64-64v-64c0-35.346-28.654-64-64-64zM960 512c35.346 0 64-28.654 64-64s-28.654-64-64-64h-64c-35.348 0-64 28.654-64 64s28.652 64 64 64h64zM192 448c0-35.346-28.654-64-64-64h-64c-35.346 0-64 28.654-64 64s28.654 64 64 64h64c35.346 0 64-28.654 64-64zM828.784 221.726l45.256-45.258c24.992-24.99 24.992-65.516 0-90.508-24.994-24.992-65.518-24.992-90.51 0l-45.256 45.256c-24.992 24.99-24.992 65.516 0 90.51 24.994 24.992 65.518 24.992 90.51 0zM195.216 674.274l-45.256 45.256c-24.994 24.994-24.994 65.516 0 90.51s65.516 24.994 90.51 0l45.256-45.256c24.994-24.994 24.994-65.516 0-90.51s-65.516-24.994-90.51 0zM828.784 674.274c-24.992-24.992-65.516-24.992-90.51 0-24.992 24.994-24.992 65.516 0 90.51l45.256 45.254c24.992 24.994 65.516 24.994 90.51 0 24.992-24.994 24.992-65.516 0-90.51l-45.256-45.254zM195.216 221.726c24.992 24.992 65.518 24.992 90.508 0 24.994-24.994 24.994-65.52 0-90.51l-45.254-45.256c-24.994-24.992-65.516-24.992-90.51 0s-24.994 65.518 0 90.508l45.256 45.258zM512 704c-141.384 0-256-114.616-256-256 0-141.382 114.616-256 256-256 141.382 0 256 114.618 256 256 0 141.384-114.616 256-256 256zM512 288c-88.366 0-160 71.634-160 160s71.634 160 160 160 160-71.634 160-160-71.634-160-160-160z" />
|
||||||
|
<glyph unicode="" glyph-name="moon" d="M715.812 895.52c-60.25 34.784-124.618 55.904-189.572 64.48 122.936-160.082 144.768-384.762 37.574-570.42-107.2-185.67-312.688-279.112-512.788-252.68 39.898-51.958 90.376-97.146 150.628-131.934 245.908-141.974 560.37-57.72 702.344 188.198 141.988 245.924 57.732 560.372-188.186 702.356z" />
|
||||||
|
<glyph unicode="" glyph-name="contrast" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM128 448c0 212.078 171.922 384 384 384v-768c-212.078 0-384 171.922-384 384z" />
|
||||||
|
<glyph unicode="" glyph-name="remove22" d="M893.254 738.746l-90.508 90.508-290.746-290.744-290.746 290.744-90.508-90.506 290.746-290.748-290.746-290.746 90.508-90.508 290.746 290.746 290.746-290.746 90.508 90.51-290.744 290.744z" />
|
||||||
|
<glyph unicode="" glyph-name="arrowleft" d="M672-64l192 192-320 320 320 320-192 192-512-512z" />
|
||||||
|
<glyph unicode="" glyph-name="resize2" d="M0 896v-384c0-35.346 28.654-64 64-64s64 28.654 64 64v229.488l677.488-677.488h-229.488c-35.346 0-64-28.652-64-64 0-35.346 28.654-64 64-64h384c35.346 0 64 28.654 64 64v384c0 35.348-28.654 64-64 64s-64-28.652-64-64v-229.488l-677.488 677.488h229.488c35.346 0 64 28.654 64 64s-28.652 64-64 64h-384c-35.346 0-64-28.654-64-64z" />
|
||||||
|
<glyph unicode="" glyph-name="crop" d="M832 704l192 192-64 64-192-192h-448v192h-128v-192h-192v-128h192v-512h512v-192h128v192h192v128h-192v448zM320 640h320l-320-320v320zM384 256l320 320v-320h-320z" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 45 KiB |