Compare commits

...

40 commits

Author SHA1 Message Date
da7a8cd269 Merge branch 'develop' into fedi-absturztau-be 2022-08-07 11:42:03 +02:00
62e179f446 make conversation-id deterministic (#154)
Reviewed-on: AkkomaGang/akkoma#154
2022-08-06 20:59:15 +00:00
21ec1edbb6 Merge pull request 'allow quote-inline span class' (#152) from allow-quote-inline into develop
Reviewed-on: AkkomaGang/akkoma#152
2022-08-05 19:37:35 +00:00
e8806fdc42 allow quote-inline span class 2022-08-05 20:35:00 +01:00
ec162b496b /notice signing checks on redirect (#150)
Reviewed-on: AkkomaGang/akkoma#150
2022-08-05 19:31:32 +00:00
3b973d0627 Merge pull request 'Update 'docs/docs/installation/migrating_to_akkoma.md'' (#151) from ShariVegas/akkoma:sharivegas-docpatch-migration into develop
Reviewed-on: AkkomaGang/akkoma#151
2022-08-05 16:40:12 +00:00
273e51cb4a Update 'docs/docs/installation/migrating_to_akkoma.md'
I ran into an issue after migrating, admin-fe wouldn't function properly. Ran the above command for my build, and got that functionality back.
2022-08-05 16:30:33 +00:00
0ec3a11895 don't persist undo of follows (#149)
Reviewed-on: AkkomaGang/akkoma#149
2022-08-05 13:28:56 +00:00
2781faaa7b Merge pull request 'Fix postgres install and setup for fedora guide' (#147) from norm/akkoma:fedora-install into develop
Reviewed-on: AkkomaGang/akkoma#147
2022-08-05 11:43:14 +00:00
a82fb2acc1 Merge pull request 'Update default paths' (#141) from norm/akkoma:update-default-paths into develop
Reviewed-on: AkkomaGang/akkoma#141
2022-08-05 11:42:14 +00:00
499d8a1056 Merge branch 'develop' into fedora-install 2022-08-05 05:03:00 +00:00
6b85b36e3a Fix postgres install and setup for fedora guide
Fedora requires some additional setup to work with Pleroma compared to Ubuntu/Debian.
2022-08-05 05:02:42 +00:00
5fe2c61029 Merge pull request 'Transmogrifier: fix reply context fixing' (#145) from misskey-thread-breakiness into develop
Reviewed-on: AkkomaGang/akkoma#145
2022-08-04 12:07:15 +00:00
c1e15ff6f8 Transmogrifier: fix reply context fixing
Incoming Pleroma replies to a Misskey thread were rejected due to a
broken context fix, which caused them to not be visible until a
non-Pleroma user interacted with the replies.

This fix properly sets the post-fix object context to its parent Create
activity as well, if it was changed.
2022-08-04 12:57:48 +01:00
9df732c42b Merge branch 'translations' into develop 2022-08-03 22:05:23 +01:00
80f444fb52 Merge pull request 'docs/installation: Update required Elixir version to 1.12' (#144) from norm/akkoma:norm-patch-1 into develop
Reviewed-on: AkkomaGang/akkoma#144
2022-08-03 12:02:41 +00:00
b5d06a3db8 docs/installation: Update required Elixir version to 1.12
Some dependencies will refuse to work on Elixir 1.10 (and presumably 1.9). One dependency states 1.13 as a requirement but will still work on 1.12 just fine.
2022-08-03 12:01:13 +00:00
456c97fda9 Merge pull request 'remove unneeded function' (#143) from compile-fix into develop
Reviewed-on: AkkomaGang/akkoma#143
2022-08-03 11:12:05 +00:00
842ab82ef0 Merge pull request 'Allow users to create backups without providing email address' (#140) from norm/akkoma:backup-without-email into develop
Reviewed-on: AkkomaGang/akkoma#140
2022-08-03 11:11:53 +00:00
sfr
6e9126a794 add code of conduct (#129)
Reviewed-on: AkkomaGang/akkoma#129
Co-authored-by: sfr <sol@solfisher.com>
Co-committed-by: sfr <sol@solfisher.com>
2022-08-03 10:55:11 +00:00
2c40d565fa Fix config path lookup (#139)
Reviewed-on: AkkomaGang/akkoma#139
Co-authored-by: Norm <normandy@biribiri.dev>
Co-committed-by: Norm <normandy@biribiri.dev>
2022-08-03 10:52:21 +00:00
359510eebc remove unneeded function 2022-08-03 11:50:48 +01:00
8bfd01b9c7
Update default paths 2022-08-03 01:05:53 -04:00
Tusooa Zhu
f08241c8ab
Allow users to create backups without providing email address
Ref: backup-without-email
2022-08-02 22:16:54 -04:00
c9600dbbbf local-only-fixed (#138)
Reviewed-on: AkkomaGang/akkoma#138
2022-08-02 14:46:46 +00:00
ca000f8301 Merge mrf_simple-reject with quarantine (#137)
Reviewed-on: AkkomaGang/akkoma#137
2022-08-02 14:19:24 +00:00
e26388a01c Support reaching user@sub.domain.tld at user@domain.tld (#134)
Reviewed-on: AkkomaGang/akkoma#134
Co-authored-by: Joel Beckmeyer <joel@beckmeyer.us>
Co-committed-by: Joel Beckmeyer <joel@beckmeyer.us>
2022-08-02 13:54:22 +00:00
c3eea8dc7d expose bubble instances via nodeinfo (#136)
Reviewed-on: AkkomaGang/akkoma#136
2022-08-02 09:11:22 +00:00
Weblate
8b14b65e39 Merge branch 'origin/develop' into Weblate. 2022-08-01 12:48:29 +00:00
55b86e45ec Merge pull request 'translations' (#133) from translations into develop
Reviewed-on: AkkomaGang/akkoma#133
2022-08-01 12:48:26 +00:00
Weblate
dbb80c79d5 Translated using Weblate (Catalan)
Currently translated at 100.0% (103 of 103 strings)

Translated using Weblate (Catalan)

Currently translated at 0.3% (4 of 1002 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (83 of 83 strings)

Translated using Weblate (Catalan)

Currently translated at 13.5% (14 of 103 strings)

Added translation using Weblate (Catalan)

Merge branch 'origin/develop' into Weblate.

Merge branch 'origin/develop' into Weblate.

Merge branch 'origin/develop' into Weblate.

Translated using Weblate (Catalan)

Currently translated at 100.0% (0 of 0 strings)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: sola <spla@mastodont.cat>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-config-descriptions/ca/
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-errors/ca/
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-static-pages/ca/
Translation: Pleroma fe/Akkoma Backend (Config Descriptions)
Translation: Pleroma fe/Akkoma Backend (Errors)
Translation: Pleroma fe/Akkoma Backend (Static pages)
2022-08-01 12:47:27 +00:00
19a27ff006 allow small/center tags in misskeymarkdown (#132)
Reviewed-on: AkkomaGang/akkoma#132
2022-08-01 12:46:52 +00:00
38659e5610 Use uppercase HTTP HEAD method for media preview proxy request (#128)
Reviewed-on: AkkomaGang/akkoma#128
Co-authored-by: Yukkuri <iamtakingiteasy@eientei.org>
Co-committed-by: Yukkuri <iamtakingiteasy@eientei.org>
2022-07-30 21:58:14 +00:00
2033d7d4fc ensure extra info in fix_follow_state prints 2022-07-29 19:50:26 +01:00
Weblate
bbb9dbc4d4 Merge branch 'origin/develop' into Weblate. 2022-07-29 11:42:45 +00:00
Weblate
c0965ed24a Added translation using Weblate (Catalan)
Merge branch 'origin/develop' into Weblate.

Merge branch 'origin/develop' into Weblate.

Merge branch 'origin/develop' into Weblate.

Translated using Weblate (Catalan)

Currently translated at 100.0% (0 of 0 strings)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: sola <spla@mastodont.cat>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-config-descriptions/ca/
Translation: Pleroma fe/Akkoma Backend (Config Descriptions)
2022-07-29 11:37:51 +00:00
Weblate
c53c967aa7 Merge branch 'origin/develop' into Weblate. 2022-07-29 09:10:28 +00:00
Weblate
e216b275fe Merge branch 'origin/develop' into Weblate. 2022-07-29 09:08:55 +00:00
Weblate
fc4dc83bba Merge branch 'origin/develop' into Weblate. 2022-07-29 09:04:18 +00:00
Weblate
f4b507f3a2 Translated using Weblate (Catalan)
Currently translated at 100.0% (0 of 0 strings)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Added translation using Weblate (Catalan)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: sola <spla@mastodont.cat>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/akkoma-backend-config-descriptions/ca/
Translation: Pleroma fe/Akkoma Backend (Config Descriptions)
2022-07-28 12:12:55 +00:00
80 changed files with 9179 additions and 275 deletions

View file

@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- quarantining is now considered absolutely; public activities are no longer
an exception.
- also merged quarantine and mrf reject - quarantine is now deprecated
- flavours:
- amd64 is built for debian stable. Compatible with ubuntu 20.
- ubuntu-jammy is built for... well, ubuntu 22 (LTS)
@ -173,6 +174,7 @@ you might end up in a situation where you don't have an ability to get it.
- Attachment dimensions and blurhashes are federated when available.
- Mastodon API: support `poll` notification.
- Pinned posts federation
- Possibility to discover users like `user@example.org`, while Akkoma is working on `akkoma.example.org`. Additional configuration required.
### Fixed
- Don't crash so hard when email settings are invalid.

24
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,24 @@
# Akkoma Code of Conduct
The Akkoma project aims to be **enjoyable** for anyone to participate in, regardless of their identity or level of expertise. To achieve this, the community must create an environment which is **safe** and **equitable**; the following guidelines have been created with these goals in mind.
1. **Treat individuals with respect.** Differing experiences and viewpoints deserve to be respected, and bigotry and harassment are not tolerated under any circumstances.
- Individuals should at all times be treated as equals, regardless of their age, gender, sexuality, race, ethnicity, _or any other characteristic_, intrinsic or otherwise.
- Behaviour that is harmful in nature should be addressed and corrected *regardless of intent*.
- Respect personal boundaries and ask for clarification whenever they are unclear.
- (Obviously, hate does not count as merely a "differing viewpoint", because it is harmful in nature.)
2. **Be understanding of differences in communication.** Not everyone is aware of unspoken social cues, and speech that is not intended to be offensive should not be treated as such simply due to an atypical manner of communication.
- Somebody who speaks bluntly is not necessarily rude, and somebody who swears a lot is not necessarily volatile.
- Try to confirm your interpretation of their intent rather than assuming bad faith.
- Someone may not communicate as, or come across as a picture of "professionalism", but this should not be seen as a reason to dismiss them. This is a **casual** space, and communication styles can reflect that.
3. **"Uncomfortable" does not mean "unsafe".** In an ideal world, the community would be safe, equitable, enjoyable, *and* comfortable for all members at all times. Unfortunately, this is not always possible in reality.
- Safety and equity will be prioritized over comfort whenever it is necessary to do so.
- Weaponizing one's own discomfort to deflect accountability or censor an individual (e.g. "white fragility") is a form of discriminatory conduct.
4. **Let people grow from their mistakes.** Nobody is perfect; even the most well-meaning individual can do something hurtful. Everyone should be given a fair opportunity to explain themselves and correct their behaviour. Portraying someone as inherently malicious prevents improvement and shifts focus away from the *action* that was problematic.
- Avoid bringing up past events that do not accurately reflect an individual's current actions or beliefs. (This is, of course, different from providing evidence of a recurring pattern of behaviour.)
---
This document was adapted from one created by ~keith as part of punks default repository template, and is licensed under CC-BY-SA 4.0. The original template is here: <https://bytes.keithhacks.cyou/keith/default-template>

View file

@ -215,7 +215,6 @@
],
allow_relay: true,
public: true,
quarantined_instances: [],
static_dir: "instance/static/",
allowed_post_formats: [
"text/plain",
@ -809,6 +808,8 @@
{Pleroma.Search, [max_running: 30, max_waiting: 50]}
]
config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: true
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
config :pleroma, Pleroma.Search.Meilisearch,

View file

@ -691,7 +691,7 @@
key_placeholder: "instance",
value_placeholder: "reason",
description:
"List of ActivityPub instances where activities will not be sent, and the reason for doing so",
"(Deprecated, will be removed in next release) List of ActivityPub instances where activities will not be sent, and the reason for doing so",
suggestions: [
{"quarantined.com", "Reason"},
{"*.quarantined.com", "Reason"}

View file

@ -126,6 +126,8 @@
config :pleroma, :cachex, provider: Pleroma.CachexMock
config :pleroma, Pleroma.Web.WebFinger, update_nickname_on_user_fetch: false
config :pleroma, :side_effects,
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
logger: Pleroma.LoggerMock

View file

@ -34,7 +34,7 @@ To add configuration to your config file, you can copy it from the base config.
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
* `quarantined_instances`: ActivityPub instances where activities will not be sent. They can still reach there via other means, we just won't send them.
* `quarantined_instances`: *DEPRECATED* ActivityPub instances where activities will not be sent. They can still reach there via other means, we just won't send them.
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
older software for theses nicknames.
@ -131,7 +131,7 @@ To add configuration to your config file, you can copy it from the base config.
* `media_removal`: List of instances to strip media attachments from and the reason for doing so.
* `media_nsfw`: List of instances to tag all media as NSFW (sensitive) from and the reason for doing so.
* `federated_timeline_removal`: List of instances to remove from the Federated Timeline (aka The Whole Known Network) and the reason for doing so.
* `reject`: List of instances to reject activities (except deletes) from and the reason for doing so.
* `reject`: List of instances to reject activities (except deletes) from and the reason for doing so. Additionally prevents activities from being sent to that instance.
* `accept`: List of instances to only accept activities (except deletes) from and the reason for doing so.
* `followers_only`: Force posts from the given instances to be visible by followers only and the reason for doing so.
* `report_removal`: List of instances to reject reports from and the reason for doing so.

View file

@ -0,0 +1,62 @@
# How to use a different domain name for Akkoma and the users it serves
Akkoma users are primarily identified by a `user@example.org` handle, and you might want this identifier to be the same as your email or jabber account, for instance.
However, in this case, you are almost certainly serving some web content on `https://example.org` already, and you might want to use another domain (say `akkoma.example.org`) for Akkoma itself.
Akkoma supports that, but it might be tricky to set up, and any error might prevent you from federating with other instances.
*If you are already running Akkoma on `example.org`, it is no longer possible to move it to `akkoma.example.org`.*
## Account identifiers
It is important to understand that for federation purposes, a user in Akkoma has two unique identifiers associated:
- A webfinger `acct:` URI, used for discovery and as a verifiable global name for the user across Akkoma instances. In our example, our account's acct: URI is `acct:user@example.org`
- An author/actor URI, used in every other aspect of federation. This is the way in which users are identified in ActivityPub, the underlying protocol used for federation with other Akkoma instances.
In our case, it is `https://akkoma.example.org/users/user`.
Both account identifiers are unique and required for Akkoma. An important risk if you set up your Akkoma instance incorrectly is to create two users (with different acct: URIs) with conflicting author/actor URIs.
## WebFinger
As said earlier, each Akkoma user has an `acct`: URI, which is used for discovery and authentication. When you add @user@example.org, a webfinger query is performed. This is done in two steps:
1. Querying `https://example.org/.well-known/host-meta` (where the domain of the URL matches the domain part of the `acct`: URI) to get information on how to perform the query.
This file will indeed contain a URL template of the form `https://example.org/.well-known/webfinger?resource={uri}` that will be used in the second step.
2. Fill the returned template with the `acct`: URI to be queried and perform the query: `https://example.org/.well-known/webfinger?resource=acct:user@example.org`
## Configuring your Akkoma instance
**_DO NOT ATTEMPT TO CONFIGURE YOUR INSTANCE THIS WAY IF YOU DID NOT UNDERSTAND THE ABOVE_**
### Configuring Akkoma
Akkoma has a two configuration settings to enable using different domains for your users and Akkoma itself. `host` in `Pleroma.Web.Endpoint` and `domain` in `Pleroma.Web.WebFinger`. When the latter is not set, it defaults to the value of `host`.
*Be extra careful when configuring your Akkoma instance, as changing `host` may cause remote instances to register different accounts with the same author/actor URI, which will result in federation issues!*
```elixir
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "pleroma.example.org"]
config :pleroma, Pleroma.Web.WebFinger, domain: "example.org"
```
- `domain` - is the domain for which your Akkoma instance has authority, it's the domain used in `acct:` URI. In our example, `domain` would be set to `example.org`.
- `host` - is the domain used for any URL generated for your instance, including the author/actor URL's. In our case, that would be `akkoma.example.org`.
### Configuring WebFinger domain
Now, you have Akkoma running at `https://akkoma.example.org` as well as a website at `https://example.org`. If you recall how webfinger queries work, the first step is to query `https://example.org/.well-known/host-meta`, which will contain an URL template.
Therefore, the easiest way to configure `example.org` is to redirect `/.well-known/host-meta` to `akkoma.example.org`.
With nginx, it would be as simple as adding:
```nginx
location = /.well-known/host-meta {
return 301 https://akkoma.example.org$request_uri;
}
```
in example.org's server block.

View file

@ -25,7 +25,16 @@ sudo dnf upgrade --refresh
* Install some of the above mentioned programs:
```shell
sudo dnf install git gcc g++ make cmake file-devel postgresql postgresql-contrib
sudo dnf install git gcc g++ make cmake file-devel postgresql-server postgresql-contrib
```
* Enable and initialize Postgres:
```shell
sudo systemctl enable postgresql.service
sudo postgresql-setup --initdb --unit postgresql
# Allow password auth for postgres
sudo sed -E -i 's|(host +all +all +127.0.0.1/32 +)ident|\1md5|' /var/lib/pgsql/data/pg_hba.conf
sudo systemctl start postgresql.service
```
### Install Elixir and Erlang

View file

@ -1,7 +1,7 @@
## Required dependencies
* PostgreSQL 9.6+
* Elixir 1.9+
* Elixir 1.12+ (1.13+ recommended)
* Erlang OTP 22.2+
* git
* file / libmagic

View file

@ -60,13 +60,18 @@ your upgrade path here depends on your setup
### I just run with the built-in frontend
You'll need to run a single command,
You'll need to run a couple of commands,
```bash
# From source
mix pleroma.frontend install pleroma-fe
# you'll probably want this too
mix pleroma.frontend install admin-fe
# OTP
./bin/pleroma_ctl frontend install pleroma-fe
# you'll probably want this too
./bin/pleroma_ctl frontend install admin-fe
```
### I've run the mix task to install a frontend

View file

@ -496,12 +496,12 @@ def run(["fix_follow_state", local_user, remote_user]) do
{:follow_data, Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(local, remote)} do
calculated_state = User.following?(local, remote)
shell_info(
IO.puts(
"Request state is #{request_state}, vs calculated state of following=#{calculated_state}"
)
if calculated_state == false && request_state == "accept" do
shell_info("Discrepancy found, fixing")
IO.puts("Discrepancy found, fixing")
Pleroma.Web.CommonAPI.reject_follow_request(local, remote)
shell_info("Relationship fixed")
else

View file

@ -17,7 +17,9 @@ defmodule Pleroma.Config.DeprecationWarnings do
{[:instance, :mrf_transparency], [:mrf, :transparency],
"\n* `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency`"},
{[:instance, :mrf_transparency_exclusions], [:mrf, :transparency_exclusions],
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"},
{[:instance, :quarantined_instances], [:mrf_simple, :reject],
"\n* `config :pleroma, :instance, :quarantined_instances` is now covered by `:pleroma, :mrf_simple, :reject`"}
]
def check_simple_policy_tuples do
@ -81,7 +83,7 @@ def check_simple_policy_tuples do
end
def check_quarantined_instances_tuples do
has_strings = Config.get([:instance, :quarantined_instances]) |> Enum.any?(&is_binary/1)
has_strings = Config.get([:instance, :quarantined_instances], []) |> Enum.any?(&is_binary/1)
if has_strings do
Logger.warn("""

View file

@ -32,9 +32,9 @@ defp get_default, do: Pleroma.Config.get(:default_config)
def release_defaults do
[
pleroma: [
{:instance, [static_dir: "/var/lib/pleroma/static"]},
{Pleroma.Uploaders.Local, [uploads: "/var/lib/pleroma/uploads"]},
{:modules, [runtime_dir: "/var/lib/pleroma/modules"]},
{:instance, [static_dir: "/var/lib/akkoma/static"]},
{Pleroma.Uploaders.Local, [uploads: "/var/lib/akkoma/uploads"]},
{:modules, [runtime_dir: "/var/lib/akkoma/modules"]},
{:release, true}
]
]

View file

@ -14,10 +14,10 @@ def load(config, opts) do
config_path =
cond do
opts[:config_path] -> opts[:config_path]
System.get_env("PLEROMA_CONFIG_PATH") -> System.get_env("PLEROMA_CONFIG_PATH")
System.get_env("AKKOMA_CONFIG_PATH") -> System.get_env("AKKOMA_CONFIG_PATH")
File.exists?("/etc/akkoma/config.exs") -> "/etc/akkoma/config.exs"
true -> "/etc/pleroma/config.exs"
System.get_env("PLEROMA_CONFIG_PATH") -> System.get_env("PLEROMA_CONFIG_PATH")
File.exists?("/etc/pleroma/config.exs") -> "/etc/pleroma/config.exs"
true -> "/etc/akkoma/config.exs"
end
with_runtime_config =
@ -31,7 +31,7 @@ def load(config, opts) do
warning = [
IO.ANSI.red(),
IO.ANSI.bright(),
"!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file",
"!!! Config path is not declared! Please ensure it exists and that AKKOMA_CONFIG_PATH and/or PLEROMA_CONFIG_PATH is unset or points to an existing file",
IO.ANSI.reset()
]

View file

@ -136,7 +136,12 @@ def html_escape(text, "text/html") do
HTML.filter_tags(text)
end
def html_escape(text, format) when format in ["text/plain", "text/x.misskeymarkdown"] do
def html_escape(text, "text/x.misskeymarkdown") do
text
|> HTML.filter_tags()
end
def html_escape(text, "text/plain") do
Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk ->
{:safe, part} = Phoenix.HTML.html_escape(chunk)

View file

@ -208,10 +208,6 @@ def get_cached_by_ap_id(ap_id) do
end
end
def context_mapping(context) do
Object.change(%Object{}, %{data: %{"id" => context}})
end
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
%ObjectTombstone{
id: id,

View file

@ -32,9 +32,7 @@ defmodule Pleroma.User.Backup do
end
def create(user, admin_id \\ nil) do
with :ok <- validate_email_enabled(),
:ok <- validate_user_email(user),
:ok <- validate_limit(user, admin_id),
with :ok <- validate_limit(user, admin_id),
{:ok, backup} <- user |> new() |> Repo.insert() do
BackupWorker.process(backup, admin_id)
end
@ -86,20 +84,6 @@ defp validate_limit(user, nil) do
end
end
defp validate_email_enabled do
if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
:ok
else
{:error, dgettext("errors", "Backups require enabled email")}
end
end
defp validate_user_email(%User{email: nil}) do
{:error, dgettext("errors", "Email is required")}
end
defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok
def get_last(user_id) do
__MODULE__
|> where(user_id: ^user_id)

View file

@ -327,7 +327,9 @@ def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
end
end
defp do_unfollow(follower, followed, activity_id, local) do
defp do_unfollow(follower, followed, activity_id, local)
defp do_unfollow(follower, followed, activity_id, local) when local == true do
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
@ -341,6 +343,32 @@ defp do_unfollow(follower, followed, activity_id, local) do
end
end
defp do_unfollow(follower, followed, activity_id, false) do
# On a remote unfollow, _remove_ their activity from the database, since some software (MISSKEEEEY)
# uses deterministic ids for follows.
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
{:ok, _activity} <- Repo.delete(follow_activity),
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
unfollow_activity <- remote_unfollow_data(unfollow_data),
_ <- notify_and_stream(unfollow_activity) do
{:ok, unfollow_activity}
else
nil -> nil
{:error, error} -> Repo.rollback(error)
end
end
defp remote_unfollow_data(data) do
{recipients, _, _} = get_recipients(data)
%Activity{
data: data,
local: false,
actor: data["actor"],
recipients: recipients
}
end
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag(params) do
with {:ok, result} <- Repo.transaction(fn -> do_flag(params) end) do
@ -486,9 +514,18 @@ def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
includes_local_public = Map.get(opts, :includes_local_public, false)
opts = Map.delete(opts, :user)
[Constants.as_public()]
intended_recipients =
if includes_local_public do
[Constants.as_public(), as_local_public()]
else
[Constants.as_public()]
end
intended_recipients
|> fetch_activities_query(opts)
|> restrict_unlisted(opts)
|> fetch_paginated_optimized(opts, pagination)
@ -588,9 +625,11 @@ defp restrict_thread_visibility(query, %{user: %User{skip_thread_containment: tr
do: query
defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do
local_public = as_local_public()
from(
a in query,
where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public)
)
end
@ -677,8 +716,12 @@ defp fetch_activities_for_reading_user(reading_user, params) do
defp user_activities_recipients(%{godmode: true}), do: []
defp user_activities_recipients(%{reading_user: reading_user}) do
if reading_user do
[Constants.as_public(), reading_user.ap_id | User.following(reading_user)]
if not is_nil(reading_user) and reading_user.local do
[
Constants.as_public(),
as_local_public(),
reading_user.ap_id | User.following(reading_user)
]
else
[Constants.as_public()]
end
@ -1437,7 +1480,7 @@ defp normalize_image(%{"url" => url}) do
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil
defp object_to_user_data(data) do
defp object_to_user_data(data, additional) do
fields =
data
|> Map.get("attachment", [])
@ -1467,18 +1510,18 @@ defp object_to_user_data(data) do
public_key =
if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
data["publicKey"]["publicKeyPem"]
else
nil
end
shared_inbox =
if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
data["endpoints"]["sharedInbox"]
else
nil
end
user_data = %{
# if WebFinger request was already done, we probably have acct, otherwise
# we request WebFinger here
nickname = additional[:nickname_from_acct] || generate_nickname(data)
%{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
ap_enabled: true,
@ -1499,21 +1542,27 @@ defp object_to_user_data(data) do
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox,
pinned_objects: pinned_objects
pinned_objects: pinned_objects,
nickname: nickname
}
end
# nickname can be nil because of virtual actors
if data["preferredUsername"] do
Map.put(
user_data,
:nickname,
"#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
)
defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
generated = "#{username}@#{URI.parse(data["id"]).host}"
if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
case WebFinger.finger(generated) do
{:ok, %{"subject" => "acct:" <> acct}} -> acct
_ -> generated
end
else
Map.put(user_data, :nickname, nil)
generated
end
end
# nickname can be nil because of virtual actors
defp generate_nickname(_), do: nil
def fetch_follow_information_for_user(user) do
with {:ok, following_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
@ -1585,17 +1634,17 @@ defp collection_private(%{"first" => first}) do
defp collection_private(_data), do: {:ok, true}
def user_data_from_user_object(data) do
def user_data_from_user_object(data, additional \\ []) do
with {:ok, data} <- MRF.filter(data) do
{:ok, object_to_user_data(data)}
{:ok, object_to_user_data(data, additional)}
else
e -> {:error, e}
end
end
def fetch_and_prepare_user_from_ap_id(ap_id) do
def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
{:ok, data} <- user_data_from_user_object(data) do
{:ok, data} <- user_data_from_user_object(data, additional) do
{:ok, maybe_update_follow_information(data)}
else
# If this has been deleted, only log a debug and not an error
@ -1693,13 +1742,13 @@ def pinned_fetch_task(%{pinned_objects: pins}) do
end
end
def make_user_from_ap_id(ap_id) do
def make_user_from_ap_id(ap_id, additional \\ []) do
user = User.get_cached_by_ap_id(ap_id)
if user && !User.ap_enabled?(user) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
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, additional) do
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
if user do
@ -1719,8 +1768,9 @@ def make_user_from_ap_id(ap_id) do
end
def make_user_from_nickname(nickname) do
with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
make_user_from_ap_id(ap_id)
with {:ok, %{"ap_id" => ap_id, "subject" => "acct:" <> acct}} when not is_nil(ap_id) <-
WebFinger.finger(nickname) do
make_user_from_ap_id(ap_id, nickname_from_acct: acct)
else
_e -> {:error, "No AP id in WebFinger"}
end

View file

@ -174,7 +174,7 @@ def changeset(struct, data) do
defp validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Article", "Note", "Page"])
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()

View file

@ -51,8 +51,6 @@ defmacro status_object_fields do
field(:summary, :string)
field(:context, :string)
# short identifier for PleromaFE to group statuses by context
field(:context_id, :integer)
field(:sensitive, :boolean, default: false)
field(:replies_count, :integer, default: 0)

View file

@ -22,14 +22,12 @@ def cast_and_filter_recipients(message, field, follower_collection, field_fallba
end
def fix_object_defaults(data) do
%{data: %{"id" => context}, id: context_id} =
Utils.create_context(data["context"] || data["conversation"])
context = Utils.maybe_create_context(data["context"] || data["conversation"])
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"])
data
|> Map.put("context", context)
|> Map.put("context_id", context_id)
|> cast_and_filter_recipients("to", follower_collection)
|> cast_and_filter_recipients("cc", follower_collection)
|> cast_and_filter_recipients("bto", follower_collection)

View file

@ -62,7 +62,7 @@ def changeset(struct, data) do
defp validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Event"])
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()

View file

@ -80,7 +80,7 @@ def changeset(struct, data) do
defp validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Question"])
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()

View file

@ -103,11 +103,16 @@ defp signature_host(%URI{port: port, scheme: scheme, host: host}) do
end
end
defp blocked_instances do
Config.get([:instance, :quarantined_instances], []) ++
Config.get([:mrf_simple, :reject], [])
end
defp should_federate?(inbox) do
%{host: host} = URI.parse(inbox)
quarantined_instances =
Config.get([:instance, :quarantined_instances], [])
blocked_instances()
|> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()

View file

@ -474,7 +474,16 @@ def handle_incoming(
|> fix_in_reply_to(fetch_options)
|> fix_quote_url(fetch_options)
data = Map.put(data, "object", object)
# Only change the Create's context if the object's context has been modified.
data =
if data["object"]["context"] != object["context"] do
data
|> Map.put("object", object)
|> Map.put("context", object["context"])
else
Map.put(data, "object", object)
end
options = Keyword.put(options, :local, false)
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),

View file

@ -154,22 +154,7 @@ def get_notified_from_object(object) do
Notification.get_notified_from_activity(%Activity{data: object}, false)
end
def create_context(context) do
context = context || generate_id("contexts")
# Ecto has problems accessing the constraint inside the jsonb,
# so we explicitly check for the existed object before insert
object = Object.get_cached_by_ap_id(context)
with true <- is_nil(object),
changeset <- Object.context_mapping(context),
{:ok, inserted_object} <- Repo.insert(changeset) do
inserted_object
else
_ ->
object
end
end
def maybe_create_context(context), do: context || generate_id("contexts")
@doc """
Enqueues an activity for federation if it's local
@ -201,18 +186,16 @@ def lazy_put_activity_defaults(map, true) do
|> Map.put_new("id", "pleroma:fakeid")
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", "pleroma:fakecontext")
|> Map.put_new("context_id", -1)
|> lazy_put_object_defaults(true)
end
def lazy_put_activity_defaults(map, _fake?) do
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
context = maybe_create_context(map["context"])
map
|> Map.put_new_lazy("id", &generate_activity_id/0)
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", context)
|> Map.put_new("context_id", context_id)
|> lazy_put_object_defaults(false)
end
@ -226,7 +209,6 @@ defp lazy_put_object_defaults(%{"object" => map} = activity, true)
|> Map.put_new("id", "pleroma:fake_object_id")
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", activity["context"])
|> Map.put_new("context_id", activity["context_id"])
|> Map.put_new("fake", true)
%{activity | "object" => object}
@ -239,7 +221,6 @@ defp lazy_put_object_defaults(%{"object" => map} = activity, _)
|> Map.put_new_lazy("id", &generate_object_id/0)
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", activity["context"])
|> Map.put_new("context_id", activity["context_id"])
%{activity | "object" => object}
end

View file

@ -85,11 +85,9 @@ def visible_for_user?(%{__struct__: module} = message, user)
x = [user.ap_id | User.following(user)]
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
if is_local_public?(message) do
user.local
else
is_public?(message) || Enum.any?(x, &(&1 in y))
end
user_is_local = user.local
federatable = not is_local_public?(message)
(is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
end
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do

View file

@ -259,8 +259,7 @@ def format_input(text, format, options \\ [])
@doc """
Formatting text to plain text, BBCode, HTML, or Markdown
"""
def format_input(text, format, options)
when format in ["text/plain", "text/x.misskeymarkdown"] do
def format_input(text, "text/plain", options) do
text
|> Formatter.html_escape("text/plain")
|> Formatter.linkify(options)
@ -284,6 +283,15 @@ def format_input(text, "text/html", options) do
|> Formatter.linkify(options)
end
def format_input(text, "text/x.misskeymarkdown", options) do
text
|> Formatter.linkify(options)
|> Formatter.html_escape("text/x.misskeymarkdown")
|> (fn {text, mentions, tags} ->
{String.replace(text, ~r/\r?\n/, "<br>"), mentions, tags}
end).()
end
def format_input(text, "text/markdown", options) do
text
|> Formatter.mentions_escape(options)
@ -450,35 +458,6 @@ def get_report_statuses(%User{ap_id: actor}, %{status_ids: status_ids})
def get_report_statuses(_, _), do: {:ok, nil}
# DEPRECATED mostly, context objects are now created at insertion time.
def context_to_conversation_id(context) do
with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
id
else
_e ->
changeset = Object.context_mapping(context)
case Repo.insert(changeset) do
{:ok, %{id: id}} ->
id
# This should be solved by an upsert, but it seems ecto
# has problems accessing the constraint inside the jsonb.
{:error, _} ->
Object.get_cached_by_ap_id(context).id
end
end
end
def conversation_id_to_context(id) do
with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
context
else
_e ->
{:error, dgettext("errors", "No such conversation")}
end
end
def validate_character_limit("" = _full_payload, [] = _attachments) do
{:error, dgettext("errors", "Cannot post an empty status without attachments")}
end

View file

@ -113,6 +113,8 @@ def public(%{assigns: %{user: user}} = conn, params) do
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:instance, params[:instance])
# Restricts unfederated content to authenticated users
|> Map.put(:includes_local_public, not is_nil(user))
|> ActivityPub.fetch_public_activities()
conn

View file

@ -57,11 +57,8 @@ defp get_replied_to_activities(activities) do
end)
end
defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id),
do: context_id
defp get_context_id(%{data: %{"context" => context}}) when is_binary(context),
do: Utils.context_to_conversation_id(context)
do: :erlang.crc32(context)
defp get_context_id(_), do: nil

View file

@ -54,7 +54,7 @@ defp handle_preview(conn, url) do
media_proxy_url = MediaProxy.url(url)
with {:ok, %{status: status} = head_response} when status in 200..299 <-
Pleroma.HTTP.request("head", media_proxy_url, [], [], name: MyFinch) do
Pleroma.HTTP.request("HEAD", media_proxy_url, [], [], name: MyFinch) do
content_type = Tesla.get_header(head_response, "content-type")
content_length = Tesla.get_header(head_response, "content-length")
content_length = content_length && String.to_integer(content_length)

View file

@ -70,7 +70,8 @@ def get_nodeinfo("2.0") do
features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
privilegedStaff: Config.get([:instance, :privileged_staff])
privilegedStaff: Config.get([:instance, :privileged_staff]),
localBubbleInstances: Config.get([:instance, :local_bubble], [])
}
}
end

View file

@ -88,7 +88,8 @@ def raw_nodeinfo do
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
localBubbleInstances: Config.get([:instance, :local_bubble], [])
}
}
end

View file

@ -5,6 +5,8 @@
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
import Plug.Conn
import Phoenix.Controller, only: [get_format: 1, text: 2]
alias Pleroma.Activity
alias Pleroma.Web.Router
require Logger
def init(options) do
@ -25,21 +27,45 @@ def call(conn, _opts) do
end
end
def route_aliases(%{path_info: ["objects", id]} = conn) do
ap_id = Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :object, id)
with %Activity{} = activity <- Activity.get_by_object_ap_id_with_object(ap_id) do
["/notice/#{activity.id}"]
else
_ -> []
end
end
def route_aliases(_), do: []
defp assign_valid_signature_on_route_aliases(conn, []), do: conn
defp assign_valid_signature_on_route_aliases(%{assigns: %{valid_signature: true}} = conn, _),
do: conn
defp assign_valid_signature_on_route_aliases(conn, [path | rest]) do
request_target = String.downcase("#{conn.method}") <> " #{path}"
conn =
conn
|> put_req_header("(request-target)", request_target)
|> case do
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
conn -> conn
end
conn
|> assign(:valid_signature, HTTPSignatures.validate_conn(conn))
|> assign_valid_signature_on_route_aliases(rest)
end
defp maybe_assign_valid_signature(conn) do
if has_signature_header?(conn) do
# set (request-target) header to the appropriate value
# we also replace the digest header with the one we computed
request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
conn =
conn
|> put_req_header("(request-target)", request_target)
|> case do
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
conn -> conn
end
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
possible_paths = route_aliases(conn) ++ [conn.request_path]
assign_valid_signature_on_route_aliases(conn, possible_paths)
else
Logger.debug("No signature header!")
conn

View file

@ -32,7 +32,13 @@ def host_meta do
def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do
host = Pleroma.Web.Endpoint.host()
regex = ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/
regex =
if webfinger_domain = Pleroma.Config.get([__MODULE__, :domain]) do
~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@(#{host}|#{webfinger_domain})/
else
~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/
end
with %{"username" => username} <- Regex.named_captures(regex, resource),
%User{} = user <- User.get_cached_by_nickname(username) do
@ -66,7 +72,7 @@ def represent_user(user, "JSON") do
{:ok, user} = User.ensure_keys_present(user)
%{
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
"subject" => "acct:#{user.nickname}@#{domain()}",
"aliases" => gather_aliases(user),
"links" => gather_links(user)
}
@ -88,12 +94,16 @@ def represent_user(user, "XML") do
:XRD,
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
[
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}
{:Subject, "acct:#{user.nickname}@#{domain()}"}
] ++ aliases ++ links
}
|> XmlBuilder.to_doc()
end
defp domain do
Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host()
end
defp webfinger_from_xml(body) do
with {:ok, doc} <- XML.parse_document(body) do
subject = XML.string_from_xpath("//Subject", doc)
@ -150,17 +160,15 @@ def get_template_from_xml(body) do
end
def find_lrdd_template(domain) do
with {:ok, %{status: status, body: body}} when status in 200..299 <-
HTTP.get("http://#{domain}/.well-known/host-meta") do
# WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
meta_url = "https://#{domain}/.well-known/host-meta"
with {:ok, %{status: status, body: body}} when status in 200..299 <- HTTP.get(meta_url) do
get_template_from_xml(body)
else
_ ->
with {:ok, %{body: body, status: status}} when status in 200..299 <-
HTTP.get("https://#{domain}/.well-known/host-meta") do
get_template_from_xml(body)
else
e -> {:error, "Can't find LRDD template: #{inspect(e)}"}
end
error ->
Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
{:error, :lrdd_not_found}
end
end
@ -174,7 +182,7 @@ defp get_address_from_domain(domain, encoded_account) when is_binary(domain) do
end
end
defp get_address_from_domain(_, _), do: nil
defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain}
@spec finger(String.t()) :: {:ok, map()} | {:error, any()}
def finger(account) do
@ -191,13 +199,11 @@ def finger(account) do
encoded_account = URI.encode("acct:#{account}")
with address when is_binary(address) <- get_address_from_domain(domain, encoded_account),
response <-
{:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
HTTP.get(
address,
[{"accept", "application/xrd+xml,application/jrd+json"}]
),
{:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
response do
) do
case List.keyfind(headers, "content-type", 0) do
{_, content_type} ->
case Plug.Conn.Utils.media_type(content_type) do
@ -215,10 +221,9 @@ def finger(account) do
{:error, {:content_type, nil}}
end
else
e ->
Logger.debug(fn -> "Couldn't finger #{account}" end)
Logger.debug(fn -> inspect(e) end)
{:error, e}
error ->
Logger.debug("Couldn't finger #{account}: #{inspect(error)}")
error
end
end
end

View file

@ -37,10 +37,7 @@ def perform(%Job{
backup_id |> Backup.get() |> Backup.process(),
{:ok, _job} <- schedule_deletion(backup),
:ok <- Backup.remove_outdated(backup),
{:ok, _} <-
backup
|> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
|> Pleroma.Emails.Mailer.deliver() do
:ok <- maybe_deliver_email(backup, admin_user_id) do
{:ok, backup}
end
end
@ -51,4 +48,23 @@ def perform(%Job{args: %{"op" => "delete", "backup_id" => backup_id}}) do
nil -> :ok
end
end
defp has_email?(user) do
not is_nil(user.email) and user.email != ""
end
defp maybe_deliver_email(backup, admin_user_id) do
has_mailer = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
backup = backup |> Pleroma.Repo.preload(:user)
if has_email?(backup.user) and has_mailer do
backup
|> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
|> Pleroma.Emails.Mailer.deliver()
:ok
else
:ok
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,546 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-29 11:37+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Translate Toolkit 3.7.1\n"
# # This file is a PO Template file.
# #
# # `msgid`s here are often extracted from source code.
# # Add new translations manually only if they're dynamic
# # translations that can't be statically extracted.
# #
# # Run `mix gettext.extract` to bring this file up to
# # date. Leave `msgstr`s empty as changing them here as no
# # effect: edit them in PO (`.po`) files instead.
# # From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr "No pot estar en blanc"
# # From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr "ja s'ha agafat"
# # From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr "és invàlid"
# # From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr "té format invàlid"
# # From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr "té una entrada no vàlida"
# # From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr "està reservat"
# # From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr "no coincideix amb la confirmació"
# # From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry"
msgstr "està encara associat amb aquesta entrada"
msgid "are still associated with this entry"
msgstr "estan encara associats amb aquesta entrada"
# # From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] "hauria de ser %{count} caràcter"
msgstr[1] "hauria de ser %{count} caràcters"
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] "hauria de tenir %{count} article"
msgstr[1] "hauria de tenir %{count} articles"
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] "hauria de ser al menys %{count} caràcter"
msgstr[1] "hauria de ser al menys %{count} caràcters"
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] "hauria de tenir al menys %{count} article"
msgstr[1] "hauria de tenir al menys %{count} articles"
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] "hauria de ser com a molt %{count} caràcter"
msgstr[1] "hauria de ser com a molt %{count} caràcters"
msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] "com a molt hauria de ser %{count} article"
msgstr[1] "com a molt haurien de ser %{count} articles"
# # From Ecto.Changeset.validate_number/3
msgid "must be less than %{number}"
msgstr "ha de ser menor que %{number}"
msgid "must be greater than %{number}"
msgstr "ha de ser major que %{number}"
msgid "must be less than or equal to %{number}"
msgstr "ha de ser menor o igual a %{number}"
msgid "must be greater than or equal to %{number}"
msgstr "ha de ser més gran o igual a %{number}"
msgid "must be equal to %{number}"
msgstr "ha de ser igual a %{number}"
#: lib/pleroma/web/common_api.ex:523
msgid "Account not found"
msgstr "Compte no trobat"
#: lib/pleroma/web/common_api.ex:316
msgid "Already voted"
msgstr "Ja votada"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:402
msgid "Bad request"
msgstr "Mala Sol·licitud"
#: lib/pleroma/web/controller_helper.ex:97
#: lib/pleroma/web/controller_helper.ex:103
msgid "Can't display this activity"
msgstr "No es pot mostrar aquesta activitat"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:325
msgid "Can't find user"
msgstr "No es pot trobar l'usuari"
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:80
msgid "Can't get favorites"
msgstr "No es poden obtenir els favorits"
#: lib/pleroma/web/common_api/utils.ex:482
msgid "Cannot post an empty status without attachments"
msgstr "No es pot publicar un apunt buit sense adjunts"
#: lib/pleroma/web/common_api/utils.ex:441
msgid "Comment must be up to %{max_size} characters"
msgstr "El comentari ha de ser fins a %{max_size} caràcters"
#: lib/pleroma/config_db.ex:200
msgid "Config with params %{params} not found"
msgstr "Configuració amb paràmetres %{params} no trobada"
#: lib/pleroma/web/common_api.ex:167 lib/pleroma/web/common_api.ex:171
msgid "Could not delete"
msgstr "No es pot esborrar"
#: lib/pleroma/web/common_api.ex:217
msgid "Could not favorite"
msgstr "No es pot afavorir"
#: lib/pleroma/web/common_api.ex:254
msgid "Could not unfavorite"
msgstr "No es pot desfer el favorit"
#: lib/pleroma/web/common_api.ex:202
msgid "Could not unrepeat"
msgstr "No es pot desfer la repetició"
#: lib/pleroma/web/common_api.ex:530 lib/pleroma/web/common_api.ex:539
msgid "Could not update state"
msgstr "No es pot actualitzar l'apunt"
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:205
msgid "Error."
msgstr "Error."
#: lib/pleroma/web/twitter_api/twitter_api.ex:105
msgid "Invalid CAPTCHA"
msgstr "CAPTCHA invàlid"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:144
#: lib/pleroma/web/o_auth/o_auth_controller.ex:631
msgid "Invalid credentials"
msgstr "Credencials invàlides"
#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:42
msgid "Invalid credentials."
msgstr "Credencials invàlides."
#: lib/pleroma/web/common_api.ex:337
msgid "Invalid indices"
msgstr "Index invàlids"
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29
msgid "Invalid parameters"
msgstr "Paràmetres invàlids"
#: lib/pleroma/web/common_api/utils.ex:349
msgid "Invalid password."
msgstr "Contrasenya invàlida."
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:255
msgid "Invalid request"
msgstr "Sol·licitud invàlida"
#: lib/pleroma/web/twitter_api/twitter_api.ex:108
msgid "Kocaptcha service unavailable"
msgstr "Servei Kocaptcha no disponible"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:140
msgid "Missing parameters"
msgstr "Falten paràmetres"
#: lib/pleroma/web/common_api/utils.ex:477
msgid "No such conversation"
msgstr "No hi ha tal conversa"
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:171
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:197
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:239
msgid "No such permission_group"
msgstr "No existeix permission_group"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:515
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11
#: lib/pleroma/web/feed/tag_controller.ex:16
#: lib/pleroma/web/feed/user_controller.ex:69
#: lib/pleroma/web/o_status/o_status_controller.ex:132
#: lib/pleroma/web/plugs/uploaded_media.ex:84
msgid "Not found"
msgstr "No trobat"
#: lib/pleroma/web/common_api.ex:308
msgid "Poll's author can't vote"
msgstr "L'autor de l'enquesta no pot votar-hi"
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:39
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:51
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:52
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:326
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
msgid "Record not found"
msgstr "Registre no trobat"
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35
#: lib/pleroma/web/feed/user_controller.ex:78
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:42
#: lib/pleroma/web/o_status/o_status_controller.ex:138
msgid "Something went wrong"
msgstr "Alguna cosa ha anat malament"
#: lib/pleroma/web/common_api/activity_draft.ex:143
msgid "The message visibility must be direct"
msgstr "La visibilitat del missatge ha de ser directe"
#: lib/pleroma/web/common_api/utils.ex:492
msgid "The status is over the character limit"
msgstr "L'apunt està per sobre del limit de caràcters"
#: lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex:36
msgid "This resource requires authentication."
msgstr "Aquest recurs requereix autenticació."
#: lib/pleroma/web/plugs/rate_limiter.ex:208
msgid "Throttled"
msgstr "Estrangulat"
#: lib/pleroma/web/common_api.ex:338
msgid "Too many choices"
msgstr "Massa opcions"
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:268
msgid "You can't revoke your own admin status."
msgstr "No pots revocar el teu propi estat d'administrador."
#: lib/pleroma/web/o_auth/o_auth_controller.ex:243
#: lib/pleroma/web/o_auth/o_auth_controller.ex:333
msgid "Your account is currently disabled"
msgstr "El teu compte està actualment desactivat"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:205
#: lib/pleroma/web/o_auth/o_auth_controller.ex:356
msgid "Your login is missing a confirmed e-mail address"
msgstr "Al teu inici de sessió li falta una adreça de correu confirmada"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:403
msgid "can't read inbox of %{nickname} as %{as_nickname}"
msgstr "no puc llegir la safata d'entrada de %{nickname} com a %{as_nickname}"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:502
msgid "can't update outbox of %{nickname} as %{as_nickname}"
msgstr ""
"no es pot actualitzar la safata de sortida de %{nickname} com a "
"%{as_nickname}"
#: lib/pleroma/web/common_api.ex:475
msgid "conversation is already muted"
msgstr "la conversa ja està silenciada"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:521
msgid "error"
msgstr "error"
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:34
msgid "mascots can only be images"
msgstr "les mascotes només poden ser imatges"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:63
msgid "not found"
msgstr "no trobat"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:437
msgid "Bad OAuth request."
msgstr "Sol·licitu OAuth dolenta."
#: lib/pleroma/web/twitter_api/twitter_api.ex:114
msgid "CAPTCHA already used"
msgstr "CAPTCHA ja usat"
#: lib/pleroma/web/twitter_api/twitter_api.ex:111
msgid "CAPTCHA expired"
msgstr "CAPTCHA expirat"
#: lib/pleroma/web/plugs/uploaded_media.ex:57
msgid "Failed"
msgstr "Fallat"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:453
msgid "Failed to authenticate: %{message}."
msgstr "No s'ha pogut autenticar: %{message}."
#: lib/pleroma/web/o_auth/o_auth_controller.ex:484
msgid "Failed to set up user account."
msgstr "No s'ha pogut configurar el compte d'usuari."
#: lib/pleroma/web/plugs/o_auth_scopes_plug.ex:37
msgid "Insufficient permissions: %{permissions}."
msgstr "Permisos insuficients: %{permissions}."
#: lib/pleroma/web/plugs/uploaded_media.ex:111
msgid "Internal Error"
msgstr "Error intern"
#: lib/pleroma/web/o_auth/fallback_controller.ex:22
#: lib/pleroma/web/o_auth/fallback_controller.ex:29
msgid "Invalid Username/Password"
msgstr "Usuari/Contrasenya invàlids"
#: lib/pleroma/web/twitter_api/twitter_api.ex:117
msgid "Invalid answer data"
msgstr "dada de resposta invàlida"
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
msgid "Nodeinfo schema version not handled"
msgstr "Versió no controlada del Esquema Nodeinfo"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:194
msgid "This action is outside the authorized scopes"
msgstr "Aquesta acció és fora dels àmbits autoritzats"
#: lib/pleroma/web/o_auth/fallback_controller.ex:14
msgid "Unknown error, please check the details and try again."
msgstr "Error desconegut, si us plau verifica els detalls i prova-ho de nou."
#: lib/pleroma/web/o_auth/o_auth_controller.ex:136
#: lib/pleroma/web/o_auth/o_auth_controller.ex:180
msgid "Unlisted redirect_uri."
msgstr "redirect_uri no llistada."
#: lib/pleroma/web/o_auth/o_auth_controller.ex:433
msgid "Unsupported OAuth provider: %{provider}."
msgstr "Proveïdor OAuth no compatible: %{provider}."
#: lib/pleroma/uploaders/uploader.ex:74
msgid "Uploader callback timeout"
msgstr "Temps d'espera esgotat del callback del carregador"
#: lib/pleroma/web/uploader_controller.ex:23
msgid "bad request"
msgstr "sol·licitud dolenta"
#: lib/pleroma/web/twitter_api/twitter_api.ex:102
msgid "CAPTCHA Error"
msgstr "Error CAPTCHA"
#: lib/pleroma/web/common_api.ex:266
msgid "Could not add reaction emoji"
msgstr "No es pot afegir la reacció emoji"
#: lib/pleroma/web/common_api.ex:277
msgid "Could not remove reaction emoji"
msgstr "No es pot treure la reacció emoji"
#: lib/pleroma/web/twitter_api/twitter_api.ex:128
msgid "Invalid CAPTCHA (Missing parameter: %{name})"
msgstr "CAPTCHA invàlid (Falta el paràmetre: %{name})"
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:96
msgid "List not found"
msgstr "Llista no trobada"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:151
msgid "Missing parameter: %{name}"
msgstr "Falta el paràmetre: %{name}"
#: lib/pleroma/web/o_auth/o_auth_controller.ex:232
#: lib/pleroma/web/o_auth/o_auth_controller.ex:346
msgid "Password reset is required"
msgstr "Es requereix restablir la contrasenya"
#: lib/pleroma/tests/auth_test_controller.ex:9
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/announcement_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/chat_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/config_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/frontend_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/instance_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/instance_document_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/relay_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/report_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/status_controller.ex:6
#: lib/pleroma/web/admin_api/controllers/user_controller.ex:6
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6
#: lib/pleroma/web/fallback/redirect_controller.ex:6
#: lib/pleroma/web/feed/tag_controller.ex:6
#: lib/pleroma/web/feed/user_controller.ex:6
#: lib/pleroma/web/mailer/subscription_controller.ex:6
#: lib/pleroma/web/manifest_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:11
#: lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/directory_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14
#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7
#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6
#: lib/pleroma/web/media_proxy/media_proxy_controller.ex:6
#: lib/pleroma/web/mongoose_im/mongoose_im_controller.ex:6
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6
#: lib/pleroma/web/o_auth/fallback_controller.ex:6
#: lib/pleroma/web/o_auth/mfa_controller.ex:10
#: lib/pleroma/web/o_auth/o_auth_controller.ex:6
#: lib/pleroma/web/o_status/o_status_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/app_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/backup_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5
#: lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/instances_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/report_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6
#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7
#: lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex:6
#: lib/pleroma/web/static_fe/static_fe_controller.ex:6
#: lib/pleroma/web/twitter_api/controller.ex:6
#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10
#: lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6
#: lib/pleroma/web/uploader_controller.ex:6
#: lib/pleroma/web/web_finger/web_finger_controller.ex:6
msgid ""
"Security violation: OAuth scopes check was neither handled nor explicitly "
"skipped."
msgstr ""
"Violació de seguretat: la verificació dels àmbits OAuth no ha estat ni "
"controlada ni explícitament omesa."
#: lib/pleroma/web/plugs/ensure_authenticated_plug.ex:32
msgid "Two-factor authentication enabled, you must use a access token."
msgstr "Autenticació de dos factor activada, has d'usar un token d'accés."
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
msgid "Web push subscription is disabled on this Pleroma instance"
msgstr "La subscripció Web push està desactivada en aquesta instància Akkoma"
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:234
msgid "You can't revoke your own admin/moderator status."
msgstr "No pots revocar els teu propi estat de admin/moderador."
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:129
msgid "authorization required for timeline view"
msgstr "es requereix autorització per a veure la línia de temps"
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
msgid "Access denied"
msgstr "Accés denegat"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:322
msgid "This API requires an authenticated user"
msgstr "Aquesta API requereix un usuari autenticat"
#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:26
#: lib/pleroma/web/plugs/user_is_admin_plug.ex:21
msgid "User is not an admin."
msgstr "L'usuari no és un admin."
#: lib/pleroma/user/backup.ex:75
msgid "Last export was less than a day ago"
msgid_plural "Last export was less than %{days} days ago"
msgstr[0] "La darrera exportació va ser fa menys d'un dia"
msgstr[1] "La darrera exportació va ser fa menys de %{days} dies"
#: lib/pleroma/user/backup.ex:93
msgid "Backups require enabled email"
msgstr "Les copies de seguretat requereixen un correu activat"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:434
msgid ""
"Character limit (%{limit} characters) exceeded, contains %{length} characters"
msgstr ""
"Limit de caràcters (%{limit} characters) excedit, conté %{length} caràcters"
#: lib/pleroma/user/backup.ex:98
msgid "Email is required"
msgstr "Es requereix correu"
#: lib/pleroma/web/common_api/utils.ex:507
msgid "Too many attachments"
msgstr "Massa adjunts"
#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:33
#: lib/pleroma/web/plugs/user_is_staff_plug.ex:20
msgid "User is not a staff member."
msgstr "L'usuari no és un membre del equip."
#: lib/pleroma/web/o_auth/o_auth_controller.ex:366
msgid "Your account is awaiting approval."
msgstr "El teu compte espera aprovació."

View file

@ -0,0 +1,163 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-28 09:15+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Translate Toolkit 3.7.1\n"
## This file is a PO Template file.
##
## `msgid`s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
msgid "eperm"
msgstr ""
msgid "eacces"
msgstr ""
msgid "eagain"
msgstr ""
msgid "ebadf"
msgstr ""
msgid "ebadmsg"
msgstr ""
msgid "ebusy"
msgstr ""
msgid "edeadlk"
msgstr ""
msgid "edeadlock"
msgstr ""
msgid "edquot"
msgstr ""
msgid "eexist"
msgstr ""
msgid "efault"
msgstr ""
msgid "efbig"
msgstr ""
msgid "eftype"
msgstr ""
msgid "eintr"
msgstr ""
msgid "einval"
msgstr ""
msgid "eio"
msgstr ""
msgid "eisdir"
msgstr ""
msgid "eloop"
msgstr ""
msgid "emfile"
msgstr ""
msgid "emlink"
msgstr ""
msgid "emultihop"
msgstr ""
msgid "enametoolong"
msgstr ""
msgid "enfile"
msgstr ""
msgid "enobufs"
msgstr ""
msgid "enodev"
msgstr ""
msgid "enolck"
msgstr ""
msgid "enolink"
msgstr ""
msgid "enoent"
msgstr ""
msgid "enomem"
msgstr ""
msgid "enospc"
msgstr ""
msgid "enosr"
msgstr ""
msgid "enostr"
msgstr ""
msgid "enosys"
msgstr ""
msgid "enotblk"
msgstr ""
msgid "enotdir"
msgstr ""
msgid "enotsup"
msgstr ""
msgid "enxio"
msgstr ""
msgid "eopnotsupp"
msgstr ""
msgid "eoverflow"
msgstr ""
msgid "epipe"
msgstr ""
msgid "erange"
msgstr ""
msgid "erofs"
msgstr ""
msgid "espipe"
msgstr ""
msgid "esrch"
msgstr ""
msgid "estale"
msgstr ""
msgid "etxtbsy"
msgstr ""
msgid "exdev"
msgstr ""

View file

@ -0,0 +1,567 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-28 09:15+0000\n"
"PO-Revision-Date: 2022-07-30 21:58+0000\n"
"Last-Translator: sola <spla@mastodont.cat>\n"
"Language-Team: Catalan <http://translate.akkoma.dev/projects/akkoma/"
"akkoma-backend-static-pages/ca/>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.13.1\n"
## This file is a PO Template file.
##
## "msgid"s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here as no
## effect: edit them in PO (.po) files instead.
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button"
msgid "Authorize"
msgstr "Autoritza"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error fetching user"
msgstr "Error obtenint usuari"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header"
msgid "Remote follow"
msgstr "Seguiment remot"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "placeholder text for auth code entry"
msgid "Authentication code"
msgstr "Codi d'autenticació"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10
#, elixir-autogen, elixir-format
msgctxt "placeholder text for password entry"
msgid "Password"
msgstr "Contrasenya"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "placeholder text for username entry"
msgid "Username"
msgstr "Nom d'usuari"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for login"
msgid "Authorize"
msgstr "Autoritza"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for mfa"
msgid "Authorize"
msgstr "Autoritza"
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error following account"
msgstr "Error seguint el compte"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header, need login"
msgid "Log in to follow"
msgstr "Inicia sessió per a seguir"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow mfa header"
msgid "Two-factor authentication"
msgstr "Autenticació de dos factors"
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow success"
msgid "Account followed!"
msgstr "Compte seguit!"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7
#, elixir-autogen, elixir-format
msgctxt "placeholder text for account id"
msgid "Your account ID, e.g. lain@quitter.se"
msgstr "L'ID del teu compte, p.e. maria@exemple.cat"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for following with a remote account"
msgid "Follow"
msgstr "Segueix"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error: %{error}"
msgstr "Error: %{error}"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header"
msgid "Remotely follow %{nickname}"
msgstr "Seguir remotament %{nickname}"
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "password reset button"
msgid "Reset"
msgstr "Reiniciar"
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "password reset failed homepage link"
msgid "Homepage"
msgstr "Pàgina d'inici"
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset failed message"
msgid "Password reset failed"
msgstr "Error al restablir la contrasenya"
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "password reset form confirm password prompt"
msgid "Confirmation"
msgstr "Confirmació"
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "password reset form password prompt"
msgid "Password"
msgstr "Contrasenya"
#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset invalid token message"
msgid "Invalid Token"
msgstr "Token Invàlid"
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "password reset successful homepage link"
msgid "Homepage"
msgstr "Pàgina d'inici"
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset successful message"
msgid "Password changed!"
msgstr "Contrasenya canviada!"
#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:15
#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7
#, elixir-autogen, elixir-format
msgctxt "tag feed description"
msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse."
msgstr ""
"Aquests son apunts públics etiquetats amb #%{tag}. Pots interactuar amb ells "
"si tens un compte en qualsevol lloc del fedivers."
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth authorization exists page title"
msgid "Authorization exists"
msgstr "Existeix autorització"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32
#, elixir-autogen, elixir-format
msgctxt "oauth authorize approve button"
msgid "Approve"
msgstr "Aprova"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30
#, elixir-autogen, elixir-format
msgctxt "oauth authorize cancel button"
msgid "Cancel"
msgstr "Cancel·la"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "oauth authorize message"
msgid "Application <strong>%{client_name}</strong> is requesting access to your account."
msgstr ""
"L'aplicació <strong>%{client_name}</strong> sol·licita accés al teu compte."
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth authorized page title"
msgid "Successfully authorized"
msgstr "Autoritzat amb èxit"
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth external provider page title"
msgid "Sign in with external provider"
msgstr "Inicia sessió amb proveïdor extern"
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13
#, elixir-autogen, elixir-format
msgctxt "oauth external provider sign in button"
msgid "Sign in with %{strategy}"
msgstr "Inicia sessió amb %{strategy}"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54
#, elixir-autogen, elixir-format
msgctxt "oauth login button"
msgid "Log In"
msgstr "Inicia sessió"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51
#, elixir-autogen, elixir-format
msgctxt "oauth login password prompt"
msgid "Password"
msgstr "Contrasenya"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47
#, elixir-autogen, elixir-format
msgctxt "oauth login username prompt"
msgid "Username"
msgstr "Nom d'usuari"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39
#, elixir-autogen, elixir-format
msgctxt "oauth register nickname prompt"
msgid "Pleroma Handle"
msgstr "Sobrenom Akkoma"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37
#, elixir-autogen, elixir-format
msgctxt "oauth register nickname unchangeable warning"
msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though."
msgstr ""
"Tria amb cura! No podràs canviar-ho més tard. En canvi, podràs canviar el "
"teu nom a mostrar."
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18
#, elixir-autogen, elixir-format
msgctxt "oauth register page email prompt"
msgid "Email"
msgstr "Correu"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10
#, elixir-autogen, elixir-format
msgctxt "oauth register page fill form prompt"
msgid "If you'd like to register a new account, please provide the details below."
msgstr ""
"Si desitges registrar un compte nou, si us plau proporciona els detalls a "
"continuació."
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35
#, elixir-autogen, elixir-format
msgctxt "oauth register page login button"
msgid "Proceed as existing user"
msgstr "Procedir com a usuari existent"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31
#, elixir-autogen, elixir-format
msgctxt "oauth register page login password prompt"
msgid "Password"
msgstr "Contrasenya"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24
#, elixir-autogen, elixir-format
msgctxt "oauth register page login prompt"
msgid "Alternatively, sign in to connect to existing account."
msgstr "Alternativament, inicia sessió per a connectar amb un compte existent."
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27
#, elixir-autogen, elixir-format
msgctxt "oauth register page login username prompt"
msgid "Name or email"
msgstr "Nom o correu"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14
#, elixir-autogen, elixir-format
msgctxt "oauth register page nickname prompt"
msgid "Nickname"
msgstr "Sobrenom"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22
#, elixir-autogen, elixir-format
msgctxt "oauth register page register button"
msgid "Proceed as new user"
msgstr "Procedir com a usuari nou"
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "oauth register page title"
msgid "Registration Details"
msgstr "Detalls del registre"
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36
#, elixir-autogen, elixir-format
msgctxt "oauth register page title"
msgid "This is the first time you visit! Please enter your Pleroma handle."
msgstr ""
"Aquesta és la primera vegada que ens visites! Si us plau introdueix el teu "
"sobrenom a Akkoma."
#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "oauth scopes message"
msgid "The following permissions will be granted"
msgstr "Es concediran els següents permisos"
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "oauth token code message"
msgid "Token code is <br>%{token}"
msgstr "El codi del Token és <br>%{token}"
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "mfa auth code prompt"
msgid "Authentication code"
msgstr "Codi d'autenticació"
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "mfa auth page title"
msgid "Two-factor authentication"
msgstr "Autenticació de dos factors"
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "mfa auth page use recovery code link"
msgid "Enter a two-factor recovery code"
msgstr "Entra el codi de recuperació de dos factors"
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20
#, elixir-autogen, elixir-format
msgctxt "mfa auth verify code button"
msgid "Verify"
msgstr "Verifica"
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "mfa recover page title"
msgid "Two-factor recovery"
msgstr "Recuperació de dos factors"
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "mfa recover recovery code prompt"
msgid "Recovery code"
msgstr "Codi de recuperació"
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "mfa recover use 2fa code link"
msgid "Enter a two-factor code"
msgstr "Entra el codi de dos factors"
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20
#, elixir-autogen, elixir-format
msgctxt "mfa recover verify recovery code button"
msgid "Verify"
msgstr "Verifica"
#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "static fe profile page remote follow button"
msgid "Remote follow"
msgstr "Seguiment remot"
#: lib/pleroma/web/templates/email/digest.html.eex:163
#, elixir-autogen, elixir-format
msgctxt "digest email header line"
msgid "Hey %{nickname}, here is what you've missed!"
msgstr "Hola %{nickname}, aquí està el que t'has perdut!"
#: lib/pleroma/web/templates/email/digest.html.eex:544
#, elixir-autogen, elixir-format
msgctxt "digest email receiver address"
msgid "The email address you are subscribed as is <a href='mailto:%{@user.email}' style='color: %{color};text-decoration: none;'>%{email}</a>. "
msgstr ""
"L'adreça de correu a la que estàs subscrit és <a href='mailto:%{@user.email}"
"' style='color: %{color};text-decoration: none;'>%{email}</a>. "
#: lib/pleroma/web/templates/email/digest.html.eex:538
#, elixir-autogen, elixir-format
msgctxt "digest email sending reason"
msgid "You have received this email because you have signed up to receive digest emails from <b>%{instance}</b> Pleroma instance."
msgstr ""
"Has rebut aquest correu perquè t'has registrat per a rebre correus resum des "
"de l'instància Akkoma <b>%{instance}2</b>."
#: lib/pleroma/web/templates/email/digest.html.eex:547
#, elixir-autogen, elixir-format
msgctxt "digest email unsubscribe action"
msgid "To unsubscribe, please go %{here}."
msgstr "Per a donar-te de baixa, si us plau ves a %{here}."
#: lib/pleroma/web/templates/email/digest.html.eex:547
#, elixir-autogen, elixir-format
msgctxt "digest email unsubscribe action link text"
msgid "here"
msgstr "aquí"
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "mailer unsubscribe failed message"
msgid "UNSUBSCRIBE FAILURE"
msgstr "ERROR DE CANCEL·LACIÓ DE LA SUBSCRIPCIÓ"
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "mailer unsubscribe successful message"
msgid "UNSUBSCRIBE SUCCESSFUL"
msgstr "SUBSCRIPCIÓ CANCEL·LADA AMB ÈXIT"
#: lib/pleroma/web/templates/email/digest.html.eex:385
#, elixir-format
msgctxt "new followers count header"
msgid "%{count} New Follower"
msgid_plural "%{count} New Followers"
msgstr[0] "%{count} Nou Seguidor"
msgstr[1] "%{count} Nous Seguidors"
#: lib/pleroma/emails/user_email.ex:356
#, elixir-autogen, elixir-format
msgctxt "account archive email body - self-requested"
msgid "<p>You requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
msgstr ""
"<p>Has sol·licitat una copia de seguretat completa del teu compte Akkoma. "
"Està llest per a descarrega:</p>\n"
"<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
#: lib/pleroma/emails/user_email.ex:384
#, elixir-autogen, elixir-format
msgctxt "account archive email subject"
msgid "Your account archive is ready"
msgstr "L'arxiu del teu compte està preparat"
#: lib/pleroma/emails/user_email.ex:188
#, elixir-autogen, elixir-format
msgctxt "approval pending email body"
msgid "<h3>Awaiting Approval</h3>\n<p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>\n"
msgstr ""
"<h3>Esperant aprovació</h3>\n"
"<p>El teu compte a %{instance_name} està sent revisat per l'equip. Rebràs un "
"altre correu quan el teu compte estigui aprovat.</p>\n"
#: lib/pleroma/emails/user_email.ex:202
#, elixir-autogen, elixir-format
msgctxt "approval pending email subject"
msgid "Your account is awaiting approval"
msgstr "El teu compte està esperant aprovació"
#: lib/pleroma/emails/user_email.ex:158
#, elixir-autogen, elixir-format
msgctxt "confirmation email body"
msgid "<h3>Thank you for registering on %{instance_name}</h3>\n<p>Email confirmation is required to activate the account.</p>\n<p>Please click the following link to <a href=\"%{confirmation_url}\">activate your account</a>.</p>\n"
msgstr ""
"<h3>Gràcies per registrar-te a %{instance_name}</h3>\n"
"<p>Es requereix correu de confirmació per a activar el reu compte.</p>\n"
"<p>Si us plau clica en l'enllaç següent per a <a href=\"%{confirmation_url}\""
">activar el teu compte</a>.</p>\n"
#: lib/pleroma/emails/user_email.ex:174
#, elixir-autogen, elixir-format
msgctxt "confirmation email subject"
msgid "%{instance_name} account confirmation"
msgstr "confirmació del compte a %{instance_name}"
#: lib/pleroma/emails/user_email.ex:310
#, elixir-autogen, elixir-format
msgctxt "digest email subject"
msgid "Your digest from %{instance_name}"
msgstr "El teu resum des de %{instance_name}"
#: lib/pleroma/emails/user_email.ex:81
#, elixir-autogen, elixir-format
msgctxt "password reset email body"
msgid "<h3>Reset your password at %{instance_name}</h3>\n<p>Someone has requested password change for your account at %{instance_name}.</p>\n<p>If it was you, visit the following link to proceed: <a href=\"%{password_reset_url}\">reset password</a>.</p>\n<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>\n"
msgstr ""
"<h3>Restableix la teva contrasenya a %{instance_name}</h3>\n"
"<p>Algú ha sol·licitat un canvi de contrasenya per el teu compte a "
"%{instance_name}.</p>\n"
"<p>Si has estat tu, visita el següent enllaç per a procedir: <a href=\""
"%{password_reset_url}\">restablir contrasenya</a>.</p>\n"
"<p>Si ha estat algú altre, no et preocupis: les teves dades estan segures i "
"la teva contrasenya no s'ha canviat.</p>\n"
#: lib/pleroma/emails/user_email.ex:98
#, elixir-autogen, elixir-format
msgctxt "password reset email subject"
msgid "Password reset"
msgstr "Restablir contrasenya"
#: lib/pleroma/emails/user_email.ex:215
#, elixir-autogen, elixir-format
msgctxt "successful registration email body"
msgid "<h3>Hello @%{nickname},</h3>\n<p>Your account at %{instance_name} has been registered successfully.</p>\n<p>No further action is required to activate your account.</p>\n"
msgstr ""
"<h3>Hola @%{nickname},</h3>\n"
"<p>El teu compte a %{instance_name} ha estat registrar amb èxit.</p>\n"
"<p>No es requereix cap altre acció per a activar el teu compte.</p>\n"
#: lib/pleroma/emails/user_email.ex:231
#, elixir-autogen, elixir-format
msgctxt "successful registration email subject"
msgid "Account registered on %{instance_name}"
msgstr "Compte registrat a %{instance_name}"
#: lib/pleroma/emails/user_email.ex:119
#, elixir-autogen, elixir-format
msgctxt "user invitation email body"
msgid "<h3>You are invited to %{instance_name}</h3>\n<p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>\n<p>Click the following link to register: <a href=\"%{registration_url}\">accept invitation</a>.</p>\n"
msgstr ""
"<h3>Has estat invitat a %{instance_name}</h3>\n"
"<p>%{inviter_name} t'invita a unir-te a %{instance_name}, una instància de "
"la plataforma de xarxa social federada Akkoma.</p>\n"
"<p>Clica el següent enllaç per a registrar-te: <a href=\"%{registration_url}"
"\">accepta invitació</a>.</p>\n"
#: lib/pleroma/emails/user_email.ex:136
#, elixir-autogen, elixir-format
msgctxt "user invitation email subject"
msgid "Invitation to %{instance_name}"
msgstr "Invitació per %{instance_name}"
#: lib/pleroma/emails/user_email.ex:53
#, elixir-autogen, elixir-format
msgctxt "welcome email html body"
msgid "Welcome to %{instance_name}!"
msgstr "Benvingut a %{instance_name}!"
#: lib/pleroma/emails/user_email.ex:41
#, elixir-autogen, elixir-format
msgctxt "welcome email subject"
msgid "Welcome to %{instance_name}!"
msgstr "Benvingut a %{instance_name}!"
#: lib/pleroma/emails/user_email.ex:65
#, elixir-autogen, elixir-format
msgctxt "welcome email text body"
msgid "Welcome to %{instance_name}!"
msgstr "Benvingut a %{instance_name}!"
#: lib/pleroma/emails/user_email.ex:368
#, elixir-autogen, elixir-format
msgctxt "account archive email body - admin requested"
msgid "<p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
msgstr ""
"<p>L'Administrador @%{admin_nickname} ha sol·licitat una copia de seguretat "
"completa del teu compte Akkoma. Està preparat per a descarrega:</p>\n"
"<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"

View file

@ -0,0 +1,153 @@
defmodule Pleroma.Repo.Migrations.ChangeThreadVisibilityToBeLocalOnlyAware do
use Ecto.Migration
def up do
execute("DROP FUNCTION IF EXISTS thread_visibility(actor varchar, activity_id varchar)")
execute(update_thread_visibility())
end
def down do
execute(
"DROP FUNCTION IF EXISTS thread_visibility(actor varchar, activity_id varchar, local_public varchar)"
)
execute(restore_thread_visibility())
end
def update_thread_visibility do
"""
CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar, local_public varchar default '') RETURNS boolean AS $$
DECLARE
public varchar := 'https://www.w3.org/ns/activitystreams#Public';
child objects%ROWTYPE;
activity activities%ROWTYPE;
author_fa varchar;
valid_recipients varchar[];
actor_user_following varchar[];
BEGIN
--- Fetch actor following
SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
JOIN users ON users.id = following_relationships.follower_id
JOIN users AS following ON following.id = following_relationships.following_id
WHERE users.ap_id = actor;
--- Fetch our initial activity.
SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
LOOP
--- Ensure that we have an activity before continuing.
--- If we don't, the thread is not satisfiable.
IF activity IS NULL THEN
RETURN false;
END IF;
--- We only care about Create activities.
IF activity.data->>'type' != 'Create' THEN
RETURN true;
END IF;
--- Normalize the child object into child.
SELECT * INTO child FROM objects
INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id';
--- Fetch the author's AS2 following collection.
SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
--- Prepare valid recipients array.
valid_recipients := ARRAY[actor, public];
--- If we specified local public, add it.
IF local_public <> '' THEN
valid_recipients := valid_recipients || local_public;
END IF;
IF ARRAY[author_fa] && actor_user_following THEN
valid_recipients := valid_recipients || author_fa;
END IF;
--- Check visibility.
IF NOT valid_recipients && activity.recipients THEN
--- activity not visible, break out of the loop
RETURN false;
END IF;
--- If there's a parent, load it and do this all over again.
IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
SELECT * INTO activity FROM activities
INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
WHERE child.data->>'inReplyTo' = objects.data->>'id';
ELSE
RETURN true;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
"""
end
# priv/repo/migrations/20191007073319_create_following_relationships.exs
def restore_thread_visibility do
"""
CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$
DECLARE
public varchar := 'https://www.w3.org/ns/activitystreams#Public';
child objects%ROWTYPE;
activity activities%ROWTYPE;
author_fa varchar;
valid_recipients varchar[];
actor_user_following varchar[];
BEGIN
--- Fetch actor following
SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
JOIN users ON users.id = following_relationships.follower_id
JOIN users AS following ON following.id = following_relationships.following_id
WHERE users.ap_id = actor;
--- Fetch our initial activity.
SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
LOOP
--- Ensure that we have an activity before continuing.
--- If we don't, the thread is not satisfiable.
IF activity IS NULL THEN
RETURN false;
END IF;
--- We only care about Create activities.
IF activity.data->>'type' != 'Create' THEN
RETURN true;
END IF;
--- Normalize the child object into child.
SELECT * INTO child FROM objects
INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id';
--- Fetch the author's AS2 following collection.
SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
--- Prepare valid recipients array.
valid_recipients := ARRAY[actor, public];
IF ARRAY[author_fa] && actor_user_following THEN
valid_recipients := valid_recipients || author_fa;
END IF;
--- Check visibility.
IF NOT valid_recipients && activity.recipients THEN
--- activity not visible, break out of the loop
RETURN false;
END IF;
--- If there's a parent, load it and do this all over again.
IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
SELECT * INTO activity FROM activities
INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
WHERE child.data->>'inReplyTo' = objects.data->>'id';
ELSE
RETURN true;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
"""
end
end

View file

@ -0,0 +1,35 @@
defmodule Pleroma.Repo.Migrations.RemoveRemoteCancelledFollowRequests do
use Ecto.Migration
def up do
statement = """
DELETE FROM
activities
WHERE
(data->>'type') = 'Follow'
AND
(data->>'state') = 'cancelled'
AND
local = false;
"""
execute(statement)
statement = """
DELETE FROM
activities
WHERE
(data->>'type') = 'Undo'
AND
(data->'object'->>'type') = 'Follow'
AND
local = false;
"""
execute(statement)
end
def down do
:ok
end
end

View file

@ -0,0 +1,14 @@
defmodule Pleroma.Repo.Migrations.RemoveNullObjects do
use Ecto.Migration
def up do
statement = """
DELETE FROM objects
WHERE (data->>'type') is null;
"""
execute(statement)
end
def down, do: :ok
end

View file

@ -56,7 +56,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes(:u, [])
Meta.allow_tag_with_these_attributes(:ul, [])
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card", "quote-inline"])
Meta.allow_tag_with_these_attributes(:span, [])
Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"])
@ -97,5 +97,8 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes(:font, ["face"])
end
Meta.allow_tag_with_these_attributes(:center, [])
Meta.allow_tag_with_these_attributes(:small, [])
Meta.strip_everything_not_covered()
end

View file

@ -0,0 +1,61 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://p.helene.moe/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://p.helene.moe/users/helene",
"attachment": [],
"attributedTo": "https://p.helene.moe/users/helene",
"cc": [
"https://p.helene.moe/users/helene/followers"
],
"context": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"conversation": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"directMessage": false,
"id": "https://p.helene.moe/activities/5f80db86-a9bb-4883-9845-fbdbd1478f3a",
"object": {
"actor": "https://p.helene.moe/users/helene",
"attachment": [],
"attributedTo": "https://p.helene.moe/users/helene",
"cc": [
"https://p.helene.moe/users/helene/followers"
],
"content": "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"AHntpQ4T3J4OSnpgMC\" href=\"https://mk.absturztau.be/@mametsuko\" rel=\"ugc\">@<span>mametsuko</span></a></span> meow",
"context": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"conversation": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"id": "https://p.helene.moe/objects/fd5910ac-d9dc-412e-8d1d-914b203296c4",
"inReplyTo": "https://mk.absturztau.be/notes/93e7nm8wqg",
"published": "2022-08-02T13:46:58.403996Z",
"sensitive": null,
"source": "@mametsuko@mk.absturztau.be meow",
"summary": "",
"tag": [
{
"href": "https://mk.absturztau.be/users/8ozbzjs3o8",
"name": "@mametsuko@mk.absturztau.be",
"type": "Mention"
}
],
"to": [
"https://mk.absturztau.be/users/8ozbzjs3o8",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note"
},
"published": "2022-08-02T13:46:58.403883Z",
"tag": [
{
"href": "https://mk.absturztau.be/users/8ozbzjs3o8",
"name": "@mametsuko@mk.absturztau.be",
"type": "Mention"
}
],
"to": [
"https://mk.absturztau.be/users/8ozbzjs3o8",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Create"
}

View file

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">framatube.org</hm:Host><Link rel="lrdd" template="http://framatube.org/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">framatube.org</hm:Host><Link rel="lrdd" template="https://framatube.org/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>

View file

@ -0,0 +1,50 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://p.helene.moe/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"alsoKnownAs": [],
"attachment": [
{
"name": "Timezone",
"type": "PropertyValue",
"value": "UTC+2 (Paris/Berlin)"
}
],
"capabilities": {
"acceptsChatMessages": true
},
"discoverable": true,
"endpoints": {
"oauthAuthorizationEndpoint": "https://p.helene.moe/oauth/authorize",
"oauthRegistrationEndpoint": "https://p.helene.moe/api/v1/apps",
"oauthTokenEndpoint": "https://p.helene.moe/oauth/token",
"sharedInbox": "https://p.helene.moe/inbox",
"uploadMedia": "https://p.helene.moe/api/ap/upload_media"
},
"featured": "https://p.helene.moe/users/helene/collections/featured",
"followers": "https://p.helene.moe/users/helene/followers",
"following": "https://p.helene.moe/users/helene/following",
"icon": {
"type": "Image",
"url": "https://p.helene.moe/media/9a39209daa5a66b7ebb0547b08bf8360aa9d8d65a4ffba2603c6ffbe6aecb432.jpg"
},
"id": "https://p.helene.moe/users/helene",
"inbox": "https://p.helene.moe/users/helene/inbox",
"manuallyApprovesFollowers": false,
"name": "Hélène",
"outbox": "https://p.helene.moe/users/helene/outbox",
"preferredUsername": "helene",
"publicKey": {
"id": "https://p.helene.moe/users/helene#main-key",
"owner": "https://p.helene.moe/users/helene",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtoSBPU/VS2Kx3f6ap3zv\nZVacJsgUfaoFb3c2ii/FRh9RmRVlarq8sJXcjsQt1e0oxWaWJaIDDwyKZPt6hXae\nrY/AiGGeNu+NA+BtY7l7+9Yu67HUyT62+1qAwYHKBXX3fLOPs/YmQI0Tt0c4wKAG\nKEkiYsRizghgpzUC6jqdKV71DJkUZ8yhckCGb2fLko1ajbWEssdaP51aLsyRMyC2\nuzeWrxtD4O/HG0ea4S6y5X6hnsAHIK4Y3nnyIQ6pn4tOsl3HgqkjXE9MmZSvMCFx\nBq89TfZrVXNa2gSZdZLdbbJstzEScQWNt1p6tA6rM+e4JXYGr+rMdF3G+jV7afI2\nFQIDAQAB\n-----END PUBLIC KEY-----\n\n"
},
"summary": "I can speak: Français, English, Deutsch (nicht sehr gut), 日本語 (not very well)",
"tag": [],
"type": "Person",
"url": "https://p.helene.moe/users/helene"
}

View file

@ -0,0 +1,65 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"Hashtag": "as:Hashtag",
"quoteUrl": "as:quoteUrl",
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji",
"featured": "toot:featured",
"discoverable": "toot:discoverable",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"misskey": "https://misskey-hub.net/ns#",
"_misskey_content": "misskey:_misskey_content",
"_misskey_quote": "misskey:_misskey_quote",
"_misskey_reaction": "misskey:_misskey_reaction",
"_misskey_votes": "misskey:_misskey_votes",
"_misskey_talk": "misskey:_misskey_talk",
"isCat": "misskey:isCat",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"type": "Person",
"id": "https://mk.absturztau.be/users/8ozbzjs3o8",
"inbox": "https://mk.absturztau.be/users/8ozbzjs3o8/inbox",
"outbox": "https://mk.absturztau.be/users/8ozbzjs3o8/outbox",
"followers": "https://mk.absturztau.be/users/8ozbzjs3o8/followers",
"following": "https://mk.absturztau.be/users/8ozbzjs3o8/following",
"featured": "https://mk.absturztau.be/users/8ozbzjs3o8/collections/featured",
"sharedInbox": "https://mk.absturztau.be/inbox",
"endpoints": {
"sharedInbox": "https://mk.absturztau.be/inbox"
},
"url": "https://mk.absturztau.be/@mametsuko",
"preferredUsername": "mametsuko",
"name": "mametschko",
"summary": "<p><span>nya, ich bin eine Brotperson</span></p>",
"icon": {
"type": "Image",
"url": "https://mk.absturztau.be/files/webpublic-3b5594f4-fa52-4548-b4e3-c379ae2143ed",
"sensitive": false,
"name": null
},
"image": {
"type": "Image",
"url": "https://mk.absturztau.be/files/webpublic-0d03b03d-b14b-4916-ac3d-8a137118ec84",
"sensitive": false,
"name": null
},
"tag": [],
"manuallyApprovesFollowers": true,
"discoverable": false,
"publicKey": {
"id": "https://mk.absturztau.be/users/8ozbzjs3o8#main-key",
"type": "Key",
"owner": "https://mk.absturztau.be/users/8ozbzjs3o8",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuN/S1spBGmh8FXI1Bt16\nXB7Cc0QutBp7UPgmDNHjOfsq0zrF4g3L1UBxvrpU0XX77XPMCd9yPvGwAYURH2mv\ntIcYuE+R90VLDmBu5MTVthcG2D874eCZ2rD2YsEYmN5AjTX7QBIqCck+qDhVWkkM\nEZ6S5Ht6IJ5Of74eKffXElQI/C6QB+9uEDOmPk0jCzgI5gw7xvJqFj/DIF4kUUAu\nA89JqaFZzZlkrSrj4cr48bLN/YOmpdaHu0BKHaDSHct4+MqlixqovgdB6RboCEDw\ne4Aeav7+Q0Y9oGIvuggg0Q+nCubnVNnaPyzd817tpPVzyZmTts+DKyDuv90SX3nR\nsPaNa5Ty60eqplUk4b7X1gSvuzBJUFBxTVV84WnjwoeoydaS6rSyjCDPGLBjaByc\nFyWMMEb/zlQyhLZfBlvT7k96wRSsMszh2hDALWmgYIhq/jNwINvALJ1GKLNHHKZ4\nyz2LnxVpRm2rWrZzbvtcnSQOt3LaPSZn8Wgwv4buyHF02iuVuIamZVtKexsE1Ixl\nIi9qa3AKEc5gOzYXhRhvHaruzoCehUbb/UHC5c8Tto8L5G1xYzjLP3qj3PT9w/wM\n+k1Ra/4JhuAnVFROOoOmx9rIELLHH7juY2nhM7plGhyt1M5gysgqEloij8QzyQU2\nZK1YlAERG2XFO6br8omhcmECAwEAAQ==\n-----END PUBLIC KEY-----\n"
},
"isCat": true,
"vcard:Address": "Vienna, Austria"
}

View file

@ -0,0 +1,44 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"Hashtag": "as:Hashtag",
"quoteUrl": "as:quoteUrl",
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji",
"featured": "toot:featured",
"discoverable": "toot:discoverable",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"misskey": "https://misskey-hub.net/ns#",
"_misskey_content": "misskey:_misskey_content",
"_misskey_quote": "misskey:_misskey_quote",
"_misskey_reaction": "misskey:_misskey_reaction",
"_misskey_votes": "misskey:_misskey_votes",
"_misskey_talk": "misskey:_misskey_talk",
"isCat": "misskey:isCat",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"id": "https://mk.absturztau.be/notes/93e7nm8wqg",
"type": "Note",
"attributedTo": "https://mk.absturztau.be/users/8ozbzjs3o8",
"summary": null,
"content": "<p><span>meow</span></p>",
"_misskey_content": "meow",
"published": "2022-08-01T11:06:49.568Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://mk.absturztau.be/users/8ozbzjs3o8/followers"
],
"inReplyTo": null,
"attachment": [],
"sensitive": false,
"tag": []
}

View file

@ -0,0 +1,36 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://p.helene.moe/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://p.helene.moe/users/helene",
"attachment": [],
"attributedTo": "https://p.helene.moe/users/helene",
"cc": [
"https://p.helene.moe/users/helene/followers"
],
"content": "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"AHntpQ4T3J4OSnpgMC\" href=\"https://mk.absturztau.be/@mametsuko\" rel=\"ugc\">@<span>mametsuko</span></a></span> meow",
"context": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"conversation": "https://p.helene.moe/contexts/cc324643-5583-4c3f-91d2-c6ed37db159d",
"id": "https://p.helene.moe/objects/fd5910ac-d9dc-412e-8d1d-914b203296c4",
"inReplyTo": "https://mk.absturztau.be/notes/93e7nm8wqg",
"published": "2022-08-02T13:46:58.403996Z",
"sensitive": null,
"source": "@mametsuko@mk.absturztau.be meow",
"summary": "",
"tag": [
{
"href": "https://mk.absturztau.be/users/8ozbzjs3o8",
"name": "@mametsuko@mk.absturztau.be",
"type": "Mention"
}
],
"to": [
"https://mk.absturztau.be/users/8ozbzjs3o8",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note"
}

View file

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">status.alpicola.com</hm:Host><Link rel="lrdd" template="http://status.alpicola.com/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">status.alpicola.com</hm:Host><Link rel="lrdd" template="https://status.alpicola.com/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
<Link rel="lrdd" template="https://{{domain}}/.well-known/webfinger?resource={uri}"/>
</XRD>

92
test/fixtures/webfinger/masto-user.json vendored Normal file
View file

@ -0,0 +1,92 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"featuredTags": {
"@id": "toot:featuredTags",
"@type": "@id"
},
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"IdentityProof": "toot:IdentityProof",
"discoverable": "toot:discoverable",
"Device": "toot:Device",
"Ed25519Signature": "toot:Ed25519Signature",
"Ed25519Key": "toot:Ed25519Key",
"Curve25519Key": "toot:Curve25519Key",
"EncryptedMessage": "toot:EncryptedMessage",
"publicKeyBase64": "toot:publicKeyBase64",
"deviceId": "toot:deviceId",
"claim": {
"@type": "@id",
"@id": "toot:claim"
},
"fingerprintKey": {
"@type": "@id",
"@id": "toot:fingerprintKey"
},
"identityKey": {
"@type": "@id",
"@id": "toot:identityKey"
},
"devices": {
"@type": "@id",
"@id": "toot:devices"
},
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"cipherText": "toot:cipherText",
"suspended": "toot:suspended",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://{{domain}}/users/{{nickname}}",
"type": "Person",
"following": "https://{{domain}}/users/{{nickname}}/following",
"followers": "https://{{domain}}/users/{{nickname}}/followers",
"inbox": "https://{{domain}}/users/{{nickname}}/inbox",
"outbox": "https://{{domain}}/users/{{nickname}}/outbox",
"featured": "https://{{domain}}/users/{{nickname}}/collections/featured",
"featuredTags": "https://{{domain}}/users/{{nickname}}/collections/tags",
"preferredUsername": "{{nickname}}",
"name": "Name Name",
"summary": "<p>Summary</p>",
"url": "https://{{domain}}/@{{nickname}}",
"manuallyApprovesFollowers": false,
"discoverable": false,
"devices": "https://{{domain}}/users/{{nickname}}/collections/devices",
"publicKey": {
"id": "https://{{domain}}/users/{{nickname}}#main-key",
"owner": "https://{{domain}}/users/{{nickname}}",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwDujxmxoYHs64MyVB3L\nG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ+3Zb6CI8zOO+nM+Q2llrVRYjZa4ZFnOLvM\nTq/Kf+Zf5wy2aCRer88gX+MsJOAtItSi412y0a/rKOuFaDYLOLeTkRvmGLgZWbsr\nZJOp+YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBO\nfOHggSt1+eAIKGIsCmINEMzs1mG9D75xKtC/sM8GfbvBclQcBstGkHAEj1VHPW0c\nh6Bok5/QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Q\nawIDAQAB\n-----END PUBLIC KEY-----\n"
},
"tag": [],
"attachment": [],
"endpoints": {
"sharedInbox": "https://{{domain}}/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://s3.wasabisys.com/merp/accounts/avatars/000/000/001/original/6fdd3eee632af247.jpg"
}
}

View file

@ -0,0 +1,23 @@
{
"subject": "acct:{{nickname}}@{{domain}}",
"aliases": [
"https://{{subdomain}}/@{{nickname}}",
"https://{{subdomain}}/users/{{nickname}}"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://{{subdomain}}/@{{nickname}}"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://{{subdomain}}/users/{{nickname}}"
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"template": "https://{{subdomain}}/authorize_interaction?uri={uri}"
}
]
}

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="https://{{domain}}/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>

View file

@ -0,0 +1,58 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://{{domain}}/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"alsoKnownAs": [],
"attachment": [],
"capabilities": {
"acceptsChatMessages": true
},
"discoverable": true,
"endpoints": {
"oauthAuthorizationEndpoint": "https://{{domain}}/oauth/authorize",
"oauthRegistrationEndpoint": "https://{{domain}}/api/v1/apps",
"oauthTokenEndpoint": "https://{{domain}}/oauth/token",
"sharedInbox": "https://{{domain}}/inbox",
"uploadMedia": "https://{{domain}}/api/ap/upload_media"
},
"followers": "https://{{domain}}/users/{{nickname}}/followers",
"following": "https://{{domain}}/users/{{nickname}}/following",
"icon": {
"type": "Image",
"url": "https://{{domain}}/media/a932a27f158b63c3a97e3a57d5384f714a82249274c6fc66c9eca581b4fd8af2.jpg"
},
"id": "https://{{domain}}/users/{{nickname}}",
"image": {
"type": "Image",
"url": "https://{{domain}}/media/db15f476d0ad14488db4762b7800479e6ef67b1824f8b9ea5c1fa05b7525c5b7.jpg"
},
"inbox": "https://{{domain}}/users/{{nickname}}/inbox",
"manuallyApprovesFollowers": false,
"name": "{{nickname}} :verified:",
"outbox": "https://{{domain}}/users/{{nickname}}/outbox",
"preferredUsername": "{{nickname}}",
"publicKey": {
"id": "https://{{domain}}/users/{{nickname}}#main-key",
"owner": "https://{{domain}}/users/{{nickname}}",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4XOAopC4nRIxNlHlt60\n//nCicuedu5wvLGIoQ+KUM2u7/PhLrrTDEqr1A7yQL95S0X8ryYtALgFLI5A54ww\nqjMIbIGAs44lEmDLMEd+XI+XxREE8wdsFpb4QQzWug0DTyqlMouTU25k0tfKh1rF\n4PMJ3uBSjDTAGgFvLNyFWTiVVgChbTNgGOmrEBucRl4NmKzQ69/FIUwENV88oQSU\n3bWvQTEH9rWH1rCLpkmQwdRiWfnhFX/4EUqXukfgoskvenKR8ff3nYhElDqFoE0e\nqUnIW1OZceyl8JewVLcL6m0/wdKeosTsfrcWc8DKfnRYQcBGNoBEq9GrOHDU0q2v\nyQIDAQAB\n-----END PUBLIC KEY-----\n\n"
},
"summary": "Pleroma BE dev",
"tag": [
{
"icon": {
"type": "Image",
"url": "https://{{domain}}/emoji/mine/6143373a807b1ae7.png"
},
"id": "https://{{domain}}/emoji/mine/6143373a807b1ae7.png",
"name": ":verified:",
"type": "Emoji",
"updated": "1970-01-01T00:00:00Z"
}
],
"type": "Person",
"url": "https://{{domain}}/users/{{nickname}}"
}

View file

@ -0,0 +1,27 @@
{
"aliases": [
"https://{{subdomain}}/users/{{nickname}}"
],
"links": [
{
"href": "https://{{subdomain}}/users/{{nickname}}",
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html"
},
{
"href": "https://{{subdomain}}/users/{{nickname}}",
"rel": "self",
"type": "application/activity+json"
},
{
"href": "https://{{subdomain}}/users/{{nickname}}",
"rel": "self",
"type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"template": "https://{{subdomain}}/ostatus_subscribe?acct={uri}"
}
],
"subject": "acct:{{nickname}}@{{domain}}"
}

View file

@ -22,15 +22,15 @@ defmodule Pleroma.User.BackupTest do
clear_config([Pleroma.Emails.Mailer, :enabled], true)
end
test "it requries enabled email" do
test "it does not requrie enabled email" do
clear_config([Pleroma.Emails.Mailer, :enabled], false)
user = insert(:user)
assert {:error, "Backups require enabled email"} == Backup.create(user)
assert {:ok, _} = Backup.create(user)
end
test "it requries user's email" do
test "it does not require user's email" do
user = insert(:user, %{email: nil})
assert {:error, "Email is required"} == Backup.create(user)
assert {:ok, _} = Backup.create(user)
end
test "it creates a backup record and an Oban job" do
@ -75,6 +75,43 @@ test "it process a backup record" do
)
end
test "it does not send an email if the user does not have an email" do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user, %{email: nil})
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it does not send an email if mailer is not on" do
clear_config([Pleroma.Emails.Mailer, :enabled], false)
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user)
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it does not send an email if the user has an empty email" do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user, %{email: ""})
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it removes outdated backups after creating a fresh one" do
clear_config([Backup, :limit_days], -1)
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)

View file

@ -738,6 +738,116 @@ test "gets an existing user by ap_id" do
end
end
describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
test "for mastodon" do
Tesla.Mock.mock(fn
%{url: "https://example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 302,
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
}
%{url: "https://sub.example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-host-meta.xml"
|> File.read!()
|> String.replace("{{domain}}", "sub.example.com")
}
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-webfinger.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "example.com")
|> String.replace("{{subdomain}}", "sub.example.com"),
headers: [{"content-type", "application/jrd+json"}]
}
%{url: "https://sub.example.com/users/a"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-user.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "sub.example.com"),
headers: [{"content-type", "application/activity+json"}]
}
%{url: "https://sub.example.com/users/a/collections/featured"} ->
%Tesla.Env{
status: 200,
body:
File.read!("test/fixtures/users_mock/masto_featured.json")
|> String.replace("{{domain}}", "sub.example.com")
|> String.replace("{{nickname}}", "a"),
headers: [{"content-type", "application/activity+json"}]
}
end)
ap_id = "a@example.com"
{:ok, fetched_user} = User.get_or_fetch(ap_id)
assert fetched_user.ap_id == "https://sub.example.com/users/a"
assert fetched_user.nickname == "a@example.com"
end
test "for pleroma" do
Tesla.Mock.mock(fn
%{url: "https://example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 302,
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
}
%{url: "https://sub.example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-host-meta.xml"
|> File.read!()
|> String.replace("{{domain}}", "sub.example.com")
}
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-webfinger.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "example.com")
|> String.replace("{{subdomain}}", "sub.example.com"),
headers: [{"content-type", "application/jrd+json"}]
}
%{url: "https://sub.example.com/users/a"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-user.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "sub.example.com"),
headers: [{"content-type", "application/activity+json"}]
}
end)
ap_id = "a@example.com"
{:ok, fetched_user} = User.get_or_fetch(ap_id)
assert fetched_user.ap_id == "https://sub.example.com/users/a"
assert fetched_user.nickname == "a@example.com"
end
end
describe "fetching a user from nickname or trying to build one" do
test "gets an existing user" do
user = insert(:user)

View file

@ -247,6 +247,27 @@ test "returns local-only objects when authenticated", %{conn: conn} do
assert json_response(response, 200) == ObjectView.render("object.json", %{object: object})
end
test "does not return local-only objects for remote users", %{conn: conn} do
user = insert(:user)
reader = insert(:user, local: false)
{:ok, post} =
CommonAPI.post(user, %{status: "test @#{reader.nickname}", visibility: "local"})
assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
object = Object.normalize(post, fetch: false)
uuid = String.split(object.data["id"], "/") |> List.last()
assert response =
conn
|> assign(:user, reader)
|> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}")
json_response(response, 404)
end
test "it returns a json representation of the object with accept application/json", %{
conn: conn
} do
@ -1297,6 +1318,35 @@ test "it returns 200 even if there're no activities", %{conn: conn} do
assert outbox_endpoint == result["id"]
end
test "it returns a local note activity when authenticated as local user", %{conn: conn} do
user = insert(:user)
reader = insert(:user)
{:ok, note_activity} = CommonAPI.post(user, %{status: "mew mew", visibility: "local"})
ap_id = note_activity.data["id"]
resp =
conn
|> assign(:user, reader)
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true")
|> json_response(200)
assert %{"orderedItems" => [%{"id" => ^ap_id}]} = resp
end
test "it does not return a local note activity when unauthenticated", %{conn: conn} do
user = insert(:user)
{:ok, _note_activity} = CommonAPI.post(user, %{status: "mew mew", visibility: "local"})
resp =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true")
|> json_response(200)
assert %{"orderedItems" => []} = resp
end
test "it returns a note activity in a collection", %{conn: conn} do
note_activity = insert(:note_activity)
note_object = Object.normalize(note_activity, fetch: false)

View file

@ -523,7 +523,6 @@ test "inserts a given map into the activity database, giving it an id if it has
assert activity.data["ok"] == data["ok"]
assert activity.data["id"] == given_id
assert activity.data["context"] == "blabla"
assert activity.data["context_id"]
end
test "adds a context when none is there" do
@ -545,8 +544,6 @@ test "adds a context when none is there" do
assert is_binary(activity.data["context"])
assert is_binary(object.data["context"])
assert activity.data["context_id"]
assert object.data["context_id"]
end
test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
@ -1375,6 +1372,21 @@ test "creates an undo activity for a pending follow request" do
assert embedded_object["object"] == followed.ap_id
assert embedded_object["id"] == follow_activity.data["id"]
end
test "it removes the follow activity if it was remote" do
follower = insert(:user, local: false)
followed = insert(:user)
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
{:ok, activity} = ActivityPub.unfollow(follower, followed, nil, false)
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == follower.ap_id
activity = Activity.get_by_id(follow_activity.id)
assert is_nil(activity)
assert is_nil(Utils.fetch_latest_follow(follower, followed))
end
end
describe "timeline post-processing" do
@ -1545,7 +1557,7 @@ test "it can create a Flag activity",
})
assert Repo.aggregate(Activity, :count, :id) == 1
assert Repo.aggregate(Object, :count, :id) == 2
assert Repo.aggregate(Object, :count, :id) == 1
assert Repo.aggregate(Notification, :count, :id) == 0
end
end

View file

@ -130,18 +130,21 @@ test "a misskey MFM status with a _misskey_content field should work and be link
|> Jason.decode!()
expected_content =
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span> linkifylink <a class=\"hashtag\" data-tag=\"dancedance\" href=\"http://localhost:4001/tag/dancedance\" rel=\"tag ugc\">#dancedance</a> $[jelly mfm goes here] <br><br>## aaa"
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span> linkifylink <a class=\"hashtag\" data-tag=\"dancedance\" href=\"http://localhost:4001/tag/dancedance\">#dancedance</a> $[jelly mfm goes here] <br><br>## aaa"
changes = ArticleNotePageValidator.cast_and_validate(note)
%{
valid?: true,
changes: %{
content: ^expected_content,
source: %{
"content" => "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
"mediaType" => "text/x.misskeymarkdown"
}
}
} = ArticleNotePageValidator.cast_and_validate(note)
} = changes
assert changes.changes[:content] == expected_content
end
end
end

View file

@ -26,6 +26,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
setup_all do
clear_config([:instance, :federating], true)
clear_config([:instance, :quarantined_instances], [])
clear_config([:mrf_simple, :reject], [])
end
describe "gather_webfinger_links/1" do
@ -270,12 +271,14 @@ test "publish to url with with different ports" do
end
describe "publish/2" do
test_with_mock "doesn't publish any activity to quarantined instances.",
test_with_mock "doesn't publish any activity to quarantined or rejected instances.",
Pleroma.Web.Federator.Publisher,
[:passthrough],
[] do
Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
Config.put([:mrf_simple, :reject], [{"rejected.com", "some reason"}])
follower =
insert(:user, %{
local: false,
@ -283,9 +286,18 @@ test "publish to url with with different ports" do
ap_enabled: true
})
another_follower =
insert(:user, %{
local: false,
inbox: "https://rejected.com/users/nick2/inbox",
ap_enabled: true
})
actor = insert(:user, follower_address: follower.ap_id)
{:ok, follower, actor} = Pleroma.User.follow(follower, actor)
{:ok, _another_follower, actor} = Pleroma.User.follow(another_follower, actor)
actor = refresh_record(actor)
note_activity =
@ -321,6 +333,22 @@ test "publish to url with with different ports" do
id: public_note_activity.data["id"]
})
)
assert not called(
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
inbox: "https://rejected.com/users/nick2/inbox",
actor_id: actor.id,
id: note_activity.data["id"]
})
)
assert not called(
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
inbox: "https://rejected.com/users/nick2/inbox",
actor_id: actor.id,
id: public_note_activity.data["id"]
})
)
end
test_with_mock "Publishes a non-public activity to non-quarantined instances.",

View file

@ -33,8 +33,6 @@ test "Mastodon Question activity" do
assert object.data["context"] ==
"tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation"
assert object.data["context_id"]
assert object.data["anyOf"] == []
assert Enum.sort(object.data["oneOf"]) ==
@ -68,7 +66,6 @@ test "Mastodon Question activity" do
reply_object = Object.normalize(reply_activity, fetch: false)
assert reply_object.data["context"] == object.data["context"]
assert reply_object.data["context_id"] == object.data["context_id"]
end
test "Mastodon Question activity with HTML tags in plaintext" do

View file

@ -107,6 +107,22 @@ test "it accepts Move activities" do
assert activity.data["target"] == new_user.ap_id
assert activity.data["type"] == "Move"
end
test "it fixes both the Create and object contexts in a reply" do
insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
insert(:user, ap_id: "https://p.helene.moe/users/helene")
create_activity =
"test/fixtures/create-pleroma-reply-to-misskey-thread.json"
|> File.read!()
|> Jason.decode!()
assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(create_activity)
object = Object.normalize(activity, fetch: false)
assert activity.data["context"] == object.data["context"]
end
end
describe "prepare outgoing" do
@ -216,7 +232,6 @@ test "it strips internal fields" do
assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
assert is_nil(modified["object"]["generator"])
end
@ -231,7 +246,6 @@ test "it strips internal fields of article" do
assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
assert is_nil(modified["object"]["likes"])
end

View file

@ -429,7 +429,6 @@ test "returns map with id and published data" do
object = Object.normalize(note_activity, fetch: false)
res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
assert res["context"] == object.data["id"]
assert res["context_id"] == object.id
assert res["id"]
assert res["published"]
end
@ -437,7 +436,6 @@ test "returns map with id and published data" do
test "returns map with fake id and published data" do
assert %{
"context" => "pleroma:fakecontext",
"context_id" => -1,
"id" => "pleroma:fakeid",
"published" => _
} = Utils.lazy_put_activity_defaults(%{}, true)
@ -454,13 +452,11 @@ test "returns activity data with object" do
})
assert res["context"] == object.data["id"]
assert res["context_id"] == object.id
assert res["id"]
assert res["published"]
assert res["object"]["id"]
assert res["object"]["published"]
assert res["object"]["context"] == object.data["id"]
assert res["object"]["context_id"] == object.id
end
end

View file

@ -4,7 +4,6 @@
defmodule Pleroma.Web.CommonAPI.UtilsTest do
alias Pleroma.Builders.UserBuilder
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.ActivityDraft
alias Pleroma.Web.CommonAPI.Utils
@ -273,22 +272,6 @@ test "delegated renderers" do
end
end
describe "context_to_conversation_id" do
test "creates a mapping object" do
conversation_id = Utils.context_to_conversation_id("random context")
object = Object.get_by_ap_id("random context")
assert conversation_id == object.id
end
test "returns an existing mapping for an existing object" do
{:ok, object} = Object.context_mapping("random context") |> Repo.insert()
conversation_id = Utils.context_to_conversation_id("random context")
assert conversation_id == object.id
end
end
describe "formats date to asctime" do
test "when date is in ISO 8601 format" do
date = DateTime.utc_now() |> DateTime.to_iso8601()
@ -517,17 +500,6 @@ test "returns empty string when date invalid" do
end
end
describe "conversation_id_to_context/1" do
test "returns id" do
object = insert(:note)
assert Utils.conversation_id_to_context(object.id) == object.data["id"]
end
test "returns error if object not found" do
assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"}
end
end
describe "maybe_notify_mentioned_recipients/2" do
test "returns recipients when activity is not `Create`" do
activity = insert(:like_activity)

View file

@ -414,6 +414,20 @@ test "unimplemented pinned statuses feature", %{conn: conn} do
assert json_response_and_validate_schema(conn, 200) == []
end
test "gets local-only statuses for authenticated users", %{user: _user, conn: conn} do
user_one = insert(:user)
{:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!", visibility: "local"})
resp =
conn
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == to_string(activity.id)
end
test "gets an users media, excludes reblogs", %{conn: conn} do
note = insert(:note_activity)
user = User.get_cached_by_ap_id(note.data["actor"])

View file

@ -67,9 +67,11 @@ test "a filter with expires_in", %{conn: conn, user: user} do
expires_at =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(in_seconds)
|> Pleroma.Web.CommonAPI.Utils.to_masto_date()
assert response["expires_at"] == expires_at
assert NaiveDateTime.diff(
NaiveDateTime.from_iso8601!(response["expires_at"]),
expires_at
) < 5
filter = Filter.get(response["id"], user)

View file

@ -1855,23 +1855,50 @@ test "expires_at is nil for another user" do
|> json_response_and_validate_schema(:ok)
end
test "posting a local only status" do
%{user: _user, conn: conn} = oauth_access(["write:statuses"])
describe "local-only statuses" do
test "posting a local only status" do
%{user: _user, conn: conn} = oauth_access(["write:statuses"])
conn_one =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "cofe",
"visibility" => "local"
})
conn_one =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "cofe",
"visibility" => "local"
})
local = Utils.as_local_public()
local = Utils.as_local_public()
assert %{"content" => "cofe", "id" => id, "visibility" => "local"} =
json_response_and_validate_schema(conn_one, 200)
assert %{"content" => "cofe", "id" => id, "visibility" => "local"} =
json_response_and_validate_schema(conn_one, 200)
assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id)
assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id)
end
test "other users can read local-only posts" do
user = insert(:user)
%{user: _reader, conn: conn} = oauth_access(["read:statuses"])
{:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
received =
conn
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:ok)
assert received["id"] == activity.id
end
test "anonymous users cannot see local-only posts" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
_received =
build_conn()
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:not_found)
end
end
describe "muted reactions" do

View file

@ -367,6 +367,47 @@ test "muted emotions", %{conn: conn} do
}
] = result
end
test "should return local-only posts for authenticated users" do
user = insert(:user)
%{user: _reader, conn: conn} = oauth_access(["read:statuses"])
{:ok, %{id: id}} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
result =
conn
|> get("/api/v1/timelines/public")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id}] = result
end
test "should not return local-only posts for users without read:statuses" do
user = insert(:user)
%{user: _reader, conn: conn} = oauth_access([])
{:ok, _activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
result =
conn
|> get("/api/v1/timelines/public")
|> json_response_and_validate_schema(200)
assert [] = result
end
test "should not return local-only posts for anonymous users" do
user = insert(:user)
{:ok, _activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
result =
build_conn()
|> get("/api/v1/timelines/public")
|> json_response_and_validate_schema(200)
assert [] = result
end
end
defp local_and_remote_activities do

View file

@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
alias Pleroma.User
alias Pleroma.UserRelationship
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
@ -240,7 +239,7 @@ test "a note activity" do
object_data = Object.normalize(note, fetch: false).data
user = User.get_cached_by_ap_id(note.data["actor"])
convo_id = Utils.context_to_conversation_id(object_data["context"])
convo_id = :erlang.crc32(object_data["context"])
status = StatusView.render("show.json", %{activity: note})

View file

@ -158,7 +158,7 @@ test "responds with 424 Failed Dependency if HEAD request to media proxy fails",
media_proxy_url: media_proxy_url
} do
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 500, body: ""}
end)
@ -173,7 +173,7 @@ test "redirects to media proxy URI on unsupported content type", %{
media_proxy_url: media_proxy_url
} do
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
end)
@ -193,7 +193,7 @@ test "with `static=true` and GIF image preview requested, responds with JPEG ima
clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{
status: 200,
body: "",
@ -218,7 +218,7 @@ test "with GIF image preview requested and no `static` param, redirects to media
media_proxy_url: media_proxy_url
} do
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
end)
@ -236,7 +236,7 @@ test "with `static` param and non-GIF image preview requested, " <>
media_proxy_url: media_proxy_url
} do
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
end)
@ -256,7 +256,7 @@ test "with :min_content_length setting not matched by Content-Length header, " <
clear_config([:media_preview_proxy, :min_content_length], 100_000)
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{
status: 200,
body: "",
@ -278,7 +278,7 @@ test "thumbnails PNG images into PNG", %{
assert_dependencies_installed()
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
%{method: :get, url: ^media_proxy_url} ->
@ -300,7 +300,7 @@ test "thumbnails JPEG images into JPEG", %{
assert_dependencies_installed()
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
%{method: :get, url: ^media_proxy_url} ->
@ -320,7 +320,7 @@ test "redirects to media proxy URI in case of thumbnailing error", %{
media_proxy_url: media_proxy_url
} do
Tesla.Mock.mock(fn
%{method: "head", url: ^media_proxy_url} ->
%{method: "HEAD", url: ^media_proxy_url} ->
%Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
%{method: :get, url: ^media_proxy_url} ->

View file

@ -188,6 +188,25 @@ test "shows extra information in the quarantined_info field for relevant entries
end
end
test "Bubble instances", %{conn: conn} do
clear_config([:instance, :local_bubble], [])
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)
assert response["metadata"]["localBubbleInstances"] == []
clear_config([:instance, :local_bubble], ["example.com"])
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)
assert response["metadata"]["localBubbleInstances"] == ["example.com"]
end
describe "MRF SimplePolicy" do
setup do
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])

View file

@ -82,4 +82,24 @@ test "POST /api/v1/pleroma/backups", %{user: _user, conn: conn} do
|> post("/api/v1/pleroma/backups")
|> json_response_and_validate_schema(400)
end
test "Backup without email address" do
user = Pleroma.Factory.insert(:user, email: nil)
%{conn: conn} = oauth_access(["read:accounts"], user: user)
assert is_nil(user.email)
assert [
%{
"content_type" => "application/zip",
"url" => _url,
"file_size" => 0,
"processed" => false,
"inserted_at" => _
}
] =
conn
|> post("/api/v1/pleroma/backups")
|> json_response_and_validate_schema(:ok)
end
end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
alias Pleroma.Web.Plugs.HTTPSignaturePlug
import Plug.Conn
@ -81,5 +82,14 @@ test "halts the connection when `signature` header is not present", %{conn: conn
assert conn.state == :sent
assert conn.resp_body == "Request not signed"
end
test "aliases redirected /object endpoints", _ do
obj = insert(:note)
act = insert(:note_activity, note: obj)
params = %{"actor" => "http://mastodon.example.org/users/admin"}
path = URI.parse(obj.data["id"]).path
conn = build_conn(:get, path, params)
assert ["/notice/#{act.id}"] == HTTPSignaturePlug.route_aliases(conn)
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
use Pleroma.Web.ConnCase
use Pleroma.Web.ConnCase, async: true
alias Pleroma.MFA
alias Pleroma.MFA.TOTP

View file

@ -48,6 +48,35 @@ test "Webfinger JRD" do
]
end
test "reach user on tld, while pleroma is runned on subdomain" do
Pleroma.Web.Endpoint.config_change(
[{Pleroma.Web.Endpoint, url: [host: "sub.example.com"]}],
[]
)
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
clear_config([Pleroma.Web.WebFinger, :domain], "example.com")
user = insert(:user, ap_id: "https://sub.example.com/users/bobby", nickname: "bobby")
response =
build_conn()
|> put_req_header("accept", "application/jrd+json")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@example.com")
|> json_response(200)
assert response["subject"] == "acct:#{user.nickname}@example.com"
assert response["aliases"] == ["https://sub.example.com/users/#{user.nickname}"]
on_exit(fn ->
Pleroma.Web.Endpoint.config_change(
[{Pleroma.Web.Endpoint, url: [host: "localhost"]}],
[]
)
end)
end
test "it returns 404 when user isn't found (JSON)" do
result =
build_conn()

View file

@ -47,7 +47,7 @@ test "returns error for nonsensical input" do
test "returns error when there is no content-type header" do
Tesla.Mock.mock(fn
%{url: "http://social.heldscal.la/.well-known/host-meta"} ->
%{url: "https://social.heldscal.la/.well-known/host-meta"} ->
{:ok,
%Tesla.Env{
status: 200,
@ -120,7 +120,7 @@ test "it gets the xrd endpoint for hubzilla" do
test "it gets the xrd endpoint for statusnet" do
{:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com")
assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
assert template == "https://status.alpicola.com/main/xrd?uri={uri}"
end
test "it works with idna domains as nickname" do
@ -147,7 +147,7 @@ test "respects json content-type" do
headers: [{"content-type", "application/jrd+json"}]
}}
%{url: "http://mastodon.social/.well-known/host-meta"} ->
%{url: "https://mastodon.social/.well-known/host-meta"} ->
{:ok,
%Tesla.Env{
status: 200,
@ -170,7 +170,7 @@ test "respects xml content-type" do
headers: [{"content-type", "application/xrd+xml"}]
}}
%{url: "http://pawoo.net/.well-known/host-meta"} ->
%{url: "https://pawoo.net/.well-known/host-meta"} ->
{:ok,
%Tesla.Env{
status: 200,

View file

@ -424,14 +424,6 @@ def get("http://mastodon.example.org/users/gargron", _, _, [
{:error, :nxdomain}
end
def get("http://osada.macgirvin.com/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 404,
body: ""
}}
end
def get("https://osada.macgirvin.com/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
@ -765,7 +757,7 @@ def get(
{:ok, %Tesla.Env{status: 406, body: ""}}
end
def get("http://squeet.me/.well-known/host-meta", _, _, _) do
def get("https://squeet.me/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/squeet.me_host_meta")}}
end
@ -806,7 +798,7 @@ def get(
{:ok, %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/jrd+json"}]}}
end
def get("http://framatube.org/.well-known/host-meta", _, _, _) do
def get("https://framatube.org/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
@ -815,7 +807,7 @@ def get("http://framatube.org/.well-known/host-meta", _, _, _) do
end
def get(
"http://framatube.org/main/xrd?uri=acct:framasoft@framatube.org",
"https://framatube.org/main/xrd?uri=acct:framasoft@framatube.org",
_,
_,
[{"accept", "application/xrd+xml,application/jrd+json"}]
@ -850,7 +842,7 @@ def get(
}}
end
def get("http://status.alpicola.com/.well-known/host-meta", _, _, _) do
def get("https://status.alpicola.com/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
@ -858,7 +850,7 @@ def get("http://status.alpicola.com/.well-known/host-meta", _, _, _) do
}}
end
def get("http://macgirvin.com/.well-known/host-meta", _, _, _) do
def get("https://macgirvin.com/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
@ -866,7 +858,7 @@ def get("http://macgirvin.com/.well-known/host-meta", _, _, _) do
}}
end
def get("http://gerzilla.de/.well-known/host-meta", _, _, _) do
def get("https://gerzilla.de/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
@ -1371,6 +1363,42 @@ def get("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", _, _, _
}}
end
def get("https://mk.absturztau.be/users/8ozbzjs3o8", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/mametsuko@mk.absturztau.be.json"),
headers: activitypub_object_headers()
}}
end
def get("https://p.helene.moe/users/helene", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/helene@p.helene.moe.json"),
headers: activitypub_object_headers()
}}
end
def get("https://mk.absturztau.be/notes/93e7nm8wqg", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/mk.absturztau.be-93e7nm8wqg.json"),
headers: activitypub_object_headers()
}}
end
def get("https://p.helene.moe/objects/fd5910ac-d9dc-412e-8d1d-914b203296c4", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/p.helene.moe-AM7S6vZQmL6pI9TgPY.json"),
headers: activitypub_object_headers()
}}
end
def get(url, query, body, headers) do
{:error,
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}