Merge remote-tracking branch 'remotes/origin/develop' into 1560-non-federating-instances-routes-restrictions
This commit is contained in:
commit
972889550d
39
CHANGELOG.md
39
CHANGELOG.md
@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [2.0.0] - 2019-03-08
|
||||||
### Security
|
### Security
|
||||||
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
|
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
|
||||||
|
|
||||||
@ -150,6 +150,43 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Mastodon API: Marking a conversation as read (`POST /api/v1/conversations/:id/read`) now no longer brings it to the top in the user's direct conversation list
|
- Mastodon API: Marking a conversation as read (`POST /api/v1/conversations/:id/read`) now no longer brings it to the top in the user's direct conversation list
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## [1.1.9] - 2020-02-10
|
||||||
|
### Fixed
|
||||||
|
- OTP: Inability to set the upload limit (again)
|
||||||
|
- Not being able to pin polls
|
||||||
|
- Streaming API: incorrect handling of reblog mutes
|
||||||
|
- Rejecting the user when field length limit is exceeded
|
||||||
|
- OpenGraph provider: html entities in descriptions
|
||||||
|
|
||||||
|
## [1.1.8] - 2020-01-10
|
||||||
|
### Fixed
|
||||||
|
- Captcha generation issues
|
||||||
|
- Returned Kocaptcha endpoint to configuration
|
||||||
|
- Captcha validity is now 5 minutes
|
||||||
|
|
||||||
|
## [1.1.7] - 2019-12-13
|
||||||
|
### Fixed
|
||||||
|
- OTP: Inability to set the upload limit
|
||||||
|
- OTP: Inability to override node name/distribution type to run 2 Pleroma instances on the same machine
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Integrated captcha provider
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Captcha enabled by default
|
||||||
|
- Default Captcha provider changed from `Pleroma.Captcha.Kocaptcha` to `Pleroma.Captcha.Native`
|
||||||
|
- Better `Cache-Control` header for static content
|
||||||
|
|
||||||
|
### Bundled Pleroma-FE Changes
|
||||||
|
#### Added
|
||||||
|
- Icons in the navigation panel
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
- Improved support unauthenticated view of private instances
|
||||||
|
|
||||||
|
#### Removed
|
||||||
|
- Whitespace hack on empty post content
|
||||||
|
|
||||||
## [1.1.6] - 2019-11-19
|
## [1.1.6] - 2019-11-19
|
||||||
### Fixed
|
### Fixed
|
||||||
- Not being able to log into to third party apps when the browser is logged into mastofe
|
- Not being able to log into to third party apps when the browser is logged into mastofe
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
||||||
7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
|
7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
|
||||||
8. Restart the Pleroma service.
|
8. Restart the Pleroma service.
|
||||||
9. After you've restarted Pleroma, you will notice that postgres will take up more cpu resources than usual. A lot in fact. To fix this you must do a VACUUM ANLAYZE. This can also be done while the instance is still running like so:
|
9. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries.
|
||||||
$ sudo -u postgres psql pleroma_database_name
|
|
||||||
pleroma=# VACUUM ANALYZE;
|
|
||||||
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
||||||
|
|
||||||
## Remove
|
## Remove
|
||||||
|
@ -12,6 +12,19 @@ defmodule Mix.Pleroma do
|
|||||||
end
|
end
|
||||||
|
|
||||||
{:ok, _} = Application.ensure_all_started(:pleroma)
|
{:ok, _} = Application.ensure_all_started(:pleroma)
|
||||||
|
|
||||||
|
if Pleroma.Config.get(:env) not in [:test, :benchmark] do
|
||||||
|
pleroma_rebooted?()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp pleroma_rebooted? do
|
||||||
|
if Restarter.Pleroma.rebooted?() do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
Process.sleep(10)
|
||||||
|
pleroma_rebooted?()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_pleroma do
|
def load_pleroma do
|
||||||
|
@ -42,7 +42,8 @@ defmodule Pleroma.Config.TransferTask do
|
|||||||
|
|
||||||
@spec load_and_update_env([ConfigDB.t()]) :: :ok | false
|
@spec load_and_update_env([ConfigDB.t()]) :: :ok | false
|
||||||
def load_and_update_env(deleted \\ [], restart_pleroma? \\ true) do
|
def load_and_update_env(deleted \\ [], restart_pleroma? \\ true) do
|
||||||
with true <- Pleroma.Config.get(:configurable_from_database),
|
with {:configurable, true} <-
|
||||||
|
{:configurable, Pleroma.Config.get(:configurable_from_database)},
|
||||||
true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
|
true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
|
||||||
started_applications <- Application.started_applications() do
|
started_applications <- Application.started_applications() do
|
||||||
# We need to restart applications for loaded settings take effect
|
# We need to restart applications for loaded settings take effect
|
||||||
@ -65,12 +66,15 @@ defmodule Pleroma.Config.TransferTask do
|
|||||||
if :pleroma in applications do
|
if :pleroma in applications do
|
||||||
List.delete(applications, :pleroma) ++ [:pleroma]
|
List.delete(applications, :pleroma) ++ [:pleroma]
|
||||||
else
|
else
|
||||||
|
Restarter.Pleroma.rebooted()
|
||||||
applications
|
applications
|
||||||
end
|
end
|
||||||
|
|
||||||
Enum.each(applications, &restart(started_applications, &1, Pleroma.Config.get(:env)))
|
Enum.each(applications, &restart(started_applications, &1, Pleroma.Config.get(:env)))
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
|
else
|
||||||
|
{:configurable, false} -> Restarter.Pleroma.rebooted()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -392,24 +392,6 @@ defmodule Pleroma.ModerationLog do
|
|||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "activate",
|
"action" => "activate",
|
||||||
"subject" => user
|
|
||||||
}
|
|
||||||
})
|
|
||||||
when is_map(user) do
|
|
||||||
get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "activate",
|
|
||||||
"subject" => [user]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "activate",
|
|
||||||
"subject" => users
|
"subject" => users
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
@ -421,24 +403,6 @@ defmodule Pleroma.ModerationLog do
|
|||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "deactivate",
|
"action" => "deactivate",
|
||||||
"subject" => user
|
|
||||||
}
|
|
||||||
})
|
|
||||||
when is_map(user) do
|
|
||||||
get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "deactivate",
|
|
||||||
"subject" => [user]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "deactivate",
|
|
||||||
"subject" => users
|
"subject" => users
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
@ -478,26 +442,6 @@ defmodule Pleroma.ModerationLog do
|
|||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "grant",
|
"action" => "grant",
|
||||||
"subject" => user,
|
|
||||||
"permission" => permission
|
|
||||||
}
|
|
||||||
})
|
|
||||||
when is_map(user) do
|
|
||||||
get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "grant",
|
|
||||||
"subject" => [user],
|
|
||||||
"permission" => permission
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "grant",
|
|
||||||
"subject" => users,
|
"subject" => users,
|
||||||
"permission" => permission
|
"permission" => permission
|
||||||
}
|
}
|
||||||
@ -510,26 +454,6 @@ defmodule Pleroma.ModerationLog do
|
|||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "revoke",
|
"action" => "revoke",
|
||||||
"subject" => user,
|
|
||||||
"permission" => permission
|
|
||||||
}
|
|
||||||
})
|
|
||||||
when is_map(user) do
|
|
||||||
get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "revoke",
|
|
||||||
"subject" => [user],
|
|
||||||
"permission" => permission
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
|
||||||
data: %{
|
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
|
||||||
"action" => "revoke",
|
|
||||||
"subject" => users,
|
"subject" => users,
|
||||||
"permission" => permission
|
"permission" => permission
|
||||||
}
|
}
|
||||||
|
@ -591,7 +591,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||||||
limit = Pleroma.Config.get([:instance, :limit])
|
limit = Pleroma.Config.get([:instance, :limit])
|
||||||
length = String.length(full_payload)
|
length = String.length(full_payload)
|
||||||
|
|
||||||
if length < limit do
|
if length <= limit do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, dgettext("errors", "The status is over the character limit")}
|
{:error, dgettext("errors", "The status is over the character limit")}
|
||||||
|
2
mix.exs
2
mix.exs
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
|||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :pleroma,
|
app: :pleroma,
|
||||||
version: version("1.1.50"),
|
version: version("2.0.50"),
|
||||||
elixir: "~> 1.8",
|
elixir: "~> 1.8",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.FixModerationLogSubjects do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
execute(
|
||||||
|
"update moderation_log set data = safe_jsonb_set(data, '{subject}', safe_jsonb_set('[]'::jsonb, '{0}', data->'subject')) where jsonb_typeof(data->'subject') != 'array' and data->>'action' = ANY('{revoke,grant,activate,deactivate,delete}');"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -1 +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=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.c836e084.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.ae93ea9f.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.30262183.js></script></body></html>
|
<!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=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.c836e084.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.ae93ea9f.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.55df3157.js></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
priv/static/adminfe/static/js/app.55df3157.js
Normal file
2
priv/static/adminfe/static/js/app.55df3157.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/static/js/app.55df3157.js.map
Normal file
1
priv/static/adminfe/static/js/app.55df3157.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.1055039ce3f2fe4dd110.css rel=stylesheet><link href=/static/fontello.1582927362782.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.c5bbd3734647f0cc7eef.js></script><script type=text/javascript src=/static/js/app.128bd8b808a3b5b6da6b.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.1055039ce3f2fe4dd110.css rel=stylesheet><link href=/static/fontello.1583594169021.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.c5bbd3734647f0cc7eef.js></script><script type=text/javascript src=/static/js/app.5c94bdec79a7d0f3cfcb.js></script></body></html>
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/font/fontello.1583594169021.woff2
Normal file
BIN
priv/static/static/font/fontello.1583594169021.woff2
Normal file
Binary file not shown.
136
priv/static/static/fontello.1581007281335.css
vendored
136
priv/static/static/fontello.1581007281335.css
vendored
@ -1,136 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: "Icons";
|
|
||||||
src: url("./font/fontello.1581007281335.eot");
|
|
||||||
src: url("./font/fontello.1581007281335.eot") format("embedded-opentype"),
|
|
||||||
url("./font/fontello.1581007281335.woff2") format("woff2"),
|
|
||||||
url("./font/fontello.1581007281335.woff") format("woff"),
|
|
||||||
url("./font/fontello.1581007281335.ttf") format("truetype"),
|
|
||||||
url("./font/fontello.1581007281335.svg") format("svg");
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class^="icon-"]::before,
|
|
||||||
[class*=" icon-"]::before {
|
|
||||||
font-family: "Icons";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
speak: none;
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: inherit;
|
|
||||||
width: 1em;
|
|
||||||
margin-right: .2em;
|
|
||||||
text-align: center;
|
|
||||||
font-variant: normal;
|
|
||||||
text-transform: none;
|
|
||||||
line-height: 1em;
|
|
||||||
margin-left: .2em;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-spin4::before { content: "\e834"; }
|
|
||||||
|
|
||||||
.icon-cancel::before { content: "\e800"; }
|
|
||||||
|
|
||||||
.icon-upload::before { content: "\e801"; }
|
|
||||||
|
|
||||||
.icon-spin3::before { content: "\e832"; }
|
|
||||||
|
|
||||||
.icon-reply::before { content: "\f112"; }
|
|
||||||
|
|
||||||
.icon-star::before { content: "\e802"; }
|
|
||||||
|
|
||||||
.icon-star-empty::before { content: "\e803"; }
|
|
||||||
|
|
||||||
.icon-retweet::before { content: "\e804"; }
|
|
||||||
|
|
||||||
.icon-eye-off::before { content: "\e805"; }
|
|
||||||
|
|
||||||
.icon-binoculars::before { content: "\f1e5"; }
|
|
||||||
|
|
||||||
.icon-cog::before { content: "\e807"; }
|
|
||||||
|
|
||||||
.icon-user-plus::before { content: "\f234"; }
|
|
||||||
|
|
||||||
.icon-menu::before { content: "\f0c9"; }
|
|
||||||
|
|
||||||
.icon-logout::before { content: "\e808"; }
|
|
||||||
|
|
||||||
.icon-down-open::before { content: "\e809"; }
|
|
||||||
|
|
||||||
.icon-attach::before { content: "\e80a"; }
|
|
||||||
|
|
||||||
.icon-link-ext::before { content: "\f08e"; }
|
|
||||||
|
|
||||||
.icon-link-ext-alt::before { content: "\f08f"; }
|
|
||||||
|
|
||||||
.icon-picture::before { content: "\e80b"; }
|
|
||||||
|
|
||||||
.icon-video::before { content: "\e80c"; }
|
|
||||||
|
|
||||||
.icon-right-open::before { content: "\e80d"; }
|
|
||||||
|
|
||||||
.icon-left-open::before { content: "\e80e"; }
|
|
||||||
|
|
||||||
.icon-up-open::before { content: "\e80f"; }
|
|
||||||
|
|
||||||
.icon-comment-empty::before { content: "\f0e5"; }
|
|
||||||
|
|
||||||
.icon-mail-alt::before { content: "\f0e0"; }
|
|
||||||
|
|
||||||
.icon-lock::before { content: "\e811"; }
|
|
||||||
|
|
||||||
.icon-lock-open-alt::before { content: "\f13e"; }
|
|
||||||
|
|
||||||
.icon-globe::before { content: "\e812"; }
|
|
||||||
|
|
||||||
.icon-brush::before { content: "\e813"; }
|
|
||||||
|
|
||||||
.icon-search::before { content: "\e806"; }
|
|
||||||
|
|
||||||
.icon-adjust::before { content: "\e816"; }
|
|
||||||
|
|
||||||
.icon-thumbs-up-alt::before { content: "\f164"; }
|
|
||||||
|
|
||||||
.icon-attention::before { content: "\e814"; }
|
|
||||||
|
|
||||||
.icon-plus-squared::before { content: "\f0fe"; }
|
|
||||||
|
|
||||||
.icon-plus::before { content: "\e815"; }
|
|
||||||
|
|
||||||
.icon-edit::before { content: "\e817"; }
|
|
||||||
|
|
||||||
.icon-play-circled::before { content: "\f144"; }
|
|
||||||
|
|
||||||
.icon-pencil::before { content: "\e818"; }
|
|
||||||
|
|
||||||
.icon-chart-bar::before { content: "\e81b"; }
|
|
||||||
|
|
||||||
.icon-smile::before { content: "\f118"; }
|
|
||||||
|
|
||||||
.icon-bell-alt::before { content: "\f0f3"; }
|
|
||||||
|
|
||||||
.icon-wrench::before { content: "\e81a"; }
|
|
||||||
|
|
||||||
.icon-pin::before { content: "\e819"; }
|
|
||||||
|
|
||||||
.icon-ellipsis::before { content: "\f141"; }
|
|
||||||
|
|
||||||
.icon-bell-ringing-o::before { content: "\e810"; }
|
|
||||||
|
|
||||||
.icon-zoom-in::before { content: "\e81c"; }
|
|
||||||
|
|
||||||
.icon-gauge::before { content: "\f0e4"; }
|
|
||||||
|
|
||||||
.icon-users::before { content: "\e81d"; }
|
|
||||||
|
|
||||||
.icon-info-circled::before { content: "\e81f"; }
|
|
||||||
|
|
||||||
.icon-home-2::before { content: "\e821"; }
|
|
||||||
|
|
||||||
.icon-chat::before { content: "\e81e"; }
|
|
||||||
|
|
||||||
.icon-login::before { content: "\e820"; }
|
|
||||||
|
|
||||||
.icon-arrow-curved::before { content: "\e822"; }
|
|
138
priv/static/static/fontello.1582927362782.css
vendored
138
priv/static/static/fontello.1582927362782.css
vendored
@ -1,138 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: "Icons";
|
|
||||||
src: url("./font/fontello.1582927362782.eot");
|
|
||||||
src: url("./font/fontello.1582927362782.eot") format("embedded-opentype"),
|
|
||||||
url("./font/fontello.1582927362782.woff2") format("woff2"),
|
|
||||||
url("./font/fontello.1582927362782.woff") format("woff"),
|
|
||||||
url("./font/fontello.1582927362782.ttf") format("truetype"),
|
|
||||||
url("./font/fontello.1582927362782.svg") format("svg");
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class^="icon-"]::before,
|
|
||||||
[class*=" icon-"]::before {
|
|
||||||
font-family: "Icons";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
speak: none;
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: inherit;
|
|
||||||
width: 1em;
|
|
||||||
margin-right: .2em;
|
|
||||||
text-align: center;
|
|
||||||
font-variant: normal;
|
|
||||||
text-transform: none;
|
|
||||||
line-height: 1em;
|
|
||||||
margin-left: .2em;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-spin4::before { content: "\e834"; }
|
|
||||||
|
|
||||||
.icon-cancel::before { content: "\e800"; }
|
|
||||||
|
|
||||||
.icon-upload::before { content: "\e801"; }
|
|
||||||
|
|
||||||
.icon-spin3::before { content: "\e832"; }
|
|
||||||
|
|
||||||
.icon-reply::before { content: "\f112"; }
|
|
||||||
|
|
||||||
.icon-star::before { content: "\e802"; }
|
|
||||||
|
|
||||||
.icon-star-empty::before { content: "\e803"; }
|
|
||||||
|
|
||||||
.icon-retweet::before { content: "\e804"; }
|
|
||||||
|
|
||||||
.icon-eye-off::before { content: "\e805"; }
|
|
||||||
|
|
||||||
.icon-binoculars::before { content: "\f1e5"; }
|
|
||||||
|
|
||||||
.icon-cog::before { content: "\e807"; }
|
|
||||||
|
|
||||||
.icon-user-plus::before { content: "\f234"; }
|
|
||||||
|
|
||||||
.icon-menu::before { content: "\f0c9"; }
|
|
||||||
|
|
||||||
.icon-logout::before { content: "\e808"; }
|
|
||||||
|
|
||||||
.icon-down-open::before { content: "\e809"; }
|
|
||||||
|
|
||||||
.icon-attach::before { content: "\e80a"; }
|
|
||||||
|
|
||||||
.icon-link-ext::before { content: "\f08e"; }
|
|
||||||
|
|
||||||
.icon-link-ext-alt::before { content: "\f08f"; }
|
|
||||||
|
|
||||||
.icon-picture::before { content: "\e80b"; }
|
|
||||||
|
|
||||||
.icon-video::before { content: "\e80c"; }
|
|
||||||
|
|
||||||
.icon-right-open::before { content: "\e80d"; }
|
|
||||||
|
|
||||||
.icon-left-open::before { content: "\e80e"; }
|
|
||||||
|
|
||||||
.icon-up-open::before { content: "\e80f"; }
|
|
||||||
|
|
||||||
.icon-comment-empty::before { content: "\f0e5"; }
|
|
||||||
|
|
||||||
.icon-mail-alt::before { content: "\f0e0"; }
|
|
||||||
|
|
||||||
.icon-lock::before { content: "\e811"; }
|
|
||||||
|
|
||||||
.icon-lock-open-alt::before { content: "\f13e"; }
|
|
||||||
|
|
||||||
.icon-globe::before { content: "\e812"; }
|
|
||||||
|
|
||||||
.icon-brush::before { content: "\e813"; }
|
|
||||||
|
|
||||||
.icon-search::before { content: "\e806"; }
|
|
||||||
|
|
||||||
.icon-adjust::before { content: "\e816"; }
|
|
||||||
|
|
||||||
.icon-thumbs-up-alt::before { content: "\f164"; }
|
|
||||||
|
|
||||||
.icon-attention::before { content: "\e814"; }
|
|
||||||
|
|
||||||
.icon-plus-squared::before { content: "\f0fe"; }
|
|
||||||
|
|
||||||
.icon-plus::before { content: "\e815"; }
|
|
||||||
|
|
||||||
.icon-edit::before { content: "\e817"; }
|
|
||||||
|
|
||||||
.icon-play-circled::before { content: "\f144"; }
|
|
||||||
|
|
||||||
.icon-pencil::before { content: "\e818"; }
|
|
||||||
|
|
||||||
.icon-chart-bar::before { content: "\e81b"; }
|
|
||||||
|
|
||||||
.icon-smile::before { content: "\f118"; }
|
|
||||||
|
|
||||||
.icon-bell-alt::before { content: "\f0f3"; }
|
|
||||||
|
|
||||||
.icon-wrench::before { content: "\e81a"; }
|
|
||||||
|
|
||||||
.icon-pin::before { content: "\e819"; }
|
|
||||||
|
|
||||||
.icon-ellipsis::before { content: "\f141"; }
|
|
||||||
|
|
||||||
.icon-bell-ringing-o::before { content: "\e810"; }
|
|
||||||
|
|
||||||
.icon-zoom-in::before { content: "\e81c"; }
|
|
||||||
|
|
||||||
.icon-gauge::before { content: "\f0e4"; }
|
|
||||||
|
|
||||||
.icon-users::before { content: "\e81d"; }
|
|
||||||
|
|
||||||
.icon-info-circled::before { content: "\e81f"; }
|
|
||||||
|
|
||||||
.icon-home-2::before { content: "\e821"; }
|
|
||||||
|
|
||||||
.icon-chat::before { content: "\e81e"; }
|
|
||||||
|
|
||||||
.icon-login::before { content: "\e820"; }
|
|
||||||
|
|
||||||
.icon-arrow-curved::before { content: "\e822"; }
|
|
||||||
|
|
||||||
.icon-link::before { content: "\e823"; }
|
|
@ -1,11 +1,11 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Icons";
|
font-family: "Icons";
|
||||||
src: url("./font/fontello.1581425930672.eot");
|
src: url("./font/fontello.1583594169021.eot");
|
||||||
src: url("./font/fontello.1581425930672.eot") format("embedded-opentype"),
|
src: url("./font/fontello.1583594169021.eot") format("embedded-opentype"),
|
||||||
url("./font/fontello.1581425930672.woff2") format("woff2"),
|
url("./font/fontello.1583594169021.woff2") format("woff2"),
|
||||||
url("./font/fontello.1581425930672.woff") format("woff"),
|
url("./font/fontello.1583594169021.woff") format("woff"),
|
||||||
url("./font/fontello.1581425930672.ttf") format("truetype"),
|
url("./font/fontello.1583594169021.ttf") format("truetype"),
|
||||||
url("./font/fontello.1581425930672.svg") format("svg");
|
url("./font/fontello.1583594169021.svg") format("svg");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
priv/static/static/js/app.5c94bdec79a7d0f3cfcb.js
Normal file
2
priv/static/static/js/app.5c94bdec79a7d0f3cfcb.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.5c94bdec79a7d0f3cfcb.js.map
Normal file
1
priv/static/static/js/app.5c94bdec79a7d0f3cfcb.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,176 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #282c37;
|
|
||||||
font-family: sans-serif;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
margin: 50px auto;
|
|
||||||
max-width: 960px;
|
|
||||||
padding: 40px;
|
|
||||||
background-color: #313543;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
margin: 50px auto;
|
|
||||||
max-width: 960px;
|
|
||||||
padding: 40px;
|
|
||||||
background-color: #313543;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity {
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1em;
|
|
||||||
padding-bottom: 2em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar img {
|
|
||||||
float: left;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content img, video, audio {
|
|
||||||
padding: 1em;
|
|
||||||
max-width: 800px;
|
|
||||||
max-height: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#selected {
|
|
||||||
background-color: #1b2735;
|
|
||||||
}
|
|
||||||
|
|
||||||
.counts dt, .counts dd {
|
|
||||||
float: left;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-card {
|
|
||||||
min-height: 48px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header a, .h-card a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header a:hover, .h-card a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-name {
|
|
||||||
padding-top: 4px;
|
|
||||||
display: block;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* keep emoji from being hilariously huge */
|
|
||||||
.display-name img {
|
|
||||||
max-height: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-name .nickname {
|
|
||||||
padding-top: 4px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nickname:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pull-right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse {
|
|
||||||
margin: 0;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #9baec8;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
background-color: rgba(0,0,0,.1);
|
|
||||||
color: white;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 2px solid #9baec8;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-bottom: 2px solid #4b8ed8;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
color: white;
|
|
||||||
background-color: #419bdd;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 30px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-danger {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
color: #D8000C;
|
|
||||||
background-color: #FFD2D2;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-info {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
color: #00529B;
|
|
||||||
background-color: #BDE5F8;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
var serviceWorkerOption = {"assets":["/static/fontello.1582927362782.css","/static/font/fontello.1582927362782.eot","/static/font/fontello.1582927362782.svg","/static/font/fontello.1582927362782.ttf","/static/font/fontello.1582927362782.woff","/static/font/fontello.1582927362782.woff2","/static/img/nsfw.74818f9.png","/static/css/app.1055039ce3f2fe4dd110.css","/static/js/app.128bd8b808a3b5b6da6b.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.c5bbd3734647f0cc7eef.js","/static/js/2.f158cbd2b8770e467dfe.js"]};
|
var serviceWorkerOption = {"assets":["/static/fontello.1583594169021.css","/static/font/fontello.1583594169021.eot","/static/font/fontello.1583594169021.svg","/static/font/fontello.1583594169021.ttf","/static/font/fontello.1583594169021.woff","/static/font/fontello.1583594169021.woff2","/static/img/nsfw.74818f9.png","/static/css/app.1055039ce3f2fe4dd110.css","/static/js/app.5c94bdec79a7d0f3cfcb.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.c5bbd3734647f0cc7eef.js","/static/js/2.f158cbd2b8770e467dfe.js"]};
|
||||||
|
|
||||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=1)}([function(e,n){
|
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=1)}([function(e,n){
|
||||||
/*!
|
/*!
|
||||||
|
@ -3,11 +3,21 @@ defmodule Restarter.Pleroma do
|
|||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@init_state %{need_reboot: false, rebooted: false, after_boot: false}
|
||||||
|
|
||||||
def start_link(_) do
|
def start_link(_) do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
def init(_), do: {:ok, %{need_reboot?: false}}
|
def init(_), do: {:ok, @init_state}
|
||||||
|
|
||||||
|
def rebooted? do
|
||||||
|
GenServer.call(__MODULE__, :rebooted?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rebooted do
|
||||||
|
GenServer.cast(__MODULE__, :rebooted)
|
||||||
|
end
|
||||||
|
|
||||||
def need_reboot? do
|
def need_reboot? do
|
||||||
GenServer.call(__MODULE__, :need_reboot?)
|
GenServer.call(__MODULE__, :need_reboot?)
|
||||||
@ -29,41 +39,51 @@ defmodule Restarter.Pleroma do
|
|||||||
GenServer.cast(__MODULE__, {:after_boot, env})
|
GenServer.cast(__MODULE__, {:after_boot, env})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_call(:rebooted?, _from, state) do
|
||||||
|
{:reply, state[:rebooted], state}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_call(:need_reboot?, _from, state) do
|
def handle_call(:need_reboot?, _from, state) do
|
||||||
{:reply, state[:need_reboot?], state}
|
{:reply, state[:need_reboot], state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_cast(:rebooted, state) do
|
||||||
|
{:noreply, Map.put(state, :rebooted, true)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_cast(:need_reboot, %{need_reboot: true} = state), do: {:noreply, state}
|
||||||
|
|
||||||
|
def handle_cast(:need_reboot, state) do
|
||||||
|
{:noreply, Map.put(state, :need_reboot, true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(:refresh, _state) do
|
def handle_cast(:refresh, _state) do
|
||||||
{:noreply, %{need_reboot?: false}}
|
{:noreply, @init_state}
|
||||||
end
|
|
||||||
|
|
||||||
def handle_cast(:need_reboot, %{need_reboot?: true} = state), do: {:noreply, state}
|
|
||||||
|
|
||||||
def handle_cast(:need_reboot, state) do
|
|
||||||
{:noreply, Map.put(state, :need_reboot?, true)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast({:restart, :test, _}, state) do
|
def handle_cast({:restart, :test, _}, state) do
|
||||||
Logger.warn("pleroma restarted")
|
Logger.warn("pleroma restarted")
|
||||||
{:noreply, Map.put(state, :need_reboot?, false)}
|
{:noreply, Map.put(state, :need_reboot, false)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast({:restart, _, delay}, state) do
|
def handle_cast({:restart, _, delay}, state) do
|
||||||
Process.sleep(delay)
|
Process.sleep(delay)
|
||||||
do_restart(:pleroma)
|
do_restart(:pleroma)
|
||||||
{:noreply, Map.put(state, :need_reboot?, false)}
|
{:noreply, Map.put(state, :need_reboot, false)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast({:after_boot, _}, %{after_boot: true} = state), do: {:noreply, state}
|
def handle_cast({:after_boot, _}, %{after_boot: true} = state), do: {:noreply, state}
|
||||||
|
|
||||||
def handle_cast({:after_boot, :test}, state) do
|
def handle_cast({:after_boot, :test}, state) do
|
||||||
Logger.warn("pleroma restarted")
|
Logger.warn("pleroma restarted")
|
||||||
{:noreply, Map.put(state, :after_boot, true)}
|
state = %{state | after_boot: true, rebooted: true}
|
||||||
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast({:after_boot, _}, state) do
|
def handle_cast({:after_boot, _}, state) do
|
||||||
do_restart(:pleroma)
|
do_restart(:pleroma)
|
||||||
{:noreply, Map.put(state, :after_boot, true)}
|
state = %{state | after_boot: true, rebooted: true}
|
||||||
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_restart(app) do
|
defp do_restart(app) do
|
||||||
|
@ -202,13 +202,15 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||||||
CommonAPI.post(user, %{"status" => ""})
|
CommonAPI.post(user, %{"status" => ""})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns error when character limit is exceeded" do
|
test "it validates character limits are correctly enforced" do
|
||||||
Pleroma.Config.put([:instance, :limit], 5)
|
Pleroma.Config.put([:instance, :limit], 5)
|
||||||
|
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
assert {:error, "The status is over the character limit"} =
|
assert {:error, "The status is over the character limit"} =
|
||||||
CommonAPI.post(user, %{"status" => "foobar"})
|
CommonAPI.post(user, %{"status" => "foobar"})
|
||||||
|
|
||||||
|
assert {:ok, activity} = CommonAPI.post(user, %{"status" => "12345"})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it can handle activities that expire" do
|
test "it can handle activities that expire" do
|
||||||
|
Loading…
Reference in New Issue
Block a user