Commit e237614e authored by hakabahitoyo's avatar hakabahitoyo

Merge branch 'develop' into deploy/ldap

parents 142eef86 e343e4ff
Pipeline #13309 passed with stages
in 6 minutes and 18 seconds
......@@ -52,8 +52,7 @@ unit-testing:
- mix deps.get
- mix ecto.create
- mix ecto.migrate
- mix test --trace --preload-modules
- mix coveralls
- mix coveralls --trace --preload-modules
unit-testing-rum:
stage: test
......@@ -122,8 +121,7 @@ review_app:
- (ssh -t dokku@pleroma.online -- postgres:create $(echo $CI_ENVIRONMENT_SLUG | sed -e 's/-/_/g')_db) || true
- (ssh -t dokku@pleroma.online -- postgres:link $(echo $CI_ENVIRONMENT_SLUG | sed -e 's/-/_/g')_db "$CI_ENVIRONMENT_SLUG") || true
- (ssh -t dokku@pleroma.online -- certs:add "$CI_ENVIRONMENT_SLUG" /home/dokku/server.crt /home/dokku/server.key) || true
- (git remote add dokku dokku@pleroma.online:$CI_ENVIRONMENT_SLUG) || true
- git push -f dokku $CI_COMMIT_SHA:refs/heads/master
- git push -f dokku@pleroma.online:$CI_ENVIRONMENT_SLUG $CI_COMMIT_SHA:refs/heads/master
stop_review_app:
image: alpine:3.9
......
......@@ -5,10 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [unreleased]
### Added
- Add a generic settings store for frontends / clients to use.
- Explicit addressing option for posting.
- Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
- [MongooseIM](https://github.com/esl/MongooseIM) http authentication support.
- LDAP authentication
- External OAuth provider authentication
- Support for building a release using [`mix release`](https://hexdocs.pm/mix/master/Mix.Tasks.Release.html)
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
- [Prometheus](https://prometheus.io/) metrics
- Support for Mastodon's remote interaction
......@@ -16,13 +19,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mix Tasks: `mix pleroma.database remove_embedded_objects`
- Mix Tasks: `mix pleroma.database update_users_following_followers_counts`
- Mix Tasks: `mix pleroma.user toggle_confirmed`
- Federation: Support for `Question` and `Answer` objects
- Federation: Support for reports
- Configuration: `poll_limits` option
- Configuration: `safe_dm_mentions` option
- Configuration: `link_name` option
- Configuration: `fetch_initial_posts` option
- Configuration: `notify_email` option
- Configuration: Media proxy `whitelist` option
- Configuration: `report_uri` option
- Configuration: `limit_unauthenticated_to_local_content` option
- Pleroma API: User subscriptions
- Pleroma API: Healthcheck endpoint
- Pleroma API: `/api/v1/pleroma/mascot` per-user frontend mascot configuration endpoints
......@@ -37,6 +43,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
- Mastodon API: `POST /api/v1/accounts` (account creation API)
- Mastodon API: [Polls](https://docs.joinmastodon.org/api/rest/polls/)
- ActivityPub C2S: OAuth endpoints
- Metadata: RelMe provider
- OAuth: added support for refresh tokens
......@@ -45,9 +52,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports from specific instances (`mrf_simple`)
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
- MRF: Support for running subchains.
- Configuration: `skip_thread_containment` option
### Changed
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
- Thread containment / test for complete visibility will be skipped by default.
- Enforcement of OAuth scopes
- Add multiple use/time expiring invite token
- Restyled OAuth pages to fit with Pleroma's default theme
......@@ -56,6 +66,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Federation: Expand the audience of delete activities to all recipients of the deleted object
- Federation: Removed `inReplyToStatusId` from objects
- Configuration: Dedupe enabled by default
- Configuration: Default log level in `prod` environment is now set to `warn`
- Configuration: Added `extra_cookie_attrs` for setting non-standard cookie attributes. Defaults to ["SameSite=Lax"] so that remote follows work.
- Timelines: Messages involving people you have blocked will be excluded from the timeline in all cases instead of just repeats.
- Admin API: Move the user related API to `api/pleroma/admin/users`
......@@ -83,6 +94,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Respond with a 404 Not implemented JSON error message when requested API is not implemented
### Fixed
- Follow requests don't get 'stuck' anymore.
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
- Followers counter not being updated when a follower is blocked
- Deactivated users being able to request an access token
......
......@@ -208,6 +208,12 @@
avatar_upload_limit: 2_000_000,
background_upload_limit: 4_000_000,
banner_upload_limit: 4_000_000,
poll_limits: %{
max_options: 20,
max_option_chars: 200,
min_expiration: 0,
max_expiration: 365 * 24 * 60 * 60
},
registrations_open: true,
federating: true,
federation_reachability_timeout_days: 7,
......@@ -237,7 +243,9 @@
max_report_comment_size: 1000,
safe_dm_mentions: false,
healthcheck: false,
remote_post_retention_days: 90
remote_post_retention_days: 90,
skip_thread_containment: true,
limit_unauthenticated_to_local_content: true
config :pleroma, :app_account_creation, enabled: true, max_requests: 25, interval: 1800
......@@ -320,6 +328,8 @@
federated_timeline_removal: [],
replace: []
config :pleroma, :mrf_subchain, match_actor: %{}
config :pleroma, :rich_media, enabled: true
config :pleroma, :media_proxy,
......@@ -453,7 +463,11 @@
config :esshd,
enabled: false
oauth_consumer_strategies = String.split(System.get_env("OAUTH_CONSUMER_STRATEGIES") || "")
oauth_consumer_strategies =
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|> to_string()
|> String.split()
|> Enum.map(&hd(String.split(&1, ":")))
ueberauth_providers =
for strategy <- oauth_consumer_strategies do
......@@ -486,6 +500,8 @@
config :pleroma, :database, rum_enabled: false
config :pleroma, :env, Mix.env()
config :http_signatures,
adapter: Pleroma.Signature
......
......@@ -17,8 +17,10 @@
http: [port: 4000],
protocol: "http"
config :phoenix, serve_endpoints: true
# Do not print debug messages in production
config :logger, level: :info
config :logger, level: :warn
# ## SSL Support
#
......
......@@ -17,6 +17,8 @@
# Print only warnings and errors during test
config :logger, level: :warn
config :pleroma, :auth, oauth_consumer_strategies: []
config :pleroma, Pleroma.Upload, filters: [], link_name: false
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
......@@ -25,7 +27,8 @@
config :pleroma, :instance,
email: "admin@example.com",
notify_email: "noreply@example.com"
notify_email: "noreply@example.com",
skip_thread_containment: false
# Configure your database
config :pleroma, Pleroma.Repo,
......
......@@ -43,6 +43,7 @@ Has these additional fields under the `pleroma` object:
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
- `hide_followers`: boolean, true when the user has follower hiding enabled
- `hide_follows`: boolean, true when the user has follow hiding enabled
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
### Source
......@@ -69,6 +70,7 @@ Additional parameters can be added to the JSON body/Form data:
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.
## PATCH `/api/v1/update_credentials`
......@@ -80,6 +82,15 @@ Additional parameters can be added to the JSON body/Form data:
- `hide_favorites` - if true, user's favorites timeline will be hidden
- `show_role` - if true, user's role (e.g admin, moderator) will be exposed to anyone in the API
- `default_scope` - the scope returned under `privacy` key in Source subentity
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
- `skip_thread_containment` - if true, skip filtering out broken threads
### Pleroma Settings Store
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
The parameter should have a form of `{frontend_name: {...}}`, with `frontend_name` identifying your type of client, e.g. `pleroma_fe`. It will overwrite everything under this property, but will not overwrite other frontend's settings.
This information is returned in the `verify_credentials` endpoint.
## Authentication
......
......@@ -71,6 +71,11 @@ config :pleroma, Pleroma.Emails.Mailer,
* `avatar_upload_limit`: File size limit of user’s profile avatars
* `background_upload_limit`: File size limit of user’s profile backgrounds
* `banner_upload_limit`: File size limit of user’s profile banners
* `poll_limits`: A map with poll limits for **local** polls
* `max_options`: Maximum number of options
* `max_option_chars`: Maximum number of characters per option
* `min_expiration`: Minimum expiration time (in seconds)
* `max_expiration`: Maximum expiration time (in seconds)
* `registrations_open`: Enable registrations for anyone, invitations can be enabled when false.
* `invites_enabled`: Enable user invitations for admins (depends on `registrations_open: false`).
* `account_activation_required`: Require users to confirm their emails before signing in.
......@@ -81,6 +86,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default)
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section)
* `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (see ``:mrf_subchain`` section)
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
......@@ -102,9 +108,11 @@ config :pleroma, Pleroma.Emails.Mailer,
* `welcome_message`: A message that will be send to a newly registered users as a direct message.
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
* `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`)
* `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``.
* `remote_post_retention_days`: the default amount of days to retain remote posts when pruning the database
* `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``.
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `limit_unauthenticated_to_local_content`: Limit unauthenticated users to search for local statutes and users only. The default is `true`.
## :app_account_creation
REST API for creating an account settings
......@@ -224,6 +232,21 @@ relates to mascots on the mastodon frontend
* `avatar_removal`: List of instances to strip avatars from
* `banner_removal`: List of instances to strip banners from
## :mrf_subchain
This policy processes messages through an alternate pipeline when a given message matches certain criteria.
All criteria are configured as a map of regular expressions to lists of policy modules.
* `match_actor`: Matches a series of regular expressions against the actor field.
Example:
```
config :pleroma, :mrf_subchain,
match_actor: %{
~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
}
```
## :mrf_rejectnonpublic
* `allow_followersonly`: whether to allow followers-only posts
* `allow_direct`: whether to allow direct messages
......@@ -492,7 +515,7 @@ Authentication / authorization settings.
* `auth_template`: authentication form template. By default it's `show.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/show.html.eex`.
* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`.
* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable.
* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by `OAUTH_CONSUMER_STRATEGIES` environment variable. Each entry in this space-delimited string should be of format `<strategy>` or `<strategy>:<dependency>` (e.g. `twitter` or `keycloak:ueberauth_keycloak_strategy` in case dependency is named differently than `ueberauth_<strategy>`).
## OAuth consumer mode
......@@ -545,6 +568,24 @@ config :ueberauth, Ueberauth,
providers: [
microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]}
]
# Keycloak
# Note: make sure to add `keycloak:ueberauth_keycloak_strategy` entry to `OAUTH_CONSUMER_STRATEGIES` environment variable
keycloak_url = "https://publicly-reachable-keycloak-instance.org:8080"
config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
client_id: System.get_env("KEYCLOAK_CLIENT_ID"),
client_secret: System.get_env("KEYCLOAK_CLIENT_SECRET"),
site: keycloak_url,
authorize_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/auth",
token_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/token",
userinfo_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/userinfo",
token_method: :post
config :ueberauth, Ueberauth,
providers: [
keycloak: {Ueberauth.Strategy.Keycloak, [uid_field: :email]}
]
```
## OAuth 2.0 provider - :oauth2
......
......@@ -7,7 +7,7 @@ def run(["search"]) do
Benchee.run(%{
"search" => fn ->
Pleroma.Web.MastodonAPI.MastodonAPIController.status_search(nil, "cofe")
Pleroma.Activity.search(nil, "cofe")
end
})
end
......
......@@ -5,23 +5,58 @@
defmodule Mix.Tasks.Pleroma.Common do
@doc "Common functions to be reused in mix tasks"
def start_pleroma do
Mix.Task.run("app.start")
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
{:ok, _} = Application.ensure_all_started(:pleroma)
end
def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
Keyword.get(options, opt) ||
case Mix.shell().prompt("#{prompt} [#{defname || defval}]") do
"\n" ->
case defval do
nil -> get_option(options, opt, prompt, defval)
defval -> defval
end
opt ->
opt |> String.trim()
end
Keyword.get(options, opt) || shell_prompt(prompt, defval, defname)
end
def shell_prompt(prompt, defval \\ nil, defname \\ nil) do
prompt_message = "#{prompt} [#{defname || defval}]"
input =
if mix_shell?(),
do: Mix.shell().prompt(prompt_message),
else: :io.get_line(prompt_message)
case input do
"\n" ->
case defval do
nil ->
shell_prompt(prompt, defval, defname)
defval ->
defval
end
input ->
String.trim(input)
end
end
def shell_yes?(message) do
if mix_shell?(),
do: Mix.shell().yes?("Continue?"),
else: shell_prompt(message, "Continue?") in ~w(Yn Y y)
end
def shell_info(message) do
if mix_shell?(),
do: Mix.shell().info(message),
else: IO.puts(message)
end
def shell_error(message) do
if mix_shell?(),
do: Mix.shell().error(message),
else: IO.puts(:stderr, message)
end
@doc "Performs a safe check whether `Mix.shell/0` is available (does not raise if Mix is not loaded)"
def mix_shell?, do: :erlang.function_exported(Mix, :shell, 0)
def escape_sh_path(path) do
~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
end
......
......@@ -155,17 +155,17 @@ def run(["gen" | rest]) do
dbpass: dbpass
)
Mix.shell().info(
Common.shell_info(
"Writing config to #{config_path}. You should rename it to config/prod.secret.exs or config/dev.secret.exs."
)
File.write(config_path, result_config)
Mix.shell().info("Writing #{psql_path}.")
Common.shell_info("Writing #{psql_path}.")
File.write(psql_path, result_psql)
write_robots_txt(indexable)
Mix.shell().info(
Common.shell_info(
"\n" <>
"""
To get started:
......@@ -179,7 +179,7 @@ def run(["gen" | rest]) do
end
)
else
Mix.shell().error(
Common.shell_error(
"The task would have overwritten the following files:\n" <>
(Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
"Rerun with `--force` to overwrite them."
......@@ -204,10 +204,10 @@ defp write_robots_txt(indexable) do
if File.exists?(robots_txt_path) do
File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
Mix.shell().info("Backing up existing robots.txt to #{robots_txt_path}.bak")
Common.shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")
end
File.write(robots_txt_path, robots_txt)
Mix.shell().info("Writing #{robots_txt_path}.")
Common.shell_info("Writing #{robots_txt_path}.")
end
end
......@@ -30,7 +30,7 @@ def run(["follow", target]) do
# put this task to sleep to allow the genserver to push out the messages
:timer.sleep(500)
else
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
{:error, e} -> Common.shell_error("Error while following #{target}: #{inspect(e)}")
end
end
......@@ -41,7 +41,7 @@ def run(["unfollow", target]) do
# put this task to sleep to allow the genserver to push out the messages
:timer.sleep(500)
else
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
{:error, e} -> Common.shell_error("Error while following #{target}: #{inspect(e)}")
end
end
end
......@@ -38,10 +38,10 @@ def run(["migrate_local", target_uploader | args]) do
Pleroma.Config.put([Upload, :uploader], uploader)
end
Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}")
Common.shell_info("Migrating files from local #{local_path} to #{to_string(uploader)}")
if delete? do
Mix.shell().info(
Common.shell_info(
"Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"
)
......@@ -78,7 +78,7 @@ def run(["migrate_local", target_uploader | args]) do
|> Enum.filter(& &1)
total_count = length(uploads)
Mix.shell().info("Found #{total_count} uploads")
Common.shell_info("Found #{total_count} uploads")
uploads
|> Task.async_stream(
......@@ -90,7 +90,7 @@ def run(["migrate_local", target_uploader | args]) do
:ok
error ->
Mix.shell().error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
Common.shell_error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
end
end,
timeout: 150_000
......@@ -99,10 +99,10 @@ def run(["migrate_local", target_uploader | args]) do
# credo:disable-for-next-line Credo.Check.Warning.UnusedEnumOperation
|> Enum.reduce(0, fn done, count ->
count = count + length(done)
Mix.shell().info("Uploaded #{count}/#{total_count} files")
Common.shell_info("Uploaded #{count}/#{total_count} files")
count
end)
Mix.shell().info("Done!")
Common.shell_info("Done!")
end
end
......@@ -115,7 +115,7 @@ def run(["new", nickname, email | rest]) do
admin? = Keyword.get(options, :admin, false)
assume_yes? = Keyword.get(options, :assume_yes, false)
Mix.shell().info("""
Common.shell_info("""
A user will be created with the following information:
- nickname: #{nickname}
- email: #{email}
......@@ -128,7 +128,7 @@ def run(["new", nickname, email | rest]) do
- admin: #{if(admin?, do: "true", else: "false")}
""")
proceed? = assume_yes? or Mix.shell().yes?("Continue?")
proceed? = assume_yes? or Common.shell_yes?("Continue?")
if proceed? do
Common.start_pleroma()
......@@ -145,7 +145,7 @@ def run(["new", nickname, email | rest]) do
changeset = User.register_changeset(%User{}, params, need_confirmation: false)
{:ok, _user} = User.register(changeset)
Mix.shell().info("User #{nickname} created")
Common.shell_info("User #{nickname} created")
if moderator? do
run(["set", nickname, "--moderator"])
......@@ -159,7 +159,7 @@ def run(["new", nickname, email | rest]) do
run(["reset_password", nickname])
end
else
Mix.shell().info("User will not be created.")
Common.shell_info("User will not be created.")
end
end
......@@ -168,10 +168,10 @@ def run(["rm", nickname]) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
User.perform(:delete, user)
Mix.shell().info("User #{nickname} deleted.")
Common.shell_info("User #{nickname} deleted.")
else
_ ->
Mix.shell().error("No local user #{nickname}")
Common.shell_error("No local user #{nickname}")
end
end
......@@ -181,12 +181,12 @@ def run(["toggle_activated", nickname]) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
{:ok, user} = User.deactivate(user, !user.info.deactivated)
Mix.shell().info(
Common.shell_info(
"Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"
)
else
_ ->
Mix.shell().error("No user #{nickname}")
Common.shell_error("No user #{nickname}")
end
end
......@@ -195,7 +195,7 @@ def run(["reset_password", nickname]) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
Mix.shell().info("Generated password reset token for #{user.nickname}")
Common.shell_info("Generated password reset token for #{user.nickname}")
IO.puts(
"URL: #{
......@@ -208,7 +208,7 @@ def run(["reset_password", nickname]) do
)
else
_ ->
Mix.shell().error("No local user #{nickname}")
Common.shell_error("No local user #{nickname}")
end
end
......@@ -216,7 +216,7 @@ def run(["unsubscribe", nickname]) do
Common.start_pleroma()
with %User{} = user <- User.get_cached_by_nickname(nickname) do
Mix.shell().info("Deactivating #{user.nickname}")
Common.shell_info("Deactivating #{user.nickname}")
User.deactivate(user)
{:ok, friends} = User.get_friends(user)
......@@ -224,7 +224,7 @@ def run(["unsubscribe", nickname]) do
Enum.each(friends, fn friend ->
user = User.get_cached_by_id(user.id)
Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
Common.shell_info("Unsubscribing #{friend.nickname} from #{user.nickname}")
User.unfollow(user, friend)
end)
......@@ -233,11 +233,11 @@ def run(["unsubscribe", nickname]) do
user = User.get_cached_by_id(user.id)
if Enum.empty?(user.following) do
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
Common.shell_info("Successfully unsubscribed all followers from #{user.nickname}")
end
else
_ ->
Mix.shell().error("No user #{nickname}")
Common.shell_error("No user #{nickname}")
end
end
......@@ -274,7 +274,7 @@ def run(["set", nickname | rest]) do
end
else
_ ->
Mix.shell().error("No local user #{nickname}")
Common.shell_error("No local user #{nickname}")
end
end
......@@ -284,10 +284,10 @@ def run(["tag", nickname | tags]) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
user = user |> User.tag(tags)
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
Common.shell_info("Tags of #{user.nickname}: #{inspect(tags)}")
else
_ ->
Mix.shell().error("Could not change user tags for #{nickname}")
Common.shell_error("Could not change user tags for #{nickname}")
end
end
......@@ -297,10 +297,10 @@ def run(["untag", nickname | tags]) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
user = user |> User.untag(tags)
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
Common.shell_info("Tags of #{user.nickname}: #{inspect(tags)}")
else
_ ->
Mix.shell().error("Could not change user tags for #{nickname}")
Common.shell_error("Could not change user tags for #{nickname}")
end
end
......@@ -326,7 +326,7 @@ def run(["invite" | rest]) do
with {:ok, val} <- options[:expires_at],
options = Map.put(options, :expires_at, val),
{:ok, invite} <- UserInviteToken.create_invite(options) do
Mix.shell().info(
Common.shell_info(
"Generated user invite token " <> String.replace(invite.invite_type, "_", " ")
)
......@@ -340,14 +340,14 @@ def run(["invite" | rest]) do
IO.puts(url)
else
error ->
Mix.shell().error("Could not create invite token: #{inspect(error)}")
Common.shell_error("Could not create invite token: #{inspect(error)}")
end
end
def run(["invites"]) do
Common.start_pleroma()
Mix.shell().info("Invites list:")
Common.shell_info("Invites list:")
UserInviteToken.list_invites()
|> Enum.each(fn invite ->
......@@ -361,7 +361,7 @@ def run(["invites"]) do
" | Max use: #{max_use} Left use: #{max_use - invite.uses}"
end
Mix.shell().info(
Common.shell_info(
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
invite.used
}#{expire_info}#{using_info}"
......@@ -374,9 +374,9 @@ def run(["revoke_invite", token]) do
with {:ok, invite} <- UserInviteToken.find_by_token(token),
{:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
Mix.shell().info("Invite for token #{token} was revoked.")
Common.shell_info("Invite for token #{token} was revoked.")
else
_ -> Mix.shell().error("No invite found with token #{token}")
_ -> Common.shell_error("No invite found with token #{token}")
end
end
......@@ -385,10 +385,10 @@ def run(["delete_activities", nickname]) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
{:ok, _} = User.delete_user_activities(user)
Mix.shell().info("User #{nickname} statuses deleted.")
Common.shell_info("User #{nickname} statuses deleted.")
else
_ ->
Mix.shell().error("No local user #{nickname}")
Common.shell_error("No local user #{nickname}")
end
end
......@@ -400,10 +400,10 @@ def run(["toggle_confirmed", nickname]) do
message = if user.info.confirmation_pending, do: "needs", else: "doesn't need"