Merge branch 'develop' into gun
This commit is contained in:
commit
13918cb545
|
@ -72,8 +72,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- User notification settings: Add `privacy_option` option.
|
- User notification settings: Add `privacy_option` option.
|
||||||
- Support for custom Elixir modules (such as MRF policies)
|
- Support for custom Elixir modules (such as MRF policies)
|
||||||
- User settings: Add _This account is a_ option.
|
- User settings: Add _This account is a_ option.
|
||||||
|
- A new users admin digest email
|
||||||
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
||||||
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
||||||
|
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
||||||
|
@ -115,6 +117,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Configuration: `feed.logo` option for tag feed.
|
- Configuration: `feed.logo` option for tag feed.
|
||||||
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
|
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
|
||||||
- Mastodon API: Add `reacted` property to `emoji_reactions`
|
- Mastodon API: Add `reacted` property to `emoji_reactions`
|
||||||
|
- Pleroma API: Add reactions for a single emoji.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -49,7 +49,8 @@
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
types: Pleroma.PostgresTypes,
|
types: Pleroma.PostgresTypes,
|
||||||
telemetry_event: [Pleroma.Repo.Instrumenter],
|
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||||
migration_lock: nil
|
migration_lock: nil,
|
||||||
|
parameters: [gin_fuzzy_search_limit: "500"]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Captcha,
|
config :pleroma, Pleroma.Captcha,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -304,7 +305,8 @@
|
||||||
unfollow_blocked: true,
|
unfollow_blocked: true,
|
||||||
outgoing_blocks: true,
|
outgoing_blocks: true,
|
||||||
follow_handshake_timeout: 500,
|
follow_handshake_timeout: 500,
|
||||||
sign_object_fetches: true
|
sign_object_fetches: true,
|
||||||
|
authorized_fetch_mode: false
|
||||||
|
|
||||||
config :pleroma, :streamer,
|
config :pleroma, :streamer,
|
||||||
workers: 3,
|
workers: 3,
|
||||||
|
@ -458,13 +460,15 @@
|
||||||
transmogrifier: 20,
|
transmogrifier: 20,
|
||||||
scheduled_activities: 10,
|
scheduled_activities: 10,
|
||||||
background: 5,
|
background: 5,
|
||||||
attachments_cleanup: 5
|
attachments_cleanup: 5,
|
||||||
|
new_users_digest: 1
|
||||||
],
|
],
|
||||||
crontab: [
|
crontab: [
|
||||||
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
|
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
|
||||||
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
|
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
|
||||||
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
|
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
|
||||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}
|
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
|
||||||
|
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
|
||||||
]
|
]
|
||||||
|
|
||||||
config :pleroma, :workers,
|
config :pleroma, :workers,
|
||||||
|
@ -538,6 +542,8 @@
|
||||||
text_muted_color: "#b9b9ba"
|
text_muted_color: "#b9b9ba"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
||||||
|
|
||||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||||
|
|
||||||
config :pleroma, Pleroma.ScheduledActivity,
|
config :pleroma, Pleroma.ScheduledActivity,
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
%{
|
%{
|
||||||
key: :versions,
|
key: :versions,
|
||||||
type: {:list, :atom},
|
type: {:list, :atom},
|
||||||
description: "List of TLS version to use",
|
description: "List of TLS versions to use",
|
||||||
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
|
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -534,7 +534,8 @@
|
||||||
%{
|
%{
|
||||||
key: :description,
|
key: :description,
|
||||||
type: :string,
|
type: :string,
|
||||||
description: "The instance's description, can be seen in nodeinfo and /api/v1/instance",
|
description:
|
||||||
|
"The instance's description. It can be seen in nodeinfo and `/api/v1/instance`",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
"Very cool instance"
|
"Very cool instance"
|
||||||
]
|
]
|
||||||
|
@ -770,7 +771,7 @@
|
||||||
key: :cleanup_attachments,
|
key: :cleanup_attachments,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: """
|
description: """
|
||||||
"Enable to remove associated attachments when status is removed.
|
Enable to remove associated attachments when status is removed.
|
||||||
This will not affect duplicates and attachments without status.
|
This will not affect duplicates and attachments without status.
|
||||||
Enabling this will increase load to database when deleting statuses on larger instances.
|
Enabling this will increase load to database when deleting statuses on larger instances.
|
||||||
"""
|
"""
|
||||||
|
@ -838,7 +839,7 @@
|
||||||
%{
|
%{
|
||||||
key: :healthcheck,
|
key: :healthcheck,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "If enabled, system data will be shown on /api/pleroma/healthcheck"
|
description: "If enabled, system data will be shown on `/api/pleroma/healthcheck`"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :remote_post_retention_days,
|
key: :remote_post_retention_days,
|
||||||
|
@ -1296,14 +1297,14 @@
|
||||||
%{
|
%{
|
||||||
key: :media_removal,
|
key: :media_removal,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to remove medias from",
|
description: "List of instances to strip media attachments from",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :media_nsfw,
|
key: :media_nsfw,
|
||||||
label: "Media NSFW",
|
label: "Media NSFW",
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to put medias as NSFW (sensitive) from",
|
description: "List of instances to tag all media as NSFW (sensitive) from",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -1422,21 +1423,21 @@
|
||||||
key: :reject,
|
key: :reject,
|
||||||
type: [:string, :regex],
|
type: [:string, :regex],
|
||||||
description:
|
description:
|
||||||
"A list of patterns which result in message being rejected, each pattern can be a string or a regular expression.",
|
"A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
|
||||||
suggestions: ["foo", ~r/foo/iu]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :federated_timeline_removal,
|
key: :federated_timeline_removal,
|
||||||
type: [:string, :regex],
|
type: [:string, :regex],
|
||||||
description:
|
description:
|
||||||
"A list of patterns which result in message being removed from federated timelines (a.k.a unlisted), each pattern can be a string or a regular expression.",
|
"A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). Each pattern can be a string or a regular expression.",
|
||||||
suggestions: ["foo", ~r/foo/iu]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :replace,
|
key: :replace,
|
||||||
type: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
|
type: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
|
||||||
description:
|
description:
|
||||||
"A list of tuples containing {pattern, replacement}, pattern can be a string or a regular expression.",
|
"A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
|
||||||
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1451,7 +1452,7 @@
|
||||||
%{
|
%{
|
||||||
key: :actors,
|
key: :actors,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "A list of actors, for which to drop any posts mentioning",
|
description: "A list of actors for which any post mentioning them will be dropped.",
|
||||||
suggestions: ["actor1", "actor2"]
|
suggestions: ["actor1", "actor2"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1855,9 +1856,8 @@
|
||||||
type: :string,
|
type: :string,
|
||||||
description:
|
description:
|
||||||
"A mailto link for the administrative contact." <>
|
"A mailto link for the administrative contact." <>
|
||||||
" It's best if this email is not a personal email address, but rather a group email so that if a person leaves an organization," <>
|
" It's best if this email is not a personal email address, but rather a group email to the instance moderation team.",
|
||||||
" is unavailable for an extended period, or otherwise can't respond, someone else on the list can.",
|
suggestions: ["mailto:moderators@pleroma.com"]
|
||||||
suggestions: ["Subject"]
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :public_key,
|
key: :public_key,
|
||||||
|
@ -1924,7 +1924,7 @@
|
||||||
key: :admin_token,
|
key: :admin_token,
|
||||||
type: :string,
|
type: :string,
|
||||||
description: "Token",
|
description: "Token",
|
||||||
suggestions: ["some_random_token"]
|
suggestions: ["We recommend a secure random string or UUID"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1986,6 +1986,7 @@
|
||||||
"Background jobs queues (keys: queues, values: max numbers of concurrent jobs)",
|
"Background jobs queues (keys: queues, values: max numbers of concurrent jobs)",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
activity_expiration: 10,
|
activity_expiration: 10,
|
||||||
|
attachments_cleanup: 5,
|
||||||
background: 5,
|
background: 5,
|
||||||
federator_incoming: 50,
|
federator_incoming: 50,
|
||||||
federator_outgoing: 50,
|
federator_outgoing: 50,
|
||||||
|
@ -2001,6 +2002,12 @@
|
||||||
description: "Activity expiration queue",
|
description: "Activity expiration queue",
|
||||||
suggestions: [10]
|
suggestions: [10]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :attachments_cleanup,
|
||||||
|
type: :integer,
|
||||||
|
description: "Attachment deletion queue",
|
||||||
|
suggestions: [5]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :background,
|
key: :background,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
@ -2099,7 +2106,7 @@
|
||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enables/disables RichMedia."
|
description: "Enables RichMedia parsing of URLs."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :ignore_hosts,
|
key: :ignore_hosts,
|
||||||
|
@ -2145,8 +2152,7 @@
|
||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
description: "Fetch posts when a new user is federated with"
|
||||||
"If enabled, when a new user is federated with, fetch some of their latest posts"
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :pages,
|
key: :pages,
|
||||||
|
@ -2165,13 +2171,13 @@
|
||||||
%{
|
%{
|
||||||
key: :class,
|
key: :class,
|
||||||
type: [:string, false],
|
type: [:string, false],
|
||||||
description: "Specify the class to be added to the generated link. `False` to clear",
|
description: "Specify the class to be added to the generated link. Disable to clear",
|
||||||
suggestions: ["auto-linker", false]
|
suggestions: ["auto-linker", false]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :rel,
|
key: :rel,
|
||||||
type: [:string, false],
|
type: [:string, false],
|
||||||
description: "Override the rel attribute. `False` to clear",
|
description: "Override the rel attribute. Disable to clear",
|
||||||
suggestions: ["ugc", "noopener noreferrer", false]
|
suggestions: ["ugc", "noopener noreferrer", false]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -2281,7 +2287,7 @@
|
||||||
key: :ssl,
|
key: :ssl,
|
||||||
label: "SSL",
|
label: "SSL",
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "`True` to use SSL, usually implies the port 636"
|
description: "Enable to use SSL, usually implies the port 636"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :sslopts,
|
key: :sslopts,
|
||||||
|
@ -2308,7 +2314,7 @@
|
||||||
key: :tls,
|
key: :tls,
|
||||||
label: "TLS",
|
label: "TLS",
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "`True` to start TLS, usually implies the port 389"
|
description: "Enable to use STARTTLS, usually implies the port 389"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :tlsopts,
|
key: :tlsopts,
|
||||||
|
@ -2358,7 +2364,7 @@
|
||||||
description:
|
description:
|
||||||
"OAuth admin scope requirement toggle. " <>
|
"OAuth admin scope requirement toggle. " <>
|
||||||
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
||||||
"(client app must support admin scopes). If `false` and token doesn't have admin scope(s)," <>
|
"(client app must support admin scopes). If disabled and token doesn't have admin scope(s)," <>
|
||||||
"`is_admin` user flag grants access to admin-specific actions."
|
"`is_admin` user flag grants access to admin-specific actions."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -2380,7 +2386,7 @@
|
||||||
key: :oauth_consumer_strategies,
|
key: :oauth_consumer_strategies,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description:
|
description:
|
||||||
"The list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
|
"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\"" <>
|
" 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>).",
|
" (e.g. twitter or keycloak:ueberauth_keycloak_strategy in case dependency is named differently than ueberauth_<strategy>).",
|
||||||
suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"]
|
suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"]
|
||||||
|
@ -2496,6 +2502,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Emails.NewUsersDigestEmail,
|
||||||
|
type: :group,
|
||||||
|
description: "New users admin email digest",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enabled,
|
||||||
|
type: :boolean,
|
||||||
|
description: "enables new users admin digest email when `true`",
|
||||||
|
suggestions: [false]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :oauth2,
|
key: :oauth2,
|
||||||
|
@ -2517,7 +2537,7 @@
|
||||||
%{
|
%{
|
||||||
key: :clean_expired_tokens,
|
key: :clean_expired_tokens,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enable a background job to clean expired oauth tokens. Default: `false`."
|
description: "Enable a background job to clean expired oauth tokens. Default: disabled."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2577,7 +2597,7 @@
|
||||||
%{
|
%{
|
||||||
key: :rum_enabled,
|
key: :rum_enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "If RUM indexes should be used. Default: `false`"
|
description: "If RUM indexes should be used. Default: disabled"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2963,7 +2983,7 @@
|
||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enable/disable the plug. Default: `false`."
|
description: "Enable/disable the plug. Default: disabled."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :headers,
|
key: :headers,
|
||||||
|
@ -3017,7 +3037,7 @@
|
||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enables the rendering of static HTML. Defaults to `false`."
|
description: "Enables the rendering of static HTML. Default: disabled."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3093,7 +3113,7 @@
|
||||||
key: :configurable_from_database,
|
key: :configurable_from_database,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
description:
|
||||||
"Allow transferring configuration to DB with the subsequent customization from Admin api. Defaults to `false`"
|
"Allow transferring configuration to DB with the subsequent customization from Admin api. Default: disabled"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,8 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.Gun.API, Pleroma.Gun.API.Mock
|
config :pleroma, Pleroma.Gun.API, Pleroma.Gun.API.Mock
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true
|
||||||
|
|
||||||
if File.exists?("./config/test.secret.exs") do
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -459,3 +459,16 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
||||||
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
|
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
|
||||||
|
### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji`
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: optional
|
||||||
|
* Params: None
|
||||||
|
* Response: JSON, a list of emoji/account list tuples
|
||||||
|
* Example Response:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -1,4 +1,21 @@
|
||||||
# Updating your instance
|
# Updating your instance
|
||||||
|
|
||||||
|
You should **always check the release notes/changelog** in case there are config deprecations, special update special update steps, etc.
|
||||||
|
|
||||||
|
Besides that, doing the following is generally enough:
|
||||||
|
|
||||||
|
## For OTP installations
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Download the new release
|
||||||
|
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
|
||||||
|
|
||||||
|
# Migrate the database, you are advised to stop the instance before doing that
|
||||||
|
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
||||||
|
```
|
||||||
|
|
||||||
|
## For from source installations (using git)
|
||||||
|
|
||||||
1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||||
2. Run `git pull`. This pulls the latest changes from upstream.
|
2. Run `git pull`. This pulls the latest changes from upstream.
|
||||||
3. Run `mix deps.get`. This pulls in any new dependencies.
|
3. Run `mix deps.get`. This pulls in any new dependencies.
|
||||||
|
|
|
@ -143,10 +143,11 @@ config :pleroma, :mrf_user_allowlist,
|
||||||
* `:reject` rejects the message entirely
|
* `:reject` rejects the message entirely
|
||||||
|
|
||||||
### :activitypub
|
### :activitypub
|
||||||
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed
|
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
|
||||||
* ``outgoing_blocks``: Whether to federate blocks to other instances
|
* `outgoing_blocks`: Whether to federate blocks to other instances
|
||||||
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question
|
* `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
|
||||||
* ``sign_object_fetches``: Sign object fetches with HTTP signatures
|
* `sign_object_fetches`: Sign object fetches with HTTP signatures
|
||||||
|
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
|
||||||
|
|
||||||
### :fetch_initial_posts
|
### :fetch_initial_posts
|
||||||
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
|
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
|
||||||
|
@ -533,6 +534,10 @@ Email notifications settings.
|
||||||
- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
|
- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
|
||||||
- `:styling` - a map with color settings for email templates.
|
- `:styling` - a map with color settings for email templates.
|
||||||
|
|
||||||
|
### Pleroma.Emails.NewUsersDigestEmail
|
||||||
|
|
||||||
|
- `:enabled` - a boolean, enables new users admin digest email when `true`. Defaults to `false`.
|
||||||
|
|
||||||
## Background jobs
|
## Background jobs
|
||||||
|
|
||||||
### Oban
|
### Oban
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Message Rewrite Facility
|
# Message Rewrite Facility
|
||||||
|
|
||||||
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
||||||
|
|
||||||
Possible uses include:
|
Possible uses include:
|
||||||
|
@ -10,7 +11,8 @@ Possible uses include:
|
||||||
* removing media from messages
|
* removing media from messages
|
||||||
* sending only public messages to a specific instance
|
* sending only public messages to a specific instance
|
||||||
|
|
||||||
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
||||||
|
|
||||||
It is possible to use multiple, active MRF policies at the same time.
|
It is possible to use multiple, active MRF policies at the same time.
|
||||||
|
|
||||||
## Quarantine Instances
|
## Quarantine Instances
|
||||||
|
@ -18,7 +20,8 @@ It is possible to use multiple, active MRF policies at the same time.
|
||||||
You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
|
You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
|
||||||
|
|
||||||
If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
|
If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
|
||||||
```
|
|
||||||
|
```elixir
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
[...]
|
[...]
|
||||||
quarantined_instances: ["instance.example", "other.example"]
|
quarantined_instances: ["instance.example", "other.example"]
|
||||||
|
@ -28,15 +31,15 @@ config :pleroma, :instance,
|
||||||
|
|
||||||
`SimplePolicy` is capable of handling most common admin tasks.
|
`SimplePolicy` is capable of handling most common admin tasks.
|
||||||
|
|
||||||
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
||||||
|
|
||||||
```
|
```elixir
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
[...]
|
[...]
|
||||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||||
```
|
```
|
||||||
|
|
||||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||||
|
|
||||||
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||||
|
@ -50,7 +53,7 @@ Servers should be configured as lists.
|
||||||
|
|
||||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
||||||
|
|
||||||
```
|
```elixir
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||||
|
|
||||||
|
@ -60,30 +63,31 @@ config :pleroma, :mrf_simple,
|
||||||
reject: ["spam.com"],
|
reject: ["spam.com"],
|
||||||
federated_timeline_removal: ["spam.university"],
|
federated_timeline_removal: ["spam.university"],
|
||||||
report_removal: ["whiny.whiner"]
|
report_removal: ["whiny.whiner"]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use with Care
|
### Use with Care
|
||||||
|
|
||||||
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
|
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
|
||||||
|
|
||||||
## Writing your own MRF Policy
|
## Writing your own MRF Policy
|
||||||
|
|
||||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
|
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
|
||||||
|
|
||||||
For example, here is a sample policy module which rewrites all messages to "new message content":
|
For example, here is a sample policy module which rewrites all messages to "new message content":
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
# This is a sample MRF policy which rewrites all Notes to have "new message
|
defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
|
||||||
# content."
|
@moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
|
||||||
defmodule Site.RewritePolicy do
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
@behavior Pleroma.Web.ActivityPub.MRF
|
|
||||||
|
|
||||||
# Catch messages which contain Note objects with actual data to filter.
|
# Catch messages which contain Note objects with actual data to filter.
|
||||||
# Capture the object as `object`, the message content as `content` and the
|
# Capture the object as `object`, the message content as `content` and the
|
||||||
# message itself as `message`.
|
# message itself as `message`.
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"type" => Create", "object" => {"type" => "Note", "content" => content} = object} = message)
|
def filter(
|
||||||
|
%{"type" => "Create", "object" => %{"type" => "Note", "content" => content} = object} =
|
||||||
|
message
|
||||||
|
)
|
||||||
when is_binary(content) do
|
when is_binary(content) do
|
||||||
# Subject / CW is stored as summary instead of `name` like other AS2 objects
|
# Subject / CW is stored as summary instead of `name` like other AS2 objects
|
||||||
# because of Mastodon doing it that way.
|
# because of Mastodon doing it that way.
|
||||||
|
@ -106,17 +110,22 @@ defmodule Site.RewritePolicy do
|
||||||
# Let all other messages through without modifying them.
|
# Let all other messages through without modifying them.
|
||||||
@impl true
|
@impl true
|
||||||
def filter(message), do: {:ok, message}
|
def filter(message), do: {:ok, message}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def describe do
|
||||||
|
{:ok, %{mrf_sample: %{content: "new message content"}}}`
|
||||||
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
If you save this file as `lib/site/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||||
|
|
||||||
```
|
```elixir
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
rewrite_policy: [
|
rewrite_policy: [
|
||||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||||
Site.RewritePolicy
|
Pleroma.Web.ActivityPub.MRF.RewritePolicy
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
|
Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
|
||||||
|
|
|
@ -259,19 +259,14 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --a
|
||||||
```
|
```
|
||||||
This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
|
This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
|
||||||
|
|
||||||
### Updating
|
|
||||||
Generally, doing the following is enough:
|
|
||||||
```sh
|
|
||||||
# Download the new release
|
|
||||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
|
|
||||||
|
|
||||||
# Migrate the database, you are advised to stop the instance before doing that
|
|
||||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
|
||||||
```
|
|
||||||
But you should **always check the release notes/changelog** in case there are config deprecations, special update steps, etc.
|
|
||||||
|
|
||||||
## Further reading
|
## Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
* [Backup your instance](../administration/backup.md)
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
* [Hardening your instance](../configuration/hardening.md)
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
||||||
|
* [Updating your instance](../administration/updating.md)
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Emails.NewUsersDigestEmail do
|
||||||
|
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email_styled}
|
||||||
|
|
||||||
|
defp instance_notify_email do
|
||||||
|
Pleroma.Config.get([:instance, :notify_email]) || Pleroma.Config.get([:instance, :email])
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_users(to, users_and_statuses) do
|
||||||
|
instance_name = Pleroma.Config.get([:instance, :name])
|
||||||
|
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
|
||||||
|
|
||||||
|
logo_url =
|
||||||
|
Pleroma.Web.Endpoint.url() <>
|
||||||
|
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to({to.name, to.email})
|
||||||
|
|> from({instance_name, instance_notify_email()})
|
||||||
|
|> subject("#{instance_name} New Users")
|
||||||
|
|> render_body("new_users_digest.html", %{
|
||||||
|
title: "New Users",
|
||||||
|
users_and_statuses: users_and_statuses,
|
||||||
|
instance: instance_name,
|
||||||
|
styling: styling,
|
||||||
|
logo_url: logo_url
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
import Phoenix.Controller, only: [get_format: 1, text: 2]
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
|
@ -15,25 +16,27 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, _opts) do
|
def call(conn, _opts) do
|
||||||
headers = get_req_header(conn, "signature")
|
if get_format(conn) == "activity+json" do
|
||||||
signature = Enum.at(headers, 0)
|
conn
|
||||||
|
|> maybe_assign_valid_signature()
|
||||||
|
|> maybe_require_signature()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if signature do
|
defp maybe_assign_valid_signature(conn) do
|
||||||
|
if has_signature_header?(conn) do
|
||||||
# set (request-target) header to the appropriate value
|
# set (request-target) header to the appropriate value
|
||||||
# we also replace the digest header with the one we computed
|
# we also replace the digest header with the one we computed
|
||||||
conn =
|
request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
||||||
conn
|
|
||||||
|> put_req_header(
|
|
||||||
"(request-target)",
|
|
||||||
String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
|
||||||
)
|
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
if conn.assigns[:digest] do
|
conn
|
||||||
conn
|
|> put_req_header("(request-target)", request_target)
|
||||||
|> put_req_header("digest", conn.assigns[:digest])
|
|> case do
|
||||||
else
|
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
|
||||||
conn
|
conn -> conn
|
||||||
end
|
end
|
||||||
|
|
||||||
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
|
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
|
||||||
|
@ -42,4 +45,21 @@ def call(conn, _opts) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp has_signature_header?(conn) do
|
||||||
|
conn |> get_req_header("signature") |> Enum.at(0, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
|
||||||
|
|
||||||
|
defp maybe_require_signature(conn) do
|
||||||
|
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
|
||||||
|
conn
|
||||||
|
|> put_status(:unauthorized)
|
||||||
|
|> text("Request not signed")
|
||||||
|
|> halt()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,24 +41,29 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
|
||||||
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
|
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||||
%Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
|
%Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
|
||||||
Object.normalize(activity) do
|
Object.normalize(activity) do
|
||||||
reactions =
|
reactions =
|
||||||
emoji_reactions
|
emoji_reactions
|
||||||
|> Enum.map(fn [emoji, user_ap_ids] ->
|
|> Enum.map(fn [emoji, user_ap_ids] ->
|
||||||
users =
|
if params["emoji"] && params["emoji"] != emoji do
|
||||||
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|
nil
|
||||||
|> Enum.filter(& &1)
|
else
|
||||||
|
users =
|
||||||
|
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
name: emoji,
|
name: emoji,
|
||||||
count: length(users),
|
count: length(users),
|
||||||
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
|
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
|
||||||
me: !!(user && user.ap_id in user_ap_ids)
|
me: !!(user && user.ap_id in user_ap_ids)
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(reactions)
|
|> json(reactions)
|
||||||
|
|
|
@ -271,6 +271,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
|
|
||||||
|
get("/statuses/:id/reactions/:emoji", PleromaAPIController, :emoji_reactions_by)
|
||||||
get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by)
|
get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
<%= for {user, total_statuses, latest_status} <- @users_and_statuses do %>
|
||||||
|
<%# user card START %>
|
||||||
|
<div style="background-color:transparent;">
|
||||||
|
<div class="block-grid mixed-two-up no-stack"
|
||||||
|
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||||
|
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 20px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num3"
|
||||||
|
style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 20px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<div align="left" class="img-container left "
|
||||||
|
style="padding-right: 0px;padding-left: 0px;">
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img
|
||||||
|
alt="<%= user.name %>" border="0" class="left " src="<%= avatar_url(user) %>"
|
||||||
|
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;"
|
||||||
|
title="<%= user.name %>" width="76" />
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num9"
|
||||||
|
style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||||
|
<div
|
||||||
|
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||||
|
<div
|
||||||
|
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||||
|
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||||
|
style="font-size: 16px; color: <%= @styling.text_color %>;"><%= user.name %></span></p>
|
||||||
|
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||||
|
style="font-size: 16px;"><%= link "@" <> user.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: admin_user_url(user) %></span></p>
|
||||||
|
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
|
||||||
|
style="font-size: 16px;">Total: <%= total_statuses %></span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%# user card END %>
|
||||||
|
|
||||||
|
<%= if latest_status do %>
|
||||||
|
<div style="background-color:transparent;">
|
||||||
|
<div class="block-grid"
|
||||||
|
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||||
|
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num12"
|
||||||
|
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||||
|
<div
|
||||||
|
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||||
|
<div
|
||||||
|
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
|
||||||
|
<span style="font-size: 16px; line-height: 19px;"><%= raw latest_status.object.data["content"] %></span></div>
|
||||||
|
</div>
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 15px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||||
|
<div
|
||||||
|
style="color:<%= @styling.text_muted_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:15px;">
|
||||||
|
<div
|
||||||
|
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_muted_color %>;">
|
||||||
|
<p style="font-size: 14px; line-height: 16px; margin: 0;"><%= format_date latest_status.object.data["published"] %></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<%# divider start %>
|
||||||
|
<div style="background-color:transparent;">
|
||||||
|
<div class="block-grid"
|
||||||
|
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||||
|
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num12"
|
||||||
|
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation"
|
||||||
|
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||||
|
valign="top" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr style="vertical-align: top;" valign="top">
|
||||||
|
<td class="divider_inner"
|
||||||
|
style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;"
|
||||||
|
valign="top">
|
||||||
|
<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content"
|
||||||
|
height="0" role="presentation"
|
||||||
|
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;"
|
||||||
|
valign="top" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr style="vertical-align: top;" valign="top">
|
||||||
|
<td height="0"
|
||||||
|
style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
|
||||||
|
valign="top"><span></span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%# divider end %>
|
||||||
|
<%# user card END %>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,193 @@
|
||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||||
|
xmlns:v="urn:schemas-microsoft-com:vml">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
|
||||||
|
<meta content="width=device-width" name="viewport" />
|
||||||
|
<!--[if !mso]><!-->
|
||||||
|
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||||
|
<!--<![endif]-->
|
||||||
|
<title><%= @email.subject %></title>
|
||||||
|
<!--[if !mso]><!-->
|
||||||
|
<!--<![endif]-->
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
|
||||||
|
color: <%= @styling.link_color %>;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table,
|
||||||
|
td,
|
||||||
|
tr {
|
||||||
|
vertical-align: top;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a[x-apple-data-detectors=true] {
|
||||||
|
color: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style id="media-query" type="text/css">
|
||||||
|
@media (max-width: 610px) {
|
||||||
|
|
||||||
|
.block-grid,
|
||||||
|
.col {
|
||||||
|
min-width: 320px !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-grid {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col>div {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col {
|
||||||
|
min-width: 0 !important;
|
||||||
|
display: table-cell !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack.two-up .col {
|
||||||
|
width: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num4 {
|
||||||
|
width: 33% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num8 {
|
||||||
|
width: 66% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num4 {
|
||||||
|
width: 33% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num3 {
|
||||||
|
width: 25% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num6 {
|
||||||
|
width: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-stack .col.num9 {
|
||||||
|
width: 75% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: <%= @styling.background_color %>;">
|
||||||
|
<!--[if IE]><div class="ie-browser"><![endif]-->
|
||||||
|
<table bgcolor="<%= @styling.background_color %>" cellpadding="0" cellspacing="0" class="nl-container" role="presentation"
|
||||||
|
style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: <%= @styling.background_color %>; width: 100%;"
|
||||||
|
valign="top" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr style="vertical-align: top;" valign="top">
|
||||||
|
<td style="word-break: break-word; vertical-align: top;" valign="top">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:<%= @styling.background_color %>"><![endif]-->
|
||||||
|
|
||||||
|
<%# header %>
|
||||||
|
<div style="background-color:transparent;">
|
||||||
|
<div class="block-grid"
|
||||||
|
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||||
|
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num12"
|
||||||
|
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<div align="center" class="img-container center"
|
||||||
|
style="padding-right: 0px;padding-left: 0px;">
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img
|
||||||
|
align="center" alt="Image" border="0" class="center" src="<%= @logo_url %>"
|
||||||
|
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;"
|
||||||
|
title="Image" height="80" />
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%# title %>
|
||||||
|
<%= if @title do %>
|
||||||
|
<div style="background-color:transparent;">
|
||||||
|
<div class="block-grid"
|
||||||
|
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
|
||||||
|
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
|
||||||
|
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||||
|
<div class="col num12"
|
||||||
|
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
|
||||||
|
<div style="width:100% !important;">
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
<div
|
||||||
|
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||||
|
<div
|
||||||
|
style="line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||||
|
<div
|
||||||
|
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;">
|
||||||
|
<p style="line-height: 36px; text-align: center; margin: 0;"><span
|
||||||
|
style="font-size: 30px; color: <%= @styling.header_color %>;"><%= @title %></span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (!mso)&(!IE)]><!-->
|
||||||
|
</div>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||||
|
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<%= render @view_module, @view_template, assigns %>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!--[if (IE)]></div><![endif]-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||||
|
@ -69,7 +69,7 @@ defp is_status?(acct) do
|
||||||
def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
|
def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
|
||||||
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||||
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
||||||
render(conn, "followed.html", %{error: false})
|
redirect(conn, to: "/users/#{followee.id}")
|
||||||
else
|
else
|
||||||
error ->
|
error ->
|
||||||
handle_follow_error(conn, error)
|
handle_follow_error(conn, error)
|
||||||
|
@ -80,7 +80,7 @@ def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" =>
|
||||||
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||||
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
|
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
|
||||||
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
||||||
render(conn, "followed.html", %{error: false})
|
redirect(conn, to: "/users/#{followee.id}")
|
||||||
else
|
else
|
||||||
error ->
|
error ->
|
||||||
handle_follow_error(conn, error)
|
handle_follow_error(conn, error)
|
||||||
|
|
|
@ -12,4 +12,8 @@ def format_date(date) when is_binary(date) do
|
||||||
|> Timex.parse!("{ISO:Extended:Z}")
|
|> Timex.parse!("{ISO:Extended:Z}")
|
||||||
|> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}")
|
|> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def admin_user_url(%{id: id}) do
|
||||||
|
Pleroma.Web.Endpoint.url() <> "/pleroma/admin/#/users/" <> id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
use Pleroma.Workers.WorkerHelper, queue: "new_users_digest"
|
||||||
|
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(_args, _job) do
|
||||||
|
if Pleroma.Config.get([Pleroma.Emails.NewUsersDigestEmail, :enabled]) do
|
||||||
|
today = NaiveDateTime.utc_now() |> Timex.beginning_of_day()
|
||||||
|
|
||||||
|
a_day_ago =
|
||||||
|
today
|
||||||
|
|> Timex.shift(days: -1)
|
||||||
|
|> Timex.beginning_of_day()
|
||||||
|
|
||||||
|
users_and_statuses =
|
||||||
|
%{
|
||||||
|
local: true,
|
||||||
|
order_by: :inserted_at
|
||||||
|
}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> where([u], u.inserted_at >= ^a_day_ago and u.inserted_at < ^today)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn user ->
|
||||||
|
latest_status =
|
||||||
|
Activity
|
||||||
|
|> Activity.Queries.by_actor(user.ap_id)
|
||||||
|
|> Activity.Queries.by_type("Create")
|
||||||
|
|> Activity.with_preloaded_object()
|
||||||
|
|> order_by(desc: :inserted_at)
|
||||||
|
|> limit(1)
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
|
total_statuses =
|
||||||
|
Activity
|
||||||
|
|> Activity.Queries.by_actor(user.ap_id)
|
||||||
|
|> Activity.Queries.by_type("Create")
|
||||||
|
|> Repo.aggregate(:count, :id)
|
||||||
|
|
||||||
|
{user, total_statuses, latest_status}
|
||||||
|
end)
|
||||||
|
|
||||||
|
if users_and_statuses != [] do
|
||||||
|
%{is_admin: true}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses))
|
||||||
|
|> Enum.each(&Pleroma.Emails.Mailer.deliver/1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,7 +17,11 @@ def up do
|
||||||
Repo.stream(query)
|
Repo.stream(query)
|
||||||
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
|
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
|
||||||
Enum.each(bookmarks, fn ap_id ->
|
Enum.each(bookmarks, fn ap_id ->
|
||||||
activity = Activity.get_create_by_object_ap_id(ap_id)
|
activity =
|
||||||
|
ap_id
|
||||||
|
|> Activity.create_by_object_ap_id()
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
|
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
|
defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
|
||||||
use Ecto.Migration
|
|
||||||
import Ecto.Query
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
import Ecto.Query
|
||||||
|
require Logger
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
def change do
|
def change do
|
||||||
query =
|
query =
|
||||||
|
@ -19,6 +20,9 @@ def change do
|
||||||
:following_address
|
:following_address
|
||||||
])
|
])
|
||||||
|> Pleroma.Repo.update()
|
|> Pleroma.Repo.update()
|
||||||
|
|
||||||
|
user ->
|
||||||
|
Logger.warn("User #{user.id} / #{user.nickname} does not seem to have source_data")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do
|
||||||
use Ecto.Migration
|
use Ecto.Migration
|
||||||
|
|
||||||
def change do
|
def change do
|
||||||
|
execute("update users set info = '{}' where info is null")
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
"update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
|
"update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
|
||||||
)
|
)
|
||||||
|
|
|
@ -138,6 +138,8 @@ test "when association is not loaded" do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([:instance, :limit_to_local_content])
|
||||||
|
|
||||||
test "finds utf8 text in statuses", %{
|
test "finds utf8 text in statuses", %{
|
||||||
japanese_activity: japanese_activity,
|
japanese_activity: japanese_activity,
|
||||||
user: user
|
user: user
|
||||||
|
@ -165,7 +167,6 @@ test "find only local statuses for unauthenticated users when `limit_to_local_c
|
||||||
%{local_activity: local_activity} do
|
%{local_activity: local_activity} do
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
|
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
|
||||||
assert [^local_activity] = Activity.search(nil, "find me")
|
assert [^local_activity] = Activity.search(nil, "find me")
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`",
|
test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`",
|
||||||
|
@ -178,8 +179,6 @@ test "find all statuses for unauthenticated users when `limit_to_local_content`
|
||||||
activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id)
|
activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id)
|
||||||
|
|
||||||
assert [^local_activity, ^remote_activity] = activities
|
assert [^local_activity, ^remote_activity] = activities
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||||
|
|
||||||
describe "headers/2" do
|
describe "headers/2" do
|
||||||
clear_config([:http, :send_user_agent])
|
clear_config([:http, :send_user_agent])
|
||||||
|
clear_config([:http, :user_agent])
|
||||||
|
|
||||||
test "don't send pleroma user agent" do
|
test "don't send pleroma user agent" do
|
||||||
assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []}
|
assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []}
|
||||||
|
|
|
@ -75,6 +75,7 @@ test "ensures cache is cleared for the object" do
|
||||||
|
|
||||||
describe "delete attachments" do
|
describe "delete attachments" do
|
||||||
clear_config([Pleroma.Upload])
|
clear_config([Pleroma.Upload])
|
||||||
|
clear_config([:instance, :cleanup_attachments])
|
||||||
|
|
||||||
test "Disabled via config" do
|
test "Disabled via config" do
|
||||||
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||||
|
|
|
@ -23,6 +23,8 @@ test "does nothing if a user is assigned", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when secret set it assigns an admin user" do
|
describe "when secret set it assigns an admin user" do
|
||||||
|
clear_config([:admin_token])
|
||||||
|
|
||||||
test "with `admin_token` query parameter", %{conn: conn} do
|
test "with `admin_token` query parameter", %{conn: conn} do
|
||||||
Pleroma.Config.put(:admin_token, "password123")
|
Pleroma.Config.put(:admin_token, "password123")
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
||||||
|
|
||||||
clear_config([:http_securiy, :enabled])
|
clear_config([:http_securiy, :enabled])
|
||||||
clear_config([:http_security, :sts])
|
clear_config([:http_security, :sts])
|
||||||
|
clear_config([:http_security, :referrer_policy])
|
||||||
|
|
||||||
describe "http security enabled" do
|
describe "http security enabled" do
|
||||||
setup do
|
setup do
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
|
||||||
alias Pleroma.Web.Plugs.HTTPSignaturePlug
|
alias Pleroma.Web.Plugs.HTTPSignaturePlug
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
import Phoenix.Controller, only: [put_format: 2]
|
||||||
import Mock
|
import Mock
|
||||||
|
|
||||||
test "it call HTTPSignatures to check validity if the actor sighed it" do
|
test "it call HTTPSignatures to check validity if the actor sighed it" do
|
||||||
|
@ -20,10 +21,69 @@ test "it call HTTPSignatures to check validity if the actor sighed it" do
|
||||||
"signature",
|
"signature",
|
||||||
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
)
|
)
|
||||||
|
|> put_format("activity+json")
|
||||||
|> HTTPSignaturePlug.call(%{})
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
assert conn.assigns.valid_signature == true
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == false
|
||||||
assert called(HTTPSignatures.validate_conn(:_))
|
assert called(HTTPSignatures.validate_conn(:_))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "requires a signature when `authorized_fetch_mode` is enabled" do
|
||||||
|
setup do
|
||||||
|
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], true)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false)
|
||||||
|
end)
|
||||||
|
|
||||||
|
params = %{"actor" => "http://mastodon.example.org/users/admin"}
|
||||||
|
conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json")
|
||||||
|
|
||||||
|
[conn: conn]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when signature header is present", %{conn: conn} do
|
||||||
|
with_mock HTTPSignatures, validate_conn: fn _ -> false end do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == false
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.status == 401
|
||||||
|
assert conn.state == :sent
|
||||||
|
assert conn.resp_body == "Request not signed"
|
||||||
|
assert called(HTTPSignatures.validate_conn(:_))
|
||||||
|
end
|
||||||
|
|
||||||
|
with_mock HTTPSignatures, validate_conn: fn _ -> true end do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == false
|
||||||
|
assert called(HTTPSignatures.validate_conn(:_))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "halts the connection when `signature` header is not present", %{conn: conn} do
|
||||||
|
conn = HTTPSignaturePlug.call(conn, %{})
|
||||||
|
assert conn.assigns[:valid_signature] == nil
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.status == 401
|
||||||
|
assert conn.state == :sent
|
||||||
|
assert conn.resp_body == "Request not signed"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,10 @@ defmodule Pleroma.Plugs.RemoteIpTest do
|
||||||
|
|
||||||
alias Pleroma.Plugs.RemoteIp
|
alias Pleroma.Plugs.RemoteIp
|
||||||
|
|
||||||
|
import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2]
|
||||||
|
|
||||||
|
clear_config(RemoteIp)
|
||||||
|
|
||||||
test "disabled" do
|
test "disabled" do
|
||||||
Pleroma.Config.put(RemoteIp, enabled: false)
|
Pleroma.Config.put(RemoteIp, enabled: false)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ defmodule Pleroma.Plugs.UserEnabledPlugTest do
|
||||||
alias Pleroma.Plugs.UserEnabledPlug
|
alias Pleroma.Plugs.UserEnabledPlug
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
clear_config([:instance, :account_activation_required])
|
||||||
|
|
||||||
test "doesn't do anything if the user isn't set", %{conn: conn} do
|
test "doesn't do anything if the user isn't set", %{conn: conn} do
|
||||||
ret_conn =
|
ret_conn =
|
||||||
conn
|
conn
|
||||||
|
@ -18,7 +20,6 @@ test "doesn't do anything if the user isn't set", %{conn: conn} do
|
||||||
|
|
||||||
test "with a user that's not confirmed and a config requiring confirmation, it removes that user",
|
test "with a user that's not confirmed and a config requiring confirmation, it removes that user",
|
||||||
%{conn: conn} do
|
%{conn: conn} do
|
||||||
old = Pleroma.Config.get([:instance, :account_activation_required])
|
|
||||||
Pleroma.Config.put([:instance, :account_activation_required], true)
|
Pleroma.Config.put([:instance, :account_activation_required], true)
|
||||||
|
|
||||||
user = insert(:user, confirmation_pending: true)
|
user = insert(:user, confirmation_pending: true)
|
||||||
|
@ -29,8 +30,6 @@ test "with a user that's not confirmed and a config requiring confirmation, it r
|
||||||
|> UserEnabledPlug.call(%{})
|
|> UserEnabledPlug.call(%{})
|
||||||
|
|
||||||
assert conn.assigns.user == nil
|
assert conn.assigns.user == nil
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :account_activation_required], old)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with a user that is deactivated, it removes that user", %{conn: conn} do
|
test "with a user that is deactivated, it removes that user", %{conn: conn} do
|
||||||
|
|
|
@ -67,6 +67,8 @@ test "return error if has not assoc " do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
|
||||||
|
|
||||||
test "raises if it detects unapplied migrations" do
|
test "raises if it detects unapplied migrations" do
|
||||||
assert_raise Pleroma.Repo.UnappliedMigrationsError, fn ->
|
assert_raise Pleroma.Repo.UnappliedMigrationsError, fn ->
|
||||||
capture_log(&Repo.check_migrations_applied!/0)
|
capture_log(&Repo.check_migrations_applied!/0)
|
||||||
|
@ -74,18 +76,8 @@ test "raises if it detects unapplied migrations" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "doesn't do anything if disabled" do
|
test "doesn't do anything if disabled" do
|
||||||
disable_migration_check =
|
|
||||||
Pleroma.Config.get([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
|
|
||||||
|
|
||||||
Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true)
|
Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true)
|
||||||
|
|
||||||
on_exit(fn ->
|
|
||||||
Pleroma.Config.put(
|
|
||||||
[:i_am_aware_this_may_cause_data_loss, :disable_migration_check],
|
|
||||||
disable_migration_check
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert :ok == Repo.check_migrations_applied!()
|
assert :ok == Repo.check_migrations_applied!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,6 +26,7 @@ defmacro clear_config(config_path, do: yield) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Stores initial config value and restores it after *all* test examples are executed."
|
||||||
defmacro clear_config_all(config_path) do
|
defmacro clear_config_all(config_path) do
|
||||||
quote do
|
quote do
|
||||||
clear_config_all(unquote(config_path)) do
|
clear_config_all(unquote(config_path)) do
|
||||||
|
@ -33,6 +34,11 @@ defmacro clear_config_all(config_path) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Stores initial config value and restores it after *all* test examples are executed.
|
||||||
|
Only use if *all* test examples should work with the same stubbed value
|
||||||
|
(*no* examples set a different value).
|
||||||
|
"""
|
||||||
defmacro clear_config_all(config_path, do: yield) do
|
defmacro clear_config_all(config_path, do: yield) do
|
||||||
quote do
|
quote do
|
||||||
setup_all do
|
setup_all do
|
||||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.UserSearchTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "User.search" do
|
describe "User.search" do
|
||||||
|
clear_config([:instance, :limit_to_local_content])
|
||||||
|
|
||||||
test "excluded invisible users from results" do
|
test "excluded invisible users from results" do
|
||||||
user = insert(:user, %{nickname: "john t1000"})
|
user = insert(:user, %{nickname: "john t1000"})
|
||||||
insert(:user, %{invisible: true, nickname: "john t800"})
|
insert(:user, %{invisible: true, nickname: "john t800"})
|
||||||
|
@ -127,8 +129,6 @@ test "find only local users for authenticated users when `limit_to_local_content
|
||||||
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
|
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
|
||||||
|
|
||||||
assert [%{id: ^id}] = User.search("lain")
|
assert [%{id: ^id}] = User.search("lain")
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
|
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
|
||||||
|
@ -145,8 +145,6 @@ test "find all users for unauthenticated users when `limit_to_local_content` is
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|
|
||||||
assert [u1.id, u2.id, u3.id] == results
|
assert [u1.id, u2.id, u3.id] == results
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not yield false-positive matches" do
|
test "does not yield false-positive matches" do
|
||||||
|
|
|
@ -297,15 +297,7 @@ test "local users do not automatically follow local locked accounts" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "unfollow/2" do
|
describe "unfollow/2" do
|
||||||
setup do
|
clear_config([:instance, :external_user_synchronization])
|
||||||
setting = Pleroma.Config.get([:instance, :external_user_synchronization])
|
|
||||||
|
|
||||||
on_exit(fn ->
|
|
||||||
Pleroma.Config.put([:instance, :external_user_synchronization], setting)
|
|
||||||
end)
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "unfollow with syncronizes external user" do
|
test "unfollow with syncronizes external user" do
|
||||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||||
|
@ -383,6 +375,7 @@ test "fetches correct profile for nickname beginning with number" do
|
||||||
password_confirmation: "test",
|
password_confirmation: "test",
|
||||||
email: "email@example.com"
|
email: "email@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_config([:instance, :autofollowed_nicknames])
|
clear_config([:instance, :autofollowed_nicknames])
|
||||||
clear_config([:instance, :welcome_message])
|
clear_config([:instance, :welcome_message])
|
||||||
clear_config([:instance, :welcome_user_nickname])
|
clear_config([:instance, :welcome_user_nickname])
|
||||||
|
@ -1754,17 +1747,14 @@ test "changes email", %{user: user} do
|
||||||
|
|
||||||
describe "get_cached_by_nickname_or_id" do
|
describe "get_cached_by_nickname_or_id" do
|
||||||
setup do
|
setup do
|
||||||
limit_to_local_content = Pleroma.Config.get([:instance, :limit_to_local_content])
|
|
||||||
local_user = insert(:user)
|
local_user = insert(:user)
|
||||||
remote_user = insert(:user, nickname: "nickname@example.com", local: false)
|
remote_user = insert(:user, nickname: "nickname@example.com", local: false)
|
||||||
|
|
||||||
on_exit(fn ->
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local_content)
|
|
||||||
end)
|
|
||||||
|
|
||||||
[local_user: local_user, remote_user: remote_user]
|
[local_user: local_user, remote_user: remote_user]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([:instance, :limit_to_local_content])
|
||||||
|
|
||||||
test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
|
test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
|
||||||
remote_user: remote_user
|
remote_user: remote_user
|
||||||
} do
|
} do
|
||||||
|
|
|
@ -1224,6 +1224,8 @@ test "creates an undo activity for the last block" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "deletion" do
|
describe "deletion" do
|
||||||
|
clear_config([:instance, :rewrite_policy])
|
||||||
|
|
||||||
test "it creates a delete activity and deletes the original object" do
|
test "it creates a delete activity and deletes the original object" do
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
object = Object.normalize(note)
|
object = Object.normalize(note)
|
||||||
|
@ -1327,11 +1329,8 @@ test "decreases reply count" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes delete activity through MRF before deleting the object" do
|
test "it passes delete activity through MRF before deleting the object" do
|
||||||
rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
|
|
||||||
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
|
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
|
||||||
|
|
||||||
on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
|
|
||||||
|
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
object = Object.normalize(note)
|
object = Object.normalize(note)
|
||||||
|
|
||||||
|
@ -1396,6 +1395,8 @@ test "it filters broken threads" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "update" do
|
describe "update" do
|
||||||
|
clear_config([:instance, :max_pinned_statuses])
|
||||||
|
|
||||||
test "it creates an update activity with the new user data" do
|
test "it creates an update activity with the new user data" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
{:ok, user} = User.ensure_keys_present(user)
|
||||||
|
|
|
@ -26,6 +26,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
[user: user, message: message]
|
[user: user, message: message]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config(:mrf_hellthread)
|
||||||
|
|
||||||
describe "reject" do
|
describe "reject" do
|
||||||
test "rejects the message if the recipient count is above reject_threshold", %{
|
test "rejects the message if the recipient count is above reject_threshold", %{
|
||||||
message: message
|
message: message
|
||||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy
|
alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy
|
||||||
|
|
||||||
|
clear_config(:mrf_keyword)
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []})
|
Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []})
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.MRF.MentionPolicy
|
alias Pleroma.Web.ActivityPub.MRF.MentionPolicy
|
||||||
|
|
||||||
|
clear_config(:mrf_mention)
|
||||||
|
|
||||||
test "pass filter if allow list is empty" do
|
test "pass filter if allow list is empty" do
|
||||||
Pleroma.Config.delete([:mrf_mention])
|
Pleroma.Config.delete([:mrf_mention])
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do
|
||||||
"object" => %{"content" => "hi"}
|
"object" => %{"content" => "hi"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_config([:mrf_subchain, :match_actor])
|
||||||
|
|
||||||
test "it matches and processes subchains when the actor matches a configured target" do
|
test "it matches and processes subchains when the actor matches a configured target" do
|
||||||
Pleroma.Config.put([:mrf_subchain, :match_actor], %{
|
Pleroma.Config.put([:mrf_subchain, :match_actor], %{
|
||||||
~r/^https:\/\/banned.com/s => [DropPolicy]
|
~r/^https:\/\/banned.com/s => [DropPolicy]
|
||||||
|
|
|
@ -19,6 +19,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "handle_incoming" do
|
describe "handle_incoming" do
|
||||||
|
clear_config([:user, :deny_follow_blocked])
|
||||||
|
|
||||||
test "it works for osada follow request" do
|
test "it works for osada follow request" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -1893,9 +1893,7 @@ test "returns error when status is not exist", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when configuration from database is off", %{conn: conn} do
|
test "when configuration from database is off", %{conn: conn} do
|
||||||
initial = Config.get(:configurable_from_database)
|
|
||||||
Config.put(:configurable_from_database, false)
|
Config.put(:configurable_from_database, false)
|
||||||
on_exit(fn -> Config.put(:configurable_from_database, initial) end)
|
|
||||||
conn = get(conn, "/api/pleroma/admin/config")
|
conn = get(conn, "/api/pleroma/admin/config")
|
||||||
|
|
||||||
assert json_response(conn, 400) ==
|
assert json_response(conn, 400) ==
|
||||||
|
|
|
@ -68,6 +68,7 @@ test "with the safe_dm_mention option set, it does not mention people beyond the
|
||||||
har = insert(:user)
|
har = insert(:user)
|
||||||
jafnhar = insert(:user)
|
jafnhar = insert(:user)
|
||||||
tridi = insert(:user)
|
tridi = insert(:user)
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
|
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "account fetching" do
|
describe "account fetching" do
|
||||||
|
clear_config([:instance, :limit_to_local_content])
|
||||||
|
|
||||||
test "works by id" do
|
test "works by id" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -44,7 +46,6 @@ test "works by nickname" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "works by nickname for remote users" do
|
test "works by nickname for remote users" do
|
||||||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], false)
|
Pleroma.Config.put([:instance, :limit_to_local_content], false)
|
||||||
user = insert(:user, nickname: "user@example.com", local: false)
|
user = insert(:user, nickname: "user@example.com", local: false)
|
||||||
|
|
||||||
|
@ -52,13 +53,11 @@ test "works by nickname for remote users" do
|
||||||
build_conn()
|
build_conn()
|
||||||
|> get("/api/v1/accounts/#{user.nickname}")
|
|> get("/api/v1/accounts/#{user.nickname}")
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
|
|
||||||
assert %{"id" => id} = json_response(conn, 200)
|
assert %{"id" => id} = json_response(conn, 200)
|
||||||
assert id == user.id
|
assert id == user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "respects limit_to_local_content == :all for remote user nicknames" do
|
test "respects limit_to_local_content == :all for remote user nicknames" do
|
||||||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
|
Pleroma.Config.put([:instance, :limit_to_local_content], :all)
|
||||||
|
|
||||||
user = insert(:user, nickname: "user@example.com", local: false)
|
user = insert(:user, nickname: "user@example.com", local: false)
|
||||||
|
@ -67,12 +66,10 @@ test "respects limit_to_local_content == :all for remote user nicknames" do
|
||||||
build_conn()
|
build_conn()
|
||||||
|> get("/api/v1/accounts/#{user.nickname}")
|
|> get("/api/v1/accounts/#{user.nickname}")
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
|
|
||||||
assert json_response(conn, 404)
|
assert json_response(conn, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
|
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
|
||||||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
|
||||||
|
|
||||||
user = insert(:user, nickname: "user@example.com", local: false)
|
user = insert(:user, nickname: "user@example.com", local: false)
|
||||||
|
@ -90,7 +87,6 @@ test "respects limit_to_local_content == :unauthenticated for remote user nickna
|
||||||
|> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
|
|> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
|
||||||
|> get("/api/v1/accounts/#{user.nickname}")
|
|> get("/api/v1/accounts/#{user.nickname}")
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
|
|
||||||
assert %{"id" => id} = json_response(conn, 200)
|
assert %{"id" => id} = json_response(conn, 200)
|
||||||
assert id == user.id
|
assert id == user.id
|
||||||
end
|
end
|
||||||
|
@ -677,6 +673,8 @@ test "returns error when user already registred", %{conn: conn, valid_params: va
|
||||||
assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
|
assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([Pleroma.Plugs.RemoteIp, :enabled])
|
||||||
|
|
||||||
test "rate limit", %{conn: conn} do
|
test "rate limit", %{conn: conn} do
|
||||||
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
|
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
|
||||||
app_token = insert(:oauth_token, user: nil)
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
||||||
|
|
||||||
clear_config([:instance, :federating])
|
clear_config([:instance, :federating])
|
||||||
clear_config([:instance, :allow_relay])
|
clear_config([:instance, :allow_relay])
|
||||||
|
clear_config([:rich_media, :enabled])
|
||||||
|
|
||||||
describe "posting statuses" do
|
describe "posting statuses" do
|
||||||
setup do: oauth_access(["write:statuses"])
|
setup do: oauth_access(["write:statuses"])
|
||||||
|
|
|
@ -7,11 +7,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
|
||||||
import Mock
|
import Mock
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
setup do
|
clear_config(:media_proxy)
|
||||||
media_proxy_config = Config.get([:media_proxy]) || []
|
clear_config([Pleroma.Web.Endpoint, :secret_key_base])
|
||||||
on_exit(fn -> Config.put([:media_proxy], media_proxy_config) end)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
|
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
|
||||||
Config.put([:media_proxy, :enabled], false)
|
Config.put([:media_proxy, :enabled], false)
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MediaProxyTest do
|
||||||
alias Pleroma.Web.MediaProxy.MediaProxyController
|
alias Pleroma.Web.MediaProxy.MediaProxyController
|
||||||
|
|
||||||
clear_config([:media_proxy, :enabled])
|
clear_config([:media_proxy, :enabled])
|
||||||
|
clear_config(Pleroma.Upload)
|
||||||
|
|
||||||
describe "when enabled" do
|
describe "when enabled" do
|
||||||
setup do
|
setup do
|
||||||
|
@ -224,7 +225,6 @@ test "does not change whitelisted urls" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ensure Pleroma.Upload base_url is always whitelisted" do
|
test "ensure Pleroma.Upload base_url is always whitelisted" do
|
||||||
upload_config = Pleroma.Config.get([Pleroma.Upload])
|
|
||||||
media_url = "https://media.pleroma.social"
|
media_url = "https://media.pleroma.social"
|
||||||
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
|
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
|
||||||
|
|
||||||
|
@ -232,8 +232,6 @@ test "ensure Pleroma.Upload base_url is always whitelisted" do
|
||||||
encoded = url(url)
|
encoded = url(url)
|
||||||
|
|
||||||
assert String.starts_with?(encoded, media_url)
|
assert String.starts_with?(encoded, media_url)
|
||||||
|
|
||||||
Pleroma.Config.put([Pleroma.Upload], upload_config)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Web.Metadata.Providers.OpenGraph
|
alias Pleroma.Web.Metadata.Providers.OpenGraph
|
||||||
|
|
||||||
|
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
|
||||||
|
|
||||||
test "it renders all supported types of attachments and skips unknown types" do
|
test "it renders all supported types of attachments and skips unknown types" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
|
||||||
alias Pleroma.Web.Metadata.Utils
|
alias Pleroma.Web.Metadata.Utils
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
|
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
|
||||||
|
|
||||||
test "it renders twitter card for user info" do
|
test "it renders twitter card for user info" do
|
||||||
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
|
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
|
||||||
avatar_url = Utils.attachment_url(User.avatar_url(user))
|
avatar_url = Utils.attachment_url(User.avatar_url(user))
|
||||||
|
|
|
@ -6,7 +6,9 @@ defmodule Pleroma.Web.NodeInfoTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
clear_config([:mrf_simple])
|
clear_config([:mrf_simple])
|
||||||
|
clear_config(:instance)
|
||||||
|
|
||||||
test "GET /.well-known/nodeinfo", %{conn: conn} do
|
test "GET /.well-known/nodeinfo", %{conn: conn} do
|
||||||
links =
|
links =
|
||||||
|
@ -63,11 +65,6 @@ test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns fieldsLimits field", %{conn: conn} do
|
test "returns fieldsLimits field", %{conn: conn} do
|
||||||
max_account_fields = Pleroma.Config.get([:instance, :max_account_fields])
|
|
||||||
max_remote_account_fields = Pleroma.Config.get([:instance, :max_remote_account_fields])
|
|
||||||
account_field_name_length = Pleroma.Config.get([:instance, :account_field_name_length])
|
|
||||||
account_field_value_length = Pleroma.Config.get([:instance, :account_field_value_length])
|
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :max_account_fields], 10)
|
Pleroma.Config.put([:instance, :max_account_fields], 10)
|
||||||
Pleroma.Config.put([:instance, :max_remote_account_fields], 15)
|
Pleroma.Config.put([:instance, :max_remote_account_fields], 15)
|
||||||
Pleroma.Config.put([:instance, :account_field_name_length], 255)
|
Pleroma.Config.put([:instance, :account_field_name_length], 255)
|
||||||
|
@ -82,11 +79,6 @@ test "returns fieldsLimits field", %{conn: conn} do
|
||||||
assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15
|
assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15
|
||||||
assert response["metadata"]["fieldsLimits"]["nameLength"] == 255
|
assert response["metadata"]["fieldsLimits"]["nameLength"] == 255
|
||||||
assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048
|
assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :max_account_fields], max_account_fields)
|
|
||||||
Pleroma.Config.put([:instance, :max_remote_account_fields], max_remote_account_fields)
|
|
||||||
Pleroma.Config.put([:instance, :account_field_name_length], account_field_name_length)
|
|
||||||
Pleroma.Config.put([:instance, :account_field_value_length], account_field_value_length)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
|
test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
|
||||||
|
@ -112,28 +104,28 @@ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
|
||||||
Pleroma.Config.put([:instance, :safe_dm_mentions], option)
|
Pleroma.Config.put([:instance, :safe_dm_mentions], option)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it shows if federation is enabled/disabled", %{conn: conn} do
|
describe "`metadata/federation/enabled`" do
|
||||||
original = Pleroma.Config.get([:instance, :federating])
|
clear_config([:instance, :federating])
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :federating], true)
|
test "it shows if federation is enabled/disabled", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :federating], true)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> get("/nodeinfo/2.1.json")
|
|> get("/nodeinfo/2.1.json")
|
||||||
|> json_response(:ok)
|
|> json_response(:ok)
|
||||||
|
|
||||||
assert response["metadata"]["federation"]["enabled"] == true
|
assert response["metadata"]["federation"]["enabled"] == true
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :federating], false)
|
Pleroma.Config.put([:instance, :federating], false)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> get("/nodeinfo/2.1.json")
|
|> get("/nodeinfo/2.1.json")
|
||||||
|> json_response(:ok)
|
|> json_response(:ok)
|
||||||
|
|
||||||
assert response["metadata"]["federation"]["enabled"] == false
|
assert response["metadata"]["federation"]["enabled"] == false
|
||||||
|
end
|
||||||
Pleroma.Config.put([:instance, :federating], original)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it shows MRF transparency data if enabled", %{conn: conn} do
|
test "it shows MRF transparency data if enabled", %{conn: conn} do
|
||||||
|
|
|
@ -17,7 +17,8 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
||||||
key: "_test",
|
key: "_test",
|
||||||
signing_salt: "cooldude"
|
signing_salt: "cooldude"
|
||||||
]
|
]
|
||||||
clear_config_all([:instance, :account_activation_required])
|
|
||||||
|
clear_config([:instance, :account_activation_required])
|
||||||
|
|
||||||
describe "in OAuth consumer mode, " do
|
describe "in OAuth consumer mode, " do
|
||||||
setup do
|
setup do
|
||||||
|
|
|
@ -96,6 +96,32 @@ test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
||||||
|
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
[%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result
|
||||||
|
|
||||||
|
assert represented_user["id"] == other_user.id
|
||||||
|
end
|
||||||
|
|
||||||
test "/api/v1/pleroma/conversations/:id" do
|
test "/api/v1/pleroma/conversations/:id" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])
|
%{user: other_user, conn: conn} = oauth_access(["read:statuses"])
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.FederatingPlugTest do
|
defmodule Pleroma.Web.FederatingPlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
clear_config_all([:instance, :federating])
|
|
||||||
|
clear_config([:instance, :federating])
|
||||||
|
|
||||||
test "returns and halt the conn when federating is disabled" do
|
test "returns and halt the conn when federating is disabled" do
|
||||||
Pleroma.Config.put([:instance, :federating], false)
|
Pleroma.Config.put([:instance, :federating], false)
|
||||||
|
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.StreamerTest do
|
||||||
@streamer_timeout 150
|
@streamer_timeout 150
|
||||||
@streamer_start_wait 10
|
@streamer_start_wait 10
|
||||||
|
|
||||||
clear_config_all([:instance, :skip_thread_containment])
|
clear_config([:instance, :skip_thread_containment])
|
||||||
|
|
||||||
describe "user streams" do
|
describe "user streams" do
|
||||||
setup do
|
setup do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
||||||
|
@ -92,15 +92,13 @@ test "follows user", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
|
|
||||||
response =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
||||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||||
|> response(200)
|
|
||||||
|
|
||||||
assert response =~ "Account followed!"
|
assert redirected_to(conn) == "/users/#{user2.id}"
|
||||||
assert user2.follower_address in User.following(user)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns error when user is deactivated", %{conn: conn} do
|
test "returns error when user is deactivated", %{conn: conn} do
|
||||||
|
@ -149,14 +147,13 @@ test "returns success result when user already in followers", %{conn: conn} do
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
{:ok, _, _, _} = CommonAPI.follow(user, user2)
|
{:ok, _, _, _} = CommonAPI.follow(user, user2)
|
||||||
|
|
||||||
response =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, refresh_record(user))
|
|> assign(:user, refresh_record(user))
|
||||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
||||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||||
|> response(200)
|
|
||||||
|
|
||||||
assert response =~ "Account followed!"
|
assert redirected_to(conn) == "/users/#{user2.id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,14 +162,13 @@ test "follows", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
|
|
||||||
response =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> post(remote_follow_path(conn, :do_follow), %{
|
|> post(remote_follow_path(conn, :do_follow), %{
|
||||||
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
|
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
|
||||||
})
|
})
|
||||||
|> response(200)
|
|
||||||
|
|
||||||
assert response =~ "Account followed!"
|
assert redirected_to(conn) == "/users/#{user2.id}"
|
||||||
assert user2.follower_address in User.following(user)
|
assert user2.follower_address in User.following(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -117,15 +117,8 @@ test "it registers a new user and parses mentions in the bio" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "register with one time token" do
|
describe "register with one time token" do
|
||||||
setup do
|
clear_config([:instance, :registrations_open]) do
|
||||||
setting = Pleroma.Config.get([:instance, :registrations_open])
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
|
||||||
if setting do
|
|
||||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
|
||||||
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns user on success" do
|
test "returns user on success" do
|
||||||
|
@ -191,14 +184,11 @@ test "returns error on expired token" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "registers with date limited token" do
|
describe "registers with date limited token" do
|
||||||
|
clear_config([:instance, :registrations_open]) do
|
||||||
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
end
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
setting = Pleroma.Config.get([:instance, :registrations_open])
|
|
||||||
|
|
||||||
if setting do
|
|
||||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
|
||||||
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
"nickname" => "vinny",
|
"nickname" => "vinny",
|
||||||
"email" => "pasta@pizza.vs",
|
"email" => "pasta@pizza.vs",
|
||||||
|
@ -256,15 +246,8 @@ test "returns an error on overdue date", %{data: data} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "registers with reusable token" do
|
describe "registers with reusable token" do
|
||||||
setup do
|
clear_config([:instance, :registrations_open]) do
|
||||||
setting = Pleroma.Config.get([:instance, :registrations_open])
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
|
||||||
if setting do
|
|
||||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
|
||||||
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns user on success, after him registration fails" do
|
test "returns user on success, after him registration fails" do
|
||||||
|
@ -309,15 +292,8 @@ test "returns user on success, after him registration fails" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "registers with reusable date limited token" do
|
describe "registers with reusable date limited token" do
|
||||||
setup do
|
clear_config([:instance, :registrations_open]) do
|
||||||
setting = Pleroma.Config.get([:instance, :registrations_open])
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
|
||||||
if setting do
|
|
||||||
Pleroma.Config.put([:instance, :registrations_open], false)
|
|
||||||
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns user on success" do
|
test "returns user on success" do
|
||||||
|
|
|
@ -19,7 +19,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||||
|
|
||||||
clear_config([:instance])
|
clear_config([:instance])
|
||||||
clear_config([:frontend_configurations, :pleroma_fe])
|
clear_config([:frontend_configurations, :pleroma_fe])
|
||||||
clear_config([:user, :deny_follow_blocked])
|
|
||||||
|
|
||||||
describe "POST /api/pleroma/follow_import" do
|
describe "POST /api/pleroma/follow_import" do
|
||||||
setup do: oauth_access(["follow"])
|
setup do: oauth_access(["follow"])
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Workers.Cron.NewUsersDigestWorker
|
||||||
|
|
||||||
|
test "it sends new users digest emails" do
|
||||||
|
yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
|
||||||
|
admin = insert(:user, %{is_admin: true})
|
||||||
|
user = insert(:user, %{inserted_at: yesterday})
|
||||||
|
user2 = insert(:user, %{inserted_at: yesterday})
|
||||||
|
CommonAPI.post(user, %{"status" => "cofe"})
|
||||||
|
|
||||||
|
NewUsersDigestWorker.perform(nil, nil)
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert_received {:email, email}
|
||||||
|
assert email.to == [{admin.name, admin.email}]
|
||||||
|
assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users"
|
||||||
|
|
||||||
|
refute email.html_body =~ admin.nickname
|
||||||
|
assert email.html_body =~ user.nickname
|
||||||
|
assert email.html_body =~ user2.nickname
|
||||||
|
assert email.html_body =~ "cofe"
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue