Merge branch 'develop' into bugfix/web-notification-special-char
This commit is contained in:
commit
cd6da3606b
|
@ -3,7 +3,6 @@
|
||||||
/db
|
/db
|
||||||
/deps
|
/deps
|
||||||
/*.ez
|
/*.ez
|
||||||
/uploads
|
|
||||||
/test/uploads
|
/test/uploads
|
||||||
/.elixir_ls
|
/.elixir_ls
|
||||||
/test/fixtures/test_tmp.txt
|
/test/fixtures/test_tmp.txt
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -16,11 +16,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Configuration: `link_name` option
|
- Configuration: `link_name` option
|
||||||
- Configuration: `fetch_initial_posts` option
|
- Configuration: `fetch_initial_posts` option
|
||||||
- Configuration: `notify_email` option
|
- Configuration: `notify_email` option
|
||||||
- Pleroma API: User subscribtions
|
- Configuration: Media proxy `whitelist` option
|
||||||
|
- Pleroma API: User subscriptions
|
||||||
|
- Pleroma API: Healthcheck endpoint
|
||||||
- Admin API: Endpoints for listing/revoking invite tokens
|
- Admin API: Endpoints for listing/revoking invite tokens
|
||||||
- Admin API: Endpoints for making users follow/unfollow each other
|
- Admin API: Endpoints for making users follow/unfollow each other
|
||||||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||||
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
||||||
|
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
|
||||||
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
||||||
- ActivityPub C2S: OAuth endpoints
|
- ActivityPub C2S: OAuth endpoints
|
||||||
- Metadata RelMe provider
|
- Metadata RelMe provider
|
||||||
|
@ -38,11 +41,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Configuration: Dedupe enabled by default
|
- Configuration: Dedupe enabled by default
|
||||||
- Configuration: Added `extra_cookie_attrs` for setting non-standard cookie attributes. Defaults to ["SameSite=Lax"] so that remote follows work.
|
- Configuration: Added `extra_cookie_attrs` for setting non-standard cookie attributes. Defaults to ["SameSite=Lax"] so that remote follows work.
|
||||||
- Pleroma API: Support for emoji tags in `/api/pleroma/emoji` resulting in a breaking API change
|
- Pleroma API: Support for emoji tags in `/api/pleroma/emoji` resulting in a breaking API change
|
||||||
|
- Timelines: Messages involving people you have blocked will be excluded from the timeline in all cases instead of just repeats.
|
||||||
- Mastodon API: Support for `exclude_types`, `limit` and `min_id` in `/api/v1/notifications`
|
- Mastodon API: Support for `exclude_types`, `limit` and `min_id` in `/api/v1/notifications`
|
||||||
- Mastodon API: Add `languages` and `registrations` to `/api/v1/instance`
|
- Mastodon API: Add `languages` and `registrations` to `/api/v1/instance`
|
||||||
- Mastodon API: Provide plaintext versions of cw/content in the Status entity
|
- Mastodon API: Provide plaintext versions of cw/content in the Status entity
|
||||||
- Mastodon API: Add `pleroma.conversation_id` field to the Status entity
|
- Mastodon API: Add `pleroma.conversation_id`, `pleroma.in_reply_to_account_acct` fields to the Status entity
|
||||||
- Mastodon API: Add `pleroma.tags`, `pleroma.relationship{}`, `pleroma.is_moderator`, `pleroma.is_admin`, `pleroma.confirmation_pending` fields to the User entity
|
- Mastodon API: Add `pleroma.tags`, `pleroma.relationship{}`, `pleroma.is_moderator`, `pleroma.is_admin`, `pleroma.confirmation_pending`, `pleroma.hide_followers`, `pleroma.hide_follows`, `pleroma.hide_favorites` fields to the User entity
|
||||||
|
- Mastodon API: Add `pleroma.show_role`, `pleroma.no_rich_text` fields to the Source subentity
|
||||||
|
- Mastodon API: Add support for updating `no_rich_text`, `hide_followers`, `hide_follows`, `hide_favorites`, `show_role` in `PATCH /api/v1/update_credentials`
|
||||||
- Mastodon API: Add `pleroma.is_seen` to the Notification entity
|
- Mastodon API: Add `pleroma.is_seen` to the Notification entity
|
||||||
- Mastodon API: Add `pleroma.local` to the Status entity
|
- Mastodon API: Add `pleroma.local` to the Status entity
|
||||||
- Mastodon API: Add `preview` parameter to `POST /api/v1/statuses`
|
- Mastodon API: Add `preview` parameter to `POST /api/v1/statuses`
|
||||||
|
@ -52,6 +58,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Deps: Updated Cowboy to 2.6
|
- Deps: Updated Cowboy to 2.6
|
||||||
- Deps: Updated Ecto to 3.0.7
|
- Deps: Updated Ecto to 3.0.7
|
||||||
- Don't ship finmoji by default, they can be installed as an emoji pack
|
- Don't ship finmoji by default, they can be installed as an emoji pack
|
||||||
|
- Mastodon API: Added support max_id & since_id for bookmark timeline endpoints.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Followers counter not being updated when a follower is blocked
|
- Followers counter not being updated when a follower is blocked
|
||||||
|
@ -66,16 +73,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Federation: Cope with missing or explicitly nulled address lists
|
- Federation: Cope with missing or explicitly nulled address lists
|
||||||
- Federation: Explicitly ensure activities addressed to `as:Public` become addressed to the followers collection
|
- Federation: Explicitly ensure activities addressed to `as:Public` become addressed to the followers collection
|
||||||
- Federation: Better cope with actors which do not declare a followers collection and use `as:Public` with these semantics
|
- Federation: Better cope with actors which do not declare a followers collection and use `as:Public` with these semantics
|
||||||
|
- Federation: Follow requests from remote users who have been blocked will be automatically rejected if appropriate
|
||||||
- MediaProxy: Parse name from content disposition headers even for non-whitelisted types
|
- MediaProxy: Parse name from content disposition headers even for non-whitelisted types
|
||||||
- MediaProxy: S3 link encoding
|
- MediaProxy: S3 link encoding
|
||||||
- Rich Media: Reject any data which cannot be explicitly encoded into JSON
|
- Rich Media: Reject any data which cannot be explicitly encoded into JSON
|
||||||
- Pleroma API: Importing follows from Mastodon 2.8+
|
- Pleroma API: Importing follows from Mastodon 2.8+
|
||||||
|
- Twitter API: Exposing default scope, `no_rich_text` of the user to anyone
|
||||||
|
- Twitter API: Returning the `role` object in user entity despite `show_role = false`
|
||||||
- Mastodon API: `/api/v1/favourites` serving only public activities
|
- Mastodon API: `/api/v1/favourites` serving only public activities
|
||||||
- Mastodon API: Reblogs having `in_reply_to_id` - `null` even when they are replies
|
- Mastodon API: Reblogs having `in_reply_to_id` - `null` even when they are replies
|
||||||
- Mastodon API: Streaming API broadcasting wrong activity id
|
- Mastodon API: Streaming API broadcasting wrong activity id
|
||||||
- Mastodon API: 500 errors when requesting a card for a private conversation
|
- Mastodon API: 500 errors when requesting a card for a private conversation
|
||||||
- Mastodon API: Handling of `reblogs` in `/api/v1/accounts/:id/follow`
|
- Mastodon API: Handling of `reblogs` in `/api/v1/accounts/:id/follow`
|
||||||
- Mastodon API: Correct `reblogged`, `favourited`, and `bookmarked` values in the reblog status JSON
|
- Mastodon API: Correct `reblogged`, `favourited`, and `bookmarked` values in the reblog status JSON
|
||||||
|
- Mastodon API: Exposing default scope of the user to anyone
|
||||||
|
|
||||||
## [0.9.9999] - 2019-04-05
|
## [0.9.9999] - 2019-04-05
|
||||||
### Security
|
### Security
|
||||||
|
|
|
@ -221,7 +221,8 @@
|
||||||
allowed_post_formats: [
|
allowed_post_formats: [
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown"
|
"text/markdown",
|
||||||
|
"text/bbcode"
|
||||||
],
|
],
|
||||||
mrf_transparency: true,
|
mrf_transparency: true,
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
|
@ -230,7 +231,8 @@
|
||||||
welcome_user_nickname: nil,
|
welcome_user_nickname: nil,
|
||||||
welcome_message: nil,
|
welcome_message: nil,
|
||||||
max_report_comment_size: 1000,
|
max_report_comment_size: 1000,
|
||||||
safe_dm_mentions: false
|
safe_dm_mentions: false,
|
||||||
|
healthcheck: false
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
@ -325,7 +327,8 @@
|
||||||
follow_redirect: true,
|
follow_redirect: true,
|
||||||
pool: :media
|
pool: :media
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
whitelist: []
|
||||||
|
|
||||||
config :pleroma, :chat, enabled: true
|
config :pleroma, :chat, enabled: true
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `local`: true if the post was made on the local instance.
|
- `local`: true if the post was made on the local instance.
|
||||||
- `conversation_id`: the ID of the conversation the status is associated with (if any)
|
- `conversation_id`: the ID of the conversation the status is associated with (if any)
|
||||||
|
- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
|
||||||
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
|
|
||||||
|
@ -37,9 +38,18 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `tags`: Lists an array of tags for the user
|
- `tags`: Lists an array of tags for the user
|
||||||
- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/api/entities/#relationship
|
- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/api/entities/#relationship
|
||||||
- `is_moderator`: boolean, true if user is a moderator
|
- `is_moderator`: boolean, nullable, true if user is a moderator
|
||||||
- `is_admin`: boolean, true if user is an admin
|
- `is_admin`: boolean, nullable, true if user is an admin
|
||||||
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
|
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
|
||||||
|
- `hide_followers`: boolean, true when the user has follower hiding enabled
|
||||||
|
- `hide_follows`: boolean, true when the user has follow hiding enabled
|
||||||
|
|
||||||
|
### Source
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown
|
||||||
|
- `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API
|
||||||
|
|
||||||
## Account Search
|
## Account Search
|
||||||
|
|
||||||
|
@ -59,3 +69,14 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
||||||
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
||||||
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
||||||
|
|
||||||
|
## PATCH `/api/v1/update_credentials`
|
||||||
|
|
||||||
|
Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
||||||
|
- `no_rich_text` - if true, html tags are stripped from all statuses requested from the API
|
||||||
|
- `hide_followers` - if true, user's followers will be hidden
|
||||||
|
- `hide_follows` - if true, user's follows will be hidden
|
||||||
|
- `hide_favorites` - if true, user's favorites timeline will be hidden
|
||||||
|
- `show_role` - if true, user's role (e.g admin, moderator) will be exposed to anyone in the API
|
||||||
|
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
||||||
|
|
|
@ -77,7 +77,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
|
||||||
* `token`: invite token required when the registrations aren't public.
|
* `token`: invite token required when the registrations aren't public.
|
||||||
* Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}`
|
* Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}`
|
||||||
* Example response:
|
* Example response:
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"background_image": null,
|
"background_image": null,
|
||||||
"cover_photo": "https://pleroma.soykaf.com/images/banner.png",
|
"cover_photo": "https://pleroma.soykaf.com/images/banner.png",
|
||||||
|
@ -187,6 +187,62 @@ See [Admin-API](Admin-API.md)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/accounts/:id/favourites`
|
||||||
|
### Returns favorites timeline of any user
|
||||||
|
* Method `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params:
|
||||||
|
* `id`: the id of the account for whom to return results
|
||||||
|
* `limit`: optional, the number of records to retrieve
|
||||||
|
* `since_id`: optional, returns results that are more recent than the specified id
|
||||||
|
* `max_id`: optional, returns results that are older than the specified id
|
||||||
|
* Response: JSON, returns a list of Mastodon Status entities on success, otherwise returns `{"error": "error_msg"}`
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"id": "9hptFmUF3ztxYh3Svg",
|
||||||
|
"url": "https://pleroma.example.org/users/nick2",
|
||||||
|
"username": "nick2",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
"application": {"name": "Web", "website": null},
|
||||||
|
"bookmarked": false,
|
||||||
|
"card": null,
|
||||||
|
"content": "This is :moominmamma: note 0",
|
||||||
|
"created_at": "2019-04-15T15:42:15.000Z",
|
||||||
|
"emojis": [],
|
||||||
|
"favourited": false,
|
||||||
|
"favourites_count": 1,
|
||||||
|
"id": "9hptFmVJ02khbzYJaS",
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"language": null,
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"muted": false,
|
||||||
|
"pinned": false,
|
||||||
|
"pleroma": {
|
||||||
|
"content": {"text/plain": "This is :moominmamma: note 0"},
|
||||||
|
"conversation_id": 13679,
|
||||||
|
"local": true,
|
||||||
|
"spoiler_text": {"text/plain": "2hu"}
|
||||||
|
},
|
||||||
|
"reblog": null,
|
||||||
|
"reblogged": false,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"replies_count": 0,
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "2hu",
|
||||||
|
"tags": [{"name": "2hu", "url": "/tag/2hu"}],
|
||||||
|
"uri": "https://pleroma.example.org/objects/198ed2a1-7912-4482-b559-244a0369e984",
|
||||||
|
"url": "https://pleroma.example.org/notice/9hptFmVJ02khbzYJaS",
|
||||||
|
"visibility": "public"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## `/api/pleroma/notification_settings`
|
## `/api/pleroma/notification_settings`
|
||||||
### Updates user notification settings
|
### Updates user notification settings
|
||||||
* Method `PUT`
|
* Method `PUT`
|
||||||
|
@ -197,3 +253,20 @@ See [Admin-API](Admin-API.md)
|
||||||
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
||||||
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
||||||
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
|
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
|
||||||
|
|
||||||
|
## `/api/pleroma/healthcheck`
|
||||||
|
### Healthcheck endpoint with additional system data.
|
||||||
|
* Method `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON, statuses (200 - healthy, 503 unhealthy).
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pool_size": 0, # database connection pool
|
||||||
|
"active": 0, # active processes
|
||||||
|
"idle": 0, # idle processes
|
||||||
|
"memory_used": 0.00, # Memory used
|
||||||
|
"healthy": true # Instance state
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -103,6 +103,7 @@ config :pleroma, Pleroma.Emails.Mailer,
|
||||||
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
||||||
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
|
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
|
||||||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
||||||
|
* `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``.
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
||||||
|
@ -204,6 +205,7 @@ This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:i
|
||||||
* `enabled`: Enables proxying of remote media to the instance’s proxy
|
* `enabled`: Enables proxying of remote media to the instance’s proxy
|
||||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
||||||
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
||||||
|
* `whitelist`: List of domains to bypass the mediaproxy
|
||||||
|
|
||||||
## :gopher
|
## :gopher
|
||||||
* `enabled`: Enables the gopher interface
|
* `enabled`: Enables the gopher interface
|
||||||
|
@ -486,3 +488,8 @@ config :ueberauth, Ueberauth,
|
||||||
microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]}
|
microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## :emoji
|
||||||
|
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
|
||||||
|
* `groups`: Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the groupname and the value the location or array of locations. `*` can be used as a wildcard. Example `[Custom: ["/emoji/*.png", "/emoji/custom/*.png"]]`
|
||||||
|
* `default_manifest`: Location of the JSON-manifest. This manifest contains information about the emoji-packs you can download. Currently only one manifest can be added (no arrays).
|
||||||
|
|
|
@ -28,6 +28,11 @@ foo, /emoji/custom/foo.png
|
||||||
|
|
||||||
The files should be PNG (APNG is okay with `.png` for `image/png` Content-type) and under 50kb for compatibility with mastodon.
|
The files should be PNG (APNG is okay with `.png` for `image/png` Content-type) and under 50kb for compatibility with mastodon.
|
||||||
|
|
||||||
|
Default file extentions and locations for emojis are set in `config.exs`. To use different locations or file-extentions, add the `shortcode_globs` to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it. Note that not all fediverse-software will show emojis with other file extentions:
|
||||||
|
```elixir
|
||||||
|
config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png", "/emoji/custom/**/*.gif"]
|
||||||
|
```
|
||||||
|
|
||||||
## Emoji tags (groups)
|
## Emoji tags (groups)
|
||||||
|
|
||||||
Default tags are set in `config.exs`. To set your own tags, copy the structure to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it.
|
Default tags are set in `config.exs`. To set your own tags, copy the structure to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it.
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
defmodule Pleroma.Healthcheck do
|
||||||
|
@moduledoc """
|
||||||
|
Module collects metrics about app and assign healthy status.
|
||||||
|
"""
|
||||||
|
alias Pleroma.Healthcheck
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
defstruct pool_size: 0,
|
||||||
|
active: 0,
|
||||||
|
idle: 0,
|
||||||
|
memory_used: 0,
|
||||||
|
healthy: true
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
pool_size: non_neg_integer(),
|
||||||
|
active: non_neg_integer(),
|
||||||
|
idle: non_neg_integer(),
|
||||||
|
memory_used: number(),
|
||||||
|
healthy: boolean()
|
||||||
|
}
|
||||||
|
|
||||||
|
@spec system_info() :: t()
|
||||||
|
def system_info do
|
||||||
|
%Healthcheck{
|
||||||
|
memory_used: Float.round(:erlang.memory(:total) / 1024 / 1024, 2)
|
||||||
|
}
|
||||||
|
|> assign_db_info()
|
||||||
|
|> check_health()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assign_db_info(healthcheck) do
|
||||||
|
database = Application.get_env(:pleroma, Repo)[:database]
|
||||||
|
|
||||||
|
query =
|
||||||
|
"select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
|
||||||
|
|
||||||
|
result = Repo.query!(query)
|
||||||
|
pool_size = Application.get_env(:pleroma, Repo)[:pool_size]
|
||||||
|
|
||||||
|
db_info =
|
||||||
|
Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
|
||||||
|
if state == "active" do
|
||||||
|
Map.put(states, :active, states.active + cnt)
|
||||||
|
else
|
||||||
|
Map.put(states, :idle, states.idle + cnt)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Map.put(:pool_size, pool_size)
|
||||||
|
|
||||||
|
Map.merge(healthcheck, db_info)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec check_health(Healthcheck.t()) :: Healthcheck.t()
|
||||||
|
def check_health(%{pool_size: pool_size, active: active} = check)
|
||||||
|
when active >= pool_size do
|
||||||
|
%{check | healthy: false}
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_health(check), do: check
|
||||||
|
end
|
|
@ -11,7 +11,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
||||||
|
|
||||||
## ls-packs
|
## ls-packs
|
||||||
|
|
||||||
mix pleroma.emoji ls-packs [OPTION...]
|
mix pleroma.emoji ls-packs [OPTION...]
|
||||||
|
|
||||||
Lists the emoji packs and metadata specified in the manifest.
|
Lists the emoji packs and metadata specified in the manifest.
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
||||||
|
|
||||||
## get-packs
|
## get-packs
|
||||||
|
|
||||||
mix pleroma.emoji get-packs [OPTION...] PACKS
|
mix pleroma.emoji get-packs [OPTION...] PACKS
|
||||||
|
|
||||||
Fetches, verifies and installs the specified PACKS from the
|
Fetches, verifies and installs the specified PACKS from the
|
||||||
manifest into the `STATIC-DIR/emoji/PACK-NAME
|
manifest into the `STATIC-DIR/emoji/PACK-NAME`
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
||||||
|
|
||||||
## gen-pack
|
## gen-pack
|
||||||
|
|
||||||
mix pleroma.emoji gen-pack PACK-URL
|
mix pleroma.emoji gen-pack PACK-URL
|
||||||
|
|
||||||
Creates a new manifest entry and a file list from the specified
|
Creates a new manifest entry and a file list from the specified
|
||||||
remote pack file. Currently, only .zip archives are recognized
|
remote pack file. Currently, only .zip archives are recognized
|
||||||
|
|
|
@ -162,7 +162,7 @@ def run(["new", nickname, email | rest]) do
|
||||||
def run(["rm", nickname]) do
|
def run(["rm", nickname]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
User.delete(user)
|
User.delete(user)
|
||||||
Mix.shell().info("User #{nickname} deleted.")
|
Mix.shell().info("User #{nickname} deleted.")
|
||||||
else
|
else
|
||||||
|
@ -174,7 +174,7 @@ def run(["rm", nickname]) do
|
||||||
def run(["toggle_activated", nickname]) do
|
def run(["toggle_activated", nickname]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{:ok, user} = User.deactivate(user, !user.info.deactivated)
|
{:ok, user} = User.deactivate(user, !user.info.deactivated)
|
||||||
|
|
||||||
Mix.shell().info(
|
Mix.shell().info(
|
||||||
|
@ -189,7 +189,7 @@ def run(["toggle_activated", nickname]) do
|
||||||
def run(["reset_password", nickname]) do
|
def run(["reset_password", nickname]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname),
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
|
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
|
||||||
Mix.shell().info("Generated password reset token for #{user.nickname}")
|
Mix.shell().info("Generated password reset token for #{user.nickname}")
|
||||||
|
|
||||||
|
@ -211,14 +211,14 @@ def run(["reset_password", nickname]) do
|
||||||
def run(["unsubscribe", nickname]) do
|
def run(["unsubscribe", nickname]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
Mix.shell().info("Deactivating #{user.nickname}")
|
Mix.shell().info("Deactivating #{user.nickname}")
|
||||||
User.deactivate(user)
|
User.deactivate(user)
|
||||||
|
|
||||||
{:ok, friends} = User.get_friends(user)
|
{:ok, friends} = User.get_friends(user)
|
||||||
|
|
||||||
Enum.each(friends, fn friend ->
|
Enum.each(friends, fn friend ->
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
||||||
User.unfollow(user, friend)
|
User.unfollow(user, friend)
|
||||||
|
@ -226,7 +226,7 @@ def run(["unsubscribe", nickname]) do
|
||||||
|
|
||||||
:timer.sleep(500)
|
:timer.sleep(500)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
if Enum.empty?(user.following) do
|
if Enum.empty?(user.following) do
|
||||||
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
|
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
|
||||||
|
@ -250,7 +250,7 @@ def run(["set", nickname | rest]) do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
user =
|
user =
|
||||||
case Keyword.get(options, :moderator) do
|
case Keyword.get(options, :moderator) do
|
||||||
nil -> user
|
nil -> user
|
||||||
|
@ -277,7 +277,7 @@ def run(["set", nickname | rest]) do
|
||||||
def run(["tag", nickname | tags]) do
|
def run(["tag", nickname | tags]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
user = user |> User.tag(tags)
|
user = user |> User.tag(tags)
|
||||||
|
|
||||||
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
||||||
|
@ -290,7 +290,7 @@ def run(["tag", nickname | tags]) do
|
||||||
def run(["untag", nickname | tags]) do
|
def run(["untag", nickname | tags]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
user = user |> User.untag(tags)
|
user = user |> User.untag(tags)
|
||||||
|
|
||||||
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
||||||
|
@ -379,7 +379,7 @@ def run(["revoke_invite", token]) do
|
||||||
def run(["delete_activities", nickname]) do
|
def run(["delete_activities", nickname]) do
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
User.delete_user_activities(user)
|
User.delete_user_activities(user)
|
||||||
Mix.shell().info("User #{nickname} statuses deleted.")
|
Mix.shell().info("User #{nickname} statuses deleted.")
|
||||||
else
|
else
|
||||||
|
|
|
@ -39,7 +39,7 @@ def used_changeset(struct) do
|
||||||
|
|
||||||
def reset_password(token, data) do
|
def reset_password(token, data) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
%User{} = user <- User.get_by_id(token.user_id),
|
%User{} = user <- User.get_cached_by_id(token.user_id),
|
||||||
{:ok, _user} <- User.reset_password(user, data),
|
{:ok, _user} <- User.reset_password(user, data),
|
||||||
{:ok, token} <- Repo.update(used_changeset(token)) do
|
{:ok, token} <- Repo.update(used_changeset(token)) do
|
||||||
{:ok, token}
|
{:ok, token}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
defmodule Pleroma.Bookmark do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.FlakeId
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
schema "bookmarks" do
|
||||||
|
belongs_to(:user, User, type: FlakeId)
|
||||||
|
belongs_to(:activity, Activity, type: FlakeId)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec create(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||||
|
def create(user_id, activity_id) do
|
||||||
|
attrs = %{
|
||||||
|
user_id: user_id,
|
||||||
|
activity_id: activity_id
|
||||||
|
}
|
||||||
|
|
||||||
|
%Bookmark{}
|
||||||
|
|> cast(attrs, [:user_id, :activity_id])
|
||||||
|
|> validate_required([:user_id, :activity_id])
|
||||||
|
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec for_user_query(FlakeId.t()) :: Ecto.Query.t()
|
||||||
|
def for_user_query(user_id) do
|
||||||
|
Bookmark
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> join(:inner, [b], activity in assoc(b, :activity))
|
||||||
|
|> preload([b, a], activity: a)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(user_id, activity_id) do
|
||||||
|
Bookmark
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> where(activity_id: ^activity_id)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec destroy(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||||
|
def destroy(user_id, activity_id) do
|
||||||
|
from(b in Bookmark,
|
||||||
|
where: b.user_id == ^user_id,
|
||||||
|
where: b.activity_id == ^activity_id
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
|> Repo.delete()
|
||||||
|
end
|
||||||
|
end
|
|
@ -76,7 +76,7 @@ def render_activities(activities) do
|
||||||
|> Enum.map(fn activity ->
|
|> Enum.map(fn activity ->
|
||||||
user = User.get_cached_by_ap_id(activity.data["actor"])
|
user = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
|
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
like_count = object["like_count"] || 0
|
like_count = object["like_count"] || 0
|
||||||
announcement_count = object["announcement_count"] || 0
|
announcement_count = object["announcement_count"] || 0
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,14 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
|
|
||||||
# links
|
# links
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "class", [
|
||||||
|
"hashtag",
|
||||||
|
"u-url",
|
||||||
|
"mention",
|
||||||
|
"u-url mention",
|
||||||
|
"mention u-url"
|
||||||
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
"tag",
|
"tag",
|
||||||
|
@ -115,12 +122,15 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
"noreferrer"
|
"noreferrer"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||||
|
|
||||||
# paragraphs and linebreaks
|
# paragraphs and linebreaks
|
||||||
Meta.allow_tag_with_these_attributes("br", [])
|
Meta.allow_tag_with_these_attributes("br", [])
|
||||||
Meta.allow_tag_with_these_attributes("p", [])
|
Meta.allow_tag_with_these_attributes("p", [])
|
||||||
|
|
||||||
# microformats
|
# microformats
|
||||||
Meta.allow_tag_with_these_attributes("span", ["class"])
|
Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"])
|
||||||
|
Meta.allow_tag_with_these_attributes("span", [])
|
||||||
|
|
||||||
# allow inline images for custom emoji
|
# allow inline images for custom emoji
|
||||||
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
||||||
|
@ -155,7 +165,14 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
Meta.strip_comments()
|
Meta.strip_comments()
|
||||||
|
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "class", [
|
||||||
|
"hashtag",
|
||||||
|
"u-url",
|
||||||
|
"mention",
|
||||||
|
"u-url mention",
|
||||||
|
"mention u-url"
|
||||||
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
"tag",
|
"tag",
|
||||||
|
@ -164,6 +181,8 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
"noreferrer"
|
"noreferrer"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes("b", [])
|
Meta.allow_tag_with_these_attributes("b", [])
|
||||||
|
@ -177,11 +196,13 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
Meta.allow_tag_with_these_attributes("ol", [])
|
Meta.allow_tag_with_these_attributes("ol", [])
|
||||||
Meta.allow_tag_with_these_attributes("p", [])
|
Meta.allow_tag_with_these_attributes("p", [])
|
||||||
Meta.allow_tag_with_these_attributes("pre", [])
|
Meta.allow_tag_with_these_attributes("pre", [])
|
||||||
Meta.allow_tag_with_these_attributes("span", ["class"])
|
|
||||||
Meta.allow_tag_with_these_attributes("strong", [])
|
Meta.allow_tag_with_these_attributes("strong", [])
|
||||||
Meta.allow_tag_with_these_attributes("u", [])
|
Meta.allow_tag_with_these_attributes("u", [])
|
||||||
Meta.allow_tag_with_these_attributes("ul", [])
|
Meta.allow_tag_with_these_attributes("ul", [])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"])
|
||||||
|
Meta.allow_tag_with_these_attributes("span", [])
|
||||||
|
|
||||||
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
||||||
|
|
||||||
if @allow_inline_images do
|
if @allow_inline_images do
|
||||||
|
|
|
@ -80,7 +80,7 @@ def get_lists_from_activity(%Activity{actor: ap_id}) do
|
||||||
|
|
||||||
# Get lists to which the account belongs.
|
# Get lists to which the account belongs.
|
||||||
def get_lists_account_belongs(%User{} = owner, account_id) do
|
def get_lists_account_belongs(%User{} = owner, account_id) do
|
||||||
user = User.get_by_id(account_id)
|
user = User.get_cached_by_id(account_id)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
|
|
|
@ -196,7 +196,7 @@ def skip?(
|
||||||
|
|
||||||
def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
|
def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
|
||||||
actor = activity.data["actor"]
|
actor = activity.data["actor"]
|
||||||
followed = User.get_by_ap_id(actor)
|
followed = User.get_cached_by_ap_id(actor)
|
||||||
User.following?(user, followed)
|
User.following?(user, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ def fetch_object_from_id(id) do
|
||||||
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
|
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
|
||||||
|
|
||||||
case OStatus.fetch_activity_from_url(id) do
|
case OStatus.fetch_activity_from_url(id) do
|
||||||
{:ok, [activity | _]} -> {:ok, Object.normalize(activity.data["object"], false)}
|
{:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)}
|
||||||
e -> e
|
e -> e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
@ -53,8 +54,8 @@ defmodule Pleroma.User do
|
||||||
field(:search_rank, :float, virtual: true)
|
field(:search_rank, :float, virtual: true)
|
||||||
field(:search_type, :integer, virtual: true)
|
field(:search_type, :integer, virtual: true)
|
||||||
field(:tags, {:array, :string}, default: [])
|
field(:tags, {:array, :string}, default: [])
|
||||||
field(:bookmarks, {:array, :string}, default: [])
|
|
||||||
field(:last_refreshed_at, :naive_datetime_usec)
|
field(:last_refreshed_at, :naive_datetime_usec)
|
||||||
|
has_many(:bookmarks, Bookmark)
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
has_many(:registrations, Registration)
|
has_many(:registrations, Registration)
|
||||||
embeds_one(:info, Pleroma.User.Info)
|
embeds_one(:info, Pleroma.User.Info)
|
||||||
|
@ -269,6 +270,7 @@ defp autofollow_users(user) do
|
||||||
def register(%Ecto.Changeset{} = changeset) do
|
def register(%Ecto.Changeset{} = changeset) do
|
||||||
with {:ok, user} <- Repo.insert(changeset),
|
with {:ok, user} <- Repo.insert(changeset),
|
||||||
{:ok, user} <- autofollow_users(user),
|
{:ok, user} <- autofollow_users(user),
|
||||||
|
{:ok, user} <- set_cache(user),
|
||||||
{:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
|
{:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
|
||||||
{:ok, _} <- try_send_confirmation_email(user) do
|
{:ok, _} <- try_send_confirmation_email(user) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
|
@ -453,10 +455,13 @@ def get_by_guessed_nickname(ap_id) do
|
||||||
name = List.last(String.split(ap_id, "/"))
|
name = List.last(String.split(ap_id, "/"))
|
||||||
nickname = "#{name}@#{domain}"
|
nickname = "#{name}@#{domain}"
|
||||||
|
|
||||||
get_by_nickname(nickname)
|
get_cached_by_nickname(nickname)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cache(user) do
|
def set_cache({:ok, user}), do: set_cache(user)
|
||||||
|
def set_cache({:error, err}), do: {:error, err}
|
||||||
|
|
||||||
|
def set_cache(%User{} = user) do
|
||||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
||||||
|
@ -544,6 +549,7 @@ def get_or_fetch_by_nickname(nickname) do
|
||||||
with [_nick, _domain] <- String.split(nickname, "@"),
|
with [_nick, _domain] <- String.split(nickname, "@"),
|
||||||
{:ok, user} <- fetch_by_nickname(nickname) do
|
{:ok, user} <- fetch_by_nickname(nickname) do
|
||||||
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
|
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
|
||||||
|
# TODO turn into job
|
||||||
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
|
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1002,7 +1008,7 @@ def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||||
|
|
||||||
# helper to handle the block given only an actor's AP id
|
# helper to handle the block given only an actor's AP id
|
||||||
def block(blocker, %{ap_id: ap_id}) do
|
def block(blocker, %{ap_id: ap_id}) do
|
||||||
block(blocker, User.get_by_ap_id(ap_id))
|
block(blocker, get_cached_by_ap_id(ap_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def unblock(blocker, %{ap_id: ap_id}) do
|
def unblock(blocker, %{ap_id: ap_id}) do
|
||||||
|
@ -1032,7 +1038,7 @@ def blocks?(user, %{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribed_to?(user, %{ap_id: ap_id}) do
|
def subscribed_to?(user, %{ap_id: ap_id}) do
|
||||||
with %User{} = target <- User.get_by_ap_id(ap_id) do
|
with %User{} = target <- get_cached_by_ap_id(ap_id) do
|
||||||
Enum.member?(target.info.subscribers, user.ap_id)
|
Enum.member?(target.info.subscribers, user.ap_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1207,7 +1213,7 @@ def fetch_by_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_fetch_by_ap_id(ap_id) do
|
def get_or_fetch_by_ap_id(ap_id) do
|
||||||
user = get_by_ap_id(ap_id)
|
user = get_cached_by_ap_id(ap_id)
|
||||||
|
|
||||||
if !is_nil(user) and !User.needs_update?(user) do
|
if !is_nil(user) and !User.needs_update?(user) do
|
||||||
user
|
user
|
||||||
|
@ -1230,7 +1236,7 @@ def get_or_fetch_by_ap_id(ap_id) do
|
||||||
def get_or_create_instance_user do
|
def get_or_create_instance_user do
|
||||||
relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
|
relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
|
||||||
|
|
||||||
if user = get_by_ap_id(relay_uri) do
|
if user = get_cached_by_ap_id(relay_uri) do
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
changes =
|
changes =
|
||||||
|
@ -1277,13 +1283,11 @@ defp blank?(""), do: nil
|
||||||
defp blank?(n), do: n
|
defp blank?(n), do: n
|
||||||
|
|
||||||
def insert_or_update_user(data) do
|
def insert_or_update_user(data) do
|
||||||
data =
|
data
|
||||||
data
|
|> Map.put(:name, blank?(data[:name]) || data[:nickname])
|
||||||
|> Map.put(:name, blank?(data[:name]) || data[:nickname])
|
|> remote_user_creation()
|
||||||
|
|> Repo.insert(on_conflict: :replace_all, conflict_target: :nickname)
|
||||||
cs = User.remote_user_creation(data)
|
|> set_cache()
|
||||||
|
|
||||||
Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ap_enabled?(%User{local: true}), do: true
|
def ap_enabled?(%User{local: true}), do: true
|
||||||
|
@ -1299,8 +1303,8 @@ def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
||||||
# this is because we have synchronous follow APIs and need to simulate them
|
# this is because we have synchronous follow APIs and need to simulate them
|
||||||
# with an async handshake
|
# with an async handshake
|
||||||
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
||||||
with %User{} = a <- User.get_by_id(a.id),
|
with %User{} = a <- User.get_cached_by_id(a.id),
|
||||||
%User{} = b <- User.get_by_id(b.id) do
|
%User{} = b <- User.get_cached_by_id(b.id) do
|
||||||
{:ok, a, b}
|
{:ok, a, b}
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
@ -1310,8 +1314,8 @@ def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
||||||
|
|
||||||
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
||||||
with :ok <- :timer.sleep(timeout),
|
with :ok <- :timer.sleep(timeout),
|
||||||
%User{} = a <- User.get_by_id(a.id),
|
%User{} = a <- User.get_cached_by_id(a.id),
|
||||||
%User{} = b <- User.get_by_id(b.id) do
|
%User{} = b <- User.get_cached_by_id(b.id) do
|
||||||
{:ok, a, b}
|
{:ok, a, b}
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
@ -1350,7 +1354,7 @@ def tag(user_identifiers, tags) when is_list(user_identifiers) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag(nickname, tags) when is_binary(nickname),
|
def tag(nickname, tags) when is_binary(nickname),
|
||||||
do: tag(User.get_by_nickname(nickname), tags)
|
do: tag(get_by_nickname(nickname), tags)
|
||||||
|
|
||||||
def tag(%User{} = user, tags),
|
def tag(%User{} = user, tags),
|
||||||
do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
|
do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
|
||||||
|
@ -1362,7 +1366,7 @@ def untag(user_identifiers, tags) when is_list(user_identifiers) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def untag(nickname, tags) when is_binary(nickname),
|
def untag(nickname, tags) when is_binary(nickname),
|
||||||
do: untag(User.get_by_nickname(nickname), tags)
|
do: untag(get_by_nickname(nickname), tags)
|
||||||
|
|
||||||
def untag(%User{} = user, tags),
|
def untag(%User{} = user, tags),
|
||||||
do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
|
do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
|
||||||
|
@ -1376,22 +1380,6 @@ defp update_tags(%User{} = user, new_tags) do
|
||||||
updated_user
|
updated_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def bookmark(%User{} = user, status_id) do
|
|
||||||
bookmarks = Enum.uniq(user.bookmarks ++ [status_id])
|
|
||||||
update_bookmarks(user, bookmarks)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unbookmark(%User{} = user, status_id) do
|
|
||||||
bookmarks = Enum.uniq(user.bookmarks -- [status_id])
|
|
||||||
update_bookmarks(user, bookmarks)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_bookmarks(%User{} = user, bookmarks) do
|
|
||||||
user
|
|
||||||
|> change(%{bookmarks: bookmarks})
|
|
||||||
|> update_and_set_cache
|
|
||||||
end
|
|
||||||
|
|
||||||
defp normalize_tags(tags) do
|
defp normalize_tags(tags) do
|
||||||
[tags]
|
[tags]
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|
|
@ -38,6 +38,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:salmon, :string, default: nil)
|
field(:salmon, :string, default: nil)
|
||||||
field(:hide_followers, :boolean, default: false)
|
field(:hide_followers, :boolean, default: false)
|
||||||
field(:hide_follows, :boolean, default: false)
|
field(:hide_follows, :boolean, default: false)
|
||||||
|
field(:hide_favorites, :boolean, default: true)
|
||||||
field(:pinned_activities, {:array, :string}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
field(:flavour, :string, default: nil)
|
field(:flavour, :string, default: nil)
|
||||||
|
|
||||||
|
@ -202,6 +203,7 @@ def profile_update(info, params) do
|
||||||
:banner,
|
:banner,
|
||||||
:hide_follows,
|
:hide_follows,
|
||||||
:hide_followers,
|
:hide_followers,
|
||||||
|
:hide_favorites,
|
||||||
:background,
|
:background,
|
||||||
:show_role
|
:show_role
|
||||||
])
|
])
|
||||||
|
@ -225,14 +227,6 @@ def confirmation_changeset(info, params) do
|
||||||
cast(info, params, [:confirmation_pending, :confirmation_token])
|
cast(info, params, [:confirmation_pending, :confirmation_token])
|
||||||
end
|
end
|
||||||
|
|
||||||
def mastodon_profile_update(info, params) do
|
|
||||||
info
|
|
||||||
|> cast(params, [
|
|
||||||
:locked,
|
|
||||||
:banner
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
def mastodon_settings_update(info, settings) do
|
def mastodon_settings_update(info, settings) do
|
||||||
params = %{settings: settings}
|
params = %{settings: settings}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,6 @@ def stream_out(activity) do
|
||||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
if activity.data["type"] in ["Create", "Announce", "Delete"] do
|
if activity.data["type"] in ["Create", "Announce", "Delete"] do
|
||||||
object = Object.normalize(activity.data["object"])
|
|
||||||
Pleroma.Web.Streamer.stream("user", activity)
|
Pleroma.Web.Streamer.stream("user", activity)
|
||||||
Pleroma.Web.Streamer.stream("list", activity)
|
Pleroma.Web.Streamer.stream("list", activity)
|
||||||
|
|
||||||
|
@ -180,6 +179,8 @@ def stream_out(activity) do
|
||||||
end
|
end
|
||||||
|
|
||||||
if activity.data["type"] in ["Create"] do
|
if activity.data["type"] in ["Create"] do
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
object.data
|
object.data
|
||||||
|> Map.get("tag", [])
|
|> Map.get("tag", [])
|
||||||
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
||||||
|
@ -197,7 +198,7 @@ def stream_out(activity) do
|
||||||
if !Enum.member?(activity.data["cc"] || [], public) &&
|
if !Enum.member?(activity.data["cc"] || [], public) &&
|
||||||
!Enum.member?(
|
!Enum.member?(
|
||||||
activity.data["to"],
|
activity.data["to"],
|
||||||
User.get_by_ap_id(activity.data["actor"]).follower_address
|
User.get_cached_by_ap_id(activity.data["actor"]).follower_address
|
||||||
),
|
),
|
||||||
do: Pleroma.Web.Streamer.stream("direct", activity)
|
do: Pleroma.Web.Streamer.stream("direct", activity)
|
||||||
end
|
end
|
||||||
|
@ -889,7 +890,7 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_user_from_ap_id(ap_id) do
|
def make_user_from_ap_id(ap_id) do
|
||||||
if _user = User.get_by_ap_id(ap_id) do
|
if _user = User.get_cached_by_ap_id(ap_id) do
|
||||||
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||||
else
|
else
|
||||||
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
|
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
|
|
|
@ -438,20 +438,46 @@ def handle_incoming(
|
||||||
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
|
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
|
||||||
%User{} = follower <- User.get_or_fetch_by_ap_id(follower),
|
%User{} = follower <- User.get_or_fetch_by_ap_id(follower),
|
||||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||||
if not User.locked?(followed) do
|
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
|
||||||
|
{:user_blocked, false} <-
|
||||||
|
{:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
|
||||||
|
{:user_locked, false} <- {:user_locked, User.locked?(followed)},
|
||||||
|
{:follow, {:ok, follower}} <- {:follow, User.follow(follower, followed)} do
|
||||||
ActivityPub.accept(%{
|
ActivityPub.accept(%{
|
||||||
to: [follower.ap_id],
|
to: [follower.ap_id],
|
||||||
actor: followed,
|
actor: followed,
|
||||||
object: data,
|
object: data,
|
||||||
local: true
|
local: true
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
{:user_blocked, true} ->
|
||||||
|
{:ok, _} = Utils.update_follow_state(activity, "reject")
|
||||||
|
|
||||||
User.follow(follower, followed)
|
ActivityPub.reject(%{
|
||||||
|
to: [follower.ap_id],
|
||||||
|
actor: followed,
|
||||||
|
object: data,
|
||||||
|
local: true
|
||||||
|
})
|
||||||
|
|
||||||
|
{:follow, {:error, _}} ->
|
||||||
|
{:ok, _} = Utils.update_follow_state(activity, "reject")
|
||||||
|
|
||||||
|
ActivityPub.reject(%{
|
||||||
|
to: [follower.ap_id],
|
||||||
|
actor: followed,
|
||||||
|
object: data,
|
||||||
|
local: true
|
||||||
|
})
|
||||||
|
|
||||||
|
{:user_locked, true} ->
|
||||||
|
:noop
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_e -> :error
|
_e ->
|
||||||
|
:error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -537,7 +563,7 @@ def handle_incoming(
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
when object_type in ["Person", "Application", "Service", "Organization"] do
|
when object_type in ["Person", "Application", "Service", "Organization"] do
|
||||||
with %User{ap_id: ^actor_id} = actor <- User.get_by_ap_id(object["id"]) do
|
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
||||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||||
|
|
||||||
banner = new_user_data[:info]["banner"]
|
banner = new_user_data[:info]["banner"]
|
||||||
|
@ -964,7 +990,7 @@ def perform(:user_upgrade, user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def upgrade_user_from_ap_id(ap_id) do
|
def upgrade_user_from_ap_id(ap_id) do
|
||||||
with %User{local: false} = user <- User.get_by_ap_id(ap_id),
|
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
|
||||||
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
||||||
already_ap <- User.ap_enabled?(user),
|
already_ap <- User.ap_enabled?(user),
|
||||||
{:ok, user} <- user |> User.upgrade_changeset(data) |> User.update_and_set_cache() do
|
{:ok, user} <- user |> User.upgrade_changeset(data) |> User.update_and_set_cache() do
|
||||||
|
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def user_delete(conn, %{"nickname" => nickname}) do
|
def user_delete(conn, %{"nickname" => nickname}) do
|
||||||
User.get_by_nickname(nickname)
|
User.get_cached_by_nickname(nickname)
|
||||||
|> User.delete()
|
|> User.delete()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -27,8 +27,8 @@ def user_delete(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||||
with %User{} = follower <- User.get_by_nickname(follower_nick),
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
%User{} = followed <- User.get_by_nickname(followed_nick) do
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
User.follow(follower, followed)
|
User.follow(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||||
with %User{} = follower <- User.get_by_nickname(follower_nick),
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
%User{} = followed <- User.get_by_nickname(followed_nick) do
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
User.unfollow(follower, followed)
|
User.unfollow(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ def user_create(
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_show(conn, %{"nickname" => nickname}) do
|
def user_show(conn, %{"nickname" => nickname}) do
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
conn
|
conn
|
||||||
|> json(AccountView.render("show.json", %{user: user}))
|
|> json(AccountView.render("show.json", %{user: user}))
|
||||||
else
|
else
|
||||||
|
@ -76,7 +76,7 @@ def user_show(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_toggle_activation(conn, %{"nickname" => nickname}) do
|
def user_toggle_activation(conn, %{"nickname" => nickname}) do
|
||||||
user = User.get_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ defp maybe_parse_filters(filters) do
|
||||||
|
|
||||||
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
||||||
when permission_group in ["moderator", "admin"] do
|
when permission_group in ["moderator", "admin"] do
|
||||||
user = User.get_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
info =
|
info =
|
||||||
%{}
|
%{}
|
||||||
|
@ -156,7 +156,7 @@ def right_add(conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_get(conn, %{"nickname" => nickname}) do
|
def right_get(conn, %{"nickname" => nickname}) do
|
||||||
user = User.get_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(%{
|
|> json(%{
|
||||||
|
@ -178,7 +178,7 @@ def right_delete(
|
||||||
|> put_status(403)
|
|> put_status(403)
|
||||||
|> json(%{error: "You can't revoke your own admin status."})
|
|> json(%{error: "You can't revoke your own admin status."})
|
||||||
else
|
else
|
||||||
user = User.get_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
info =
|
info =
|
||||||
%{}
|
%{}
|
||||||
|
@ -204,7 +204,7 @@ def right_delete(conn, _) do
|
||||||
|
|
||||||
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
|
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
|
||||||
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
||||||
%User{} = user <- User.get_by_nickname(nickname),
|
%User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, _} <- User.deactivate(user, !status),
|
{:ok, _} <- User.deactivate(user, !status),
|
||||||
do: json_response(conn, :no_content, "")
|
do: json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
@ -277,7 +277,7 @@ def revoke_invite(conn, %{"token" => token}) do
|
||||||
|
|
||||||
@doc "Get a password reset token (base64 string) for given nickname"
|
@doc "Get a password reset token (base64 string) for given nickname"
|
||||||
def get_password_reset(conn, %{"nickname" => nickname}) do
|
def get_password_reset(conn, %{"nickname" => nickname}) do
|
||||||
(%User{local: true} = user) = User.get_by_nickname(nickname)
|
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
|
||||||
{:ok, token} = Pleroma.PasswordResetToken.create_token(user)
|
{:ok, token} = Pleroma.PasswordResetToken.create_token(user)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.UserSocket do
|
||||||
def connect(%{"token" => token}, socket) do
|
def connect(%{"token" => token}, socket) do
|
||||||
with true <- Pleroma.Config.get([:chat, :enabled]),
|
with true <- Pleroma.Config.get([:chat, :enabled]),
|
||||||
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
||||||
%User{} = user <- Pleroma.User.get_by_id(user_id) do
|
%User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
|
||||||
{:ok, assign(socket, :user_name, user.nickname)}
|
{:ok, assign(socket, :user_name, user.nickname)}
|
||||||
else
|
else
|
||||||
_e -> :error
|
_e -> :error
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI do
|
defmodule Pleroma.Web.CommonAPI do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.ThreadMute
|
alias Pleroma.ThreadMute
|
||||||
|
@ -282,9 +283,18 @@ def thread_muted?(user, activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bookmarked?(user, activity) do
|
||||||
|
with %Bookmark{} <- Bookmark.get(user.id, activity.id) do
|
||||||
|
true
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def report(user, data) do
|
def report(user, data) do
|
||||||
with {:account_id, %{"account_id" => account_id}} <- {:account_id, data},
|
with {:account_id, %{"account_id" => account_id}} <- {:account_id, data},
|
||||||
{:account, %User{} = account} <- {:account, User.get_by_id(account_id)},
|
{:account, %User{} = account} <- {:account, User.get_cached_by_id(account_id)},
|
||||||
{:ok, {content_html, _, _}} <- make_report_content_html(data["comment"]),
|
{:ok, {content_html, _, _}} <- make_report_content_html(data["comment"]),
|
||||||
{:ok, statuses} <- get_report_statuses(account, data),
|
{:ok, statuses} <- get_report_statuses(account, data),
|
||||||
{:ok, activity} <-
|
{:ok, activity} <-
|
||||||
|
|
|
@ -182,6 +182,18 @@ def format_input(text, "text/plain", options) do
|
||||||
end).()
|
end).()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Formatting text as BBCode.
|
||||||
|
"""
|
||||||
|
def format_input(text, "text/bbcode", options) do
|
||||||
|
text
|
||||||
|
|> String.replace(~r/\r/, "")
|
||||||
|
|> Formatter.html_escape("text/plain")
|
||||||
|
|> BBCode.to_html()
|
||||||
|
|> (fn {:ok, html} -> html end).()
|
||||||
|
|> Formatter.linkify(options)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Formatting text to html.
|
Formatting text to html.
|
||||||
"""
|
"""
|
||||||
|
@ -226,7 +238,7 @@ def make_note_data(
|
||||||
}
|
}
|
||||||
|
|
||||||
if in_reply_to do
|
if in_reply_to do
|
||||||
in_reply_to_object = Object.normalize(in_reply_to.data["object"])
|
in_reply_to_object = Object.normalize(in_reply_to)
|
||||||
|
|
||||||
object
|
object
|
||||||
|> Map.put("inReplyTo", in_reply_to_object.data["id"])
|
|> Map.put("inReplyTo", in_reply_to_object.data["id"])
|
||||||
|
@ -284,7 +296,7 @@ defp shortname(name) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_current_password(user, password) do
|
def confirm_current_password(user, password) do
|
||||||
with %User{local: true} = db_user <- User.get_by_id(user.id),
|
with %User{local: true} = db_user <- User.get_cached_by_id(user.id),
|
||||||
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
|
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
|
||||||
{:ok, db_user}
|
{:ok, db_user}
|
||||||
else
|
else
|
||||||
|
|
|
@ -186,7 +186,7 @@ def perform(type, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def ap_enabled_actor(id) do
|
def ap_enabled_actor(id) do
|
||||||
user = User.get_by_ap_id(id)
|
user = User.get_cached_by_ap_id(id)
|
||||||
|
|
||||||
if User.ap_enabled?(user) do
|
if User.ap_enabled?(user) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -35,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
alias Pleroma.Web.ControllerHelper
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -46,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def create_app(conn, params) do
|
def create_app(conn, params) do
|
||||||
scopes = oauth_scopes(params, ["read"])
|
scopes = ControllerHelper.oauth_scopes(params, ["read"])
|
||||||
|
|
||||||
app_attrs =
|
app_attrs =
|
||||||
params
|
params
|
||||||
|
@ -96,8 +97,13 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
info_params =
|
info_params =
|
||||||
%{}
|
[:no_rich_text, :locked, :hide_followers, :hide_follows, :hide_favorites, :show_role]
|
||||||
|> add_if_present(params, "locked", :locked, fn value -> {:ok, value == "true"} end)
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
|
add_if_present(acc, params, to_string(key), key, fn value ->
|
||||||
|
{:ok, ControllerHelper.truthy_param?(value)}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> add_if_present(params, "default_scope", :default_scope)
|
||||||
|> add_if_present(params, "header", :banner, fn value ->
|
|> add_if_present(params, "header", :banner, fn value ->
|
||||||
with %Plug.Upload{} <- value,
|
with %Plug.Upload{} <- value,
|
||||||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do
|
{:ok, object} <- ActivityPub.upload(value, type: :banner) do
|
||||||
|
@ -107,7 +113,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
info_cng = User.Info.mastodon_profile_update(user.info, info_params)
|
info_cng = User.Info.profile_update(user.info, info_params)
|
||||||
|
|
||||||
with changeset <- User.update_changeset(user, user_params),
|
with changeset <- User.update_changeset(user, user_params),
|
||||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||||
|
@ -279,6 +285,8 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.contain_timeline(user)
|
|> ActivityPub.contain_timeline(user)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:home_timeline, activities)
|
|> add_link_headers(:home_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -297,6 +305,8 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -304,7 +314,8 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- User.get_by_id(params["id"]) do
|
with %User{} = user <- User.get_cached_by_id(params["id"]),
|
||||||
|
reading_user <- Repo.preload(reading_user, :bookmarks) do
|
||||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -331,6 +342,8 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> ActivityPub.fetch_activities_query(params)
|
|> ActivityPub.fetch_activities_query(params)
|
||||||
|> Pagination.fetch_paginated(params)
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:dm_timeline, activities)
|
|> add_link_headers(:dm_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -340,6 +353,8 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||||
true <- Visibility.visible_for_user?(activity, user) do
|
true <- Visibility.visible_for_user?(activity, user) do
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user})
|
|> try_render("status.json", %{activity: activity, for: user})
|
||||||
|
@ -489,6 +504,8 @@ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
||||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
|
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
|
||||||
|
@ -498,6 +515,8 @@ def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -545,10 +564,11 @@ def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
|
||||||
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
%User{} = user <- User.get_by_nickname(user.nickname),
|
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, user} <- User.bookmark(user, object.data["id"]) do
|
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -557,10 +577,11 @@ def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
|
|
||||||
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
%User{} = user <- User.get_by_nickname(user.nickname),
|
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, user} <- User.unbookmark(user, object.data["id"]) do
|
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -750,7 +771,7 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
with %User{} = user <- User.get_by_id(id),
|
with %User{} = user <- User.get_cached_by_id(id),
|
||||||
followers <- MastodonAPI.get_followers(user, params) do
|
followers <- MastodonAPI.get_followers(user, params) do
|
||||||
followers =
|
followers =
|
||||||
cond do
|
cond do
|
||||||
|
@ -767,7 +788,7 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
with %User{} = user <- User.get_by_id(id),
|
with %User{} = user <- User.get_cached_by_id(id),
|
||||||
followers <- MastodonAPI.get_friends(user, params) do
|
followers <- MastodonAPI.get_friends(user, params) do
|
||||||
followers =
|
followers =
|
||||||
cond do
|
cond do
|
||||||
|
@ -792,7 +813,7 @@ def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||||
with %User{} = follower <- User.get_by_id(id),
|
with %User{} = follower <- User.get_cached_by_id(id),
|
||||||
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|
@ -806,7 +827,7 @@ def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||||
with %User{} = follower <- User.get_by_id(id),
|
with %User{} = follower <- User.get_cached_by_id(id),
|
||||||
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|
@ -872,7 +893,7 @@ def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
||||||
with %User{} = muted <- User.get_by_id(id),
|
with %User{} = muted <- User.get_cached_by_id(id),
|
||||||
{:ok, muter} <- User.mute(muter, muted) do
|
{:ok, muter} <- User.mute(muter, muted) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|
@ -886,7 +907,7 @@ def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
||||||
with %User{} = muted <- User.get_by_id(id),
|
with %User{} = muted <- User.get_cached_by_id(id),
|
||||||
{:ok, muter} <- User.unmute(muter, muted) do
|
{:ok, muter} <- User.unmute(muter, muted) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|
@ -907,7 +928,7 @@ def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
||||||
with %User{} = blocked <- User.get_by_id(id),
|
with %User{} = blocked <- User.get_cached_by_id(id),
|
||||||
{:ok, blocker} <- User.block(blocker, blocked),
|
{:ok, blocker} <- User.block(blocker, blocked),
|
||||||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
|
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
|
||||||
conn
|
conn
|
||||||
|
@ -922,7 +943,7 @@ def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
||||||
with %User{} = blocked <- User.get_by_id(id),
|
with %User{} = blocked <- User.get_cached_by_id(id),
|
||||||
{:ok, blocker} <- User.unblock(blocker, blocked),
|
{:ok, blocker} <- User.unblock(blocker, blocked),
|
||||||
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
|
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
|
||||||
conn
|
conn
|
||||||
|
@ -1081,21 +1102,65 @@ def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
ActivityPub.fetch_activities([], params)
|
ActivityPub.fetch_activities([], params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:favourites, activities)
|
|> add_link_headers(:favourites, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def bookmarks(%{assigns: %{user: user}} = conn, _) do
|
def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
user = User.get_by_id(user.id)
|
with %User{} = user <- User.get_by_id(id),
|
||||||
|
false <- user.info.hide_favorites do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.put("type", "Create")
|
||||||
|
|> Map.put("favorited_by", user.ap_id)
|
||||||
|
|> Map.put("blocking_user", for_user)
|
||||||
|
|
||||||
|
recipients =
|
||||||
|
if for_user do
|
||||||
|
["https://www.w3.org/ns/activitystreams#Public"] ++
|
||||||
|
[for_user.ap_id | for_user.following]
|
||||||
|
else
|
||||||
|
["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
end
|
||||||
|
|
||||||
|
activities =
|
||||||
|
recipients
|
||||||
|
|> ActivityPub.fetch_activities(params)
|
||||||
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(:favourites, activities)
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> render("index.json", %{activities: activities, for: for_user, as: :activity})
|
||||||
|
else
|
||||||
|
nil ->
|
||||||
|
{:error, :not_found}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
conn
|
||||||
|
|> put_status(403)
|
||||||
|
|> json(%{error: "Can't get favorites"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
|
bookmarks =
|
||||||
|
Bookmark.for_user_query(user.id)
|
||||||
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
user.bookmarks
|
bookmarks
|
||||||
|> Enum.map(fn id -> Activity.get_create_by_object_ap_id(id) end)
|
|> Enum.map(fn b -> b.activity end)
|
||||||
|> Enum.reverse()
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> add_link_headers(:bookmarks, bookmarks)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
@ -1145,7 +1210,7 @@ def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" =>
|
||||||
accounts
|
accounts
|
||||||
|> Enum.each(fn account_id ->
|
|> Enum.each(fn account_id ->
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||||
%User{} = followed <- User.get_by_id(account_id) do
|
%User{} = followed <- User.get_cached_by_id(account_id) do
|
||||||
Pleroma.List.follow(list, followed)
|
Pleroma.List.follow(list, followed)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -1157,7 +1222,7 @@ def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_id
|
||||||
accounts
|
accounts
|
||||||
|> Enum.each(fn account_id ->
|
|> Enum.each(fn account_id ->
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||||
%User{} = followed <- Pleroma.User.get_by_id(account_id) do
|
%User{} = followed <- Pleroma.User.get_cached_by_id(account_id) do
|
||||||
Pleroma.List.unfollow(list, followed)
|
Pleroma.List.unfollow(list, followed)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -1201,6 +1266,8 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|
||||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, bookmarks: :activity)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
|
@ -1450,7 +1517,7 @@ def logout(conn, _) do
|
||||||
def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
Logger.debug("Unimplemented, returning unmodified relationship")
|
Logger.debug("Unimplemented, returning unmodified relationship")
|
||||||
|
|
||||||
with %User{} = target <- User.get_by_id(id) do
|
with %User{} = target <- User.get_cached_by_id(id) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: user, target: target})
|
|> render("relationship.json", %{user: user, target: target})
|
||||||
|
|
|
@ -68,7 +68,7 @@ def render("relationships.json", %{user: user, targets: targets}) do
|
||||||
defp do_render("account.json", %{user: user} = opts) do
|
defp do_render("account.json", %{user: user} = opts) do
|
||||||
image = User.avatar_url(user) |> MediaProxy.url()
|
image = User.avatar_url(user) |> MediaProxy.url()
|
||||||
header = User.banner_url(user) |> MediaProxy.url()
|
header = User.banner_url(user) |> MediaProxy.url()
|
||||||
user_info = User.user_info(user)
|
user_info = User.get_cached_user_info(user)
|
||||||
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
|
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
|
@ -113,21 +113,23 @@ defp do_render("account.json", %{user: user} = opts) do
|
||||||
bot: bot,
|
bot: bot,
|
||||||
source: %{
|
source: %{
|
||||||
note: "",
|
note: "",
|
||||||
privacy: user_info.default_scope,
|
sensitive: false,
|
||||||
sensitive: false
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
|
|
||||||
# Pleroma extension
|
# Pleroma extension
|
||||||
pleroma:
|
pleroma: %{
|
||||||
%{
|
confirmation_pending: user_info.confirmation_pending,
|
||||||
confirmation_pending: user_info.confirmation_pending,
|
tags: user.tags,
|
||||||
tags: user.tags,
|
hide_followers: user.info.hide_followers,
|
||||||
is_moderator: user.info.is_moderator,
|
hide_follows: user.info.hide_follows,
|
||||||
is_admin: user.info.is_admin,
|
hide_favorites: user.info.hide_favorites,
|
||||||
relationship: relationship
|
relationship: relationship
|
||||||
}
|
}
|
||||||
|> with_notification_settings(user, opts[:for])
|
|
||||||
}
|
}
|
||||||
|
|> maybe_put_role(user, opts[:for])
|
||||||
|
|> maybe_put_settings(user, opts[:for], user_info)
|
||||||
|
|> maybe_put_notification_settings(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp username_from_nickname(string) when is_binary(string) do
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
@ -136,9 +138,37 @@ defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
|
||||||
defp username_from_nickname(_), do: nil
|
defp username_from_nickname(_), do: nil
|
||||||
|
|
||||||
defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
|
defp maybe_put_settings(
|
||||||
Map.put(data, :notification_settings, user.info.notification_settings)
|
data,
|
||||||
|
%User{id: user_id} = user,
|
||||||
|
%User{id: user_id},
|
||||||
|
user_info
|
||||||
|
) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in([:source, :privacy], user_info.default_scope)
|
||||||
|
|> Kernel.put_in([:source, :pleroma, :show_role], user.info.show_role)
|
||||||
|
|> Kernel.put_in([:source, :pleroma, :no_rich_text], user.info.no_rich_text)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_notification_settings(data, _, _), do: data
|
defp maybe_put_settings(data, _, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|
||||||
|
|> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|
||||||
|
|> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_role(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
Kernel.put_in(data, [:pleroma, :notification_settings], user.info.notification_settings)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_notification_settings(data, _, _), do: data
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,7 +31,7 @@ defp get_replied_to_activities(activities) do
|
||||||
|> Activity.create_by_object_ap_id()
|
|> Activity.create_by_object_ap_id()
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Enum.reduce(%{}, fn activity, acc ->
|
|> Enum.reduce(%{}, fn activity, acc ->
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
Map.put(acc, object.data["id"], activity)
|
Map.put(acc, object.data["id"], activity)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -85,7 +85,8 @@ def render(
|
||||||
|
|
||||||
activity_object = Object.normalize(activity)
|
activity_object = Object.normalize(activity)
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
||||||
bookmarked = opts[:for] && activity_object.data["id"] in opts[:for].bookmarks
|
|
||||||
|
bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], reblogged_activity)
|
||||||
|
|
||||||
mentions =
|
mentions =
|
||||||
activity.recipients
|
activity.recipients
|
||||||
|
@ -148,7 +149,7 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
|
||||||
|
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
||||||
|
|
||||||
bookmarked = opts[:for] && object.data["id"] in opts[:for].bookmarks
|
bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], activity)
|
||||||
|
|
||||||
attachment_data = object.data["attachment"] || []
|
attachment_data = object.data["attachment"] || []
|
||||||
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
||||||
|
@ -238,6 +239,7 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
local: activity.local,
|
local: activity.local,
|
||||||
conversation_id: get_context_id(activity),
|
conversation_id: get_context_id(activity),
|
||||||
|
in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
|
||||||
content: %{"text/plain" => content_plaintext},
|
content: %{"text/plain" => content_plaintext},
|
||||||
spoiler_text: %{"text/plain" => summary_plaintext}
|
spoiler_text: %{"text/plain" => summary_plaintext}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +318,7 @@ def render("attachment.json", %{attachment: attachment}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
with nil <- replied_to_activities[object.data["inReplyTo"]] do
|
with nil <- replied_to_activities[object.data["inReplyTo"]] do
|
||||||
# If user didn't participate in the thread
|
# If user didn't participate in the thread
|
||||||
|
|
|
@ -90,7 +90,7 @@ defp allow_request(stream, nil) when stream in @anonymous_streams do
|
||||||
# Authenticated streams.
|
# Authenticated streams.
|
||||||
defp allow_request(stream, {"access_token", access_token}) when stream in @streams do
|
defp allow_request(stream, {"access_token", access_token}) when stream in @streams do
|
||||||
with %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token),
|
with %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token),
|
||||||
user = %User{} <- User.get_by_id(user_id) do
|
user = %User{} <- User.get_cached_by_id(user_id) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
_ -> {:error, 403}
|
_ -> {:error, 403}
|
||||||
|
|
|
@ -13,32 +13,44 @@ def url("/" <> _ = url), do: url
|
||||||
|
|
||||||
def url(url) do
|
def url(url) do
|
||||||
config = Application.get_env(:pleroma, :media_proxy, [])
|
config = Application.get_env(:pleroma, :media_proxy, [])
|
||||||
|
domain = URI.parse(url).host
|
||||||
|
|
||||||
if !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) do
|
cond do
|
||||||
url
|
!Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) ->
|
||||||
else
|
|
||||||
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
|
||||||
|
|
||||||
# Must preserve `%2F` for compatibility with S3
|
|
||||||
# https://git.pleroma.social/pleroma/pleroma/issues/580
|
|
||||||
replacement = get_replacement(url, ":2F:")
|
|
||||||
|
|
||||||
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
|
||||||
base64 =
|
|
||||||
url
|
url
|
||||||
|> String.replace("%2F", replacement)
|
|
||||||
|> URI.decode()
|
|
||||||
|> URI.encode()
|
|
||||||
|> String.replace(replacement, "%2F")
|
|
||||||
|> Base.url_encode64(@base64_opts)
|
|
||||||
|
|
||||||
sig = :crypto.hmac(:sha, secret, base64)
|
Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
|
||||||
sig64 = sig |> Base.url_encode64(@base64_opts)
|
String.equivalent?(domain, pattern)
|
||||||
|
end) ->
|
||||||
|
url
|
||||||
|
|
||||||
build_url(sig64, base64, filename(url))
|
true ->
|
||||||
|
encode_url(url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def encode_url(url) do
|
||||||
|
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
||||||
|
|
||||||
|
# Must preserve `%2F` for compatibility with S3
|
||||||
|
# https://git.pleroma.social/pleroma/pleroma/issues/580
|
||||||
|
replacement = get_replacement(url, ":2F:")
|
||||||
|
|
||||||
|
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
||||||
|
base64 =
|
||||||
|
url
|
||||||
|
|> String.replace("%2F", replacement)
|
||||||
|
|> URI.decode()
|
||||||
|
|> URI.encode()
|
||||||
|
|> String.replace(replacement, "%2F")
|
||||||
|
|> Base.url_encode64(@base64_opts)
|
||||||
|
|
||||||
|
sig = :crypto.hmac(:sha, secret, base64)
|
||||||
|
sig64 = sig |> Base.url_encode64(@base64_opts)
|
||||||
|
|
||||||
|
build_url(sig64, base64, filename(url))
|
||||||
|
end
|
||||||
|
|
||||||
def decode_url(sig, url) do
|
def decode_url(sig, url) do
|
||||||
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
||||||
sig = Base.url_decode64!(sig, @base64_opts)
|
sig = Base.url_decode64!(sig, @base64_opts)
|
||||||
|
|
|
@ -143,7 +143,7 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
fixed_token = fix_padding(params["code"]),
|
fixed_token = fix_padding(params["code"]),
|
||||||
%Authorization{} = auth <-
|
%Authorization{} = auth <-
|
||||||
Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
|
Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
|
||||||
%User{} = user <- User.get_by_id(auth.user_id),
|
%User{} = user <- User.get_cached_by_id(auth.user_id),
|
||||||
{:ok, token} <- Token.exchange_token(app, auth),
|
{:ok, token} <- Token.exchange_token(app, auth),
|
||||||
{:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
|
{:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
|
||||||
response = %{
|
response = %{
|
||||||
|
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
def exchange_token(app, auth) do
|
def exchange_token(app, auth) do
|
||||||
with {:ok, auth} <- Authorization.use_token(auth),
|
with {:ok, auth} <- Authorization.use_token(auth),
|
||||||
true <- auth.app_id == app.id do
|
true <- auth.app_id == app.id do
|
||||||
create_token(app, User.get_by_id(auth.user_id), auth.scopes)
|
create_token(app, User.get_cached_by_id(auth.user_id), auth.scopes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ def to_simple_form(activity, user, with_author \\ false)
|
||||||
def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do
|
def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do
|
||||||
h = fn str -> [to_charlist(str)] end
|
h = fn str -> [to_charlist(str)] end
|
||||||
|
|
||||||
object = Object.normalize(activity.data["object"])
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
updated_at = object.data["published"]
|
updated_at = object.data["published"]
|
||||||
inserted_at = object.data["published"]
|
inserted_at = object.data["published"]
|
||||||
|
|
|
@ -294,7 +294,7 @@ def make_user(uri, update \\ false) do
|
||||||
}
|
}
|
||||||
|
|
||||||
with false <- update,
|
with false <- update,
|
||||||
%User{} = user <- User.get_by_ap_id(data.ap_id) do
|
%User{} = user <- User.get_cached_by_ap_id(data.ap_id) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
_e -> User.insert_or_update_user(data)
|
_e -> User.insert_or_update_user(data)
|
||||||
|
|
|
@ -21,8 +21,10 @@ defmodule Pleroma.Web.Push.Impl do
|
||||||
@doc "Performs sending notifications for user subscriptions"
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
@spec perform(Notification.t()) :: list(any) | :error
|
@spec perform(Notification.t()) :: list(any) | :error
|
||||||
def perform(
|
def perform(
|
||||||
%{activity: %{data: %{"type" => activity_type}, id: activity_id}, user_id: user_id} =
|
%{
|
||||||
notif
|
activity: %{data: %{"type" => activity_type}, id: activity_id} = activity,
|
||||||
|
user_id: user_id
|
||||||
|
} = notif
|
||||||
)
|
)
|
||||||
when activity_type in @types do
|
when activity_type in @types do
|
||||||
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
||||||
|
@ -30,13 +32,14 @@ def perform(
|
||||||
type = Activity.mastodon_notification_type(notif.activity)
|
type = Activity.mastodon_notification_type(notif.activity)
|
||||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||||
avatar_url = User.avatar_url(actor)
|
avatar_url = User.avatar_url(actor)
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
for subscription <- fetch_subsriptions(user_id),
|
for subscription <- fetch_subsriptions(user_id),
|
||||||
get_in(subscription.data, ["alerts", type]) do
|
get_in(subscription.data, ["alerts", type]) do
|
||||||
%{
|
%{
|
||||||
title: format_title(notif),
|
title: format_title(notif),
|
||||||
access_token: subscription.token.token,
|
access_token: subscription.token.token,
|
||||||
body: format_body(notif, actor),
|
body: format_body(notif, actor, object),
|
||||||
notification_id: notif.id,
|
notification_id: notif.id,
|
||||||
notification_type: type,
|
notification_type: type,
|
||||||
icon: avatar_url,
|
icon: avatar_url,
|
||||||
|
@ -95,25 +98,25 @@ def build_sub(subscription) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}},
|
%{activity: %{data: %{"type" => "Create"}}},
|
||||||
actor
|
actor,
|
||||||
|
%{data: %{"content" => content}}
|
||||||
) do
|
) do
|
||||||
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Announce", "object" => activity_id}}},
|
%{activity: %{data: %{"type" => "Announce"}}},
|
||||||
actor
|
actor,
|
||||||
|
%{data: %{"content" => content}}
|
||||||
) do
|
) do
|
||||||
%Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id)
|
|
||||||
%Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id)
|
|
||||||
|
|
||||||
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => type}}},
|
%{activity: %{data: %{"type" => type}}},
|
||||||
actor
|
actor,
|
||||||
|
_object
|
||||||
)
|
)
|
||||||
when type in ["Follow", "Like"] do
|
when type in ["Follow", "Like"] do
|
||||||
case type do
|
case type do
|
||||||
|
|
|
@ -135,6 +135,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/password_reset", UtilController, :password_reset)
|
post("/password_reset", UtilController, :password_reset)
|
||||||
get("/emoji", UtilController, :emoji)
|
get("/emoji", UtilController, :emoji)
|
||||||
get("/captcha", UtilController, :captcha)
|
get("/captcha", UtilController, :captcha)
|
||||||
|
get("/healthcheck", UtilController, :healthcheck)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma", Pleroma.Web do
|
scope "/api/pleroma", Pleroma.Web do
|
||||||
|
@ -394,6 +395,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/accounts/:id", MastodonAPIController, :user)
|
get("/accounts/:id", MastodonAPIController, :user)
|
||||||
|
|
||||||
get("/search", MastodonAPIController, :search)
|
get("/search", MastodonAPIController, :search)
|
||||||
|
|
||||||
|
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
|
||||||
_ ->
|
_ ->
|
||||||
Pleroma.List.get_lists_from_activity(item)
|
Pleroma.List.get_lists_from_activity(item)
|
||||||
|> Enum.filter(fn list ->
|
|> Enum.filter(fn list ->
|
||||||
owner = User.get_by_id(list.user_id)
|
owner = User.get_cached_by_id(list.user_id)
|
||||||
|
|
||||||
Visibility.visible_for_user?(item, owner)
|
Visibility.visible_for_user?(item, owner)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
|
|
||||||
def show_password_reset(conn, %{"token" => token}) do
|
def show_password_reset(conn, %{"token" => token}) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
%User{} = user <- User.get_by_id(token.user_id) do
|
%User{} = user <- User.get_cached_by_id(token.user_id) do
|
||||||
render(conn, "password_reset.html", %{
|
render(conn, "password_reset.html", %{
|
||||||
token: token,
|
token: token,
|
||||||
user: user
|
user: user
|
||||||
|
@ -113,13 +113,13 @@ defp is_status?(acct) do
|
||||||
def do_remote_follow(conn, %{
|
def do_remote_follow(conn, %{
|
||||||
"authorization" => %{"name" => username, "password" => password, "id" => id}
|
"authorization" => %{"name" => username, "password" => password, "id" => id}
|
||||||
}) do
|
}) do
|
||||||
followee = User.get_by_id(id)
|
followee = User.get_cached_by_id(id)
|
||||||
avatar = User.avatar_url(followee)
|
avatar = User.avatar_url(followee)
|
||||||
name = followee.nickname
|
name = followee.nickname
|
||||||
|
|
||||||
with %User{} = user <- User.get_cached_by_nickname(username),
|
with %User{} = user <- User.get_cached_by_nickname(username),
|
||||||
true <- Pbkdf2.checkpw(password, user.password_hash),
|
true <- Pbkdf2.checkpw(password, user.password_hash),
|
||||||
%User{} = _followed <- User.get_by_id(id),
|
%User{} = _followed <- User.get_cached_by_id(id),
|
||||||
{:ok, follower} <- User.follow(user, followee),
|
{:ok, follower} <- User.follow(user, followee),
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
|
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
|
||||||
conn
|
conn
|
||||||
|
@ -141,7 +141,7 @@ def do_remote_follow(conn, %{
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
|
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
|
||||||
with %User{} = followee <- User.get_by_id(id),
|
with %User{} = followee <- User.get_cached_by_id(id),
|
||||||
{:ok, follower} <- User.follow(user, followee),
|
{:ok, follower} <- User.follow(user, followee),
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
|
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
|
||||||
conn
|
conn
|
||||||
|
@ -363,4 +363,22 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||||
def captcha(conn, _params) do
|
def captcha(conn, _params) do
|
||||||
json(conn, Pleroma.Captcha.new())
|
json(conn, Pleroma.Captcha.new())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def healthcheck(conn, _params) do
|
||||||
|
info =
|
||||||
|
if Pleroma.Config.get([:instance, :healthcheck]) do
|
||||||
|
Pleroma.Healthcheck.system_info()
|
||||||
|
else
|
||||||
|
%{}
|
||||||
|
end
|
||||||
|
|
||||||
|
conn =
|
||||||
|
if info[:healthy] do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
Plug.Conn.put_status(conn, :service_unavailable)
|
||||||
|
end
|
||||||
|
|
||||||
|
json(conn, info)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -240,7 +240,7 @@ def get_user(user \\ nil, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
%{"screen_name" => nickname} ->
|
%{"screen_name" => nickname} ->
|
||||||
case User.get_by_nickname(nickname) do
|
case User.get_cached_by_nickname(nickname) do
|
||||||
nil -> {:error, "No user with such screen_name"}
|
nil -> {:error, "No user with such screen_name"}
|
||||||
target -> {:ok, target}
|
target -> {:ok, target}
|
||||||
end
|
end
|
||||||
|
|
|
@ -434,7 +434,7 @@ def password_reset(conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||||
with %User{} = user <- User.get_by_id(uid),
|
with %User{} = user <- User.get_cached_by_id(uid),
|
||||||
true <- user.local,
|
true <- user.local,
|
||||||
true <- user.info.confirmation_pending,
|
true <- user.info.confirmation_pending,
|
||||||
true <- user.info.confirmation_token == token,
|
true <- user.info.confirmation_token == token,
|
||||||
|
@ -587,7 +587,7 @@ def friend_requests(conn, params) do
|
||||||
|
|
||||||
def approve_friend_request(conn, %{"user_id" => uid} = _params) do
|
def approve_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||||
with followed <- conn.assigns[:user],
|
with followed <- conn.assigns[:user],
|
||||||
%User{} = follower <- User.get_by_id(uid),
|
%User{} = follower <- User.get_cached_by_id(uid),
|
||||||
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
||||||
conn
|
conn
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -599,7 +599,7 @@ def approve_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||||
|
|
||||||
def deny_friend_request(conn, %{"user_id" => uid} = _params) do
|
def deny_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||||
with followed <- conn.assigns[:user],
|
with followed <- conn.assigns[:user],
|
||||||
%User{} = follower <- User.get_by_id(uid),
|
%User{} = follower <- User.get_cached_by_id(uid),
|
||||||
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
||||||
conn
|
conn
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -632,7 +632,7 @@ def raw_empty_array(conn, _params) do
|
||||||
|
|
||||||
defp build_info_cng(user, params) do
|
defp build_info_cng(user, params) do
|
||||||
info_params =
|
info_params =
|
||||||
["no_rich_text", "locked", "hide_followers", "hide_follows", "show_role"]
|
["no_rich_text", "locked", "hide_followers", "hide_follows", "hide_favorites", "show_role"]
|
||||||
|> Enum.reduce(%{}, fn key, res ->
|
|> Enum.reduce(%{}, fn key, res ->
|
||||||
if value = params[key] do
|
if value = params[key] do
|
||||||
Map.put(res, key, value == "true")
|
Map.put(res, key, value == "true")
|
||||||
|
|
|
@ -74,58 +74,49 @@ defp do_render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||||
|
|
||||||
data = %{
|
|
||||||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
|
||||||
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
|
||||||
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)),
|
|
||||||
"favourites_count" => 0,
|
|
||||||
"followers_count" => user_info[:follower_count],
|
|
||||||
"following" => following,
|
|
||||||
"follows_you" => follows_you,
|
|
||||||
"statusnet_blocking" => statusnet_blocking,
|
|
||||||
"friends_count" => user_info[:following_count],
|
|
||||||
"id" => user.id,
|
|
||||||
"name" => user.name || user.nickname,
|
|
||||||
"name_html" =>
|
|
||||||
if(user.name,
|
|
||||||
do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji),
|
|
||||||
else: user.nickname
|
|
||||||
),
|
|
||||||
"profile_image_url" => image,
|
|
||||||
"profile_image_url_https" => image,
|
|
||||||
"profile_image_url_profile_size" => image,
|
|
||||||
"profile_image_url_original" => image,
|
|
||||||
"rights" => %{
|
|
||||||
"delete_others_notice" => !!user.info.is_moderator,
|
|
||||||
"admin" => !!user.info.is_admin
|
|
||||||
},
|
|
||||||
"screen_name" => user.nickname,
|
|
||||||
"statuses_count" => user_info[:note_count],
|
|
||||||
"statusnet_profile_url" => user.ap_id,
|
|
||||||
"cover_photo" => User.banner_url(user) |> MediaProxy.url(),
|
|
||||||
"background_image" => image_url(user.info.background) |> MediaProxy.url(),
|
|
||||||
"is_local" => user.local,
|
|
||||||
"locked" => user.info.locked,
|
|
||||||
"default_scope" => user.info.default_scope,
|
|
||||||
"no_rich_text" => user.info.no_rich_text,
|
|
||||||
"hide_followers" => user.info.hide_followers,
|
|
||||||
"hide_follows" => user.info.hide_follows,
|
|
||||||
"fields" => fields,
|
|
||||||
|
|
||||||
# Pleroma extension
|
|
||||||
"pleroma" =>
|
|
||||||
%{
|
|
||||||
"confirmation_pending" => user_info.confirmation_pending,
|
|
||||||
"tags" => user.tags
|
|
||||||
}
|
|
||||||
|> maybe_with_activation_status(user, for_user)
|
|
||||||
}
|
|
||||||
|
|
||||||
data =
|
data =
|
||||||
if(user.info.is_admin || user.info.is_moderator,
|
%{
|
||||||
do: maybe_with_role(data, user, for_user),
|
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
||||||
else: data
|
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||||
)
|
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)),
|
||||||
|
"favourites_count" => 0,
|
||||||
|
"followers_count" => user_info[:follower_count],
|
||||||
|
"following" => following,
|
||||||
|
"follows_you" => follows_you,
|
||||||
|
"statusnet_blocking" => statusnet_blocking,
|
||||||
|
"friends_count" => user_info[:following_count],
|
||||||
|
"id" => user.id,
|
||||||
|
"name" => user.name || user.nickname,
|
||||||
|
"name_html" =>
|
||||||
|
if(user.name,
|
||||||
|
do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji),
|
||||||
|
else: user.nickname
|
||||||
|
),
|
||||||
|
"profile_image_url" => image,
|
||||||
|
"profile_image_url_https" => image,
|
||||||
|
"profile_image_url_profile_size" => image,
|
||||||
|
"profile_image_url_original" => image,
|
||||||
|
"screen_name" => user.nickname,
|
||||||
|
"statuses_count" => user_info[:note_count],
|
||||||
|
"statusnet_profile_url" => user.ap_id,
|
||||||
|
"cover_photo" => User.banner_url(user) |> MediaProxy.url(),
|
||||||
|
"background_image" => image_url(user.info.background) |> MediaProxy.url(),
|
||||||
|
"is_local" => user.local,
|
||||||
|
"locked" => user.info.locked,
|
||||||
|
"hide_followers" => user.info.hide_followers,
|
||||||
|
"hide_follows" => user.info.hide_follows,
|
||||||
|
"fields" => fields,
|
||||||
|
|
||||||
|
# Pleroma extension
|
||||||
|
"pleroma" =>
|
||||||
|
%{
|
||||||
|
"confirmation_pending" => user_info.confirmation_pending,
|
||||||
|
"tags" => user.tags
|
||||||
|
}
|
||||||
|
|> maybe_with_activation_status(user, for_user)
|
||||||
|
}
|
||||||
|
|> maybe_with_user_settings(user, for_user)
|
||||||
|
|> maybe_with_role(user, for_user)
|
||||||
|
|
||||||
if assigns[:token] do
|
if assigns[:token] do
|
||||||
Map.put(data, "token", token_string(assigns[:token]))
|
Map.put(data, "token", token_string(assigns[:token]))
|
||||||
|
@ -141,15 +132,35 @@ defp maybe_with_activation_status(data, user, %User{info: %{is_admin: true}}) do
|
||||||
defp maybe_with_activation_status(data, _, _), do: data
|
defp maybe_with_activation_status(data, _, _), do: data
|
||||||
|
|
||||||
defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do
|
defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do
|
||||||
Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role})
|
Map.merge(data, %{
|
||||||
|
"role" => role(user),
|
||||||
|
"show_role" => user.info.show_role,
|
||||||
|
"rights" => %{
|
||||||
|
"delete_others_notice" => !!user.info.is_moderator,
|
||||||
|
"admin" => !!user.info.is_admin
|
||||||
|
}
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_with_role(data, %User{info: %{show_role: true}} = user, _user) do
|
defp maybe_with_role(data, %User{info: %{show_role: true}} = user, _user) do
|
||||||
Map.merge(data, %{"role" => role(user)})
|
Map.merge(data, %{
|
||||||
|
"role" => role(user),
|
||||||
|
"rights" => %{
|
||||||
|
"delete_others_notice" => !!user.info.is_moderator,
|
||||||
|
"admin" => !!user.info.is_admin
|
||||||
|
}
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_with_role(data, _, _), do: data
|
defp maybe_with_role(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_with_user_settings(data, %User{info: info, id: id} = _user, %User{id: id}) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in(["default_scope"], info.default_scope)
|
||||||
|
|> Kernel.put_in(["no_rich_text"], info.no_rich_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_with_user_settings(data, _, _), do: data
|
||||||
defp role(%User{info: %{:is_admin => true}}), do: "admin"
|
defp role(%User{info: %{:is_admin => true}}), do: "admin"
|
||||||
defp role(%User{info: %{:is_moderator => true}}), do: "moderator"
|
defp role(%User{info: %{:is_moderator => true}}), do: "moderator"
|
||||||
defp role(_), do: "member"
|
defp role(_), do: "member"
|
||||||
|
|
|
@ -37,7 +37,7 @@ def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do
|
||||||
regex = ~r/(acct:)?(?<username>\w+)@#{host}/
|
regex = ~r/(acct:)?(?<username>\w+)@#{host}/
|
||||||
|
|
||||||
with %{"username" => username} <- Regex.named_captures(regex, resource),
|
with %{"username" => username} <- Regex.named_captures(regex, resource),
|
||||||
%User{} = user <- User.get_by_nickname(username) do
|
%User{} = user <- User.get_cached_by_nickname(username) do
|
||||||
{:ok, represent_user(user, fmt)}
|
{:ok, represent_user(user, fmt)}
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -84,6 +84,7 @@ defp deps do
|
||||||
{:ex_aws, "~> 2.0"},
|
{:ex_aws, "~> 2.0"},
|
||||||
{:ex_aws_s3, "~> 2.0"},
|
{:ex_aws_s3, "~> 2.0"},
|
||||||
{:earmark, "~> 1.3"},
|
{:earmark, "~> 1.3"},
|
||||||
|
{:bbcode, "~> 0.1"},
|
||||||
{:ex_machina, "~> 2.3", only: :test},
|
{:ex_machina, "~> 2.3", only: :test},
|
||||||
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
||||||
{:mock, "~> 0.3.1", only: :test},
|
{:mock, "~> 0.3.1", only: :test},
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -2,6 +2,7 @@
|
||||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm"},
|
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm"},
|
||||||
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "90613b4bae875a3610c275b7056b61ffdd53210d", [ref: "90613b4bae875a3610c275b7056b61ffdd53210d"]},
|
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "90613b4bae875a3610c275b7056b61ffdd53210d", [ref: "90613b4bae875a3610c275b7056b61ffdd53210d"]},
|
||||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||||
|
"bbcode": {:hex, :bbcode, "0.1.0", "400e618b640b635261611d7fb7f79d104917fc5b084aae371ab6b08477cb035b", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||||
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateBookmarks do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:bookmarks) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all))
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create(unique_index(:bookmarks, [:user_id, :activity_id]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do
|
||||||
|
use Ecto.Migration
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
def change do
|
||||||
|
query =
|
||||||
|
from(u in User,
|
||||||
|
where: u.local == true,
|
||||||
|
where: fragment("array_length(bookmarks, 1)") > 0,
|
||||||
|
select: %{id: u.id, bookmarks: fragment("bookmarks")}
|
||||||
|
)
|
||||||
|
|
||||||
|
Repo.stream(query)
|
||||||
|
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
|
||||||
|
Enum.each(bookmarks, fn ap_id ->
|
||||||
|
activity = Activity.get_create_by_object_ap_id(ap_id)
|
||||||
|
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:bookmarks)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.ea66966b753e709d7ce58f910a2c003e.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.0b2f423dda42f0dbbf65.js></script><script type=text/javascript src=/static/js/vendor.e4475fde034685231799.js></script><script type=text/javascript src=/static/js/app.77434de4e756a5d79672.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.a81578273cb4c57163939ab70c80eb06.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.bf15f24d205c8cf4ee4a.js></script><script type=text/javascript src=/static/js/vendor.0d1eeaf25aa1d2fc51b0.js></script><script type=text/javascript src=/static/js/app.c914d9a57d5da7aa5553.js></script></body></html>
|
|
@ -8,7 +8,6 @@
|
||||||
"redirectRootLogin": "/main/friends",
|
"redirectRootLogin": "/main/friends",
|
||||||
"chatDisabled": false,
|
"chatDisabled": false,
|
||||||
"showInstanceSpecificPanel": false,
|
"showInstanceSpecificPanel": false,
|
||||||
"scopeOptionsEnabled": false,
|
|
||||||
"formattingOptionsEnabled": false,
|
"formattingOptionsEnabled": false,
|
||||||
"collapseMessageWithSubject": false,
|
"collapseMessageWithSubject": false,
|
||||||
"scopeCopy": true,
|
"scopeCopy": true,
|
||||||
|
@ -21,5 +20,6 @@
|
||||||
"webPushNotifications": false,
|
"webPushNotifications": false,
|
||||||
"noAttachmentLinks": false,
|
"noAttachmentLinks": false,
|
||||||
"nsfwCensorImage": "",
|
"nsfwCensorImage": "",
|
||||||
"showFeaturesPanel": true
|
"showFeaturesPanel": true,
|
||||||
|
"minimalScopesMode": false
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -239,6 +239,18 @@
|
||||||
"css": "pencil",
|
"css": "pencil",
|
||||||
"code": 59416,
|
"code": 59416,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "671f29fa10dda08074a4c6a341bb4f39",
|
||||||
|
"css": "bell-alt",
|
||||||
|
"code": 61683,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "5bb103cd29de77e0e06a52638527b575",
|
||||||
|
"css": "wrench",
|
||||||
|
"code": 59418,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -229,11 +229,11 @@ body {
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('./font/fontello.eot?50378338');
|
src: url('./font/fontello.eot?60799712');
|
||||||
src: url('./font/fontello.eot?50378338#iefix') format('embedded-opentype'),
|
src: url('./font/fontello.eot?60799712#iefix') format('embedded-opentype'),
|
||||||
url('./font/fontello.woff?50378338') format('woff'),
|
url('./font/fontello.woff?60799712') format('woff'),
|
||||||
url('./font/fontello.ttf?50378338') format('truetype'),
|
url('./font/fontello.ttf?60799712') format('truetype'),
|
||||||
url('./font/fontello.svg?50378338#fontello') format('svg');
|
url('./font/fontello.svg?60799712#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -335,24 +335,29 @@ body {
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xe818"><i class="demo-icon icon-pencil"></i> <span class="i-name">icon-pencil</span><span class="i-code">0xe818</span></div>
|
<div class="the-icons span3" title="Code: 0xe818"><i class="demo-icon icon-pencil"></i> <span class="i-name">icon-pencil</span><span class="i-code">0xe818</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe819"><i class="demo-icon icon-verified"></i> <span class="i-name">icon-verified</span><span class="i-code">0xe819</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe81a"><i class="demo-icon icon-wrench"></i> <span class="i-name">icon-wrench</span><span class="i-code">0xe81a</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
<div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
|
||||||
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0f3"><i class="demo-icon icon-bell-alt"></i> <span class="i-name">icon-bell-alt</span><span class="i-code">0xf0f3</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
<div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
<div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div>
|
|
||||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Binary file not shown.
|
@ -56,6 +56,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="verified" unicode="" d="M926 453l-19 13c-21 14-30 41-23 65l6 22c10 34-13 69-48 75l-23 4c-25 4-45 23-49 48l-4 23c-6 35-41 57-75 47l-22-7c-24-7-51 2-65 22l-14 20c-21 29-62 33-88 9l-17-16c-19-17-46-21-69-8l-20 11c-31 17-70 3-84-30l-9-22c-9-24-33-39-58-37l-23 1c-36 2-65-28-62-63l2-23c2-25-13-49-36-59l-21-9c-33-14-46-53-29-84l12-20c13-22 10-50-7-69l-15-17c-24-27-19-68 11-88l19-13c21-14 30-41 23-65l-9-23c-10-34 13-69 48-75l23-4c25-4 45-23 49-48l4-23c6-35 41-57 75-47l22 7c24 7 51-2 65-22l14-19c21-29 62-33 88-9l17 16c19 17 46 21 69 8l20-11c31-17 70-3 84 30l9 22c9 24 33 39 58 37l23-1c36-2 65 28 62 63l-1 23c-2 25 13 49 36 59l21 9c33 14 46 53 29 84l-12 20c-13 22-10 50 7 69l15 17c25 26 20 68-9 88z m-399-189l-82-81-81 82-78 79 82 81 78-79 187 186 81-82-187-186z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="wrench" unicode="" d="M214 36q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m360 234l-381-381q-21-20-50-20-29 0-51 20l-59 61q-21 20-21 50 0 29 21 51l380 380q22-55 64-97t97-64z m354 243q0-22-13-59-27-75-92-122t-144-46q-104 0-177 73t-73 177 73 176 177 74q32 0 67-10t60-26q9-6 9-15t-9-16l-163-94v-125l108-60q2 2 44 27t75 45 40 20q8 0 13-5t5-14z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
||||||
|
@ -70,6 +74,8 @@
|
||||||
|
|
||||||
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
<glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="bell-alt" unicode="" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m455 160q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" />
|
<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="reply" unicode="" d="M1000 232q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" />
|
<glyph glyph-name="reply" unicode="" d="M1000 232q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" />
|
||||||
|
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 748 B After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
@ -0,0 +1,52 @@
|
||||||
|
defmodule Pleroma.BookmarkTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
describe "create/2" do
|
||||||
|
test "with valid params" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"})
|
||||||
|
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
assert bookmark.user_id == user.id
|
||||||
|
assert bookmark.activity_id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid params" do
|
||||||
|
{:error, changeset} = Bookmark.create(nil, "")
|
||||||
|
refute changeset.valid?
|
||||||
|
|
||||||
|
assert changeset.errors == [
|
||||||
|
user_id: {"can't be blank", [validation: :required]},
|
||||||
|
activity_id: {"can't be blank", [validation: :required]}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "destroy/2" do
|
||||||
|
test "with valid params" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"})
|
||||||
|
{:ok, _bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
|
||||||
|
{:ok, _deleted_bookmark} = Bookmark.destroy(user.id, activity.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get/2" do
|
||||||
|
test "gets a bookmark" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" =>
|
||||||
|
"Scientists Discover The Secret Behind Tenshi Eating A Corndog Being So Cute – Science Daily"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
assert bookmark == Bookmark.get(user.id, activity.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,22 @@
|
||||||
|
defmodule Pleroma.HealthcheckTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Healthcheck
|
||||||
|
|
||||||
|
test "system_info/0" do
|
||||||
|
result = Healthcheck.system_info() |> Map.from_struct()
|
||||||
|
|
||||||
|
assert Map.keys(result) == [:active, :healthy, :idle, :memory_used, :pool_size]
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "check_health/1" do
|
||||||
|
test "pool size equals active connections" do
|
||||||
|
result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 10})
|
||||||
|
refute result.healthy
|
||||||
|
end
|
||||||
|
|
||||||
|
test "chech_health/1" do
|
||||||
|
result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 9})
|
||||||
|
assert result.healthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,6 +20,18 @@ defmodule Pleroma.HTMLTest do
|
||||||
<img src="http://example.com/image.jpg" onerror="alert('hacked')">
|
<img src="http://example.com/image.jpg" onerror="alert('hacked')">
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@html_span_class_sample """
|
||||||
|
<span class="animate-spin">hi</span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
@html_span_microformats_sample """
|
||||||
|
<span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
@html_span_invalid_microformats_sample """
|
||||||
|
<span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
describe "StripTags scrubber" do
|
describe "StripTags scrubber" do
|
||||||
test "works as expected" do
|
test "works as expected" do
|
||||||
expected = """
|
expected = """
|
||||||
|
@ -64,6 +76,36 @@ test "does not allow attribute-based XSS" do
|
||||||
|
|
||||||
assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText)
|
assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not allow spans with invalid classes" do
|
||||||
|
expected = """
|
||||||
|
<span>hi</span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does allow microformats" do
|
||||||
|
expected = """
|
||||||
|
<span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "filters invalid microformats markup" do
|
||||||
|
expected = """
|
||||||
|
<span class="h-card"><a>@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
HTML.filter_tags(
|
||||||
|
@html_span_invalid_microformats_sample,
|
||||||
|
Pleroma.HTML.Scrubber.TwitterText
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "default scrubber" do
|
describe "default scrubber" do
|
||||||
|
@ -88,5 +130,34 @@ test "does not allow attribute-based XSS" do
|
||||||
|
|
||||||
assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default)
|
assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not allow spans with invalid classes" do
|
||||||
|
expected = """
|
||||||
|
<span>hi</span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does allow microformats" do
|
||||||
|
expected = """
|
||||||
|
<span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "filters invalid microformats markup" do
|
||||||
|
expected = """
|
||||||
|
<span class="h-card"><a>@<span>foo</span></a></span>
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
HTML.filter_tags(
|
||||||
|
@html_span_invalid_microformats_sample,
|
||||||
|
Pleroma.HTML.Scrubber.Default
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -177,4 +177,13 @@ defp decode_result(encoded) do
|
||||||
{:ok, decoded} = decode_url(sig, base64)
|
{:ok, decoded} = decode_url(sig, base64)
|
||||||
decoded
|
decoded
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "mediaproxy whitelist" do
|
||||||
|
Pleroma.Config.put([:media_proxy, :enabled], true)
|
||||||
|
Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
|
||||||
|
url = "https://feld.me/foo.png"
|
||||||
|
|
||||||
|
unencoded = url(url)
|
||||||
|
assert unencoded == url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,7 +46,7 @@ test "it creates a notification for subscribed users" do
|
||||||
describe "create_notification" do
|
describe "create_notification" do
|
||||||
test "it doesn't create a notification for user if the user blocks the activity author" do
|
test "it doesn't create a notification for user if the user blocks the activity author" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.block(user, author)
|
{:ok, user} = User.block(user, author)
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ test "it disables notifications from people the user follows" do
|
||||||
|
|
||||||
test "it doesn't create a notification for user if he is the activity author" do
|
test "it doesn't create a notification for user if he is the activity author" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, author)
|
assert nil == Notification.create_notification(activity, author)
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,7 +31,7 @@ test "relay is followed" do
|
||||||
local_user = Relay.get_actor()
|
local_user = Relay.get_actor()
|
||||||
assert local_user.ap_id =~ "/relay"
|
assert local_user.ap_id =~ "/relay"
|
||||||
|
|
||||||
target_user = User.get_by_ap_id(target_instance)
|
target_user = User.get_cached_by_ap_id(target_instance)
|
||||||
refute target_user.local
|
refute target_user.local
|
||||||
|
|
||||||
activity = Utils.fetch_latest_follow(local_user, target_user)
|
activity = Utils.fetch_latest_follow(local_user, target_user)
|
||||||
|
@ -48,7 +48,7 @@ test "relay is unfollowed" do
|
||||||
Mix.Tasks.Pleroma.Relay.run(["follow", target_instance])
|
Mix.Tasks.Pleroma.Relay.run(["follow", target_instance])
|
||||||
|
|
||||||
%User{ap_id: follower_id} = local_user = Relay.get_actor()
|
%User{ap_id: follower_id} = local_user = Relay.get_actor()
|
||||||
target_user = User.get_by_ap_id(target_instance)
|
target_user = User.get_cached_by_ap_id(target_instance)
|
||||||
follow_activity = Utils.fetch_latest_follow(local_user, target_user)
|
follow_activity = Utils.fetch_latest_follow(local_user, target_user)
|
||||||
|
|
||||||
Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])
|
Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])
|
||||||
|
|
|
@ -50,7 +50,7 @@ test "user is created" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ "created"
|
assert message =~ "created"
|
||||||
|
|
||||||
user = User.get_by_nickname(unsaved.nickname)
|
user = User.get_cached_by_nickname(unsaved.nickname)
|
||||||
assert user.name == unsaved.name
|
assert user.name == unsaved.name
|
||||||
assert user.email == unsaved.email
|
assert user.email == unsaved.email
|
||||||
assert user.bio == unsaved.bio
|
assert user.bio == unsaved.bio
|
||||||
|
@ -75,7 +75,7 @@ test "user is not created" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ "will not be created"
|
assert message =~ "will not be created"
|
||||||
|
|
||||||
refute User.get_by_nickname(unsaved.nickname)
|
refute User.get_cached_by_nickname(unsaved.nickname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ test "user is deleted" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ " deleted"
|
assert message =~ " deleted"
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
assert user.info.deactivated
|
assert user.info.deactivated
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ test "user is deactivated" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ " deactivated"
|
assert message =~ " deactivated"
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
assert user.info.deactivated
|
assert user.info.deactivated
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ test "user is activated" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ " activated"
|
assert message =~ " activated"
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
refute user.info.deactivated
|
refute user.info.deactivated
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ test "user is unsubscribed" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ "Successfully unsubscribed"
|
assert message =~ "Successfully unsubscribed"
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
assert Enum.empty?(user.following)
|
assert Enum.empty?(user.following)
|
||||||
assert user.info.deactivated
|
assert user.info.deactivated
|
||||||
end
|
end
|
||||||
|
@ -178,7 +178,7 @@ test "All statuses set" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ ~r/Admin status .* true/
|
assert message =~ ~r/Admin status .* true/
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
assert user.info.is_moderator
|
assert user.info.is_moderator
|
||||||
assert user.info.locked
|
assert user.info.locked
|
||||||
assert user.info.is_admin
|
assert user.info.is_admin
|
||||||
|
@ -204,7 +204,7 @@ test "All statuses unset" do
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ ~r/Admin status .* false/
|
assert message =~ ~r/Admin status .* false/
|
||||||
|
|
||||||
user = User.get_by_nickname(user.nickname)
|
user = User.get_cached_by_nickname(user.nickname)
|
||||||
refute user.info.is_moderator
|
refute user.info.is_moderator
|
||||||
refute user.info.locked
|
refute user.info.locked
|
||||||
refute user.info.is_admin
|
refute user.info.is_admin
|
||||||
|
|
|
@ -123,9 +123,9 @@ test "follow takes a user and another user" do
|
||||||
|
|
||||||
{:ok, user} = User.follow(user, followed)
|
{:ok, user} = User.follow(user, followed)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
followed = User.get_by_ap_id(followed.ap_id)
|
followed = User.get_cached_by_ap_id(followed.ap_id)
|
||||||
assert followed.info.follower_count == 1
|
assert followed.info.follower_count == 1
|
||||||
|
|
||||||
assert User.ap_followers(followed) in user.following
|
assert User.ap_followers(followed) in user.following
|
||||||
|
@ -188,7 +188,7 @@ test "unfollow takes a user and another user" do
|
||||||
|
|
||||||
{:ok, user, _activity} = User.unfollow(user, followed)
|
{:ok, user, _activity} = User.unfollow(user, followed)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert user.following == []
|
assert user.following == []
|
||||||
end
|
end
|
||||||
|
@ -198,7 +198,7 @@ test "unfollow doesn't unfollow yourself" do
|
||||||
|
|
||||||
{:error, _} = User.unfollow(user, user)
|
{:error, _} = User.unfollow(user, user)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.following == [user.ap_id]
|
assert user.following == [user.ap_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -556,8 +556,8 @@ test "gets all friends (followed users) for a given user" do
|
||||||
|
|
||||||
{:ok, res} = User.get_friends(user)
|
{:ok, res} = User.get_friends(user)
|
||||||
|
|
||||||
followed_one = User.get_by_ap_id(followed_one.ap_id)
|
followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
|
||||||
followed_two = User.get_by_ap_id(followed_two.ap_id)
|
followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
|
||||||
assert Enum.member?(res, followed_one)
|
assert Enum.member?(res, followed_one)
|
||||||
assert Enum.member?(res, followed_two)
|
assert Enum.member?(res, followed_two)
|
||||||
refute Enum.member?(res, not_followed)
|
refute Enum.member?(res, not_followed)
|
||||||
|
@ -568,7 +568,7 @@ test "gets all friends (followed users) for a given user" do
|
||||||
test "it sets the info->note_count property" do
|
test "it sets the info->note_count property" do
|
||||||
note = insert(:note)
|
note = insert(:note)
|
||||||
|
|
||||||
user = User.get_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
|
||||||
assert user.info.note_count == 0
|
assert user.info.note_count == 0
|
||||||
|
|
||||||
|
@ -579,7 +579,7 @@ test "it sets the info->note_count property" do
|
||||||
|
|
||||||
test "it increases the info->note_count property" do
|
test "it increases the info->note_count property" do
|
||||||
note = insert(:note)
|
note = insert(:note)
|
||||||
user = User.get_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
|
||||||
assert user.info.note_count == 0
|
assert user.info.note_count == 0
|
||||||
|
|
||||||
|
@ -594,7 +594,7 @@ test "it increases the info->note_count property" do
|
||||||
|
|
||||||
test "it decreases the info->note_count property" do
|
test "it decreases the info->note_count property" do
|
||||||
note = insert(:note)
|
note = insert(:note)
|
||||||
user = User.get_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
|
||||||
assert user.info.note_count == 0
|
assert user.info.note_count == 0
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ test "blocks tear down cyclical follow relationships" do
|
||||||
assert User.following?(blocked, blocker)
|
assert User.following?(blocked, blocker)
|
||||||
|
|
||||||
{:ok, blocker} = User.block(blocker, blocked)
|
{:ok, blocker} = User.block(blocker, blocked)
|
||||||
blocked = User.get_by_id(blocked.id)
|
blocked = User.get_cached_by_id(blocked.id)
|
||||||
|
|
||||||
assert User.blocks?(blocker, blocked)
|
assert User.blocks?(blocker, blocked)
|
||||||
|
|
||||||
|
@ -714,7 +714,7 @@ test "blocks tear down blocker->blocked follow relationships" do
|
||||||
refute User.following?(blocked, blocker)
|
refute User.following?(blocked, blocker)
|
||||||
|
|
||||||
{:ok, blocker} = User.block(blocker, blocked)
|
{:ok, blocker} = User.block(blocker, blocked)
|
||||||
blocked = User.get_by_id(blocked.id)
|
blocked = User.get_cached_by_id(blocked.id)
|
||||||
|
|
||||||
assert User.blocks?(blocker, blocked)
|
assert User.blocks?(blocker, blocked)
|
||||||
|
|
||||||
|
@ -732,7 +732,7 @@ test "blocks tear down blocked->blocker follow relationships" do
|
||||||
assert User.following?(blocked, blocker)
|
assert User.following?(blocked, blocker)
|
||||||
|
|
||||||
{:ok, blocker} = User.block(blocker, blocked)
|
{:ok, blocker} = User.block(blocker, blocked)
|
||||||
blocked = User.get_by_id(blocked.id)
|
blocked = User.get_cached_by_id(blocked.id)
|
||||||
|
|
||||||
assert User.blocks?(blocker, blocked)
|
assert User.blocks?(blocker, blocked)
|
||||||
|
|
||||||
|
@ -852,9 +852,9 @@ test ".delete deactivates a user, all follow relationships and all create activi
|
||||||
|
|
||||||
{:ok, _} = User.delete(user)
|
{:ok, _} = User.delete(user)
|
||||||
|
|
||||||
followed = User.get_by_id(followed.id)
|
followed = User.get_cached_by_id(followed.id)
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert user.info.deactivated
|
assert user.info.deactivated
|
||||||
|
|
||||||
|
@ -1008,7 +1008,7 @@ test "works with URIs" do
|
||||||
results = User.search("http://mastodon.example.org/users/admin", resolve: true)
|
results = User.search("http://mastodon.example.org/users/admin", resolve: true)
|
||||||
result = results |> List.first()
|
result = results |> List.first()
|
||||||
|
|
||||||
user = User.get_by_ap_id("http://mastodon.example.org/users/admin")
|
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
|
||||||
|
|
||||||
assert length(results) == 1
|
assert length(results) == 1
|
||||||
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
|
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
|
||||||
|
@ -1125,33 +1125,6 @@ test "Adds rel=me on linkbacked urls" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bookmarks" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, activity1} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
"status" => "heweoo!"
|
|
||||||
})
|
|
||||||
|
|
||||||
id1 = Object.normalize(activity1).data["id"]
|
|
||||||
|
|
||||||
{:ok, activity2} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
"status" => "heweoo!"
|
|
||||||
})
|
|
||||||
|
|
||||||
id2 = Object.normalize(activity2).data["id"]
|
|
||||||
|
|
||||||
assert {:ok, user_state1} = User.bookmark(user, id1)
|
|
||||||
assert user_state1.bookmarks == [id1]
|
|
||||||
|
|
||||||
assert {:ok, user_state2} = User.unbookmark(user, id1)
|
|
||||||
assert user_state2.bookmarks == []
|
|
||||||
|
|
||||||
assert {:ok, user_state3} = User.bookmark(user, id2)
|
|
||||||
assert user_state3.bookmarks == [id2]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "follower count is updated when a follower is blocked" do
|
test "follower count is updated when a follower is blocked" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
|
|
|
@ -50,7 +50,7 @@ test "it returns a json representation of the user with accept application/json"
|
||||||
|> put_req_header("accept", "application/json")
|
|> put_req_header("accept", "application/json")
|
||||||
|> get("/users/#{user.nickname}")
|
|> get("/users/#{user.nickname}")
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
||||||
end
|
end
|
||||||
|
@ -65,7 +65,7 @@ test "it returns a json representation of the user with accept application/activ
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|> get("/users/#{user.nickname}")
|
|> get("/users/#{user.nickname}")
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
||||||
end
|
end
|
||||||
|
@ -83,7 +83,7 @@ test "it returns a json representation of the user with accept application/ld+js
|
||||||
)
|
)
|
||||||
|> get("/users/#{user.nickname}")
|
|> get("/users/#{user.nickname}")
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
||||||
end
|
end
|
||||||
|
@ -572,7 +572,7 @@ test "it works for more than 10 users", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
Enum.each(1..15, fn _ ->
|
Enum.each(1..15, fn _ ->
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
User.follow(user, other_user)
|
User.follow(user, other_user)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -228,18 +228,30 @@ test "increases user note count only for public activities" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "1", "visibility" => "public"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "1",
|
||||||
|
"visibility" => "public"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "2", "visibility" => "unlisted"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "2",
|
||||||
|
"visibility" => "unlisted"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "2", "visibility" => "private"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "2",
|
||||||
|
"visibility" => "private"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "3", "visibility" => "direct"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "3",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.note_count == 2
|
assert user.info.note_count == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -772,23 +784,35 @@ test "decrements user note count only for public activities" do
|
||||||
user = insert(:user, info: %{note_count: 10})
|
user = insert(:user, info: %{note_count: 10})
|
||||||
|
|
||||||
{:ok, a1} =
|
{:ok, a1} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "yeah", "visibility" => "public"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "yeah",
|
||||||
|
"visibility" => "public"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, a2} =
|
{:ok, a2} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "yeah", "visibility" => "unlisted"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "yeah",
|
||||||
|
"visibility" => "unlisted"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, a3} =
|
{:ok, a3} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "yeah", "visibility" => "private"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "yeah",
|
||||||
|
"visibility" => "private"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, a4} =
|
{:ok, a4} =
|
||||||
CommonAPI.post(User.get_by_id(user.id), %{"status" => "yeah", "visibility" => "direct"})
|
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||||
|
"status" => "yeah",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
|
{:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
|
||||||
{:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
|
{:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
|
||||||
{:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
|
{:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
|
||||||
{:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
|
{:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.note_count == 10
|
assert user.info.note_count == 10
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ test "it works for incoming notices" do
|
||||||
|
|
||||||
assert object["sensitive"] == true
|
assert object["sensitive"] == true
|
||||||
|
|
||||||
user = User.get_by_ap_id(object["actor"])
|
user = User.get_cached_by_ap_id(object["actor"])
|
||||||
|
|
||||||
assert user.info.note_count == 1
|
assert user.info.note_count == 1
|
||||||
end
|
end
|
||||||
|
@ -212,7 +212,27 @@ test "it works for incoming follow requests" do
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
assert data["type"] == "Follow"
|
assert data["type"] == "Follow"
|
||||||
assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
|
assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
|
||||||
assert User.following?(User.get_by_ap_id(data["actor"]), user)
|
assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do
|
||||||
|
Pleroma.Config.put([:user, :deny_follow_blocked], true)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
target = User.get_or_fetch("http://mastodon.example.org/users/admin")
|
||||||
|
|
||||||
|
{:ok, user} = User.block(user, target)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", user.ap_id)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
%Activity{} = activity = Activity.get_by_ap_id(id)
|
||||||
|
|
||||||
|
assert activity.data["state"] == "reject"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming follow requests from hubzilla" do
|
test "it works for incoming follow requests from hubzilla" do
|
||||||
|
@ -229,7 +249,7 @@ test "it works for incoming follow requests from hubzilla" do
|
||||||
assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
|
assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
|
||||||
assert data["type"] == "Follow"
|
assert data["type"] == "Follow"
|
||||||
assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
|
assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
|
||||||
assert User.following?(User.get_by_ap_id(data["actor"]), user)
|
assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming likes" do
|
test "it works for incoming likes" do
|
||||||
|
@ -540,7 +560,7 @@ test "it works for incomming unfollows with an existing follow" do
|
||||||
assert data["object"]["object"] == user.ap_id
|
assert data["object"]["object"] == user.ap_id
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
refute User.following?(User.get_by_ap_id(data["actor"]), user)
|
refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming blocks" do
|
test "it works for incoming blocks" do
|
||||||
|
@ -557,7 +577,7 @@ test "it works for incoming blocks" do
|
||||||
assert data["object"] == user.ap_id
|
assert data["object"] == user.ap_id
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
blocker = User.get_by_ap_id(data["actor"])
|
blocker = User.get_cached_by_ap_id(data["actor"])
|
||||||
|
|
||||||
assert User.blocks?(blocker, user)
|
assert User.blocks?(blocker, user)
|
||||||
end
|
end
|
||||||
|
@ -584,8 +604,8 @@ test "incoming blocks successfully tear down any follow relationship" do
|
||||||
assert data["object"] == blocked.ap_id
|
assert data["object"] == blocked.ap_id
|
||||||
assert data["actor"] == blocker.ap_id
|
assert data["actor"] == blocker.ap_id
|
||||||
|
|
||||||
blocker = User.get_by_ap_id(data["actor"])
|
blocker = User.get_cached_by_ap_id(data["actor"])
|
||||||
blocked = User.get_by_ap_id(data["object"])
|
blocked = User.get_cached_by_ap_id(data["object"])
|
||||||
|
|
||||||
assert User.blocks?(blocker, blocked)
|
assert User.blocks?(blocker, blocked)
|
||||||
|
|
||||||
|
@ -614,7 +634,7 @@ test "it works for incoming unblocks with an existing block" do
|
||||||
assert data["object"]["object"] == user.ap_id
|
assert data["object"]["object"] == user.ap_id
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
blocker = User.get_by_ap_id(data["actor"])
|
blocker = User.get_cached_by_ap_id(data["actor"])
|
||||||
|
|
||||||
refute User.blocks?(blocker, user)
|
refute User.blocks?(blocker, user)
|
||||||
end
|
end
|
||||||
|
@ -645,7 +665,7 @@ test "it works for incoming accepts which were pre-accepted" do
|
||||||
|
|
||||||
assert activity.data["object"] == follow_activity.data["id"]
|
assert activity.data["object"] == follow_activity.data["id"]
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == true
|
assert User.following?(follower, followed) == true
|
||||||
end
|
end
|
||||||
|
@ -667,7 +687,7 @@ test "it works for incoming accepts which were orphaned" do
|
||||||
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
|
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
|
||||||
assert activity.data["object"] == follow_activity.data["id"]
|
assert activity.data["object"] == follow_activity.data["id"]
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == true
|
assert User.following?(follower, followed) == true
|
||||||
end
|
end
|
||||||
|
@ -687,7 +707,7 @@ test "it works for incoming accepts which are referenced by IRI only" do
|
||||||
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
|
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
|
||||||
assert activity.data["object"] == follow_activity.data["id"]
|
assert activity.data["object"] == follow_activity.data["id"]
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == true
|
assert User.following?(follower, followed) == true
|
||||||
end
|
end
|
||||||
|
@ -706,7 +726,7 @@ test "it fails for incoming accepts which cannot be correlated" do
|
||||||
|
|
||||||
:error = Transmogrifier.handle_incoming(accept_data)
|
:error = Transmogrifier.handle_incoming(accept_data)
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
refute User.following?(follower, followed) == true
|
refute User.following?(follower, followed) == true
|
||||||
end
|
end
|
||||||
|
@ -725,7 +745,7 @@ test "it fails for incoming rejects which cannot be correlated" do
|
||||||
|
|
||||||
:error = Transmogrifier.handle_incoming(accept_data)
|
:error = Transmogrifier.handle_incoming(accept_data)
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
refute User.following?(follower, followed) == true
|
refute User.following?(follower, followed) == true
|
||||||
end
|
end
|
||||||
|
@ -750,7 +770,7 @@ test "it works for incoming rejects which are orphaned" do
|
||||||
{:ok, activity} = Transmogrifier.handle_incoming(reject_data)
|
{:ok, activity} = Transmogrifier.handle_incoming(reject_data)
|
||||||
refute activity.local
|
refute activity.local
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == false
|
assert User.following?(follower, followed) == false
|
||||||
end
|
end
|
||||||
|
@ -772,7 +792,7 @@ test "it works for incoming rejects which are referenced by IRI only" do
|
||||||
|
|
||||||
{:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
|
{:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
|
||||||
|
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == false
|
assert User.following?(follower, followed) == false
|
||||||
end
|
end
|
||||||
|
@ -1026,7 +1046,7 @@ test "it upgrades a user to activitypub" do
|
||||||
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
|
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
|
||||||
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
|
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.note_count == 1
|
assert user.info.note_count == 1
|
||||||
|
|
||||||
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
|
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
|
||||||
|
@ -1034,7 +1054,7 @@ test "it upgrades a user to activitypub" do
|
||||||
assert user.info.note_count == 1
|
assert user.info.note_count == 1
|
||||||
assert user.follower_address == "https://niu.moe/users/rye/followers"
|
assert user.follower_address == "https://niu.moe/users/rye/followers"
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.note_count == 1
|
assert user.info.note_count == 1
|
||||||
|
|
||||||
activity = Activity.get_by_id(activity.id)
|
activity = Activity.get_by_id(activity.id)
|
||||||
|
@ -1063,7 +1083,7 @@ test "it upgrades a user to activitypub" do
|
||||||
unrelated_activity = Activity.get_by_id(unrelated_activity.id)
|
unrelated_activity = Activity.get_by_id(unrelated_activity.id)
|
||||||
refute user.follower_address in unrelated_activity.recipients
|
refute user.follower_address in unrelated_activity.recipients
|
||||||
|
|
||||||
user_two = User.get_by_id(user_two.id)
|
user_two = User.get_cached_by_id(user_two.id)
|
||||||
assert user.follower_address in user_two.following
|
assert user.follower_address in user_two.following
|
||||||
refute "..." in user_two.following
|
refute "..." in user_two.following
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
@ -12,8 +11,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
describe "fetch the latest Follow" do
|
describe "fetch the latest Follow" do
|
||||||
test "fetches the latest Follow activity" do
|
test "fetches the latest Follow activity" do
|
||||||
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
||||||
follower = Repo.get_by(User, ap_id: activity.data["actor"])
|
follower = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
followed = Repo.get_by(User, ap_id: activity.data["object"])
|
followed = User.get_cached_by_ap_id(activity.data["object"])
|
||||||
|
|
||||||
assert activity == Utils.fetch_latest_follow(follower, followed)
|
assert activity == Utils.fetch_latest_follow(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
|
@ -89,8 +89,8 @@ test "allows to force-follow another user" do
|
||||||
"followed" => user.nickname
|
"followed" => user.nickname
|
||||||
})
|
})
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, user)
|
assert User.following?(follower, user)
|
||||||
end
|
end
|
||||||
|
@ -112,8 +112,8 @@ test "allows to force-unfollow another user" do
|
||||||
"followed" => user.nickname
|
"followed" => user.nickname
|
||||||
})
|
})
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
follower = User.get_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
refute User.following?(follower, user)
|
refute User.following?(follower, user)
|
||||||
end
|
end
|
||||||
|
@ -145,13 +145,13 @@ test "it appends specified tags to users with specified nicknames", %{
|
||||||
user2: user2
|
user2: user2
|
||||||
} do
|
} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_by_id(user1.id).tags == ["x", "foo", "bar"]
|
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
|
||||||
assert User.get_by_id(user2.id).tags == ["y", "foo", "bar"]
|
assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_by_id(user3.id).tags == ["unchanged"]
|
assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -181,13 +181,13 @@ test "it removes specified tags from users with specified nicknames", %{
|
||||||
user2: user2
|
user2: user2
|
||||||
} do
|
} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_by_id(user1.id).tags == []
|
assert User.get_cached_by_id(user1.id).tags == []
|
||||||
assert User.get_by_id(user2.id).tags == ["y"]
|
assert User.get_cached_by_id(user2.id).tags == ["y"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_by_id(user3.id).tags == ["unchanged"]
|
assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ test "deactivates the user", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false})
|
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false})
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.deactivated == true
|
assert user.info.deactivated == true
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
end
|
end
|
||||||
|
@ -269,7 +269,7 @@ test "activates the user", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: true})
|
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: true})
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.deactivated == false
|
assert user.info.deactivated == false
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
end
|
end
|
||||||
|
|
|
@ -119,6 +119,31 @@ test "works for bare text/markdown" do
|
||||||
assert output == expected
|
assert output == expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "works for bare text/bbcode" do
|
||||||
|
text = "[b]hello world[/b]"
|
||||||
|
expected = "<strong>hello world</strong>"
|
||||||
|
|
||||||
|
{output, [], []} = Utils.format_input(text, "text/bbcode")
|
||||||
|
|
||||||
|
assert output == expected
|
||||||
|
|
||||||
|
text = "[b]hello world![/b]\n\nsecond paragraph!"
|
||||||
|
expected = "<strong>hello world!</strong><br>\n<br>\nsecond paragraph!"
|
||||||
|
|
||||||
|
{output, [], []} = Utils.format_input(text, "text/bbcode")
|
||||||
|
|
||||||
|
assert output == expected
|
||||||
|
|
||||||
|
text = "[b]hello world![/b]\n\n<strong>second paragraph!</strong>"
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<strong>hello world!</strong><br>\n<br>\n<strong>second paragraph!</strong>"
|
||||||
|
|
||||||
|
{output, [], []} = Utils.format_input(text, "text/bbcode")
|
||||||
|
|
||||||
|
assert output == expected
|
||||||
|
end
|
||||||
|
|
||||||
test "works for text/markdown with mentions" do
|
test "works for text/markdown with mentions" do
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
UserBuilder.insert(%{nickname: "user__test", ap_id: "http://foo.com/user__test"})
|
UserBuilder.insert(%{nickname: "user__test", ap_id: "http://foo.com/user__test"})
|
||||||
|
|
|
@ -56,14 +56,17 @@ test "Represent a user account" do
|
||||||
bot: false,
|
bot: false,
|
||||||
source: %{
|
source: %{
|
||||||
note: "",
|
note: "",
|
||||||
privacy: "public",
|
sensitive: false,
|
||||||
sensitive: false
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
hide_favorites: true,
|
||||||
|
hide_followers: false,
|
||||||
|
hide_follows: false,
|
||||||
relationship: %{}
|
relationship: %{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,8 +84,12 @@ test "Represent the user account for the account owner" do
|
||||||
"follows" => true
|
"follows" => true
|
||||||
}
|
}
|
||||||
|
|
||||||
assert %{pleroma: %{notification_settings: ^notification_settings}} =
|
privacy = user.info.default_scope
|
||||||
AccountView.render("account.json", %{user: user, for: user})
|
|
||||||
|
assert %{
|
||||||
|
pleroma: %{notification_settings: ^notification_settings},
|
||||||
|
source: %{privacy: ^privacy}
|
||||||
|
} = AccountView.render("account.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Represent a Service(bot) account" do
|
test "Represent a Service(bot) account" do
|
||||||
|
@ -114,14 +121,17 @@ test "Represent a Service(bot) account" do
|
||||||
bot: true,
|
bot: true,
|
||||||
source: %{
|
source: %{
|
||||||
note: "",
|
note: "",
|
||||||
privacy: "public",
|
sensitive: false,
|
||||||
sensitive: false
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
hide_favorites: true,
|
||||||
|
hide_followers: false,
|
||||||
|
hide_follows: false,
|
||||||
relationship: %{}
|
relationship: %{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,15 +179,15 @@ test "represent a relationship" do
|
||||||
test "represent an embedded relationship" do
|
test "represent an embedded relationship" do
|
||||||
user =
|
user =
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
info: %{note_count: 5, follower_count: 3, source_data: %{"type" => "Service"}},
|
info: %{note_count: 5, follower_count: 0, source_data: %{"type" => "Service"}},
|
||||||
nickname: "shp@shitposter.club",
|
nickname: "shp@shitposter.club",
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||||
})
|
})
|
||||||
|
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, other_user} = User.follow(other_user, user)
|
{:ok, other_user} = User.follow(other_user, user)
|
||||||
{:ok, other_user} = User.block(other_user, user)
|
{:ok, other_user} = User.block(other_user, user)
|
||||||
|
{:ok, _} = User.follow(insert(:user), user)
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
|
@ -186,7 +196,7 @@ test "represent an embedded relationship" do
|
||||||
display_name: user.name,
|
display_name: user.name,
|
||||||
locked: false,
|
locked: false,
|
||||||
created_at: "2017-08-15T15:47:06.000Z",
|
created_at: "2017-08-15T15:47:06.000Z",
|
||||||
followers_count: 3,
|
followers_count: 1,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
statuses_count: 5,
|
statuses_count: 5,
|
||||||
note: user.bio,
|
note: user.bio,
|
||||||
|
@ -200,14 +210,17 @@ test "represent an embedded relationship" do
|
||||||
bot: true,
|
bot: true,
|
||||||
source: %{
|
source: %{
|
||||||
note: "",
|
note: "",
|
||||||
privacy: "public",
|
sensitive: false,
|
||||||
sensitive: false
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
hide_favorites: true,
|
||||||
|
hide_followers: false,
|
||||||
|
hide_follows: false,
|
||||||
relationship: %{
|
relationship: %{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
following: false,
|
following: false,
|
||||||
|
|
|
@ -445,7 +445,7 @@ test "get a status", %{conn: conn} do
|
||||||
describe "deleting a status" do
|
describe "deleting a status" do
|
||||||
test "when you created it", %{conn: conn} do
|
test "when you created it", %{conn: conn} do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -1022,7 +1022,7 @@ test "reblogged status for another user", %{conn: conn} do
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
user3 = insert(:user)
|
user3 = insert(:user)
|
||||||
CommonAPI.favorite(activity.id, user2)
|
CommonAPI.favorite(activity.id, user2)
|
||||||
{:ok, user2} = User.bookmark(user2, activity.data["object"]["id"])
|
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
|
||||||
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
|
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
|
||||||
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
|
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
|
||||||
|
|
||||||
|
@ -1167,7 +1167,7 @@ test "gets a users statuses", %{conn: conn} do
|
||||||
|
|
||||||
test "unimplemented pinned statuses feature", %{conn: conn} do
|
test "unimplemented pinned statuses feature", %{conn: conn} do
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
user = User.get_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -1178,7 +1178,7 @@ test "unimplemented pinned statuses feature", %{conn: conn} do
|
||||||
|
|
||||||
test "gets an users media", %{conn: conn} do
|
test "gets an users media", %{conn: conn} do
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
user = User.get_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
|
||||||
file = %Plug.Upload{
|
file = %Plug.Upload{
|
||||||
content_type: "image/jpg",
|
content_type: "image/jpg",
|
||||||
|
@ -1253,8 +1253,8 @@ test "/api/v1/follow_requests works" do
|
||||||
|
|
||||||
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
other_user = User.get_by_id(other_user.id)
|
other_user = User.get_cached_by_id(other_user.id)
|
||||||
|
|
||||||
assert User.following?(other_user, user) == false
|
assert User.following?(other_user, user) == false
|
||||||
|
|
||||||
|
@ -1273,8 +1273,8 @@ test "/api/v1/follow_requests/:id/authorize works" do
|
||||||
|
|
||||||
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
other_user = User.get_by_id(other_user.id)
|
other_user = User.get_cached_by_id(other_user.id)
|
||||||
|
|
||||||
assert User.following?(other_user, user) == false
|
assert User.following?(other_user, user) == false
|
||||||
|
|
||||||
|
@ -1286,8 +1286,8 @@ test "/api/v1/follow_requests/:id/authorize works" do
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response(conn, 200)
|
||||||
assert to_string(other_user.id) == relationship["id"]
|
assert to_string(other_user.id) == relationship["id"]
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
other_user = User.get_by_id(other_user.id)
|
other_user = User.get_cached_by_id(other_user.id)
|
||||||
|
|
||||||
assert User.following?(other_user, user) == true
|
assert User.following?(other_user, user) == true
|
||||||
end
|
end
|
||||||
|
@ -1310,7 +1310,7 @@ test "/api/v1/follow_requests/:id/reject works" do
|
||||||
|
|
||||||
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -1320,8 +1320,8 @@ test "/api/v1/follow_requests/:id/reject works" do
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response(conn, 200)
|
||||||
assert to_string(other_user.id) == relationship["id"]
|
assert to_string(other_user.id) == relationship["id"]
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
other_user = User.get_by_id(other_user.id)
|
other_user = User.get_cached_by_id(other_user.id)
|
||||||
|
|
||||||
assert User.following?(other_user, user) == false
|
assert User.following?(other_user, user) == false
|
||||||
end
|
end
|
||||||
|
@ -1606,7 +1606,7 @@ test "following / unfollowing a user", %{conn: conn} do
|
||||||
|
|
||||||
assert %{"id" => _id, "following" => true} = json_response(conn, 200)
|
assert %{"id" => _id, "following" => true} = json_response(conn, 200)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -1615,7 +1615,7 @@ test "following / unfollowing a user", %{conn: conn} do
|
||||||
|
|
||||||
assert %{"id" => _id, "following" => false} = json_response(conn, 200)
|
assert %{"id" => _id, "following" => false} = json_response(conn, 200)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -1709,7 +1709,7 @@ test "muting / unmuting a user", %{conn: conn} do
|
||||||
|
|
||||||
assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
|
assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -1764,7 +1764,7 @@ test "blocking / unblocking a user", %{conn: conn} do
|
||||||
|
|
||||||
assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
|
assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -1988,6 +1988,199 @@ test "returns the favorites of a user", %{conn: conn} do
|
||||||
assert [] = json_response(third_conn, 200)
|
assert [] = json_response(third_conn, 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "getting favorites timeline of specified user" do
|
||||||
|
setup do
|
||||||
|
[current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
|
||||||
|
[current_user: current_user, user: user]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns list of statuses favorited by specified user", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
[activity | _] = insert_pair(:note_activity)
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
[like] = response
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
assert like["id"] == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns favorites for specified user_id when user is not logged in", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns favorited DM only when user is logged in and he is one of recipients", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
{:ok, direct} =
|
||||||
|
CommonAPI.post(current_user, %{
|
||||||
|
"status" => "Hi @#{user.nickname}!",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.favorite(direct.id, user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
|
||||||
|
anonymous_response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(anonymous_response) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not return others' favorited DM when user is not one of recipients", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
{:ok, direct} =
|
||||||
|
CommonAPI.post(user_two, %{
|
||||||
|
"status" => "Hi @#{user.nickname}!",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.favorite(direct.id, user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(response) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates favorites using since_id and max_id", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
activities = insert_list(10, :note_activity)
|
||||||
|
|
||||||
|
Enum.each(activities, fn activity ->
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
end)
|
||||||
|
|
||||||
|
third_activity = Enum.at(activities, 2)
|
||||||
|
seventh_activity = Enum.at(activities, 6)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
|
||||||
|
since_id: third_activity.id,
|
||||||
|
max_id: seventh_activity.id
|
||||||
|
})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(response) == 3
|
||||||
|
refute third_activity in response
|
||||||
|
refute seventh_activity in response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "limits favorites using limit parameter", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
7
|
||||||
|
|> insert_list(:note_activity)
|
||||||
|
|> Enum.each(fn activity ->
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
end)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert length(response) == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns empty response when user does not have any favorited statuses", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert Enum.empty?(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 error when specified user is not exist", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
|
||||||
|
|
||||||
|
assert json_response(conn, 404) == %{"error" => "Record not found"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 error when user has hidden own favorites", %{
|
||||||
|
conn: conn,
|
||||||
|
current_user: current_user
|
||||||
|
} do
|
||||||
|
user = insert(:user, %{info: %{hide_favorites: true}})
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|
||||||
|
assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
|
||||||
|
user = insert(:user)
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, current_user)
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|
|
||||||
|
assert user.info.hide_favorites
|
||||||
|
assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "updating credentials" do
|
describe "updating credentials" do
|
||||||
test "updates the user's bio", %{conn: conn} do
|
test "updates the user's bio", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -2021,6 +2214,78 @@ test "updates the user's locking status", %{conn: conn} do
|
||||||
assert user["locked"] == true
|
assert user["locked"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates the user's default scope", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["privacy"] == "cofe"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_followers status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_followers"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_follows status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_follows"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_favorites status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_favorites"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's show_role status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["pleroma"]["show_role"] == false
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's no_rich_text status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["pleroma"]["no_rich_text"] == true
|
||||||
|
end
|
||||||
|
|
||||||
test "updates the user's name", %{conn: conn} do
|
test "updates the user's name", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -2124,7 +2389,7 @@ test "get instance stats", %{conn: conn} do
|
||||||
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
|
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
|
||||||
|
|
||||||
# Stats should count users with missing or nil `info.deactivated` value
|
# Stats should count users with missing or nil `info.deactivated` value
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
info_change = Changeset.change(user.info, %{deactivated: nil})
|
info_change = Changeset.change(user.info, %{deactivated: nil})
|
||||||
|
|
||||||
{:ok, _user} =
|
{:ok, _user} =
|
||||||
|
|
|
@ -21,7 +21,7 @@ test "Mention notification" do
|
||||||
mentioned_user = insert(:user)
|
mentioned_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{mentioned_user.nickname}"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{mentioned_user.nickname}"})
|
||||||
{:ok, [notification]} = Notification.create_notifications(activity)
|
{:ok, [notification]} = Notification.create_notifications(activity)
|
||||||
user = User.get_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -128,6 +129,7 @@ test "a note activity" do
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
local: true,
|
local: true,
|
||||||
conversation_id: convo_id,
|
conversation_id: convo_id,
|
||||||
|
in_reply_to_account_acct: nil,
|
||||||
content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
|
content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
|
||||||
spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
|
spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +154,25 @@ test "tells if the message is muted for some reason" do
|
||||||
assert status.muted == true
|
assert status.muted == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "tells if the status is bookmarked" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Cute girls doing cute things"})
|
||||||
|
status = StatusView.render("status.json", %{activity: activity})
|
||||||
|
|
||||||
|
assert status.bookmarked == false
|
||||||
|
|
||||||
|
status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
assert status.bookmarked == false
|
||||||
|
|
||||||
|
{:ok, _bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
|
||||||
|
status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
assert status.bookmarked == true
|
||||||
|
end
|
||||||
|
|
||||||
test "a reply" do
|
test "a reply" do
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -178,7 +199,7 @@ test "contains mentions" do
|
||||||
|
|
||||||
status = StatusView.render("status.json", %{activity: activity})
|
status = StatusView.render("status.json", %{activity: activity})
|
||||||
|
|
||||||
actor = User.get_by_ap_id(activity.actor)
|
actor = User.get_cached_by_ap_id(activity.actor)
|
||||||
|
|
||||||
assert status.mentions ==
|
assert status.mentions ==
|
||||||
Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
|
Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
@ -41,7 +40,8 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
|
||||||
assert response(conn, 200)
|
assert response(conn, 200)
|
||||||
|
|
||||||
# Set a wrong magic-key for a user so it has to refetch
|
# Set a wrong magic-key for a user so it has to refetch
|
||||||
salmon_user = User.get_by_ap_id("http://gs.example.org:4040/index.php/user/1")
|
salmon_user = User.get_cached_by_ap_id("http://gs.example.org:4040/index.php/user/1")
|
||||||
|
|
||||||
# Wrong key
|
# Wrong key
|
||||||
info_cng =
|
info_cng =
|
||||||
User.Info.remote_user_creation(salmon_user.info, %{
|
User.Info.remote_user_creation(salmon_user.info, %{
|
||||||
|
@ -52,7 +52,7 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
|
||||||
salmon_user
|
salmon_user
|
||||||
|> Ecto.Changeset.change()
|
|> Ecto.Changeset.change()
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||||
|> Repo.update()
|
|> User.update_and_set_cache()
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -86,7 +86,7 @@ test "returns 404 for a missing feed", %{conn: conn} do
|
||||||
|
|
||||||
test "gets an object", %{conn: conn} do
|
test "gets an object", %{conn: conn} do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
user = User.get_by_ap_id(note_activity.data["actor"])
|
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
|
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
|
||||||
url = "/objects/#{uuid}"
|
url = "/objects/#{uuid}"
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue