diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
index 368ef06..1b25b15 100644
--- a/.gitea/workflows/deploy.yml
+++ b/.gitea/workflows/deploy.yml
@@ -103,11 +103,18 @@ jobs:
[ "$served" = "$BUILD_COMMIT" ] || { echo "::error::served build is '$served', expected '$BUILD_COMMIT'"; exit 1; }
code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$PUBLIC_URL/peasants/privapub")
[ "$code" = "200" ] || { echo "::error::the instance actor answered $code"; exit 1; }
- # SecureMode (owner decision 2026-10-04): an unsigned reader gets 401 from a persona, a browser the public page
- code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$PUBLIC_URL/peasants/thepra")
+ # SecureMode (owner decision 2026-10-04): an unsigned reader gets 401 before any lookup, so a name no persona can have
+ # (a hyphen is outside UserNameRegex) checks it without naming anyone; a browser asking for a persona gets its page
+ code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$PUBLIC_URL/peasants/no-such-persona")
[ "$code" = "401" ] || { echo "::error::an unsigned GET of a persona answered $code, not 401: SecureMode is off"; exit 1; }
- code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: text/html' "$PUBLIC_URL/peasants/thepra")
- [ "$code" = "302" ] || { echo "::error::a browser asking for a persona got $code, not a redirect"; exit 1; }
+ directory=$(curl -fsS "$PUBLIC_URL/api/v1/directory?local=true&limit=1") || { echo "::error::the directory did not answer"; exit 1; }
+ persona=$(echo "$directory" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d[0]['username'] if d else '')")
+ if [ -z "$persona" ]; then
+ echo "::notice::no persona in the directory: the browser redirect is not checked"
+ else
+ code=$(curl -s -o /dev/null -w '%{http_code}' -H 'Accept: text/html' "$PUBLIC_URL/peasants/$persona")
+ [ "$code" = "302" ] || { echo "::error::a browser asking for @$persona got $code, not a redirect"; exit 1; }
+ fi
code=$(curl -s -o /dev/null -w '%{http_code}' "$PUBLIC_URL/.well-known/nodeinfo")
[ "$code" = "200" ] || { echo "::error::nodeinfo answered $code"; exit 1; }
code=$(curl -s -o /dev/null -w '%{http_code}' "$PUBLIC_URL/swagger/index.html")
diff --git a/CLAUDE.md b/CLAUDE.md
index 2dfa738..7515459 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -18,7 +18,7 @@ Its defining idea: **one private login owns several public personas.**
Root and avatar are linked only through `RootToAvatar`.
- **Personas must stay unlinkable** to other users and servers; only the instance admin can see the link.
- **`Group` is an ActivityPub Group actor.** It has members, an invitation code and an optional password. Groups are
- becoming two kinds: a public **community** (FEP-1b12, Lemmy-compatible) and a private, invitation-only **circle**.
+ of two kinds: a public **community** (FEP-1b12, Lemmy-compatible) and a private, invitation-only **circle**.
- **`DmGroup` is a direct-message conversation.**
- **`privapub` is the instance actor** (type Application). It signs fetches no persona should be tied to.
- **`privapub_reports` is the reporter** (type Service, `LocalActorKind.Reporter`). It carries reports to Lemmy, which
@@ -30,9 +30,9 @@ Its defining idea: **one private login owns several public personas.**
shown as accounts.
Privacy features in the model:
-- location-ranged posts (`Post.Location`, `RangeKm`), to become local-only and never federated;
-- contacts a persona can share (`Avatar.SharedPersonalContacts`);
-- private notes (`RootUserNote`, `PersonalNote`).
+- location-ranged posts (`Post.Location`, `RangeKm`), local-only and never federated;
+- contacts a persona can share (`Avatar.SharedPersonalContacts`) and private notes (`RootUserNote`, `PersonalNote`)
+ exist only as model classes, with no endpoint.
The intended client is the Mastodon client API (Tusky, Elk, Phanpy, Ivory), where each avatar logs in as its own
account. The private `/clientapi` covers what Mastodon can't express. `thepra/decePubClient`
@@ -56,10 +56,17 @@ actor-scoped route gets a name in the same spirit, agreed with the owner, and a
| Activities | `/peasants/{name}/grunts/{id}` (`create-{postId}` resolves) |
| DM context | `/peasants/{name}/whispers/{id}` |
| Quote permission (FEP-044f `QuoteAuthorization`) | `/peasants/{name}/parrot-licences/{id}` |
+| Featured (pinned posts) | `/peasants/{name}/trophies` |
+| Featured tags | `/peasants/{name}/tattoos` |
+| Wall (FEP-400e) | `/peasants/{name}/graffiti` |
+| Followers synchronisation (FEP-8fcf) | `/peasants/{name}/groupies/roll-call` |
+| A group's members and moderators | `/peasants/{name}/flock`, `/peasants/{name}/wardens` |
+| A post's replies and thread | `/peasants/{name}/scribbles/{id}/replies`, `/peasants/{name}/scribbles/{id}/context` |
| Token refresh | `/clientapi/user/sniff/again` |
-Agreed for later phases: `/gossip`, `/drool`, `/echoes` (replies, likes, shares), `/trophies` (featured), `/tattoos`
-(featured tags), `/flock` and `/wardens` (group members and moderators).
+All of these have federated, so they are frozen. Agreed for later: `/drool` and `/echoes` (likes, shares). `/gossip` was
+agreed for replies before they were served at `…/scribbles/{id}/replies` (2026-10-06); whether it is still wanted is
+asked in the completion program's R10.
Routes other software looks up by name stay conventional:
- `/.well-known/*` and `/nodeinfo/*`;
@@ -74,15 +81,21 @@ PrivaPub.sln
PrivaPub/ ASP.NET Core Web API, net10.0
Program.cs host, Mongo init (GUIDs Standard), pipeline, /build.json
Middleware/SocialPubConfigurations.cs every DI registration (auth, federation, services, swagger, CORS)
- Controllers/ClientToServer/ /clientapi/*: RootUser (signup/login/invitations/recovery), PrivateAvatar,
- Group, Post (posts + DMs), Admin, Data
+ Controllers/ClientToServer/ /clientapi/*: RootUser (signup, setup, login, invitations, recovery), PrivateAvatar,
+ Group, Post (posts + DMs), Social, Timeline, Data, PersonaArchive, Moderator (reports),
+ Admin (logins), DomainBlock, Statistics, Backup
Infrastructure/
Http/ FederationHttp + SafeHttpHandlerFactory + IpRangeGuard: the only way out
Jobs/ JobQueue (leases), JobWorker, Backoff, HostCircuitBreaker
Ids/ PrivacyIds (day-only ids for personas and groups, arrival-ordered ids for remote posts)
Data/ Indexes (created at start), EntityMaps.Warm, Migrations/_NNN_*.cs
- Cli/ AdminCommands (`PrivaPub admin promote|demote `)
+ Cli/ AdminCommands (`PrivaPub admin promote|demote|create-root|move-persona|delete-root`,
+ `media audit`, `backup`, `backups`, `restore`)
+ Backup/ ServerBackup, ServerRestore, ProtectiveMerge, MaintenanceLock, TransferStore
+ Geo/ GeoLocator, GeoUpdater (DB-IP Lite), CdnCatalog, CdnUpdater
+ Statistics/ InteractionLedger, Rollup, TrafficMeter, StatisticsOptions
RateLimiting.cs accounts (per client address) and inbox (per sending origin) policies
+ RegistrationOptions.cs who may make a login (`Registrations:Mode`)
Federation/
Controllers/ PeasantsController (actor, outbox, followers, following, posts, inboxes),
WellKnownController (webfinger, nodeinfo), UsersController (redirect)
@@ -100,13 +113,17 @@ PrivaPub/ ASP.NET Core Web API, net10.0
ReplyRelay (third-party replies to a persona's posts passed on to its followers)
Outbox/ OutboxPublisher (who a post goes to), DeliveryService (queues jobs) + DeliveryJobHandler
Rendering/ ActivityPubRenderer (Mastodon @context, actors, notes, collections)
+ Crawler/ Crawler, PeerHosts, RobotsTxt (the opt-in crawler behind `/stargazing`)
+ Relays/ Relays (the instance actor's relay subscriptions)
Domain/
Content/ ContentRenderer (Markdown or plain text → HTML with h-card mentions and hashtags)
Social/ FollowService (local in-process, remote Follow/Accept), Notifications
Timelines/ Fanout (TimelineEntry rows, Mastodon's home rules), TimelineService
Privacy/ VisibilityPolicy (IsPublic expression, CanSee)
Relationships/ RelationshipService (blocks, mutes, account domain blocks; Hidden), ReportService
- Media/ MediaService (libvips, ffmpeg remux, blurhash), MediaProxy, MediaJanitor
+ Media/ MediaService (libvips, ffmpeg, blurhash), MediaProxy, MediaJanitor
+ Portability/ persona archives exported and imported (Mastodon's layout)
+ Statistics/ StatisticsQueries, PublicGeo, ServerPlaces, SelfLocation
Domain/Statuses/ StatusService: publish, edit, remove, favourite, reblog, for a persona (both client APIs use it);
InteractionApprovals: GoToSocial's canReply/canLike/canAnnounce, judged, asked and answered;
Participations: a persona's Join and Leave of a remote event, and the organiser's answer
@@ -114,7 +131,9 @@ PrivaPub/ ASP.NET Core Web API, net10.0
Auth/ OpenIddict setup (keys in Mongo), MastodonScopes, TokenController, OAuthPruner
Infrastructure/ MastodonController (avatar context, scopes, errors, Link), MastodonParams, MastodonJson, Page
Entities/ Mappers/ Mastodon entities; MastodonMapper (Account, Status), AccountSearch
- Controllers/ apps, instance, accounts, statuses, timelines, notifications, search, stubs
+ Controllers/ apps, instance, accounts, statuses, timelines, notifications, search, lists, filters,
+ media, polls, reactions, reports, scheduled statuses, streaming, tags, trends, cdns,
+ provenance; the stubs are `StubsController` in `SearchController.cs`
Web/Pages/ Razor: /@{user}, /@{user}/{id} (public posts only, strict CSP, noindex);
OAuth/: /oauth/login (root password), /oauth/authorize (choose persona, consent)
Services/ RootUsersService, GroupUsersService, PostsService, AppConfigurationService, …
@@ -127,8 +146,8 @@ deploy/ nginx vhost, systemd units (privapub, privapub
docs/ROADMAP.md decisions + phased plan
```
-The roadmap moves code towards `Infrastructure/`, `Federation/`, `Domain/`, `Api/{ClientApi,Mastodon}` and `Web/`,
-one pure-move commit at a time.
+The planned move of `/clientapi` into `Api/ClientApi` was dropped (2026-10-17): it stays in `Controllers/ClientToServer/`
+and `Services/`.
## Commands
@@ -607,11 +626,12 @@ Interop is checked against real servers, starting with the workstation's own pas
```bash
DOTNET=~/.dotnet/dotnet tools/pasture/run.sh up [gts mastodon ...] # podman: PrivaPub + the named peers (default gts) + Mongo, behind Caddy
tools/pasture/interop.sh [gts mastodon ...] # each peer's scenario, then what PrivaPub's statistics saw of it
+tools/pasture/sweep.sh [A|B|C|D ...] # every peer's scenario, batch by batch (up, interop, down); out/sweep-.log
tools/pasture/run.sh down # removes every pasture container and volume
```
- **A change to what PrivaPub sends** (the `@context`, an actor's properties, an activity's shape, a delivery's headers)
- is run against every peer's scenario before it is committed, not only the peers it was written for. One reader
+ is run against every peer's scenario before it is committed, not only the peers it was written for (`sweep.sh`). One reader
stricter than the rest fails on it alone: the 2026-10-06 `proof` term passed every peer but Smithereen, whose JSON-LD
1.0 reader refused every document carrying it.
- **Layout:** `lib/pasture.sh` (network, Caddy, Mongo, PrivaPub), `peers/.sh` (`_up`, plus `peers/shared.sh`
@@ -664,7 +684,7 @@ tools/pasture/run.sh down # removes e
read from its `user_relationships`. `akkoma_token ` signs one of its users in. 45 checks.
- **Lemmy (1.0.0-beta.2):** the backend alone on the shared Postgres, its admin made by `setup` in the generated
`config.hjson`. It trusts Caddy's CA through `SSL_CERT_FILE` and reaches the network through
- `DANGER_FEDERATION_ALLOW_LOCAL_IP=1`; 0.19 cannot join (its rustls trusts only its bundled roots). Its API is
+ `DANGER_FEDERATION_ALLOW_LOCAL_IP=1`; 0.19's release can't join as it is, so it runs as its own build (`lemmy19`, below). Its API is
`/api/v4/` with a bearer token (`lm` in the scenario), and `sort` values are lowercase. It logs no refused
activity at `warn` (`LEMMY_LOG` sets `RUST_LOG`); the reason is in the 400's body. It answers our community's echo of
its own activity and every bare `Announce{object}` 400 by design, and the echo is still needed (see
diff --git a/FEDERATION.md b/FEDERATION.md
index 789ce2f..41be554 100644
--- a/FEDERATION.md
+++ b/FEDERATION.md
@@ -65,8 +65,8 @@ Checked both ways, where the peer has the feature:
- communities and circles;
- blocks and unfollows.
-`docs/INTEROP.md` has each peer's evidence and what is still expected to fail. The platforms not yet in the pasture are
-covered by unit tests written in their documents' shape.
+`docs/INTEROP.md` has each peer's evidence and what is still expected to fail. The platforms not in the pasture are
+read from documents in their published shape (`docs/INTEROP.md` records each one); there is no test project.
## Supported FEPs
@@ -98,10 +98,19 @@ covered by unit tests written in their documents' shape.
(sent and honoured; see "Followers synchronisation")
- [FEP-5624: Per-object reply control policies](https://codeberg.org/fediverse/fep/src/branch/main/fep/5624/fep-5624.md):
read (see "Replies its author approves")
+- [FEP-7888: Demystifying the context property](https://codeberg.org/fediverse/fep/src/branch/main/fep/7888/fep-7888.md)
+ and [FEP-f228: Backfilling conversations](https://codeberg.org/fediverse/fep/src/branch/main/fep/f228/fep-f228.md):
+ a root post serves its `context` collection, and a remote thread is read from its `context` (or `contextHistory`)
+ before its `replies` (see "Activities")
+- [FEP-7628: Move actor](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md): received; a persona's
+ follows move with the account (see `Move` under "Activities")
+- [FEP-ae0c: Fediverse Relay Protocols](https://codeberg.org/fediverse/fep/src/branch/main/fep/ae0c/fep-ae0c.md): the
+ instance actor follows the relays configured, and their `Announce`s are taken as their origin vouches (see "Relays")
+- [FEP-8967: Generic attachments, link previews](https://codeberg.org/fediverse/fep/src/branch/main/fep/8967/fep-8967.md):
+ received; a `preview` builds the card
-Planned (see `docs/ROADMAP.md`, phases P7 and P8, and the per-platform notes in `docs/INTEROP.md`): FEP-9098 (custom
-emoji), FEP-7888 and FEP-f228 (threads), FEP-7628 (Move), FEP-8967 (link
-attachments), FEP-ae0c (relays).
+Planned (see `docs/ROADMAP.md`, the completion program, and the per-platform notes in `docs/INTEROP.md`): FEP-9098
+custom emoji offered (R7) and FEP-8967 link attachments sent (R10).
## Actors
@@ -128,7 +137,7 @@ The names are the project's own and are stable; resolve actors through WebFinger
- The key is `{actor}#main-key`, RSA 2048, served as SPKI PEM with `owner` set to the actor.
- `published` on an actor is a whole day, chosen at random up to two weeks before the account was made, so two
personas made on the same day do not share a date. `indexable` is `false` unless the persona turns it on, and
- `discoverable` is `true` unless it turns that off (the deploy's own persona, @thepra, is undiscoverable).
+ `discoverable` is `true` unless it turns that off.
- The instance actor is `/peasants/privapub` (type `Application`). It signs every fetch PrivaPub makes, so no avatar's key
is used to read another server's content. It also answers at the server's root (`/`) for a request that asks for
ActivityPub, as Lemmy's site actor does: PieFed sends a community's announces to the inbox of the Application at a
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.Designer.cs b/PrivaPub.ClientModels/Resources/FieldsNameResource.Designer.cs
index b309584..7a4f645 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.Designer.cs
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.Designer.cs
@@ -60,132 +60,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to (necessary in case of password recovery).
- ///
- public static string _necessary_in_case_of_password_recovery_ {
- get {
- return ResourceManager.GetString("(necessary in case of password recovery)", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to (<a class="is-underlined has-text-info" tabindex="-1" target="_blank" href="https://keepassxc.org/">password manager</a> suggested).
- ///
- public static string _password_manager_suggested_ {
- get {
- return ResourceManager.GetString("(password manager suggested)", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Amount.
- ///
- public static string Amount {
- get {
- return ResourceManager.GetString("Amount", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Answer id.
- ///
- public static string AnswerId {
- get {
- return ResourceManager.GetString("AnswerId", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Answer text.
- ///
- public static string AnswerText {
- get {
- return ResourceManager.GetString("AnswerText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Custom character(emoji).
- ///
- public static string Character {
- get {
- return ResourceManager.GetString("Character", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Colour.
- ///
- public static string ColourIndex {
- get {
- return ResourceManager.GetString("ColourIndex", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Comment id.
- ///
- public static string CommentId {
- get {
- return ResourceManager.GetString("CommentId", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Comment text.
- ///
- public static string CommentText {
- get {
- return ResourceManager.GetString("CommentText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Confrontation.
- ///
- public static string Confrontation {
- get {
- return ResourceManager.GetString("Confrontation", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Confrontation id.
- ///
- public static string ConfrontationId {
- get {
- return ResourceManager.GetString("ConfrontationId", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Context.
- ///
- public static string Context {
- get {
- return ResourceManager.GetString("Context", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to End date.
- ///
- public static string DateEnd {
- get {
- return ResourceManager.GetString("DateEnd", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Start date.
- ///
- public static string DateStart {
- get {
- return ResourceManager.GetString("DateStart", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Description.
///
@@ -195,60 +69,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Discussion.
- ///
- public static string Discussion {
- get {
- return ResourceManager.GetString("Discussion", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to End date.
- ///
- public static string DiscussionEndDate {
- get {
- return ResourceManager.GetString("DiscussionEndDate", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to End time.
- ///
- public static string DiscussionEndTime {
- get {
- return ResourceManager.GetString("DiscussionEndTime", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Discussion id.
- ///
- public static string DiscussionId {
- get {
- return ResourceManager.GetString("DiscussionId", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Discussion text.
- ///
- public static string DiscussionText {
- get {
- return ResourceManager.GetString("DiscussionText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Discussion title.
- ///
- public static string DiscussionTitle {
- get {
- return ResourceManager.GetString("DiscussionTitle", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Email.
///
@@ -267,42 +87,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Max amount of confrontations.
- ///
- public static string ExpectedMaxConfrontationsBeforeClosingAndShowing {
- get {
- return ResourceManager.GetString("ExpectedMaxConfrontationsBeforeClosingAndShowing", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Fact.
- ///
- public static string Fact {
- get {
- return ResourceManager.GetString("Fact", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Facts.
- ///
- public static string Facts {
- get {
- return ResourceManager.GetString("Facts", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to File id.
- ///
- public static string FileId {
- get {
- return ResourceManager.GetString("FileId", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Groups.
///
@@ -312,15 +96,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Include the first answer in the invitation preview? (making it more engaging to join the discussion).
- ///
- public static string IncludeFirstAnswerInInvitation {
- get {
- return ResourceManager.GetString("IncludeFirstAnswerInInvitation", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Invitation code.
///
@@ -330,15 +105,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Invitation code or link.
- ///
- public static string InvitationCodeLink {
- get {
- return ResourceManager.GetString("InvitationCodeLink", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Invitation password.
///
@@ -348,42 +114,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Require password for preview.
- ///
- public static string IsPasswordProtectedForPreview {
- get {
- return ResourceManager.GetString("IsPasswordProtectedForPreview", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Require password to show the preview.
- ///
- public static string IsPasswordProtectedForPreviewText {
- get {
- return ResourceManager.GetString("IsPasswordProtectedForPreviewText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Add password protection.
- ///
- public static string IsPasswordProtectedText {
- get {
- return ResourceManager.GetString("IsPasswordProtectedText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to IsStructured.
- ///
- public static string IsStructured {
- get {
- return ResourceManager.GetString("IsStructured", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Language id.
///
@@ -426,15 +156,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Enable notifications?(useful to receive app updates).
- ///
- public static string NativeNotificationsEnabled {
- get {
- return ResourceManager.GetString("NativeNotificationsEnabled", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to New password.
///
@@ -462,15 +183,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to One time only.
- ///
- public static string OneTime {
- get {
- return ResourceManager.GetString("OneTime", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Password.
///
@@ -480,24 +192,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Perception.
- ///
- public static string Perception {
- get {
- return ResourceManager.GetString("Perception", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Perceptions.
- ///
- public static string Perceptions {
- get {
- return ResourceManager.GetString("Perceptions", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Prefer system theme.
///
@@ -516,96 +210,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Recurring.
- ///
- public static string Recurring {
- get {
- return ResourceManager.GetString("Recurring", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Scan for:.
- ///
- public static string Scan_for_ {
- get {
- return ResourceManager.GetString("Scan for:", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Search.
- ///
- public static string SearchText {
- get {
- return ResourceManager.GetString("SearchText", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Sharing Secret Code.
- ///
- public static string ShareCode {
- get {
- return ResourceManager.GetString("ShareCode", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Show donator badge.
- ///
- public static string ShowDonatorBadge {
- get {
- return ResourceManager.GetString("ShowDonatorBadge", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Show the logs tab to support troubleshooting..
- ///
- public static string ShowLogs {
- get {
- return ResourceManager.GetString("ShowLogs", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Take type.
- ///
- public static string TakeType {
- get {
- return ResourceManager.GetString("TakeType", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Simple.
- ///
- public static string TakeTypeSimple {
- get {
- return ResourceManager.GetString("TakeTypeSimple", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Structured.
- ///
- public static string TakeTypeStructured {
- get {
- return ResourceManager.GetString("TakeTypeStructured", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Theme image.
- ///
- public static string Theme_image {
- get {
- return ResourceManager.GetString("Theme image", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Dark mode.
///
@@ -615,15 +219,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Ticks.
- ///
- public static string Ticks {
- get {
- return ResourceManager.GetString("Ticks", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Title.
///
@@ -633,24 +228,6 @@ namespace PrivaPub.ClientModels.Resources {
}
}
- ///
- /// Looks up a localized string similar to Update reason.
- ///
- public static string UpdateReason {
- get {
- return ResourceManager.GetString("UpdateReason", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Submit anonymously.
- ///
- public static string UpsertUserTakeIsAnonymous {
- get {
- return ResourceManager.GetString("UpsertUserTakeIsAnonymous", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Username.
///
@@ -659,23 +236,5 @@ namespace PrivaPub.ClientModels.Resources {
return ResourceManager.GetString("Username", resourceCulture);
}
}
-
- ///
- /// Looks up a localized string similar to Username to search and add.
- ///
- public static string Username_to_search_and_add {
- get {
- return ResourceManager.GetString("Username to search and add", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Usernames to search and add.
- ///
- public static string Usernames_to_search_and_add {
- get {
- return ResourceManager.GetString("Usernames to search and add", resourceCulture);
- }
- }
}
}
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.bg.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.bg.resx
index 8e091d9..7942039 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.bg.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.bg.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (необходимо в случай на възстановяване на парола)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">предложен мениджър на пароли</a>)
-
-
- Сума
-
-
- Отговор id
-
-
- Текст на отговора
-
-
- Персонализиран символ (емотикон)
-
-
- Цвят
-
-
- Коментар id
-
-
- Текст на коментара
-
-
- Конфронтация
-
-
- Конфронтация id
-
-
- Контекст
-
-
- Крайна дата
-
-
- Начална дата
-
Описание
-
- Дискусия
-
-
- Крайна дата
-
-
- Краен час
-
-
- Дискусия id
-
-
- Текст за обсъждане
-
-
- Заглавие на дискусията
-
ИмейлЕмотикони
-
- Максимален брой конфронтации
-
-
- Факт
-
-
- Факти
-
-
- Идентификатор на файла
-
Групи
-
- Да включите първия отговор в предварителния преглед на поканата? (за да се включите в дискусията по-ангажиращо)
-
Код на поканата
-
- Код или връзка за покана
-
Парола за покана
-
- Изискване на парола за предварителен преглед
-
-
- Изискване на парола за показване на предварителния преглед
-
-
- Добавяне на защита с парола
-
-
- IsStructured
-
Идентификатор на езикаИме
-
- Активиране на известията?(полезно за получаване на актуализации на приложението)
-
Нова парола
@@ -243,76 +150,22 @@
Стара парола
-
- Само веднъж
-
Парола
-
- Възприемане
-
-
- Възприятия
-
Предпочитайте темата на систематаКод за възстановяване
-
- Повтарящи се
-
-
- Сканиране за:
-
-
- Търсене
-
-
- Споделяне на тайния код
-
-
- Покажи значката на дарителя
-
-
- Покажете раздела с дневници, за да подпомогнете отстраняването на неизправности.
-
-
- Вид на вземането
-
-
- Прост
-
-
- Структуриран
-
-
- Изображение на темата
-
Тъмен режим
-
- Кърлежи
-
Заглавие
-
- Причина за актуализиране
-
-
- Подайте анонимно
-
Потребителско име
-
- Потребителско име за търсене и добавяне
-
-
- Потребителски имена за търсене и добавяне
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.cs.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.cs.resx
index abdb4ba..1a4e4c4 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.cs.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.cs.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nutné v případě obnovení hesla)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">správce hesel</a> navrženo)
-
-
- Částka
-
-
- Odpověď id
-
-
- Text odpovědi
-
-
- Vlastní znak(emoji)
-
-
- Barva
-
-
- Komentář id
-
-
- Text komentáře
-
-
- Konfrontace
-
-
- Id konfrontace
-
-
- Kontext
-
-
- Datum ukončení
-
-
- Datum zahájení
-
Popis
-
- Diskuse
-
-
- Datum ukončení
-
-
- Čas ukončení
-
-
- Diskuze id
-
-
- Text k diskusi
-
-
- Název diskuse
-
E-mailEmoji
-
- Maximální počet konfrontací
-
-
- Fakta
-
-
- Fakta
-
-
- Identifikátor souboru
-
Skupiny
-
- Zahrnout první odpověď do náhledu pozvánky? (abyste se mohli zapojit do diskuse)
-
Kód pozvánky
-
- Pozvánkový kód nebo odkaz
-
Heslo pozvánky
-
- Vyžadovat heslo pro náhled
-
-
- Vyžadování hesla pro zobrazení náhledu
-
-
- Přidání ochrany heslem
-
-
- IsStructured
-
Identifikace jazykaNázev
-
- Povolit oznámení?(užitečné pro příjem aktualizací aplikace)
-
Nové heslo
@@ -243,76 +150,22 @@
Staré heslo
-
- Pouze jednou
-
Heslo
-
- Vnímání
-
-
- Vnímání
-
Preferovat systémové témaKód obnovy
-
- Opakující se
-
-
- Vyhledat:
-
-
- Vyhledávání
-
-
- Sdílení tajného kódu
-
-
- Zobrazit odznak dárce
-
-
- Zobrazit kartu protokolů pro podporu řešení problémů.
-
-
- Vezměte si typ
-
-
- Jednoduché
-
-
- Strukturovaný
-
-
- Tématický obrázek
-
Tmavý režim
-
- Klíšťata
-
Název
-
- Důvod aktualizace
-
-
- Anonymní odeslání
-
Uživatelské jméno
-
- Uživatelské jméno pro vyhledávání a přidávání
-
-
- Uživatelská jména pro vyhledávání a přidávání
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.da.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.da.resx
index 356eed5..71aa94f 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.da.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.da.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nødvendig i tilfælde af inddrivelse af adgangskode)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">password manager</a> foreslået)
-
-
- Beløb
-
-
- Svar id
-
-
- Svar tekst
-
-
- Brugerdefineret tegn (emoji)
-
-
- Farve
-
-
- Kommentar id
-
-
- Kommentar tekst
-
-
- Konfrontation
-
-
- Konfrontation id
-
-
- Kontekst
-
-
- Slutdato
-
-
- Startdato
-
Beskrivelse
-
- Diskussion
-
-
- Slutdato
-
-
- Sluttidspunkt
-
-
- Diskussion id
-
-
- Tekst til drøftelse
-
-
- Diskussionstitel
-
E-mailEmoji
-
- Maksimalt antal konfrontationer
-
-
- Fakta
-
-
- Fakta
-
-
- Fil id
-
Grupper
-
- Medtage det første svar i forhåndsvisningen af invitationen? (gør det mere indbydende at deltage i diskussionen)
-
Kode for indbydelse
-
- Indbydelseskode eller link
-
Adgangskode til invitationen
-
- Kræver adgangskode til visning af preview
-
-
- Kræver adgangskode for at vise forhåndsvisningen
-
-
- Tilføj adgangskodebeskyttelse
-
-
- IsStructured
-
Sprog-idNavn
-
- Aktiver notifikationer? (nyttigt for at modtage opdateringer til appen)
-
Ny adgangskode
@@ -243,76 +150,22 @@
Gammel adgangskode
-
- Kun én gang
-
Adgangskode
-
- Opfattelse
-
-
- Opfattelser
-
Foretrækker systemtemaKode for inddrivelse
-
- Tilbagevendende
-
-
- Scan efter:
-
-
- Søg på
-
-
- Deling af den hemmelige kode
-
-
- Vis donator-badge
-
-
- Vis fanen Logfiler for at støtte fejlfinding.
-
-
- Tag type
-
-
- Enkel
-
-
- Struktureret
-
-
- Temabillede
-
Mørk tilstand
-
- Flåter
-
Titel
-
- Årsag til opdatering
-
-
- Indsend anonymt
-
Brugernavn
-
- Brugernavn til at søge og tilføje
-
-
- Brugernavne til at søge og tilføje
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.de.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.de.resx
index a3290e5..a07c490 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.de.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.de.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (notwendig für die Wiederherstellung des Passworts)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">Passwortmanager</a> vorgeschlagen)
-
-
- Betrag
-
-
- Antwort id
-
-
- Antwort-Text
-
-
- Benutzerdefiniertes Zeichen (Emoji)
-
-
- Farbe
-
-
- Kommentar
-
-
- Text kommentieren
-
-
- Konfrontation
-
-
- Konfrontation id
-
-
- Kontext
-
-
- Datum des Endes
-
-
- Datum des Beginns
-
Beschreibung
-
- Diskussion
-
-
- Datum des Endes
-
-
- Endzeit
-
-
- Diskussion id
-
-
- Text zur Diskussion
-
-
- Titel der Diskussion
-
E-MailEmoji
-
- Maximale Anzahl von Konfrontationen
-
-
- Tatsache
-
-
- Fakten
-
-
- Datei-ID
-
Gruppen
-
- Die erste Antwort in die Vorschau der Einladung aufnehmen? (um den Anreiz zur Teilnahme an der Diskussion zu erhöhen)
-
Einladungs-Code
-
- Einladungscode oder Link
-
Passwort für die Einladung
-
- Passwort für Vorschau erforderlich
-
-
- Passwort erforderlich, um die Vorschau anzuzeigen
-
-
- Passwortschutz hinzufügen
-
-
- IsStructured
-
Sprache idName
-
- Benachrichtigungen aktivieren?(nützlich, um App-Updates zu erhalten)
-
Neues Passwort
@@ -243,76 +150,22 @@
Altes Passwort
-
- Nur einmalig
-
Passwort
-
- Wahrnehmung
-
-
- Wahrnehmungen
-
Systemthema bevorzugenWiederherstellungscode
-
- Wiederkehrende
-
-
- Scannen Sie nach:
-
-
- Suche
-
-
- Gemeinsamer Geheimcode
-
-
- Spenderausweis anzeigen
-
-
- Zeigen Sie die Registerkarte Protokolle an, um die Fehlersuche zu unterstützen.
-
-
- Typ nehmen
-
-
- Einfach
-
-
- Strukturiert
-
-
- Thema Bild
-
Dunkler Modus
-
- Zecken
-
Titel
-
- Grund für die Aktualisierung
-
-
- Anonym einreichen
-
Benutzername
-
- Benutzername zum Suchen und Hinzufügen
-
-
- Nutzernamen zum Suchen und Hinzufügen
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.el.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.el.resx
index a8be981..fe80456 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.el.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.el.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (απαραίτητο σε περίπτωση ανάκτησης κωδικού πρόσβασης)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">διαχειριστής κωδικών πρόσβασης</a> προτείνεται)
-
-
- Ποσό
-
-
- Απάντηση id
-
-
- Κείμενο απάντησης
-
-
- Προσαρμοσμένος χαρακτήρας (emoji)
-
-
- Χρώμα
-
-
- Σχόλιο id
-
-
- Κείμενο σχολιασμού
-
-
- Αντιπαράθεση
-
-
- id αντιπαράθεσης
-
-
- Πλαίσιο
-
-
- Ημερομηνία λήξης
-
-
- Ημερομηνία έναρξης
-
Περιγραφή
-
- Συζήτηση
-
-
- Ημερομηνία λήξης
-
-
- Ώρα λήξης
-
-
- Συζήτηση id
-
-
- Κείμενο συζήτησης
-
-
- Τίτλος συζήτησης
-
Ηλεκτρονικό ταχυδρομείοEmoji
-
- Μέγιστος αριθμός αντιπαραθέσεων
-
-
- Γεγονός
-
-
- Γεγονότα
-
-
- id αρχείου
-
Ομάδες
-
- Να συμπεριλάβετε την πρώτη απάντηση στην προεπισκόπηση της πρόσκλησης; (καθιστώντας την πιο ελκυστική για να συμμετάσχετε στη συζήτηση)
-
Κωδικός πρόσκλησης
-
- Κωδικός πρόσκλησης ή σύνδεσμος
-
Κωδικός πρόσκλησης
-
- Απαίτηση κωδικού πρόσβασης για προεπισκόπηση
-
-
- Απαιτείται κωδικός πρόσβασης για την εμφάνιση της προεπισκόπησης
-
-
- Προσθήκη προστασίας με κωδικό πρόσβασης
-
-
- IsStructured
-
Id γλώσσαςΌνομα
-
- Ενεργοποίηση ειδοποιήσεων; (χρήσιμο για να λαμβάνετε ενημερώσεις εφαρμογών)
-
Νέος κωδικός πρόσβασης
@@ -243,76 +150,22 @@
Παλαιός κωδικός πρόσβασης
-
- Μόνο μία φορά
-
Κωδικός πρόσβασης
-
- Αντίληψη
-
-
- Αντιλήψεις
-
Προτιμήστε το θέμα του συστήματοςΚωδικός ανάκτησης
-
- Επαναλαμβανόμενο
-
-
- Σάρωση για:
-
-
- Αναζήτηση
-
-
- Κοινή χρήση μυστικού κώδικα
-
-
- Εμφάνιση σήματος δωρητή
-
-
- Εμφάνιση της καρτέλας αρχείων καταγραφής για την υποστήριξη της αντιμετώπισης προβλημάτων.
-
-
- Πάρτε τον τύπο
-
-
- Απλό
-
-
- Δομημένο
-
-
- Εικόνα θέματος
-
Σκοτεινή λειτουργία
-
- Κρότωνες
-
Τίτλος
-
- Αιτία ενημέρωσης
-
-
- Υποβάλετε ανώνυμα
-
Όνομα χρήστη
-
- Όνομα χρήστη για αναζήτηση και προσθήκη
-
-
- Ονόματα χρηστών για αναζήτηση και προσθήκη
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.es.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.es.resx
index 58a58ce..1730c92 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.es.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.es.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (necesario en caso de recuperación de la contraseña)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">gestor de contraseñas</a> sugerido)
-
-
- Importe
-
-
- Respuesta
-
-
- Texto de respuesta
-
-
- Carácter personalizado (emoji)
-
-
- Color
-
-
- Comentario id
-
-
- Texto de los comentarios
-
-
- Confrontación
-
-
- Identificación de la confrontación
-
-
- Contexto
-
-
- Fecha de finalización
-
-
- Fecha de inicio
-
Descripción
-
- Discusión
-
-
- Fecha de finalización
-
-
- Hora de finalización
-
-
- Discusión
-
-
- Texto de debate
-
-
- Título del debate
-
Envíe un correo electrónico aEmoji
-
- Máxima cantidad de enfrentamientos
-
-
- Datos
-
-
- Hechos
-
-
- ID de archivo
-
Grupos
-
- ¿Incluir la primera respuesta en la vista previa de la invitación? (para que sea más atractivo participar en el debate)
-
Código de invitación
-
- Código o enlace de invitación
-
Contraseña de la invitación
-
- Requerir contraseña para la vista previa
-
-
- Requiere contraseña para mostrar la vista previa
-
-
- Añadir protección por contraseña
-
-
- IsStructured
-
Id. de idiomaNombre
-
- Activar las notificaciones (útiles para recibir las actualizaciones de la aplicación)
-
Nueva contraseña
@@ -243,76 +150,22 @@
Contraseña antigua
-
- Una sola vez
-
Contraseña
-
- Percepción
-
-
- Percepciones
-
Prefiero el tema del sistemaCódigo de recuperación
-
- Recurrente
-
-
- Busca:
-
-
- Buscar en
-
-
- Compartir el código secreto
-
-
- Mostrar el distintivo de donante
-
-
- Mostrar la pestaña de registros para apoyar la resolución de problemas.
-
-
- Toma de tipo
-
-
- Simple
-
-
- Estructurado
-
-
- Imagen del tema
-
Modo oscuro
-
- Garrapatas
-
Título
-
- Motivo de actualización
-
-
- Presentar de forma anónima
-
Nombre de usuario
-
- Nombre de usuario para buscar y añadir
-
-
- Nombres de usuario para buscar y añadir
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.et.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.et.resx
index 73beceb..f874741 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.et.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.et.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (vajalik parooli taastamise korral)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">Password manager</a> soovitatav)
-
-
- Summa
-
-
- Vastus id
-
-
- Vastuse tekst
-
-
- Kohandatud märk (emotikoni)
-
-
- Värv
-
-
- Kommentaar id
-
-
- Kommentaaride tekst
-
-
- Vastasseis
-
-
- Vastasseisu id
-
-
- Kontekst
-
-
- Lõppkuupäev
-
-
- Alguskuupäev
-
Kirjeldus
-
- Arutelu
-
-
- Lõppkuupäev
-
-
- Lõpuaeg
-
-
- Arutelu id
-
-
- Arutelu tekst
-
-
- Arutelu pealkiri
-
E-postEmoji
-
- Vastasseisude maksimaalne arv
-
-
- Fakt
-
-
- Faktid
-
-
- Faili id
-
Rühmad
-
- Lisage esimene vastus kutse eelvaates? (muudab aruteluga liitumise atraktiivsemaks)
-
Kutse kood
-
- Kutse kood või link
-
Kutse parool
-
- Nõuab eelvaate jaoks parooli
-
-
- Eelvaate kuvamiseks nõutakse parooli
-
-
- Lisage paroolikaitse
-
-
- IsStructured
-
Keele idNimi
-
- Teavituste lubamine?(kasulik, et saada rakenduse uuendusi)
-
Uus parool
@@ -243,76 +150,22 @@
Vana parool
-
- Ainult üks kord
-
Parool
-
- Tajumine
-
-
- Arvamused
-
Eelistage süsteemi teematTaastamiskood
-
- Korduvad
-
-
- Skaneeri:
-
-
- Otsi
-
-
- Salajase koodi jagamine
-
-
- Näita doonori märki
-
-
- Näidake veaotsingu toetamiseks logide vahekaarti.
-
-
- Võtke tüüp
-
-
- Lihtne
-
-
- Struktureeritud
-
-
- Teema pilt
-
Tume režiim
-
- Puugid
-
Pealkiri
-
- Uuendamise põhjus
-
-
- Esitage anonüümselt
-
Kasutajanimi
-
- Kasutajanimi otsimiseks ja lisamiseks
-
-
- Kasutajanimed otsimiseks ja lisamiseks
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.fi.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.fi.resx
index 069a323..d40b2fd 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.fi.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.fi.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (tarvitaan salasanan palautuksen yhteydessä)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">salasananhallinta</a> ehdotettu)
-
-
- Määrä
-
-
- Vastaa id
-
-
- Vastaus teksti
-
-
- Mukautettu merkki (emoji)
-
-
- Väri
-
-
- Kommentti id
-
-
- Kommenttiteksti
-
-
- Vastakkainasettelu
-
-
- Vastakkainasettelu id
-
-
- Konteksti
-
-
- Loppupäivä
-
-
- Aloituspäivä
-
Kuvaus
-
- Keskustelu
-
-
- Loppupäivä
-
-
- Loppuaika
-
-
- Keskustelu id
-
-
- Keskusteluteksti
-
-
- Keskustelun otsikko
-
SähköpostiEmoji
-
- Vastakkainasettelujen enimmäismäärä
-
-
- Fakta
-
-
- Faktat
-
-
- Tiedoston id
-
Ryhmät
-
- Sisällytä ensimmäinen vastaus kutsun esikatseluun? (mikä tekee keskusteluun osallistumisesta houkuttelevampaa)
-
Kutsun koodi
-
- Kutsukoodi tai linkki
-
Kutsun salasana
-
- Vaadi salasana esikatselua varten
-
-
- Vaadi salasana esikatselun näyttämiseksi
-
-
- Lisää salasanasuojaus
-
-
- IsStructured
-
KielitunnusNimi
-
- Ota ilmoitukset käyttöön?(hyödyllinen sovelluksen päivitysten saamiseksi)
-
Uusi salasana
@@ -243,76 +150,22 @@
Vanha salasana
-
- Vain kerran
-
Salasana
-
- Havainto
-
-
- Käsitykset
-
Suositeltava järjestelmäteemaPalautuskoodi
-
- Toistuvat
-
-
- Etsi:
-
-
- Etsi
-
-
- Salaisen koodin jakaminen
-
-
- Näytä lahjoittajan merkki
-
-
- Näytä lokit-välilehti vianmäärityksen tueksi.
-
-
- Ota tyyppi
-
-
- Yksinkertainen
-
-
- Strukturoitu
-
-
- Aiheen kuva
-
Tumma tila
-
- Punkit
-
Otsikko
-
- Päivityksen syy
-
-
- Lähetä nimettömänä
-
Käyttäjätunnus
-
- Käyttäjätunnus hakuun ja lisäämiseen
-
-
- Käyttäjätunnukset, joita voit etsiä ja lisätä
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.fr.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.fr.resx
index 2adedb7..14478cc 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.fr.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.fr.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nécessaire en cas de récupération du mot de passe)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">gestionnaire de mots de passe</a> suggéré)
-
-
- Montant
-
-
- Réponse id
-
-
- Texte de réponse
-
-
- Caractère personnalisé (emoji)
-
-
- Couleur
-
-
- Commentaire
-
-
- Texte du commentaire
-
-
- Confrontation
-
-
- Confrontation id
-
-
- Contexte
-
-
- Date de fin
-
-
- Date de début
-
Description
-
- Discussion
-
-
- Date de fin
-
-
- Heure de fin
-
-
- ID de discussion
-
-
- Texte de discussion
-
-
- Titre de la discussion
-
CourrielEmoji
-
- Nombre maximal de confrontations
-
-
- Fait
-
-
- Faits et chiffres
-
-
- Identifiant du fichier
-
Groupes
-
- Inclure la première réponse dans l'aperçu de l'invitation ? (ce qui rend plus attrayant le fait de rejoindre la discussion)
-
Code d'invitation
-
- Code ou lien d'invitation
-
Mot de passe de l'invitation
-
- Exiger un mot de passe pour la prévisualisation
-
-
- Exiger un mot de passe pour afficher l'aperçu
-
-
- Ajouter une protection par mot de passe
-
-
- EstStructuré
-
Identifiant de langueNom
-
- Activer les notifications ? (utile pour recevoir les mises à jour de l'application)
-
Nouveau mot de passe
@@ -243,76 +150,22 @@
Ancien mot de passe
-
- En une seule fois
-
Mot de passe
-
- Perception
-
-
- Perceptions
-
Préférer le thème du systèmeCode de récupération
-
- Récurrent
-
-
- Scanner pour :
-
-
- Recherche
-
-
- Partage du code secret
-
-
- Afficher le badge de donateur
-
-
- Affichez l'onglet des journaux pour faciliter le dépannage.
-
-
- Type de prise
-
-
- Simple
-
-
- Structuré
-
-
- Image du thème
-
Mode foncé
-
- Tiques
-
Titre
-
- Raison de la mise à jour
-
-
- Soumettre anonymement
-
Nom d'utilisateur
-
- Nom d'utilisateur pour rechercher et ajouter
-
-
- Noms d'utilisateur à rechercher et à ajouter
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.hu.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.hu.resx
index 5fb87eb..0e7900d 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.hu.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.hu.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (jelszó helyreállítása esetén szükséges)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">jelszókezelő</a> javasolt)
-
-
- Összeg
-
-
- Válasz id
-
-
- Válasz szöveg
-
-
- Egyéni karakter (emoji)
-
-
- Színes
-
-
- Megjegyzés id
-
-
- Megjegyzés szövege
-
-
- Konfrontáció
-
-
- Konfrontációs azonosító
-
-
- Kontextus
-
-
- Végdátum
-
-
- Kezdeti időpont
-
Leírás
-
- Megbeszélés
-
-
- Végdátum
-
-
- Végidő
-
-
- Megbeszélés id
-
-
- Megbeszélés szövege
-
-
- Vita címe
-
E-mailEmoji
-
- A konfrontációk maximális száma
-
-
- Tény
-
-
- Tények
-
-
- Fájl azonosító
-
Csoportok
-
- Az első válasz szerepeljen a meghívó előnézetében? (így vonzóbbá teszi a vitához való csatlakozást)
-
Meghívó kód
-
- Meghívó kód vagy link
-
Meghívó jelszó
-
- Jelszó megkövetelése az előnézethez
-
-
- Jelszó megkövetelése az előnézet megjelenítéséhez
-
-
- Jelszóvédelem hozzáadása
-
-
- IsStructured
-
Nyelvi azonosítóNév
-
- Értesítések engedélyezése?(hasznos az alkalmazás frissítéseinek fogadásához)
-
Új jelszó
@@ -243,76 +150,22 @@
Régi jelszó
-
- Csak egyszeri alkalom
-
Jelszó
-
- Percepció
-
-
- Perceptions
-
A rendszer témájának előnyben részesítéseHelyreállítási kód
-
- Visszatérő
-
-
- Keresés:
-
-
- Keresés
-
-
- Titkos kód megosztása
-
-
- Adományozói jelvény megjelenítése
-
-
- A naplók lap megjelenítése a hibaelhárítás támogatására.
-
-
- Vegye a típust
-
-
- Egyszerű
-
-
- Strukturált
-
-
- Témakép
-
Sötét üzemmód
-
- Kullancsok
-
Cím
-
- Frissítés oka
-
-
- Névtelenül beküldeni
-
Felhasználónév
-
- Felhasználónév kereséshez és hozzáadáshoz
-
-
- Felhasználónevek keresése és hozzáadása
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.it.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.it.resx
index 3f1bccc..5f90679 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.it.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.it.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (necessaria in caso di recupero password)
-
-
- (è suggerito l'uso del <a class="is-underlined has-text-info" tabindex="-1" target="_blank" href="https://keepassxc.org/">gestore password</a>)
-
-
- Importo
-
-
- ID risposta
-
-
- Testo risposta
-
-
- Carattere personalizzato (emoji)
-
-
- Colore
-
-
- ID commento
-
-
- Testo commento
-
-
- Confronto
-
-
- Id confronto
-
-
- Contesto
-
-
- Data di fine
-
-
- Data di inizio
-
Descrizione
-
- Discussione
-
-
- Data di fine
-
-
- Tempo di fine
-
-
- ID discussione
-
-
- Testo discussione
-
-
- Titolo discussione
-
EmailEmoji
-
- Quantità massima di scontri
-
-
- Fatto
-
-
- I fatti
-
-
- ID file
-
Gruppi
-
- Includere la prima risposta nell'anteprima dell'invito? (rendendo più coinvolgente l'adesione alla discussione)
-
Codice d'invito
-
- Collegamento o codice di invito
-
Password dell'invito
-
- Richiedi la password per l'anteprima
-
-
- Richiedi la password per l'anteprima della discussione
-
-
- Aggiungere la protezione con password
-
-
- È strutturato
-
ID linguaNome
-
- Abilitare le notifiche?(utili per percepire aggiornamenti)
-
Nuova password
@@ -243,78 +150,24 @@
Attuale password
-
- Paga una volta
-
Password
-
- La percezione
-
-
- Percezioni
-
Preferire il tema di sistemaCodice di recupero
-
- Ricorrente
-
-
- Scansione per:
-
-
- Ricerca
-
-
- Condivisione del codice segreto
-
-
- Mostra il badge del donatore
-
-
- Mostra il tab dei log a supporto della risoluzione dei problemi.
-
-
- Prendi il tipo
-
-
- Semplice
-
-
- Strutturato
-
-
- Immagine a tema
-
Modalità scura
-
- Zecche
-
Titolo
-
- Motivo dell'aggiornamento
-
-
- Invia in forma anonima
-
Soprannome
-
- Soprannome da ricercare e aggiungere
-
-
- Soprannomi da ricercare e aggiungere
-
Avatar
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.ja.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.ja.resx
index d12ddc9..cb9a634 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.ja.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.ja.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (パスワード復旧時に必要)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">パスワードマネージャー</a>の提案)
-
-
- 金額
-
-
- 回答ID
-
-
- 回答テキスト
-
-
- カスタムキャラクター(絵文字)
-
-
- カラー
-
-
- コメントID
-
-
- コメント文
-
-
- 対決
-
-
- コンフロンティアID
-
-
- コンテキスト
-
-
- 終了日
-
-
- 開始日
-
商品説明
-
- ディスカッション
-
-
- 終了日
-
-
- 終了時間
-
-
- ディスカッションID
-
-
- ディスカッションテキスト
-
-
- 議論のタイトル
-
電子メール絵文字
-
- 対決の最大量
-
-
- 事実
-
-
- 事実
-
-
- ファイルID
-
グループ
-
- 招待状のプレビューに最初の回答を含める?(議論への参加をより魅力的にするために)
-
招待状コード
-
- 招待コードまたはリンク
-
招待状のパスワード
-
- プレビューにパスワードが必要
-
-
- プレビューを表示するにはパスワードが必要です
-
-
- パスワード保護機能の追加
-
-
- IsStructured
-
言語ID名前
-
- 通知を有効にしますか?(アプリの更新情報を受け取るのに便利です)
-
新しいパスワード
@@ -243,76 +150,22 @@
古いパスワード
-
- 1回限り
-
パスワード
-
- パーセプション
-
-
- 認知度
-
システムテーマを優先するリカバリーコード
-
- リカーリング
-
-
- スキャンしてください:
-
-
- 検索
-
-
- 秘密の暗号を共有する
-
-
- 寄付者バッジを表示する
-
-
- トラブルシューティングをサポートするために、ログタブを表示します。
-
-
- テイクタイプ
-
-
- シンプル
-
-
- 構造化
-
-
- テーマイメージ
-
ダークモード
-
- ダニ
-
タイトル
-
- 更新理由
-
-
- 匿名で投稿する
-
ユーザー名
-
- 検索して追加するユーザー名
-
-
- 検索して追加するユーザー名
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.lt.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.lt.resx
index f4c2554..018d831 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.lt.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.lt.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (būtina slaptažodžio atkūrimo atveju)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">paslapčių tvarkyklė</a> siūloma)
-
-
- Suma
-
-
- Atsakymas id
-
-
- Atsakymo tekstas
-
-
- Pasirinktinis simbolis (emoji)
-
-
- Spalva
-
-
- Komentaras id
-
-
- Komentaro tekstas
-
-
- Konfrontacija
-
-
- Konfrontacijos id
-
-
- Kontekstas
-
-
- Galutinė data
-
-
- Pradžios data
-
Aprašymas
-
- Diskusija
-
-
- Galutinė data
-
-
- Pabaigos laikas
-
-
- Diskusijos id
-
-
- Diskusijos tekstas
-
-
- Diskusijos pavadinimas
-
El. paštasEmoji
-
- Didžiausias konfrontacijų skaičius
-
-
- Faktas
-
-
- Faktai
-
-
- Failo ID
-
Grupės
-
- Įtraukti pirmąjį atsakymą į kvietimo peržiūrą? (kad būtų patraukliau prisijungti prie diskusijos)
-
Kvietimo kodas
-
- Kvietimo kodas arba nuoroda
-
Kvietimo slaptažodis
-
- Reikalauti slaptažodžio peržiūrai
-
-
- Reikalauti slaptažodžio, kad būtų rodoma peržiūra
-
-
- Pridėti apsaugą slaptažodžiu
-
-
- IsStructured
-
Kalbos IDPavadinimas
-
- Įjungti pranešimus? (naudinga norint gauti programėlės atnaujinimus)
-
Naujas slaptažodis
@@ -243,76 +150,22 @@
Senasis slaptažodis
-
- Tik vieną kartą
-
Slaptažodis
-
- Suvokimas
-
-
- Suvokimas
-
Pirmenybė sistemos temaiAtkūrimo kodas
-
- Pasikartojantis
-
-
- Ieškoti:
-
-
- Paieška
-
-
- Dalijimasis slaptuoju kodu
-
-
- Rodyti rėmėjo ženklelį
-
-
- Parodykite skirtuką "Žurnalai", kad galėtumėte padėti šalinti trikdžius.
-
-
- Paimkite tipą
-
-
- Paprastas
-
-
- Struktūrinis
-
-
- Temos vaizdas
-
Tamsus režimas
-
- Erkės
-
Pavadinimas
-
- Atnaujinimo priežastis
-
-
- Pateikti anonimiškai
-
Vartotojo vardas
-
- Vartotojo vardas paieškai ir pridėjimui
-
-
- Vartotojų vardų paieška ir pridėjimas
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.lv.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.lv.resx
index 2430ee0..f6c9469 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.lv.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.lv.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nepieciešams paroles atjaunošanas gadījumā)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">vārdu pārvaldnieks</a> ierosināts)
-
-
- Summa
-
-
- Atbilde id
-
-
- Atbildes teksts
-
-
- Pielāgots raksturs (emodži)
-
-
- Krāsa
-
-
- Komentārs id
-
-
- Komentāru teksts
-
-
- Konfrontācija
-
-
- Konfrontācijas id
-
-
- Konteksts
-
-
- Beigu datums
-
-
- Sākuma datums
-
Apraksts
-
- Diskusija
-
-
- Beigu datums
-
-
- Beigu laiks
-
-
- Diskusijas id
-
-
- Diskusijas teksts
-
-
- Diskusijas nosaukums
-
E-pastsEmocijas
-
- Maksimālais konfrontāciju skaits
-
-
- Fakti
-
-
- Fakti
-
-
- Faila id
-
Grupas
-
- Iekļaut pirmo atbildi uzaicinājuma priekšskatījumā? (lai iesaistīšanās diskusijā būtu saistošāka)
-
Uzaicinājuma kods
-
- Uzaicinājuma kods vai saite
-
Uzaicinājuma parole
-
- Pieprasīt paroli priekšskatīšanai
-
-
- Pieprasīt paroli, lai parādītu priekšskatījumu
-
-
- Pievienojiet paroles aizsardzību
-
-
- IsStructured
-
Valodas identifikatorsNosaukums
-
- Ieslēgt paziņojumus?(noderīgi, lai saņemtu lietotnes atjauninājumus)
-
Jauna parole
@@ -243,76 +150,22 @@
Vecā parole
-
- Tikai vienu reizi
-
Parole
-
- Uztvere
-
-
- Uzskati
-
Dodiet priekšroku sistēmas tēmaiAtgūšanas kods
-
- Atkārtoti
-
-
- Skenēt, lai atrastu:
-
-
- Meklēšana
-
-
- Koplietošanas slepenais kods
-
-
- Rādīt ziedotāja nozīmīti
-
-
- Parādiet cilni žurnāli, lai atbalstītu problēmu novēršanu.
-
-
- Veikt veidu
-
-
- Vienkāršs
-
-
- Strukturēts
-
-
- Tēmas attēls
-
Tumšais režīms
-
- Ērces
-
Nosaukums
-
- Atjaunināšanas iemesls
-
-
- Iesniedziet anonīmi
-
Lietotājvārds
-
- Lietotājvārds, lai meklētu un pievienotu
-
-
- Meklēšanas un pievienošanas lietotājvārdi
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.nl.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.nl.resx
index 766bcb8..4fcfff7 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.nl.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.nl.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nodig in geval van wachtwoordherstel)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">wachtwoord manager</a> voorgesteld)
-
-
- Bedrag
-
-
- Antwoord id
-
-
- Antwoord tekst
-
-
- Aangepast karakter (emoji)
-
-
- Kleur
-
-
- Commentaar id
-
-
- Commentaar tekst
-
-
- Confrontatie
-
-
- Confrontatie id
-
-
- Context
-
-
- Einddatum
-
-
- Startdatum
-
Beschrijving
-
- Discussie
-
-
- Einddatum
-
-
- Eindtijd
-
-
- Discussie id
-
-
- Besprekingstekst
-
-
- Titel van de discussie
-
E-mailEmoji
-
- Maximum aantal confrontaties
-
-
- Feit
-
-
- Feiten
-
-
- Bestand id
-
Groepen
-
- Het eerste antwoord opnemen in de preview van de uitnodiging? (maakt het aantrekkelijker om deel te nemen aan de discussie)
-
Uitnodiging code
-
- Uitnodigingscode of link
-
Uitnodiging wachtwoord
-
- Wachtwoord nodig voor preview
-
-
- Wachtwoord vereist om de preview te tonen
-
-
- Wachtwoordbeveiliging toevoegen
-
-
- IsStructured
-
Taal idNaam
-
- Meldingen inschakelen?(handig om app updates te ontvangen)
-
Nieuw wachtwoord
@@ -243,76 +150,22 @@
Oud wachtwoord
-
- Eenmalig
-
Wachtwoord
-
- Perceptie
-
-
- Percepties
-
Voorkeur systeemthemaHerstelcode
-
- Terugkerend
-
-
- Scan voor:
-
-
- Zoeken op
-
-
- Het delen van de geheime code
-
-
- Toon donateur badge
-
-
- Toon de logs tab ter ondersteuning van troubleshooting.
-
-
- Neem type
-
-
- Eenvoudig
-
-
- Gestructureerd
-
-
- Thema-afbeelding
-
Donkere modus
-
- Teken
-
Titel
-
- Update reden
-
-
- Anoniem indienen
-
Gebruikersnaam
-
- Gebruikersnaam om te zoeken en toe te voegen
-
-
- Gebruikersnamen om te zoeken en toe te voegen
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.pl.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.pl.resx
index d8eeeb3..2c62305 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.pl.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.pl.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (konieczne w przypadku odzyskiwania hasła)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">password manager</a> sugerowane)
-
-
- Kwota
-
-
- Odpowiedź
-
-
- Odpowiedź
-
-
- Znak niestandardowy (emoji)
-
-
- Kolor
-
-
- Komentarz id
-
-
- Tekst komentarza
-
-
- Konfrontacja
-
-
- Konfrontacja id
-
-
- Kontekst
-
-
- Data końcowa
-
-
- Data rozpoczęcia
-
Opis
-
- Dyskusja
-
-
- Data końcowa
-
-
- Czas zakończenia
-
-
- Dyskusja id
-
-
- Tekst do dyskusji
-
-
- Tytuł dyskusji
-
E-mailEmoji
-
- Maksymalna ilość konfrontacji
-
-
- Fakt
-
-
- Fakty
-
-
- Id pliku
-
Grupy
-
- Zamieścić pierwszą odpowiedź w podglądzie zaproszenia? (sprawia, że dołączenie do dyskusji jest bardziej zachęcające)
-
Kod zaproszenia
-
- Kod lub link do zaproszenia
-
Hasło do zaproszenia
-
- Wymagaj hasła do podglądu
-
-
- Wymagaj hasła, aby wyświetlić podgląd
-
-
- Dodaj ochronę hasłem
-
-
- IsStructured
-
Język idNazwa
-
- Włączyć powiadomienia? (przydatne do otrzymywania aktualizacji aplikacji)
-
Nowe hasło
@@ -243,76 +150,22 @@
Stare hasło
-
- Tylko jeden raz
-
Hasło
-
- Percepcja
-
-
- Spostrzeżenia
-
Preferowany motyw systemowyKod odzyskiwania
-
- Powtarzające się
-
-
- Skanuj dla:
-
-
- Szukaj
-
-
- Dzielenie się tajnym kodem
-
-
- Pokaż odznakę darczyńcy
-
-
- Pokaż kartę logów, aby ułatwić rozwiązywanie problemów.
-
-
- Rodzaj ujęcia
-
-
- Proste
-
-
- Strukturalne
-
-
- Obrazek tematyczny
-
Tryb ciemny
-
- Kleszcze
-
Tytuł
-
- Powód aktualizacji
-
-
- Zgłoś się anonimowo
-
Nazwa użytkownika
-
- Nazwa użytkownika do wyszukiwania i dodawania
-
-
- Nazwy użytkowników do wyszukiwania i dodawania
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.pt.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.pt.resx
index f9f2369..bf3ddfd 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.pt.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.pt.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (necessário em caso de recuperação de senha)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">gerenciador de palavras-passe</a> sugerido)
-
-
- Montante
-
-
- Responder id
-
-
- Responder texto
-
-
- Personalidade(emoji)
-
-
- Cor
-
-
- Comentar id
-
-
- Comentar texto
-
-
- Confrontação
-
-
- Id de confronto
-
-
- Contexto
-
-
- Data final
-
-
- Data de início
-
Descrição
-
- Discussão
-
-
- Data final
-
-
- Tempo final
-
-
- Id de discussão
-
-
- Texto para discussão
-
-
- Título do debate
-
EmailEmoji
-
- Quantidade máxima de confrontos
-
-
- Facto
-
-
- Fatos
-
-
- Identificação do arquivo
-
Grupos
-
- Incluir a primeira resposta na pré-visualização do convite? (tornando-a mais envolvente para participar da discussão)
-
Código de convite
-
- Código do convite ou link
-
Senha do convite
-
- Exigir senha para visualização
-
-
- Exigir senha para mostrar a visualização
-
-
- Adicionar proteção por senha
-
-
- IsStructured
-
Identificação do idiomaNome
-
- Habilitar notificações?(útil para receber atualizações de aplicativos)
-
Nova senha
@@ -243,76 +150,22 @@
Senha antiga
-
- Só uma vez
-
Senha
-
- Percepção
-
-
- Percepções
-
Tema do sistema de preferênciasCódigo de recuperação
-
- Recurrente
-
-
- Procura por:
-
-
- Pesquisa
-
-
- Partilha do Código Secreto
-
-
- Mostrar crachá do doador
-
-
- Mostrar o separador de registos para suportar a resolução de problemas.
-
-
- Tirar tipo
-
-
- Simples
-
-
- Estruturado
-
-
- Imagem temática
-
Modo escuro
-
- Carraças
-
Título
-
- Motivo da actualização
-
-
- Submeter de forma anónima
-
Nome de usuário
-
- Nome de usuário para pesquisar e adicionar
-
-
- Nomes de usuário para pesquisar e adicionar
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.resx
index bb92c82..f56ec17 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (necessary in case of password recovery)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="_blank" href="https://keepassxc.org/">password manager</a> suggested)
-
-
- Amount
-
-
- Answer id
-
-
- Answer text
-
-
- Custom character(emoji)
-
-
- Colour
-
-
- Comment id
-
-
- Comment text
-
-
- Confrontation
-
-
- Confrontation id
-
-
- Context
-
-
- End date
-
-
- Start date
-
Description
-
- Discussion
-
-
- End date
-
-
- End time
-
-
- Discussion id
-
-
- Discussion text
-
-
- Discussion title
-
EmailEmoji
-
- Max amount of confrontations
-
-
- Fact
-
-
- Facts
-
-
- File id
-
Groups
-
- Include the first answer in the invitation preview? (making it more engaging to join the discussion)
-
Invitation code
-
- Invitation code or link
-
Invitation password
-
- Require password for preview
-
-
- Require password to show the preview
-
-
- Add password protection
-
-
- IsStructured
-
Language idName
-
- Enable notifications?(useful to receive app updates)
-
New password
@@ -243,78 +150,24 @@
Old password
-
- One time only
-
Password
-
- Perception
-
-
- Perceptions
-
Prefer system themeRecovery code
-
- Recurring
-
-
- Scan for:
-
-
- Search
-
-
- Sharing Secret Code
-
-
- Show donator badge
-
-
- Show the logs tab to support troubleshooting.
-
-
- Take type
-
-
- Simple
-
-
- Structured
-
-
- Theme image
-
Dark mode
-
- Ticks
-
Title
-
- Update reason
-
-
- Submit anonymously
-
Username
-
- Username to search and add
-
-
- Usernames to search and add
-
Avatar
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.ro.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.ro.resx
index 0a79425..f88a731 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.ro.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.ro.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (necesar în cazul recuperării parolei)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">manager de parole</a> sugerat)
-
-
- Suma
-
-
- Răspuns id
-
-
- Text de răspuns
-
-
- Caracter personalizat (emoji)
-
-
- Culoare
-
-
- Comentariu id
-
-
- Comentariu text
-
-
- Confruntare
-
-
- Id de confruntare
-
-
- Context
-
-
- Data de încheiere
-
-
- Data de începere
-
Descriere
-
- Discuție
-
-
- Data de încheiere
-
-
- Ora de sfârșit
-
-
- Discuție id
-
-
- Text de discuție
-
-
- Titlul discuției
-
EmailEmoji
-
- Numărul maxim de confruntări
-
-
- Fapt
-
-
- Fapte
-
-
- ID fișier
-
Grupuri
-
- Includeți primul răspuns în previzualizarea invitației? (pentru a face mai atractivă participarea la discuție)
-
Codul de invitație
-
- Cod sau link de invitație
-
Parola de invitație
-
- Necesită o parolă pentru previzualizare
-
-
- Necesită o parolă pentru a afișa previzualizarea
-
-
- Adăugați protecție prin parolă
-
-
- IsStructured
-
ID-ul limbiiNume
-
- Activați notificările?(util pentru a primi actualizări ale aplicației)
-
Parolă nouă
@@ -243,76 +150,22 @@
Parolă veche
-
- O singură dată
-
Parola
-
- Percepție
-
-
- Percepții
-
Preferați tema de sistemCod de recuperare
-
- Recurent
-
-
- Scanează pentru:
-
-
- Căutare
-
-
- Partajarea codului secret
-
-
- Arată insigna de donator
-
-
- Afișați fila jurnale pentru a sprijini depanarea.
-
-
- Tipul de luare
-
-
- Simplu
-
-
- Structurat
-
-
- Imagine tematică
-
Mod întunecat
-
- Căpușe
-
Titlu
-
- Motivul actualizării
-
-
- Trimiteți în mod anonim
-
Nume utilizator
-
- Nume de utilizator pentru a căuta și adăuga
-
-
- Nume de utilizator pentru căutare și adăugare
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.ru.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.ru.resx
index 967227e..55eec31 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.ru.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.ru.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (необходимо в случае восстановления пароля)
-
-
- (Предлагается <a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">password manager</a>)
-
-
- Сумма
-
-
- Идентификатор ответа
-
-
- Текст ответа
-
-
- Пользовательский символ (эмодзи)
-
-
- Цвет
-
-
- Идентификатор комментария
-
-
- Текст комментария
-
-
- Противостояние
-
-
- Идентификатор противостояния
-
-
- Контекст
-
-
- Дата окончания
-
-
- Дата начала
-
Описание
-
- Обсуждение
-
-
- Дата окончания
-
-
- Время окончания
-
-
- Обсуждаемый иди
-
-
- Текст для обсуждения
-
-
- Название дискуссии
-
Электронная почтаEmoji
-
- Максимальное количество противостояний
-
-
- Факт
-
-
- Факты
-
-
- Идентификатор файла
-
Группы
-
- Включить первый ответ в предварительный просмотр приглашения? (что делает его более привлекательным для участия в обсуждении)
-
Код приглашения
-
- Код приглашения или ссылка
-
Пароль приглашения
-
- Требуется пароль для предварительного просмотра
-
-
- Требуется пароль для показа предварительного просмотра
-
-
- Добавить защиту паролем
-
-
- IsStructured
-
Языковой идентификаторИмя
-
- Включить уведомления? (полезно для получения обновлений приложения)
-
Новый пароль
@@ -243,76 +150,22 @@
Старый пароль
-
- Только один раз
-
Пароль
-
- Восприятие
-
-
- Восприятие
-
Предпочитайте системную темуКод восстановления
-
- Повторяющийся
-
-
- Сканирование для:
-
-
- Поиск
-
-
- Обмен секретным кодом
-
-
- Показать значок жертвователя
-
-
- Покажите вкладку журналов для поддержки поиска и устранения неисправностей.
-
-
- Возьмите тип
-
-
- Простой
-
-
- Структурированный
-
-
- Тематическое изображение
-
Темный режим
-
- Клещи
-
Название
-
- Причина обновления
-
-
- Подавать анонимно
-
Имя пользователя
-
- Имя пользователя для поиска и добавления
-
-
- Имена пользователей для поиска и добавления
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.sk.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.sk.resx
index 8b2d898..e10f3e4 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.sk.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.sk.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (potrebné v prípade obnovenia hesla)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">správca hesiel</a> navrhnuté)
-
-
- Suma
-
-
- Odpoveď id
-
-
- Text odpovede
-
-
- Vlastný znak (emoji)
-
-
- Farba
-
-
- Komentár id
-
-
- Text komentára
-
-
- Konfrontácia
-
-
- Id konfrontácie
-
-
- Kontext
-
-
- Dátum ukončenia
-
-
- Dátum začiatku
-
Popis
-
- Diskusia
-
-
- Dátum ukončenia
-
-
- Čas ukončenia
-
-
- Diskusia id
-
-
- Text diskusie
-
-
- Názov diskusie
-
E-mailEmoji
-
- Maximálny počet konfrontácií
-
-
- Skutočnosť
-
-
- Fakty
-
-
- Identifikátor súboru
-
Skupiny
-
- Zahrnúť prvú odpoveď do náhľadu pozvánky? (aby bolo zapojenie do diskusie pútavejšie)
-
Pozývací kód
-
- Pozývací kód alebo odkaz
-
Heslo pozvánky
-
- Vyžadovať heslo pre náhľad
-
-
- Vyžadovať heslo na zobrazenie náhľadu
-
-
- Pridanie ochrany heslom
-
-
- IsStructured
-
Identifikátor jazykaNázov
-
- Povolenie oznámení?(užitočné na prijímanie aktualizácií aplikácie)
-
Nové heslo
@@ -243,76 +150,22 @@
Staré heslo
-
- Iba jedenkrát
-
Heslo
-
- Vnímanie
-
-
- Vnímanie
-
Uprednostniť tému systémuKód obnovy
-
- Opakujúce sa
-
-
- Vyhľadávanie:
-
-
- Vyhľadávanie
-
-
- Zdieľanie tajného kódu
-
-
- Zobraziť odznak darcu
-
-
- Zobrazenie karty protokolov na podporu riešenia problémov.
-
-
- Vezmite si typ
-
-
- Jednoduché
-
-
- Štruktúrované
-
-
- Obrázok témy
-
Tmavý režim
-
- Kliešte
-
Názov
-
- Dôvod aktualizácie
-
-
- Odoslať anonymne
-
Používateľské meno
-
- Používateľské meno na vyhľadávanie a pridávanie
-
-
- Používateľské mená na vyhľadávanie a pridávanie
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.sl.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.sl.resx
index f14f013..7e89d04 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.sl.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.sl.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (potrebno v primeru obnovitve gesla)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">predlagani upravitelj gesel</a>)
-
-
- Znesek
-
-
- Odgovor id
-
-
- Besedilo odgovora
-
-
- Znak po meri (emoji)
-
-
- Barva
-
-
- Id komentarja
-
-
- Besedilo komentarja
-
-
- Konfrontacija
-
-
- Id konfrontacije
-
-
- Kontekst
-
-
- Končni datum
-
-
- Datum začetka
-
Opis
-
- Razprava
-
-
- Končni datum
-
-
- Končni čas
-
-
- Razprava id
-
-
- Besedilo za razpravo
-
-
- Naslov razprave
-
E-poštaEmoji
-
- Največje število soočenj
-
-
- Dejstva
-
-
- Dejstva
-
-
- Id datoteke
-
Skupine
-
- Vključiti prvi odgovor v predogled vabila? (da bo vključitev v razpravo bolj privlačna)
-
Koda vabila
-
- Koda vabila ali povezava
-
Geslo za povabilo
-
- Zahtevajte geslo za predogled
-
-
- Zahtevajte geslo za prikaz predogleda
-
-
- Dodajanje zaščite z geslom
-
-
- IsStructured
-
Id jezikaIme
-
- Omogočite obvestila? (koristno za prejemanje posodobitev aplikacije)
-
Novo geslo
@@ -243,76 +150,22 @@
Staro geslo
-
- Samo enkrat
-
Geslo
-
- Zaznavanje
-
-
- Dojemanje
-
Prednost sistemske temeKoda za obnovitev
-
- Ponavljajoče se
-
-
- Iskanje za:
-
-
- Iskanje
-
-
- Deljenje skrivne kode
-
-
- Prikaži značko donatorja
-
-
- Prikažite zavihek dnevniki za podporo pri odpravljanju težav.
-
-
- Vrsta posnetka
-
-
- Enostavno
-
-
- Strukturiran
-
-
- Slika teme
-
Temni način
-
- Klopi
-
Naslov
-
- Razlog za posodobitev
-
-
- Pošlji anonimno
-
Uporabniško ime
-
- Uporabniško ime za iskanje in dodajanje
-
-
- Uporabniška imena za iskanje in dodajanje
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.sv.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.sv.resx
index 68eafc7..71b6a71 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.sv.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.sv.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (nödvändig vid återställning av lösenord)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">password manager</a> föreslås)
-
-
- Belopp
-
-
- Svar id
-
-
- Svarstext
-
-
- Anpassat tecken (emoji)
-
-
- Färg
-
-
- Kommentar id
-
-
- Kommentar
-
-
- Konfrontation
-
-
- Konfrontation id
-
-
- Kontext
-
-
- Slutdatum
-
-
- Startdatum
-
Beskrivning
-
- Diskussion
-
-
- Slutdatum
-
-
- Sluttid
-
-
- Diskussion id
-
-
- Diskussionstext
-
-
- Titel på diskussionen
-
E-postEmoji
-
- Max antal konfrontationer
-
-
- Fakta
-
-
- Fakta
-
-
- Filid
-
Grupper
-
- Inkludera det första svaret i förhandsgranskningen av inbjudan? (för att göra det mer lockande att delta i diskussionen)
-
Kod för inbjudan
-
- Inbjudningskod eller länk
-
Lösenord för inbjudan
-
- Kräver lösenord för förhandsgranskning
-
-
- Kräver lösenord för att visa förhandsgranskningen
-
-
- Lägg till lösenordsskydd
-
-
- IsStructured
-
Språk-idNamn
-
- Aktivera meddelanden (användbart för att få uppdateringar av appen)
-
Nytt lösenord
@@ -243,76 +150,22 @@
Gammalt lösenord
-
- Endast en gång
-
Lösenord
-
- Uppfattning
-
-
- Uppfattningar
-
Föredrar systemtemaKod för återvinning
-
- Återkommande
-
-
- Skanna efter:
-
-
- Sök på
-
-
- Att dela med sig av den hemliga koden
-
-
- Visa donatormärket
-
-
- Visa fliken Loggar för att underlätta felsökning.
-
-
- Typ av tagning
-
-
- Enkelt
-
-
- Strukturerad
-
-
- Tema bild
-
Mörkt läge
-
- Fästingar
-
Titel
-
- Orsak till uppdatering
-
-
- Skicka in anonymt
-
Användarnamn
-
- Användarnamn för att söka och lägga till
-
-
- Användarnamn att söka och lägga till
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/Resources/FieldsNameResource.zh.resx b/PrivaPub.ClientModels/Resources/FieldsNameResource.zh.resx
index 93ce9c9..ef11f83 100644
--- a/PrivaPub.ClientModels/Resources/FieldsNameResource.zh.resx
+++ b/PrivaPub.ClientModels/Resources/FieldsNameResource.zh.resx
@@ -117,123 +117,30 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- (在恢复密码的情况下是必要的)
-
-
- (<a class="is-underlined has-text-info" tabindex="-1" target="blank" href="https://keepassxc.org/">密码管理器</a>建议)
-
-
- 数量
-
-
- 答案是
-
-
- 回答文本
-
-
- 自定义字符(表情符号)
-
-
- 颜色
-
-
- 评论编号
-
-
- 评论文本
-
-
- 对抗
-
-
- 对抗的身份
-
-
- 背景介绍
-
-
- 结束日期
-
-
- 开始日期
-
描述
-
- 讨论
-
-
- 结束日期
-
-
- 结束时间
-
-
- 讨论的内容
-
-
- 讨论文本
-
-
- 讨论题目
-
电子邮件表情符号
-
- 对抗的最大数量
-
-
- 事实
-
-
- 事实
-
-
- 文件编号
-
群体
-
- 在邀请预览中包括第一个答案?(使其更加吸引人加入讨论)
-
邀请函代码
-
- 邀请函代码或链接
-
邀请函密码
-
- 预览时需要密码
-
-
- 需要密码才能显示预览
-
-
- 添加密码保护
-
-
- 是结构化的
-
语言标识命名
-
- 启用通知?(对接收应用程序的更新很有用)
-
新密码
@@ -243,76 +150,22 @@
旧密码
-
- 仅限一次
-
密码
-
- 感知
-
-
- 观念
-
倾向于系统主题恢复代码
-
- 经常性的
-
-
- 进行扫描:
-
-
- 搜索
-
-
- 分享秘密代码
-
-
- 显示捐赠者徽章
-
-
- 显示日志标签以支持故障排除。
-
-
- 采取类型
-
-
- 简单
-
-
- 结构化
-
-
- 主题图片
-
黑暗模式
-
- 蜱虫
-
标题
-
- 更新原因
-
-
- 匿名提交
-
帐号
-
- 搜索和添加的用户名
-
-
- 搜索和添加的用户名
-
\ No newline at end of file
diff --git a/PrivaPub.ClientModels/User/UsersIds.cs b/PrivaPub.ClientModels/User/UsersIds.cs
index a2f7b13..226adcd 100644
--- a/PrivaPub.ClientModels/User/UsersIds.cs
+++ b/PrivaPub.ClientModels/User/UsersIds.cs
@@ -2,6 +2,6 @@
{
public class UsersIds
{
- public IList UserIdList { get; set; } = Array.Empty();
+ public List UserIdList { get; set; } = [];
}
}
diff --git a/PrivaPub/Controllers/ClientToServer/AdminController.cs b/PrivaPub/Controllers/ClientToServer/AdminController.cs
index 130b886..3269be6 100644
--- a/PrivaPub/Controllers/ClientToServer/AdminController.cs
+++ b/PrivaPub/Controllers/ClientToServer/AdminController.cs
@@ -29,14 +29,20 @@ namespace PrivaPub.Controllers.ClientToServer
RootUsersService = rootUsersService;
}
+ // an administrator never removes, bans or unbans itself; the request's own list is left as it came
+ UsersIds Others(UsersIds usersIds)
+ {
+ var selfId = User.GetUserId();
+ return new UsersIds { UserIdList = usersIds.UserIdList.Where(id => id != selfId).ToList() };
+ }
+
[HttpDelete, Route("/clientapi/admin/remove/users"), Authorize(Policy = Policies.IsAdmin)]
public async Task RemoveUsers([Required] UsersIds usersIds)
{
var result = new WebResult();
try
{
- usersIds.UserIdList.Remove(User.GetUserId());
- result = await RootUsersService.RemoveUserAsync(usersIds);
+ result = await RootUsersService.RemoveUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -57,13 +63,7 @@ namespace PrivaPub.Controllers.ClientToServer
var result = new WebResult();
try
{
- //var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
- //if (isUserResult.IsValid && !(bool)isUserResult.Data)
- //if (isUserResult is { IsValid: true } and not { Data: bool })
- // return Unauthorized();
-
- usersIds.UserIdList.Remove(User.GetUserId());
- result = await RootUsersService.BanUserAsync(usersIds);
+ result = await RootUsersService.BanUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -85,12 +85,7 @@ namespace PrivaPub.Controllers.ClientToServer
var result = new WebResult();
try
{
- //var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
- //if (isUserResult.IsValid && !(bool)isUserResult.Data)
- // return Unauthorized();
-
- usersIds.UserIdList.Remove(User.GetUserId());
- result = await RootUsersService.UnbanUserAsync(usersIds);
+ result = await RootUsersService.UnbanUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -102,28 +97,5 @@ namespace PrivaPub.Controllers.ClientToServer
return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
}
}
-
- //[HttpGet, Authorize(Policy = Policies.IsAdmin)]
- //public async Task GetUsers()
- //{
- // var result = new WebResult();
- // try
- // {
- // var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
- // if (isUserResult.IsValid && !(bool)isUserResult.Data)
- // return Unauthorized();
-
- // result = await UsersService.GetUsersAsync();
- // if (!result.IsValid)
- // return StatusCode(result.StatusCode, result);
-
- // return Ok(result.Data);
- // }
- // catch (Exception ex)
- // {
- // Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUsers)}()");
- // return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
- // }
- //}
}
}
diff --git a/PrivaPub/Controllers/ClientToServer/RootUserController.cs b/PrivaPub/Controllers/ClientToServer/RootUserController.cs
index db38e79..a643dc9 100644
--- a/PrivaPub/Controllers/ClientToServer/RootUserController.cs
+++ b/PrivaPub/Controllers/ClientToServer/RootUserController.cs
@@ -92,7 +92,7 @@ namespace PrivaPub.Controllers.ClientToServer
[HttpPost, Route("/clientapi/user/login"), EnableRateLimiting(RateLimiting.Accounts), Authorize(Policy = Policies.IsUser), AllowAnonymous]
public async Task Login(LoginForm loginForm)
{
- if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions");
+ if (User.Identity?.IsAuthenticated ?? false) return Redirect("/");
var result = new WebResult();
if (!ModelState.IsValid)
return BadRequest(result.Invalidate(Localizer["Invalid model."]));
@@ -132,7 +132,7 @@ namespace PrivaPub.Controllers.ClientToServer
if (personaProblem != default)
return BadRequest(result.Invalidate(personaProblem));
- result = await UsersService.SignUpAsync(signUpForm, signUpForm.InvitationCode);
+ result = await UsersService.SignUpAsync(signUpForm);
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
var (user, userSettings) = ((RootUser, ViewUserSettings))result.Data;
@@ -171,7 +171,7 @@ namespace PrivaPub.Controllers.ClientToServer
if (!invitation.IsValid)
return StatusCode(invitation.StatusCode, invitation);
- result = await UsersService.LoginAsync(loginForm, loginForm.InvitationCode);
+ result = await UsersService.LoginAsync(loginForm);
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
var (user, userSettings) = ((RootUser, ViewUserSettings))result.Data;
@@ -370,25 +370,6 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
- //[HttpGet, Authorize(Policy = Policies.IsUser)]
- //public async Task GetUser()
- //{
- // var result = new WebResult();
- // try
- // {
- // result = await UsersService.GetUserAsync(User.GetUserId());
- // if (!result.IsValid)
- // return StatusCode(result.StatusCode, result);
-
- // return Ok(result.Data);
- // }
- // catch (Exception ex)
- // {
- // Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUser)}()");
- // return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
- // }
- //}
-
// what the login's media take, every persona's, and its quota when the server sets one
[HttpGet, Route("/clientapi/user/storage"), Authorize(Policy = Policies.IsUser)]
public async Task GetStorage([FromServices] Microsoft.Extensions.Options.IOptionsMonitor media, CancellationToken token)
diff --git a/PrivaPub/Data/InitDb.cs b/PrivaPub/Data/InitDb.cs
index a2e9db2..58ab623 100644
--- a/PrivaPub/Data/InitDb.cs
+++ b/PrivaPub/Data/InitDb.cs
@@ -10,68 +10,12 @@ namespace PrivaPub.Data
{
public static class InitDb
{
- public static async Task Init(this DbEntities dbClient, IPasswordHasher passwordHasher)
+ public static async Task Init(this DbEntities dbClient)
{
await SyncLanguages(dbClient);
await UpdateNewLanguages(dbClient);
- //await SyncUserSettings(dbClient);
- //await ClearUsers(dbClient);
- //var botsInsertionAwaiter = InsertBots(dbClient, passwordHasher);
- //var updateAnswersAwaiter = UpdateAnswersLikes(dbClient);
-
- //Task.WaitAll(botsInsertionAwaiter);
- //await SyncShareDiscussions(dbClient);
}
- //static async Task UpdateAnswersLikes(DbCollections dbClient)
- //{
- // if (await dbClient.AnswersLikes.Find(al => al.AnswerId != null).AnyAsync()) return;
-
- // foreach (var answerLikes in await dbClient.AnswersLikes.Find(al => true).ToListAsync())
- // {
- // answerLikes.AnswerId = answerLikes.Id;
- // answerLikes.Id = ObjectId.GenerateNewId();
- // await dbClient.AnswersLikes.InsertOneAsync(answerLikes);
- // _ = await dbClient.AnswersLikes.DeleteOneAsync(al => al.Id == answerLikes.AnswerId);
- // }
- //}
-
- //static async Task ClearUsers(DbColl dbClient)
- //{
- // await dbClient.Users.DeleteManyAsync(u => true);
- // await dbClient.UsersSettings.DeleteManyAsync(u => true);
- // await dbClient.Answers.DeleteManyAsync(u => true);
- // await dbClient.AnswersLikes.DeleteManyAsync(u => true);
- // await dbClient.Comments.DeleteManyAsync(u => true);
- // await dbClient.EmailRecoveries.DeleteManyAsync(u => true);
- // await dbClient.Threads.DeleteManyAsync(u => true);
- // await dbClient.EDiscussions.DeleteManyAsync(u => true);
- // await dbClient.Logs.DeleteManyAsync(u => true);
- // await dbClient.ShareDiscussions.De(u => true);
- // await dbClient.DiscussionFiles.DeleteManyAsync(u => true);
- // await dbClient.UsersGroups.DeleteManyAsync(u => true);
- //}
-
- // static async Task SyncShareDiscussions(DbColl dbClient)
- // {
- // var anyThread = await dbClient.Discussions.Match(t => true).ExecuteAnyAsync();
- // var anyShareDiscussion = await dbClient.ShareDiscussions.Match(st => true).ExecuteAnyAsync();
- //
- // if (anyThread == anyShareDiscussion) return;
- //
- // var discussions = await dbClient.Discussions.ManyAsync(t => true);
- //
- // foreach (var discussion in discussions)
- // {
- // var newShareDiscussion = new ShareDiscussion
- // {
- // DiscussionId = discussion.ID,
- // InvitationCode = $"{Guid.NewGuid():N}{Guid.NewGuid():N}"
- // };
- // await newShareDiscussion.SaveAsync();
- // }
- // }
-
static async Task SyncLanguages(DbEntities dbClient)
{
if (await dbClient.Languages.ExecuteAnyAsync()) return;
@@ -157,57 +101,5 @@ namespace PrivaPub.Data
await DB.Default.SaveAsync(bgLang);
}
}
-
- // static async Task SyncUserSettings(DbColl dbClient)
- // {
- // if (!await dbClient.Users.ExecuteAnyAsync()) return;
- //
- // var users = await dbClient.Users.ManyAsync(u => true);
- // var usersId = users.Select(u => u.ID).ToList();
- // var usersSettings = await dbClient.UsersSettings
- // .ManyAsync(us => true);
- //
- // var usersWithoutSettings = users.Where(u => !usersSettings.Any(us => us.UserId == u.ID)).Select(u => u.ID).ToList();
- // if (usersWithoutSettings.Count == 0) return;
- //
- // var defaultLanguage = await dbClient.Languages
- // .Match(l => l.International2Code == "en")
- // .ExecuteFirstAsync();
- // foreach (var userId in usersWithoutSettings)
- // await DB.SaveAsync(new UserSettings
- // {
- // UserId = userId,
- // LanguageId = defaultLanguage.International2Code
- // });
- // }
-
- //static async Task InsertBots(DbEntities dbClient, IPasswordHasher passwordHasher)
- //{
- // if (await dbClient.Users.Match(u => u.UserName == "bot0").ExecuteAnyAsync()) return;
-
- // var newBots = new List();
- // var newBotsUserSettings = new List();
-
- // for (int i = 0; i < 100; i++)
- // newBots.Add(new User
- // {
- // HashedPassword = passwordHasher.Hash("Asdfmov13!!!"),
- // UserName = $"bot{i}"
- // });
- // await newBots.SaveAsync();
-
- // var defaultLanguage = await dbClient.Languages
- // .Match(l => l.International2Code == "en")
- // .ExecuteFirstAsync();
-
- // foreach (var newBot in newBots)
- // newBotsUserSettings.Add(new()
- // {
- // UserId = newBot.ID,
- // LanguageId = defaultLanguage.International2Code,
- // });
-
- // await newBotsUserSettings.SaveAsync();
- //}
}
}
\ No newline at end of file
diff --git a/PrivaPub/Extensions/Extensions.cs b/PrivaPub/Extensions/Extensions.cs
index 515dd80..de1e631 100644
--- a/PrivaPub/Extensions/Extensions.cs
+++ b/PrivaPub/Extensions/Extensions.cs
@@ -1,11 +1,7 @@
using Microsoft.AspNetCore.Authorization;
-
using PrivaPub.ClientModels;
-using PrivaPub.Models.User;
-using System.Security.Claims;
-
namespace PrivaPub.Extensions
{
public static class Extensions
@@ -27,22 +23,5 @@ namespace PrivaPub.Extensions
.RequireClaim(Policies.IsUser, true.ToString().ToLower())
.RequireClaim(Policies.IsModerator, true.ToString().ToLower())
.Build();
-
- public static string GetHostWithPath(this HttpContext httpContext) =>
- $"https://{httpContext.Request.Host}{httpContext.Request.Path}";
-
- public static string GetHost(this HttpContext httpContext) =>
- $"https://{httpContext.Request.Host}";
-
- public static UserPolicyType GetHighestPolicy(this ClaimsPrincipal claimsPrincipal)
- {
- if (claimsPrincipal.FindFirstValue(Policies.IsAdmin) == "true")
- return UserPolicyType.IsAdmin;
-
- if (claimsPrincipal.FindFirstValue(Policies.IsModerator) == "true")
- return UserPolicyType.IsModerator;
-
- return UserPolicyType.IsUser;
- }
}
}
diff --git a/PrivaPub/Extensions/StringExtensions.cs b/PrivaPub/Extensions/StringExtensions.cs
index eb26015..362ce0f 100644
--- a/PrivaPub/Extensions/StringExtensions.cs
+++ b/PrivaPub/Extensions/StringExtensions.cs
@@ -1,6 +1,4 @@
-using PrivaPub.ClientModels;
-
-using System.Security.Claims;
+using System.Security.Claims;
namespace PrivaPub.Extensions
{
@@ -10,27 +8,5 @@ namespace PrivaPub.Extensions
{
return claimsPrincipal?.Claims?.FirstOrDefault(c => c.Type == ClaimTypes.UserData)?.Value;
}
-
- public static string GetUserName(this ClaimsPrincipal claimsPrincipal)
- {
- var userId = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
- return string.IsNullOrEmpty(userId) ? default : userId;
- }
-
- public static List GetUserPolicies(this ClaimsPrincipal claimsPrincipal)
- {
- var policies = new List();
-
- if (claimsPrincipal.Claims.Any(c => c.Type == Policies.IsAdmin && bool.Parse(c.Value)))
- policies.Add(Policies.IsAdmin);
-
- if (claimsPrincipal.Claims.Any(c => c.Type == Policies.IsUser && bool.Parse(c.Value)))
- policies.Add(Policies.IsUser);
-
- if (claimsPrincipal.Claims.Any(c => c.Type == Policies.IsModerator && bool.Parse(c.Value)))
- policies.Add(Policies.IsModerator);
-
- return policies;
- }
}
}
diff --git a/PrivaPub/Infrastructure/Backup/ProtectiveMerge.cs b/PrivaPub/Infrastructure/Backup/ProtectiveMerge.cs
index a7d0a67..2ff2395 100644
--- a/PrivaPub/Infrastructure/Backup/ProtectiveMerge.cs
+++ b/PrivaPub/Infrastructure/Backup/ProtectiveMerge.cs
@@ -75,7 +75,7 @@ namespace PrivaPub.Infrastructure.Backup
continue;
}
await roots.UpdateOneAsync(Id(root["_id"]), Copy(root, "HashedPassword", "Email", "IsEmailValidated", "IsBanned", "Policies",
- "CredentialsChangedAt", "ResetPasswordToken", "ResetPasswordTokenSentAt"), cancellationToken: token);
+ "CredentialsChangedAt"), cancellationToken: token);
}
var avatars = database.GetCollection("Avatar");
diff --git a/PrivaPub/Infrastructure/Data/Indexes.cs b/PrivaPub/Infrastructure/Data/Indexes.cs
index cb615c4..0531d1b 100644
--- a/PrivaPub/Infrastructure/Data/Indexes.cs
+++ b/PrivaPub/Infrastructure/Data/Indexes.cs
@@ -49,9 +49,6 @@ namespace PrivaPub.Infrastructure.Data
await Plain(token, m => m.ProfileOfAvatarId);
await Plain(token, m => m.TrashedAt);
- await Unique(p => p.ObjectURI, Builders.Filter.Gt(p => p.ObjectURI, ""), token);
- await Plain(token, p => p.GroupId, p => p.ID);
-
await Unique(a => a.ActorURI, Builders.Filter.Gt(a => a.ActorURI, ""), token);
await Plain(token, a => a.PublicKeyId);
diff --git a/PrivaPub/Middleware/SocialPubConfigurations.cs b/PrivaPub/Middleware/SocialPubConfigurations.cs
index 9ce248e..3061dc3 100644
--- a/PrivaPub/Middleware/SocialPubConfigurations.cs
+++ b/PrivaPub/Middleware/SocialPubConfigurations.cs
@@ -44,13 +44,6 @@ namespace PrivaPub.Middleware
.Configure(configuration.GetSection(nameof(AppConfiguration)))
.Configure(configuration.GetSection("Registrations"));
}
- public static IServiceCollection PrivaPubWorkersConfiguration(this IServiceCollection service)
- {
- return service;
- //.AddHostedService()
- //.AddHostedService()
- //.AddHostedService();
- }
public static IServiceCollection PrivaPubFederationConfiguration(this IServiceCollection service, IConfiguration configuration)
{
service.Configure(configuration.GetSection("Federation"));
@@ -228,7 +221,6 @@ namespace PrivaPub.Middleware
.AddTransient()
.AddScoped()
.AddScoped()
- .AddTransient()
.AddTransient()
.AddTransient()
.AddTransient()
@@ -240,8 +232,7 @@ namespace PrivaPub.Middleware
.AddTransient()
.AddSingleton()
.AddHttpContextAccessor()
- .AddMemoryCache()
- .AddSingleton();
+ .AddMemoryCache();
}
public static IServiceCollection PrivaPubMiddlewareConfiguration(this IServiceCollection service)
diff --git a/PrivaPub/Models/AppConfiguration.cs b/PrivaPub/Models/AppConfiguration.cs
index 3b42b6d..8b950fd 100644
--- a/PrivaPub/Models/AppConfiguration.cs
+++ b/PrivaPub/Models/AppConfiguration.cs
@@ -7,11 +7,6 @@ namespace PrivaPub.Models
{
public class AppConfiguration : Entity
{
- public bool RequiresFirstTimeSetup { get; set; } = true;
- public string Version { get; set; }
- public int MaxAllowedUploadFiles { get; set; }
- public int MaxAllowedFileSize { get; set; }
- public string ValidDiscussionFilesTypes { get; set; }
public List SupportedLanguages { get; set; } = new();
public string BackendBaseAddress { get; set; }
public string FrontendBaseAddress { get; set; }
diff --git a/PrivaPub/Models/MongoSettings.cs b/PrivaPub/Models/MongoSettings.cs
index 826f65c..e8bf4eb 100644
--- a/PrivaPub/Models/MongoSettings.cs
+++ b/PrivaPub/Models/MongoSettings.cs
@@ -3,7 +3,6 @@
public class MongoSettings
{
public string Database { get; set; }
- public string LogsDatabase { get; set; }
public string ConnectionString { get; set; }
}
}
\ No newline at end of file
diff --git a/PrivaPub/Models/User/Avatar.cs b/PrivaPub/Models/User/Avatar.cs
index fd92ffa..9d4ae15 100644
--- a/PrivaPub/Models/User/Avatar.cs
+++ b/PrivaPub/Models/User/Avatar.cs
@@ -29,7 +29,6 @@ namespace PrivaPub.Models.User
public string PictureURL { get; set; }//icon
public string ThumbnailURL { get; set; }//image
public AvatarType AvatarType { get; set; } = AvatarType.Person;
- public AvatarServer AvatarServer { get; set; } = AvatarServer.PrivaPub;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
diff --git a/PrivaPub/Models/User/EmailRecovery.cs b/PrivaPub/Models/User/EmailRecovery.cs
index 7b1cd2e..9201c97 100644
--- a/PrivaPub/Models/User/EmailRecovery.cs
+++ b/PrivaPub/Models/User/EmailRecovery.cs
@@ -5,10 +5,7 @@ namespace PrivaPub.Models.User
public class EmailRecovery : Entity
{
public string RootUserId { get; set; }
- public string RecoveryCode { get; set; }//plaintext codes before 2026-10-04; migration _011 removed them, never written now
public string CodeHash { get; set; }//sha-256 of the code, hex; the code itself exists only in the email
public DateTime ExpiresAt { get; set; }
- public string RequestIP { get; set; }
- public DateTime CreationDate { get; set; } = DateTime.UtcNow;
}
}
\ No newline at end of file
diff --git a/PrivaPub/Models/User/RootUser.cs b/PrivaPub/Models/User/RootUser.cs
index 9a6e377..b989add 100644
--- a/PrivaPub/Models/User/RootUser.cs
+++ b/PrivaPub/Models/User/RootUser.cs
@@ -14,12 +14,8 @@ namespace PrivaPub.Models.User
public bool IsBanned { get; set; } = false;
public string HashedPassword { get; set; }
public List Policies { get; set; } = new() { ClientModels.Policies.IsUser };
- public List Contacts { get; set; } = new();
public RootUserSettings Settings { get; set; } = new();
- public string ResetPasswordToken { get; set; }
- public DateTime? ResetPasswordTokenSentAt { get; set; }
-
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime? DeletedAt { get; set; }
@@ -38,14 +34,6 @@ namespace PrivaPub.Models.User
}
}
- public class ContactItem
- {
- public string Type { get; set; }
- public string Contact { get; set; }
- public string Note { get; set; }
- public DateTime CreationDate { get; set; } = DateTime.UtcNow;
- }
-
public class RootUserSettings
{
public string LanguageCode { get; set; } = "en-GB";
diff --git a/PrivaPub/Models/User/UserPolicyType.cs b/PrivaPub/Models/User/UserPolicyType.cs
deleted file mode 100644
index dcb8dea..0000000
--- a/PrivaPub/Models/User/UserPolicyType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace PrivaPub.Models.User
-{
- public enum UserPolicyType
- {
- IsUser,
- IsModerator,
- IsAdmin
- }
-}
diff --git a/PrivaPub/PrivaPub.csproj b/PrivaPub/PrivaPub.csproj
index 61c2b00..59d71ad 100644
--- a/PrivaPub/PrivaPub.csproj
+++ b/PrivaPub/PrivaPub.csproj
@@ -19,7 +19,6 @@
-
diff --git a/PrivaPub/Program.cs b/PrivaPub/Program.cs
index 7d3fe81..1a6d81a 100644
--- a/PrivaPub/Program.cs
+++ b/PrivaPub/Program.cs
@@ -32,12 +32,7 @@ try
{
if (builder.Environment.IsProduction())
{
- serverOptions.ListenLocalhost(6970
- //, options =>
- //{
- // options.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
- //}
- );
+ serverOptions.ListenLocalhost(6970);
serverOptions.UseSystemd();
serverOptions.AddServerHeader = false;
}
@@ -50,7 +45,6 @@ try
try
{
builder.Services.PrivaPubAppSettingsConfiguration(builder.Configuration)
- .PrivaPubWorkersConfiguration()
.PrivaPubAuthServicesConfiguration(builder.Configuration)
.PrivaPubInternalizationConfiguration(builder.Configuration)
.PrivaPubOptimizationConfiguration()
@@ -206,9 +200,6 @@ try
app.UseAuthentication();
app.UseAuthorization();
- //app.UseWhen(context => context.Request.Path.StartsWithSegments("/peasants") ||
- // context.Request.Path.StartsWithSegments("/users"),
- // app => app.UseSignatureVerification().UseDigestVerification());
app.MapGet("/build.json", () => Results.Json(new
{
@@ -218,7 +209,6 @@ try
}));
app.MapControllers();
app.MapRazorPages();
- //app.MapFallbackToFile("index.html");
}
catch (Exception ex)
{
@@ -226,12 +216,11 @@ try
throw;
}
- Log.ForContext().Information($"Starting collAnon at {nameof(Program)}()");
+ Log.ForContext().Information($"Starting PrivaPub at {nameof(Program)}()");
try
{
var dbClient = app.Services.GetService(typeof(DbEntities)) as DbEntities;
- var passwordHasher = app.Services.GetService(typeof(IPasswordHasher)) as IPasswordHasher;
- await dbClient.Init(passwordHasher);
+ await dbClient.Init();
await app.Services.GetRequiredService().Reload(CancellationToken.None);
await PrivaPub.Api.Mastodon.Auth.PersonaExchange.EnsureFirstPartyClient(app.Services, CancellationToken.None);
}
diff --git a/PrivaPub/Services/ClientToServer/Public/DataService.cs b/PrivaPub/Services/ClientToServer/Public/DataService.cs
index 82ec815..2537932 100644
--- a/PrivaPub/Services/ClientToServer/Public/DataService.cs
+++ b/PrivaPub/Services/ClientToServer/Public/DataService.cs
@@ -14,8 +14,9 @@ namespace PrivaPub.Services.ClientToServer.Public
AppConfigurationService = appConfigurationService;
}
+ // the build's ref (its tag, or the branch a manual deploy ran from), as /build.json and NodeInfo name it
public string GetCurrentVersion() =>
- AppConfigurationService.AppConfiguration.Version;
+ string.IsNullOrEmpty(BuildInfo.Ref) ? BuildInfo.Commit : BuildInfo.Ref;
public async Task> GetLanguages(CancellationToken cancellationToken)
{
@@ -23,15 +24,5 @@ namespace PrivaPub.Services.ClientToServer.Public
.Match(l => AppConfigurationService.AppConfiguration.SupportedLanguages.Contains(l.International2Code))
.ExecuteAsync(cancellationToken)).OrderBy(l => l.NativeName).ToArray();
}
-
- public async Task IsValidLanguageId(string languageId)
- {
- var anyLanguageWithId = await DbEntities.Languages.Match(l => l.ID == languageId).ExecuteAnyAsync();
- if (!anyLanguageWithId)
- return false;
- var language2Code = (await DbEntities.Languages
- .Match(l => l.ID == languageId).ExecuteFirstAsync())?.International2Code;
- return AppConfigurationService.AppConfiguration.SupportedLanguages.Contains(language2Code);
- }
}
}
\ No newline at end of file
diff --git a/PrivaPub/Services/ClientToServer/Public/IDataService.cs b/PrivaPub/Services/ClientToServer/Public/IDataService.cs
index 0b6ae12..7d40115 100644
--- a/PrivaPub/Services/ClientToServer/Public/IDataService.cs
+++ b/PrivaPub/Services/ClientToServer/Public/IDataService.cs
@@ -7,7 +7,5 @@ namespace PrivaPub.Services.ClientToServer.Public
string GetCurrentVersion();
Task> GetLanguages(CancellationToken cancellationToken);
-
- Task IsValidLanguageId(string languageId);
}
}
\ No newline at end of file
diff --git a/PrivaPub/Services/ClientToServer/Public/IPublicAvatarUsersService.cs b/PrivaPub/Services/ClientToServer/Public/IPublicAvatarUsersService.cs
deleted file mode 100644
index 6fa031a..0000000
--- a/PrivaPub/Services/ClientToServer/Public/IPublicAvatarUsersService.cs
+++ /dev/null
@@ -1,174 +0,0 @@
-using Microsoft.Extensions.Localization;
-
-using PrivaPub.ClientModels;
-using PrivaPub.Models.User;
-using PrivaPub.Resources;
-using PrivaPub.StaticServices;
-
-namespace PrivaPub.Services.ClientToServer.Public
-{
- public interface IPublicAvatarUsersService
- {
- Task GetLocalAvatar(UserPolicyType userPolicyType, string actor, CancellationToken token);
- Task> GetLocalAvatars(UserPolicyType userPolicyType, CancellationToken token);
-
- Task GetForeignAvatar(UserPolicyType userPolicyType, string actor, CancellationToken token);
- Task> GetForeignAvatars(UserPolicyType userPolicyType, CancellationToken token);
-
- Task> GetRootAvatars(string rootUserId, CancellationToken token);
- }
-
- public class PublicAvatarUsersService : IPublicAvatarUsersService
- {
- readonly DbEntities _dbEntities;
- readonly IStringLocalizer _localizer;
- readonly ILogger _logger;
-
- public PublicAvatarUsersService(DbEntities dbEntities,
- IStringLocalizer localizer,
- ILogger logger)
- {
- _dbEntities = dbEntities;
- _localizer = localizer;
- _logger = logger;
- }
-
- public async Task GetLocalAvatar(UserPolicyType userPolicyType, string actor, CancellationToken token)
- {
- var result = new WebResult();
- try
- {
- var query = _dbEntities.Avatars;
- switch (userPolicyType)
- {
- case UserPolicyType.IsUser:
- query.Match(a => !a.DeletionAt.HasValue && a.UserName == actor);
- break;
- case UserPolicyType.IsModerator:
- query.Match(a => !a.DeletionAt.HasValue && a.UserName == actor);
- break;
- case UserPolicyType.IsAdmin:
- query.Match(a => a.UserName == actor);
- break;
- }
- var avatar = await query.ExecuteFirstAsync(token);
- if (avatar == default)
- return result.Invalidate(_localizer["User '{0}' not found.", actor], StatusCodes.Status404NotFound);
- result.Data = avatar;
- return result;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(PublicAvatarUsersService)}.{nameof(GetLocalAvatar)}");
- return result.Invalidate(_localizer["Error: {0}", ex.ToString()], exception: ex);
- }
- }
-
- public async Task> GetLocalAvatars(UserPolicyType userPolicyType, CancellationToken token)
- {
- try
- {
- var query = _dbEntities.Avatars;
- switch (userPolicyType)
- {
- case UserPolicyType.IsUser:
- query.Match(a => !a.DeletionAt.HasValue);
- break;
- case UserPolicyType.IsModerator:
- query.Match(a => !a.DeletionAt.HasValue);
- break;
- case UserPolicyType.IsAdmin:
- break;
- }
- var avatars = await query.ExecuteAsync(token);
- return avatars;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(PublicAvatarUsersService)}.{nameof(GetLocalAvatars)}");
- return Enumerable.Empty();
- }
- }
-
- public async Task GetForeignAvatar(UserPolicyType userPolicyType, string actor, CancellationToken token)
- {
- var result = new WebResult();
- try
- {
- var query = _dbEntities.ForeignAvatars;
- switch (userPolicyType)
- {
- case UserPolicyType.IsUser:
- query.Match(a => !a.DeletionAt.HasValue && a.UserName == actor);
- break;
- case UserPolicyType.IsModerator:
- query.Match(a => !a.DeletionAt.HasValue && a.UserName == actor);
- break;
- case UserPolicyType.IsAdmin:
- query.Match(a => a.UserName == actor);
- break;
- }
- var avatar = await query.ExecuteFirstAsync(token);
- if (avatar == default)
- return result.Invalidate(_localizer["User '{0}' not found.", actor], StatusCodes.Status404NotFound);
- result.Data = avatar;
- return result;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(PublicAvatarUsersService)}.{nameof(GetForeignAvatar)}");
- return result.Invalidate(_localizer["Error: {0}", ex.ToString()], exception: ex);
- }
- }
-
- public async Task> GetForeignAvatars(UserPolicyType userPolicyType, CancellationToken token)
- {
- try
- {
- var query = _dbEntities.ForeignAvatars;
- switch (userPolicyType)
- {
- case UserPolicyType.IsUser:
- query.Match(a => !a.DeletionAt.HasValue);
- break;
- case UserPolicyType.IsModerator:
- query.Match(a => !a.DeletionAt.HasValue);
- break;
- case UserPolicyType.IsAdmin:
- break;
- }
- var foreignAvatars = await query.ExecuteAsync(token);
- return foreignAvatars;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(PublicAvatarUsersService)}.{nameof(GetForeignAvatars)}");
- return Enumerable.Empty();
- }
- }
-
- public async Task> GetRootAvatars(string rootUserId, CancellationToken token)
- {
- try
- {
- var rootUser = await _dbEntities.RootUsers.MatchID(rootUserId).ExecuteFirstAsync(token);
- if (rootUser == default)
- return Enumerable.Empty();
- var rootToAvatars = await _dbEntities.RootToAvatars.Match(ra => ra.RootId == rootUserId).ExecuteAsync(token);
- if (rootToAvatars.Count == 0)
- return Enumerable.Empty();
- var avatarIds = rootToAvatars.Select(ra => ra.AvatarId).ToArray();
- var avatars = await _dbEntities.Avatars
- .Match(a => avatarIds.Contains(a.ID))
- .ProjectExcluding(a => a.Settings)
- .ExecuteAsync(token);
- return avatars;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(PublicAvatarUsersService)}.{nameof(GetRootAvatars)}");
- return Enumerable.Empty();
- }
- }
- }
-}
diff --git a/PrivaPub/Services/GroupUsersService.cs b/PrivaPub/Services/GroupUsersService.cs
index 9028423..b9e43c6 100644
--- a/PrivaPub/Services/GroupUsersService.cs
+++ b/PrivaPub/Services/GroupUsersService.cs
@@ -18,7 +18,6 @@ namespace PrivaPub.Services
{
public interface IGroupUsersService
{
- Task GetGroup(string actor, CancellationToken token);
Task GetGroups(string rootUserId, string avatarId, CancellationToken token);
Task InsertGroup(string rootUserId, InsertGroupForm form, CancellationToken token);
Task UpdateGroup(string rootUserId, UpdateGroupForm form, CancellationToken token);
@@ -58,27 +57,6 @@ namespace PrivaPub.Services
_logger = logger;
}
- public async Task GetGroup(string actor, CancellationToken token)
- {
- var result = new WebResult();
- try
- {
- var userName = actor?.ToLowerInvariant();
- var group = await _dbEntities.Groups
- .Match(g => g.UserName == userName && !g.DeletionAt.HasValue)
- .ExecuteFirstAsync(token);
- if (group == default)
- return result.Invalidate(_localizer["Group '{0}' not found.", actor], StatusCodes.Status404NotFound);
- result.Data = group;
- return result;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"{nameof(GroupUsersService)}.{nameof(GetGroup)}");
- return result.Invalidate(_localizer["Something went wrong."], exception: ex);
- }
- }
-
public async Task GetGroups(string rootUserId, string avatarId, CancellationToken token)
{
var result = new WebResult();
diff --git a/PrivaPub/Services/IRootUsersService.cs b/PrivaPub/Services/IRootUsersService.cs
index 6fd9b5a..28482bb 100644
--- a/PrivaPub/Services/IRootUsersService.cs
+++ b/PrivaPub/Services/IRootUsersService.cs
@@ -7,9 +7,9 @@ namespace PrivaPub.Services
{
public interface IRootUsersService
{
- Task LoginAsync(LoginForm loginForm, string invitationCode = default, bool isPasswordRequired = false);
+ Task LoginAsync(LoginForm loginForm);
- Task SignUpAsync(LoginForm signUpForm, string invitationCode = default, bool isPasswordRequired = false);
+ Task SignUpAsync(LoginForm signUpForm);
Task BanUserAsync(UsersIds usersIds);
diff --git a/PrivaPub/Services/RootUsersService.cs b/PrivaPub/Services/RootUsersService.cs
index c9f4e2c..f9b55d9 100644
--- a/PrivaPub/Services/RootUsersService.cs
+++ b/PrivaPub/Services/RootUsersService.cs
@@ -1,14 +1,8 @@
-using MailKit.Net.Smtp;
-
using Microsoft.Extensions.Localization;
-using MimeKit;
-
using MongoDB.Driver;
using MongoDB.Entities;
-using PasswordGenerator;
-
using PrivaPub.ClientModels;
using PrivaPub.ClientModels.User;
using PrivaPub.Models;
@@ -33,7 +27,6 @@ namespace PrivaPub.Services
readonly IStringLocalizer Localizer;
readonly ILogger Logger;
readonly AppConfigurationService AppConfigurationService;
- readonly AuthTokenManager AuthTokenManager;
readonly IJobQueue Jobs;
readonly IRootSessions Sessions;
readonly IRootRemoval Removal;
@@ -46,14 +39,12 @@ namespace PrivaPub.Services
ILogger logger,
IPasswordHasher passwordHasher,
DbEntities dbEntities,
- AppConfigurationService appConfigurationService,
- AuthTokenManager authTokenManager)
+ AppConfigurationService appConfigurationService)
{
Jobs = jobs;
Sessions = sessions;
Removal = removal;
DbEntities = dbEntities;
- AuthTokenManager = authTokenManager;
PasswordHasher = passwordHasher;
Localizer = localizer;
Logger = logger;
@@ -66,8 +57,7 @@ namespace PrivaPub.Services
// a hash nobody's password matches, so an unknown login costs the same hashing as a wrong password
string Decoy => _decoy ??= PasswordHasher.Hash(Convert.ToHexString(System.Security.Cryptography.RandomNumberGenerator.GetBytes(16)));
- public async Task SignUpAsync(LoginForm signUpForm, string invitationCode = default,
- bool isPasswordRequired = false)
+ public async Task SignUpAsync(LoginForm signUpForm)
{
var result = new WebResult();
try
@@ -98,23 +88,6 @@ namespace PrivaPub.Services
};
await DB.Default.SaveAsync(newUser);
- //if (!string.IsNullOrEmpty(invitationCode))
- //{
- // if (isPasswordRequired)
- // result = await DiscussionService.InviteUserToDiscussion(new PwDiscussionPreviewForm
- // {
- // InvitationCode = invitationCode,
- // Password = signUpForm.InvitationPassword
- // }, newUser.ID);
- // else
- // result = await DiscussionService.InviteUserToDiscussion(new NoPwDiscussionPreviewForm
- // {
- // InvitationCode = invitationCode,
- // }, newUser.ID);
- // if (!result.IsValid)
- // return result;
- //}
-
result.Data = (newUser, ToViewSettings(newUser.Settings));
return result;
@@ -126,8 +99,7 @@ namespace PrivaPub.Services
}
}
- public async Task LoginAsync(LoginForm loginForm, string invitationCode = default,
- bool isPasswordRequired = false)
+ public async Task LoginAsync(LoginForm loginForm)
{
var result = new WebResult();
try
@@ -148,23 +120,6 @@ namespace PrivaPub.Services
var userSettingsResult = await GetUserSettingsAsync(user.ID, loginForm);
var userSettings = (ViewUserSettings)userSettingsResult.Data;
- //if (!string.IsNullOrEmpty(invitationCode))
- //{
- // if (isPasswordRequired)
- // result = await DiscussionService.InviteUserToDiscussion(new PwDiscussionPreviewForm
- // {
- // InvitationCode = invitationCode,
- // Password = loginForm.InvitationPassword
- // }, user.ID);
- // else
- // result = await DiscussionService.InviteUserToDiscussion(new NoPwDiscussionPreviewForm
- // {
- // InvitationCode = invitationCode,
- // }, user.ID);
- // if (!result.IsValid)
- // return result;
- //}
-
result.Data = (user, userSettings);
return result;
diff --git a/PrivaPub/appsettings.Development.json b/PrivaPub/appsettings.Development.json
index 2cbaca3..1b6da45 100644
--- a/PrivaPub/appsettings.Development.json
+++ b/PrivaPub/appsettings.Development.json
@@ -1,18 +1,12 @@
{
"MongoSettings": {
"Database": "PrivaPub",
- "LogsDatabase": "logs",
"ConnectionString": "mongodb://localhost:27017"
},
"Backups": {
"Nightly": false
},
"AppConfiguration": {
- "RequiresFirstTimeSetup": null,
- "Version": "0.0.0",
- "MaxAllowedUploadFiles": 3,
- "MaxAllowedFileSize": 2097152,
- "ValidDiscussionFilesTypes": ".odt,.docx,.pdf,.xlsx,.ods,.odp,.pptx,.png,.jpg,.jpeg",
"SupportedLanguages": [
"en",
"it",
@@ -83,7 +77,7 @@
"Name": "MongoDBCapped",
"Args": {
"databaseUrl": "mongodb://localhost:27017/logs",
- "collectionName": "collanonquick",
+ "collectionName": "PrivaPub",
"cappedMaxSizeMb": "1024",
"cappedMaxDocuments": "10000"
}
diff --git a/PrivaPub/appsettings.Production.json b/PrivaPub/appsettings.Production.json
index 74c401f..d95b313 100644
--- a/PrivaPub/appsettings.Production.json
+++ b/PrivaPub/appsettings.Production.json
@@ -30,16 +30,11 @@
},
"MongoSettings": {
"Database": "PrivaPub",
- "LogsDatabase": "logs",
"ConnectionString": "mongodb://127.0.0.1:27022/?directConnection=true"
},
"AppConfiguration": {
- "Version": "0.0.0",
"BackendBaseAddress": "https://privapub.thepra.dev",
"FrontendBaseAddress": "https://decepub.thepra.dev",
- "MaxAllowedUploadFiles": 3,
- "MaxAllowedFileSize": 2097152,
- "ValidDiscussionFilesTypes": ".odt,.docx,.pdf,.xlsx,.ods,.odp,.pptx,.png,.jpg,.jpeg",
"SupportedLanguages": [
"en", "it", "de", "fr", "es", "ja", "ru", "zh", "bg", "cs", "da", "nl", "et", "fi", "el", "hu", "lv", "lt", "pl", "pt", "ro", "sk", "sl", "sv"
],
diff --git a/docs/INTEROP.md b/docs/INTEROP.md
index 1d97adc..40a9df5 100644
--- a/docs/INTEROP.md
+++ b/docs/INTEROP.md
@@ -535,7 +535,7 @@ on a Page: pins live in `featured`, locks in `Lock`.
| Gap | P | Client surface |
|---|---|---|
| `Dislike` and its `Undo`: a vote ledger per object (actor, ±1, activity, relaying group, time) | P1 | `favourites_count` = upvotes; own `privapub.vote{score, up, down, mine}`, `POST …/vote` |
-| Announces of activities other than Create (votes, moderation): trust the inner activity when the object's own group signed the Announce; refetching every vote does not scale. Keep the origin refetch for Create and Update. Votes and their undoing **done** 2026-10-04 (`AnnounceHandler.Relayed`: trusted from the community's own server or on its own posts, otherwise fetched from the voter's origin); moderation still open | P1 | — |
+| Announces of activities other than Create (votes, moderation): trust the inner activity when the object's own group signed the Announce; refetching every vote does not scale. Keep the origin refetch for Create and Update. Votes and their undoing **done** 2026-10-04 (`AnnounceHandler.Relayed`: trusted from the community's own server or on its own posts, otherwise fetched from the voter's origin); relayed locks and bans **done** 2026-10-05 (G-0006) | P1 | — |
| Moderation state: removals (reason, by, at, cascade), locks, bans with `endTime`/`removeData`, featured, moderators, `Update{Group}` by a moderator | P1 | removed posts hidden plus `privapub.removed`; `privapub.locked` (replying answers 422); pins as `pinned=true` |
| Link posts: keep `Link.href`, the thumbnail `image` and alt text; build the card (Lemmy sends no title or description for the link) | P1 | `Status.card` |
| `ChatMessage` in and out (out to someone who writes to us that way, and to Lemmy 0.19 and Mbin by NodeInfo, done 2026-10-05) | P1 | `visibility: direct` |
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index 8bbc0f0..2aa45af 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -9,10 +9,10 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
- [x] P1.2 Social graph and timelines: v1.3.0, deployed and verified 2026-10-01
- [x] P2 Mastodon client API: v1.4.0, deployed 2026-10-01; OAuth and the anonymous API verified on production, the signed-in API verified locally (no real-client login on production yet)
- [x] P3 Social features: v1.5.0, deployed 2026-10-01; media, proxy, blocks, mutes, bookmarks, pins and reports verified by tests and locally (no upload on production yet)
-- [x] P4 Groups and privacy features: v1.6.0, deployed 2026-10-01; communities, circles and local-only located posts verified by tests. v1.6.1 adds the pasture (`tools/pasture/`): live interop with GoToSocial 0.22.1 passes all 25 checks, three runs in a row. Lemmy and a live Mastodon circle member are not run yet; the pasture has GoToSocial only
+- [x] P4 Groups and privacy features: v1.6.0, deployed 2026-10-01; communities, circles and local-only located posts verified by tests. v1.6.1 adds the pasture (`tools/pasture/`): live interop with GoToSocial 0.22.1 passes all 25 checks, three runs in a row. Lemmy and a live Mastodon circle member are not run yet; the pasture has GoToSocial only (both run since: T/M and v1.19.1)
- [x] P5 Lose nothing: v1.7.0 to v1.9.1, deployed 2026-10-01; parsing, typed details, provenance, downvotes, tombstones, federated blocks, all checked live against GoToSocial. Book reviews and forum threads keep their raw form only (typed in P7 and P8)
- [x] P6 Emoji, polls, quotes, reactions, cards, players: v1.10.0 to v1.15.0, deployed 2026-10-01; polls, link cards, ranged video streaming and quote policies checked live against GoToSocial
-- [ ] T/M Test sweep, the full pasture and the interaction ledger (see "Sweep and statistics" below). T1–T4: v1.15.1, deployed and verified 2026-10-03 (CI runs all tests on a throwaway mongod; nothing answers 500). M1–M6: v1.16.0, deployed and verified 2026-10-03 (the interaction ledger records every inbox answer, handler verdict, delivery attempt and outbound request; the GoToSocial pasture passes 33/33 with no names in the statistics). T5–T9, T11 and M7–M11: v1.17.0, 2026-10-03:
+- [x] T/M Test sweep, the full pasture and the interaction ledger (see "Sweep and statistics" below). T1–T4: v1.15.1, deployed and verified 2026-10-03 (CI runs all tests on a throwaway mongod; nothing answers 500). M1–M6: v1.16.0, deployed and verified 2026-10-03 (the interaction ledger records every inbox answer, handler verdict, delivery attempt and outbound request; the GoToSocial pasture passes 33/33 with no names in the statistics). T5–T9, T11 and M7–M11: v1.17.0, 2026-10-03:
- 639 tests, now over HTTP too;
- daily rollups, every touched server described and located, the admin statistics API, the opt-in crawler and `/stargazing`;
- the pasture as plugins: GoToSocial 37/37, Mastodon 49 plus 2 expected failures.
@@ -30,7 +30,7 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
- Akkoma's open polls showed as ended and refused votes.
Both open items were closed without a root step (owner decisions, 2026-10-04): the server fetches its geolocation
- databases itself, and the deploy makes and signs in as @thepra.
+ databases itself, and the deploy makes and signs in as @thepra (that signed-in check was dropped on 2026-10-17).
- [x] Everything on in production (owner decisions 2026-10-04):
- v1.18.0, deployed and verified 2026-10-04: geolocation that updates itself (DB-IP Lite 2026-10 loaded), the deploy
signing in as @thepra (undiscoverable), the crawler on (1010 servers known within the hour), sign-up by invitation
@@ -55,7 +55,8 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
gap it finds fixed. Runs alongside P7, P8 and P9. Done so far:
- the town toolkit (`tools/pasture/town/`): drivers for PrivaPub and the six peers, the seeded `village` (23 accounts,
205 objects, 970 steps), the coherence sweep with known gaps (`gaps.json`), the report and `docs/INTEROP-BACKLOG.md`;
- - decePubClient's end-to-end tests against the pasture (`decePubClient/tests/e2e`, Playwright for .NET);
+ - decePubClient's end-to-end tests against the pasture (`decePubClient/tests/e2e`, Playwright for .NET; removed with the
+ test projects on 2026-10-17);
- three bugs the first village found, fixed: likes, reactions, votes and downvotes from followers on followers-only
posts and from circle members were dropped (G-0001); a Follow refused with Mastodon's first-contact 422 was lost for
good; a persona addressed without being named could not see the post (silent mentions).
@@ -108,6 +109,14 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
- [x] Status search by words, scoped as Mastodon scopes it: what the persona wrote, boosted, favourited, bookmarked or
was named in, and public posts of authors who let themselves be indexed (off by default); newest first, through a
text index over the posts' words in every language alike.
+- [x] First-time setup and invitations (2026-10-16/17): PrivaPub v1.25.0, the first login made from the client while no
+ administrator exists, then setup sealed for good; decePub v1.6.0, `/setup` and `/join?code=`; PrivaPub v1.26.0,
+ `admin move-persona` and `admin delete-root`, and the deploy no longer signs in (@thepra is the administrator's). The
+ test projects were deleted the same day.
+- [ ] C The completion program (owner decisions 2026-10-17, below): everything unfinished, in sixteen releases. Cleanup;
+ accounts (email, recovery, sessions, TOTP); the client catching up; the server's own settings; moderation; custom
+ emoji; the Mastodon API made real; Web Push; P7's communities; P8's signatures; the long tail with HEIC/HEIF;
+ hosting events; town drivers for every peer; public `/stargazing` and Bluesky bridging; the close-out.
## Intent
@@ -244,7 +253,7 @@ and circles (see Owner decisions).
| Question | Decision |
|---|---|
-| What runs in production | **Everything that is built is on and checked by the deploy**, and nothing waits on a person running a command. |
+| What runs in production | **Everything that is built is on and checked by the deploy's anonymous checks** (nothing signs in since 2026-10-17), and nothing waits on a person running a command. |
| Geolocation | **The server fetches DB-IP Lite itself** (`GeoUpdater`): it checks daily, installs a new month's databases once they are published, refuses a file that does not open as the right kind of database, and keeps the old one when anything fails. No timer and no root step. The deploy fails if the databases are missing or more than 40 days old. |
| Crawler | **On in production**, seeded with a handful of large servers of different kinds (`appsettings.Production.json`); the deploy fails if `/stargazing` does not say it is on. |
| Sign-up | **Closed: invitations only.** A group invitation creates an account; open sign-up answers 403. NodeInfo, `/api/v1/instance` and `/api/v2/instance` read the same switch (`Registrations:Mode`), and the deploy fails if they disagree. The first login on a server is made from the client's setup page while no administrator exists (2026-10-16: first come, first served, no setup code); it becomes the administrator with its first persona, and setup closes for good (`ServerSetup`, kept across restores). `PrivaPub admin create-root` remains the way in from the box. |
@@ -253,7 +262,7 @@ and circles (see Owner decisions).
| Signed fetches (SecureMode) | **On in production** once the pasture passes with it on (Phase 2 of the 2026-10-04 plan). |
| Circles on Mastodon and GoToSocial | **Each member's copy names that member** in `cc`; a member's refetch names the member, an instance actor's refetch the members on its server. Nothing new is revealed to anyone outside the circle. |
| What circles and located posts reveal | **Unchanged**: circles still answer WebFinger, and circle and located posts still count in a persona's post count, "a good balance for the fediverse to work". |
-| Public `/stargazing` statistics | **Later**, as decided on 2026-10-03; the crawler and the admin API keep collecting meanwhile. |
+| Public `/stargazing` statistics | **Approved 2026-10-17** (C, release R15), built from public counters only. Until then the crawler and the admin API keep collecting. |
### Owner decisions on decePubClient (2026-10-04)
@@ -269,10 +278,10 @@ and circles (see Owner decisions).
| Question | Decision |
|---|---|
| Scope | **Every self-hostable fediverse platform** in the pasture, accounts of every kind, a fake community across them, and coherence of state between all of them and PrivaPub, checked from both the servers and decePubClient. What the town finds is fixed until PrivaPub is the best federating server and decePubClient the best client for the fediverse at volume. |
-| The "Cut" list | **Lists, server-side filters, scheduled posts, followed hashtags, trends and a directory come back** (P9). The directory lists only discoverable personas; trends count only public posts PrivaPub already holds. Translation, local custom emoji, the Mastodon admin API, API registration, S3 and JSON-LD processing stay cut. |
-| decePubClient tests | **Playwright for .NET**, against the pasture, never in CI. |
-| How work lands | Commits on master in both repositories; nothing is pushed, tagged or deployed without asking. |
-| Still asked first | Anything new that tells another server something about a persona, Web Push (traffic to push services), any admin endpoint in production, and anything acting from production or the owner's accounts. |
+| The "Cut" list | **Lists, server-side filters, scheduled posts, followed hashtags, trends and a directory come back** (P9). The directory lists only discoverable personas; trends count only public posts PrivaPub already holds. Translation, local custom emoji, the Mastodon admin API, API registration, S3 and JSON-LD processing stay cut. *Local custom emoji came back on 2026-10-17 (C, R7).* |
+| decePubClient tests | **Removed 2026-10-17.** They were Playwright for .NET against the pasture, never in CI. |
+| How work lands | Commits on master in both repositories; nothing is pushed, tagged or deployed without asking. *For the completion program (2026-10-17): each release is pushed to master, and the owner tags.* |
+| Still asked first | Anything new that tells another server something about a persona, Web Push (traffic to push services), any admin endpoint in production, and anything acting from production or the owner's accounts. *2026-10-17: Web Push, admin endpoints in production, public statistics and per-persona Bluesky bridging are approved; the rest still asks.* |
### Owner decisions on events and threads (2026-10-05)
@@ -321,6 +330,26 @@ directory; the nginx locations), then v1.23.0, whose own pre-deploy backup was v
which held statistics salts, were deleted (owner's go-ahead); a salt-free dump taken just before the replica-set
conversion is kept in `/var/backups/privapub.thepra.dev/`.
+### Owner decisions on finishing everything (2026-10-17)
+
+The owner found decePubClient's Administration page mostly placeholders and asked for every unfinished thing, there
+and elsewhere, to be finished end to end. The plan is sixteen releases (Status, C): R1 cleanup, R2 accounts, R3 TOTP,
+R4 the client catching up, R5 the server's own settings, R6 moderation, R7 custom emoji, R8 the Mastodon API, R9 Web
+Push, R10 communities (P7), R11 signatures (P8), R12 the long tail, R13 hosting events, R14 town drivers, R15 public
+statistics and Bluesky bridging, R16 the close-out.
+
+| Question | Decision |
+|---|---|
+| Scope | **Everything, in waves**: the product (administration, accounts, the client, the Mastodon API, Web Push), then P7 and P8, then the long tail. |
+| Administration | **Users and personas, instance and rules, relays and media limits, custom emoji** become real. The other Pleroma-era placeholders (Authentication, Frontend, Link formatter) go. |
+| Secrets | **Stay committed**, by the existing convention; only code-level security is fixed. |
+| Approved | **Web Push, the public `/stargazing` statistics, per-persona Bluesky bridging, email recovery in the client, local custom emoji, admin endpoints in production.** |
+| Built after all | **Hosting events, HEIC/HEIF uploads, town drivers for every peer.** Discourse, NeoDB and Loops peers stay out. |
+| Two-factor | **TOTP, right after email recovery** (R3). |
+| A suspended local persona | **Tells other servers nothing new**: its posts and collections answer 404 and nothing is delivered from it; the actor stays as it is, so lifting the suspension is clean. |
+| Releases | **Pushed to master, not tagged**: the owner tags when they choose. |
+| Cut | FASP; rewriting HLS playlists; `Announce(object)` per peer; cross-post detection, PieFed's genAI flag, accepted answers and batched Announces; Ed25519 inbound RFC 9421; typed book reviews; the `Api/ClientApi` folder move; Pleroma `Listen` and `acceptsChatMessages` out; Owncast live state; a dislikes collection; Lemmy's `removeData`; translation; the Mastodon admin API; `POST /api/v1/accounts`; S3. |
+
## Libraries (researched; no maintained .NET ActivityPub library exists, so Letterbook and Iceshrimp.NET both wrote their own)
| Area | Choice |
@@ -332,14 +361,14 @@ conversion is kept in `/var/backups/privapub.thepra.dev/`.
| Media | **NetVips** (+NetVips.Native) for images: autorotate, strip EXIF/GPS, thumbnails. **Blurhash.Core**, **FFMpegCore** for video. Own `IMediaStore` (local disk now). Not ImageSharp 4, whose license key is enforced at build. |
| OAuth | **OpenIddict 7.7 + OpenIddict.MongoDb**: authorization code with PKCE, client_credentials, `oob` redirect, dynamic apps from `POST /api/v1/apps`, reference tokens |
| Jobs | Own Mongo job collection for both inbox processing and delivery: `FindOneAndUpdate` leases, a `Channel` wake-up, per-host circuit breaker, Mastodon's backoff (`n⁴+15+jitter`, 16 tries). No MassTransit (commercial from v9, no Mongo transport). |
-| Tests | New xUnit project with fixture JSON captured from Mastodon, GoToSocial, Misskey, Lemmy and Akkoma; interop checked with Fediverse Pasture, verify.funfedi.dev and activitypub.academy |
+| Tests | *Removed 2026-10-17 (owner decision): there is no test project.* Interop is checked with the pasture, verify.funfedi.dev and activitypub.academy |
**FEPs, now:** FEP-f1d5 (NodeInfo 2.0 and 2.1), FEP-67ff (FEDERATION.md), FEP-8fcf, FEP-5feb `indexable`,
FEP-7628 Move, FEP-2c59, FEP-044f quotes (parse, plus `interactionPolicy`), FEP-1b12 groups.
-**FEPs, later:** FEP-521a, FEP-8b32, FEP-e232, relays (FEP-ae0c).
+**FEPs, later:** FEP-521a, FEP-8b32, FEP-e232, relays (FEP-ae0c); all done since.
-**FEPs, skipped:** FEP-c390, FEP-ef61, FEP-fb2a, FEP-844e.
+**FEPs, skipped:** FEP-c390, FEP-ef61, FEP-fb2a. (FEP-844e was implemented after all, 2026-10-06.)
## Route names (deliberate; part of the project's character)
@@ -434,9 +463,10 @@ The first refactor commit is a pure move with namespaces only. Logic changes fol
- Plain: `PublicKeyId`, `(GroupUserId, _id)`, `(GroupId, _id)`.
- **Rate limiting** on login, signup, invitation and inbox (per key host).
- **Cleanup:** delete `Services/ActivityPubClient.cs` and `Models/Post/PostBoost.cs`.
-- **New `PrivaPub.Tests`** (xUnit v3): IpRangeGuard table, signature fixtures (a real Mastodon request plus tampering
+- **New `PrivaPub.Tests`** (xUnit v3; removed 2026-10-17): IpRangeGuard table, signature fixtures (a real Mastodon request plus tampering
cases), key-poisoning, origin rules, XSS corpus, DM injection.
-- **CI:** `build.yml` runs the tests. `deploy.yml` runs the tests and a `mongodump` before swapping.
+- **CI:** `build.yml` runs the tests. `deploy.yml` runs the tests and a `mongodump` before swapping. *Since then the server's
+ own pre-deploy backup replaced the mongodump, and on 2026-10-17 the tests went.*
### P1 Interop foundations (two deploys)
@@ -573,7 +603,7 @@ The first refactor commit is a pure move with namespaces only. Logic changes fol
- **Bookmarks and pins:** pins are served as the `featured` collection at `/peasants/{name}/trophies`, with featured
tags at `/tattoos`.
- **Blocks and mutes:** per avatar. **Block is not federated**: it sends Reject or Undo Follow instead and drops the
- blocked actor's traffic. Domain blocks per account as well.
+ blocked actor's traffic. *Superseded: blocks federate (owner decision, "Blocks").* Domain blocks per account as well.
- **Reports:** inbound Flag becomes a `Report`. Outbound Flag is **sent by the instance actor**, or for a post in a
community on Lemmy, PieFed or Mbin by the reporter `privapub_reports` (2026-10-06), so the reporting persona isn't
revealed. Moderator endpoints on `/clientapi`.
@@ -759,8 +789,8 @@ it, raw where it doesn't.
- `implements` (FEP-844e): **done 2026-10-06** (RFC 9421, on the instance actor and as every actor's `generator`).
- **Mastodon API:** ~~streaming WebSocket~~ (done 2026-10-05: `/api/v1/streaming` as a WebSocket and as server-sent
events, user, notification, public, hashtag and list streams, each event mapped for its persona; a deletion reaches
- only the streams that showed the post), Web Push (gated: outbound traffic to push services), grouped notifications.
- Then advertise an honest version.
+ only the streams that showed the post), Web Push (gated: outbound traffic to push services; approved 2026-10-17, C R9), grouped
+ notifications. Then advertise an honest version.
- **Backfill:** an author's outbox after following them: **done 2026-10-05** (`OutboxBackfill`, the first page's
public posts, twenty at most, once a day; checked live against Mastodon).
- **Long tail:**
@@ -793,25 +823,27 @@ buckets and only for servers reporting at least 10 users; the owner never intend
### Cut or deferred (deliberately)
- **Cut:**
- Link-preview cards *fetched from the linked page* by default. Cards built from the object, or from FEP-8967
- `preview` data, are in P6; fetching the page is an owner decision (INTEROP §6.1).
+ `preview` data, are in P6; fetching the page is an owner decision (INTEROP §6.1). *The owner decided to fetch ("Link
+ previews"); shipped in P6.*
- Translation. (Trends, the directory, lists, filters and scheduled posts were cut too, until the owner brought them
back on 2026-10-04: P9.)
- The Mastodon admin API: moderation stays on `/clientapi`.
- `POST /api/v1/accounts` registration: avatars are created through `/clientapi`.
- - Local custom emoji.
+ - Local custom emoji. *Back on 2026-10-17 (C, R7).*
- S3 storage.
- JSON-LD and LD-signature processing. FEP-8b32 proofs need neither (JCS), so they are in P8.
-- **Deferred:** video transcoding (remux only for now).
+- **Deferred:** video transcoding (remux only for now). *Done since: video is transcoded (CLAUDE.md, Media invariants).*
## Verification (per phase)
-- **Unit tests** (`PrivaPub.Tests`) on every phase. Fixture JSON is captured per peer into
+- **Unit tests** (`PrivaPub.Tests`) on every phase. *Removed 2026-10-17: no test project remains.* Fixture JSON is captured per peer into
`Fixtures/{mastodon,gotosocial,misskey,akkoma,lemmy}/`. Renderers are checked against golden JSON. Inbound
scenarios run as integration tests on a throwaway mongod.
- **Interop:** **Fediverse Pasture** runs on the workstation with podman: Mastodon, GoToSocial, Misskey/Sharkey,
Akkoma, plus Lemmy for P4. As built, `tools/pasture/run.sh` runs PrivaPub and GoToSocial behind one Caddy on a
podman network, with `AllowPrivateNetworks`, `AllowPlainHttp` and `AcceptAnyCertificate`; startup refuses all three in
- Production. `tools/pasture/interop.sh` drives the matrix for GoToSocial; the other peers are still to be added. The
+ Production. `tools/pasture/interop.sh` drives the matrix for GoToSocial; the other peers are still to be added. *(Since added: the
+ pasture runs every peer in `tools/pasture/peers/`, and `tools/pasture/sweep.sh` runs them all.)* The
matrix, per peer:
- follow both ways;
- public, unlisted, followers-only and direct posts both ways;
@@ -829,7 +861,7 @@ buckets and only for servers reporting at least 10 users; the owner never intend
- **P2:** Tusky, Elk, Phanpy and Ivory each log in with two avatars of one root as separate accounts. Home paginates
both ways; post, reply, DM, follow-by-search, notifications, CW, and edit or delete-and-redraft all work.
`tools/smoke/mastodon-api.sh` runs after every deploy. A test asserts that no response for avatar A contains avatar
- B's id.
+ B's id. *(That test went with the test projects.)*
- **P3:**
- `exiftool` shows no GPS data on an uploaded original.
- Elk renders the blurhash.
@@ -845,7 +877,7 @@ buckets and only for servers reporting at least 10 users; the owner never intend
## Risks and sequencing
- Unique indexes fail on existing duplicates, so the dedupe migration runs first. Run a `mongodump` before every
- tagged deploy from P0 on.
+ tagged deploy from P0 on. *(The server's own pre-deploy backup does this now.)*
- Merge DmPost into Post and register `PrivacyIdGenerator` **before P2** exposes ids to clients.
- The publish grows by about 20 MB (OpenIddict, NetVips.Native, FFMpegCore). Keep `deploy.yml`'s asserted file list
current, and add ffmpeg in `deploy/max/setup.sh`.
diff --git a/tools/pasture/appsettings.Pasture.json b/tools/pasture/appsettings.Pasture.json
index 61b2395..e848047 100644
--- a/tools/pasture/appsettings.Pasture.json
+++ b/tools/pasture/appsettings.Pasture.json
@@ -1,13 +1,9 @@
{
"MongoSettings": {
"Database": "PrivaPub",
- "LogsDatabase": "logs",
"ConnectionString": "mongodb://mongo:27017/?directConnection=true"
},
"AppConfiguration": {
- "Version": "0.0.0",
- "MaxAllowedUploadFiles": 3,
- "MaxAllowedFileSize": 2097152,
"SupportedLanguages": [ "en" ],
"BackendBaseAddress": "https://privapub.test",
"FrontendBaseAddress": "https://privapub.test",
diff --git a/tools/pasture/interop.sh b/tools/pasture/interop.sh
index 9e91866..74115b9 100755
--- a/tools/pasture/interop.sh
+++ b/tools/pasture/interop.sh
@@ -1,8 +1,9 @@
#!/usr/bin/env bash
# Drives PrivaPub and each peer (started by run.sh) through each other's federation, as their users would, then checks
# that PrivaPub's statistics saw it all without naming anyone.
-# usage: tools/pasture/interop.sh [peer...] peers: gts (default), mastodon, misskey, sharkey, akkoma, lemmy,
-# peertube, crawler
+# usage: tools/pasture/interop.sh [scenario...] gts (default), and every other file in scenarios/: a peer's (with
+# that peer up) or one across peers (threads, relay, restore, ...)
+# tools/pasture/sweep.sh [A|B|C|D ...] every peer's scenario, a batch of peers at a time
set -uo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$here/lib/interop.sh"
diff --git a/tools/pasture/run.sh b/tools/pasture/run.sh
index 73b1cd6..c941412 100755
--- a/tools/pasture/run.sh
+++ b/tools/pasture/run.sh
@@ -5,6 +5,7 @@
# usage: tools/pasture/run.sh up [peer...] | add | down [--purge] | logs | ps | stats
# up starts everything afresh (PrivaPub republished, Mongo empty); add starts more peers next to a running pasture.
# peers: gts (default), mastodon, misskey, sharkey, akkoma, lemmy, decepub, and every other file in peers/
+# tools/pasture/sweep.sh runs every peer's scenario, a batch of peers at a time (up, interop.sh, down).
set -euo pipefail
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/pasture.sh"
diff --git a/tools/pasture/scenarios/gts.sh b/tools/pasture/scenarios/gts.sh
index 03dc4fa..6cf33ca 100644
--- a/tools/pasture/scenarios/gts.sh
+++ b/tools/pasture/scenarios/gts.sh
@@ -192,7 +192,8 @@ curl -s -o /dev/null -X POST -H "$LH" "$P/api/v1/follow_requests/$requester/auth
until_true 20 '[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$locked_on_gts" | j "print(d[0][\"following\"])")" = "True" ]' && ok "an authorized request makes gtsuser a follower" || ko "authorization not applied on GoToSocial"
echo "smoke"
-"$here/../smoke/mastodon-api.sh" "$P" "$PT" >/dev/null 2>&1 && ok "the deploy's Mastodon smoke check passes, signed in" || ko "the Mastodon smoke check fails"
+# tools/smoke/mastodon-api.sh: the deploy runs it anonymously; here a persona's token adds its signed-in checks
+"$here/../smoke/mastodon-api.sh" "$P" "$PT" >/dev/null 2>&1 && ok "the Mastodon smoke check passes, anonymously and with a persona's token" || ko "the Mastodon smoke check fails"
echo "interaction policies"
# gtsuser's post asks before anyone but its author replies or likes, and lets nobody else boost it: PrivaPub shows the
diff --git a/tools/pasture/scenarios/mobilizon.sh b/tools/pasture/scenarios/mobilizon.sh
index a0c4905..a111464 100644
--- a/tools/pasture/scenarios/mobilizon.sh
+++ b/tools/pasture/scenarios/mobilizon.sh
@@ -1,8 +1,8 @@
# Mobilizon 5.2.4: a group and its events. alice_mobilizon follows the group, which announces the events its members
# organise; an event arrives as an Event with its dates and place; she joins it and leaves; comments both ways; an edit
-# and comments closed; deletes; a group post; an event kept for decePubClient's e2e; the unfollow. An event made through its API without options has its comments closed
-# (`commentsEnabled: false`), which PrivaPub honours, so this one opens them. Mobilizon's API is GraphQL (/api), signed in by its login mutation; what it holds is read from
-# its PostGIS (database mobilizon).
+# and comments closed; deletes; a group post; the unfollow. An event made through its API without options has its
+# comments closed (`commentsEnabled: false`), which PrivaPub honours, so this one opens them. Mobilizon's API is GraphQL
+# (/api), signed in by its login mutation; what it holds is read from its PostGIS (database mobilizon).
MZ=https://mobilizon.test:6443
mz_sql() { podman exec pasture-postgis psql -U pasture -d mobilizon -tAc "$1" 2>/dev/null; }
# mz [variables as JSON]: a GraphQL request as mzuser, its JSON answer
@@ -91,16 +91,6 @@ mz 'mutation($g:ID!,$t:String!){createPost(attributedToId:$g,title:$t,body:"
t
"{\"g\":\"$group\",\"t\":\"$post_title\"}" >/dev/null
until_true 60 '[ -n "$(p_home_has "$post_title")" ]' && ok "the group's post reaches alice_mobilizon's home, with its title" || ko "the group's post never arrived"
-echo " an event kept for decePub's tests"
-# decePubClient's e2e joins it from its card (tests/e2e/Actions/EventTests.cs); the one a run before this one kept goes
-kept="$here/.state/mobilizon-kept-event.json"
-[ -s "$kept" ] && mz 'mutation($e:ID!){deleteEvent(eventId:$e){id}}' "{\"e\":\"$(j "print(d['id'])" < "$kept")\"}" >/dev/null
-kept_title="A concert kept for decePub $(date +%s)"
-kept_begins=$(date -u -d '+10 days' +%Y-%m-%dT19:00:00Z); kept_ends=$(date -u -d '+10 days' +%Y-%m-%dT22:00:00Z)
-mz 'mutation($t:String!,$b:DateTime!,$e:DateTime!,$o:ID!,$g:ID){createEvent(title:$t,description:"
come and listen
",beginsOn:$b,endsOn:$e,organizerActorId:$o,attributedToId:$g,visibility:PUBLIC,joinOptions:FREE,options:{commentModeration:ALLOW_ALL},physicalAddress:{description:"Piazza del Popolo",locality:"Roma",country:"Italia",geom:"12.4763;41.9109"}){id url}}' \
- "{\"t\":\"$kept_title\",\"b\":\"$kept_begins\",\"e\":\"$kept_ends\",\"o\":\"$me\",\"g\":\"$group\"}" | j "print(json.dumps(d['data']['createEvent']))" > "$kept"
-until_true 60 '[ -n "$(p_home_has "$kept_title")" ]' && ok "an event kept for decePub's tests reaches PrivaPub" || ko "the kept event never arrived"
-
echo " unfollow"
curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/accounts/$group_on_p/unfollow"
until_true 45 '[ "$(mz_sql "select count(*) from followers f join actors a on a.id = f.actor_id where a.url like '"'%alice_mobilizon%'"'")" = "0" ]' \
diff --git a/tools/pasture/sweep.sh b/tools/pasture/sweep.sh
new file mode 100755
index 0000000..8aae459
--- /dev/null
+++ b/tools/pasture/sweep.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+# Every peer's scenario, a batch of peers at a time: a change to what PrivaPub sends (the @context, an actor's
+# properties, an activity's shape, a delivery's headers) runs against all of them before it is committed, since one
+# reader stricter than the rest fails on it alone. Each batch starts its own pasture afresh (run.sh up), runs its
+# scenarios (interop.sh) and takes the pasture down (run.sh down), and the next batch runs whatever the one before did.
+# PrivaPub runs with SecureMode on, as in production, unless PRIVAPUB_ENV says otherwise; DOTNET and PASTURE_PORT reach
+# run.sh as they are. Each batch's output is kept in out/sweep-.log.
+# usage: tools/pasture/sweep.sh [A|B|C|D ...] (default: every batch, in that order)
+# A gts mastodon misskey sharkey akkoma, with relay and aoderelay; scenarios: those five, threads followsync pins
+# moves relay (relay's covers both relays)
+# B lemmy lemmy19 piefed mbin nodebb
+# C peertube pixelfed vernissage wordpress ghost writefreely owncast castopod funkwhale
+# D friendica mobilizon gancio bookwyrm smithereen mitra snac ktistec forte hubzilla
+# Not swept: restore (run it alone: it restores the whole server), crawler (it needs the crawler's own settings) and
+# persona-archive; hollo, iceshrimp, pleroma, flood and decepub have no scenario of their own.
+set -uo pipefail
+here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+export PRIVAPUB_ENV=${PRIVAPUB_ENV:-Federation__SecureMode=true}
+
+batch_peers() {
+ case "$1" in
+ A) echo "gts mastodon misskey sharkey akkoma relay aoderelay" ;;
+ B) echo "lemmy lemmy19 piefed mbin nodebb" ;;
+ C) echo "peertube pixelfed vernissage wordpress ghost writefreely owncast castopod funkwhale" ;;
+ D) echo "friendica mobilizon gancio bookwyrm smithereen mitra snac ktistec forte hubzilla" ;;
+ esac
+}
+batch_scenarios() {
+ case "$1" in
+ A) echo "gts mastodon misskey sharkey akkoma threads followsync pins moves relay" ;;
+ *) batch_peers "$1" ;;
+ esac
+}
+
+[ $# -gt 0 ] || set -- A B C D
+for batch in "$@"; do
+ [ -n "$(batch_peers "$batch")" ] || { echo "no such batch: $batch (A, B, C or D)" >&2; exit 2; }
+ for peer in $(batch_peers "$batch"); do
+ [ -f "$here/peers/$peer.sh" ] || { echo "batch $batch: no such peer: $peer" >&2; exit 2; }
+ done
+ for scenario in $(batch_scenarios "$batch"); do
+ [ -f "$here/scenarios/$scenario.sh" ] || { echo "batch $batch: no scenario for $scenario" >&2; exit 2; }
+ done
+done
+
+mkdir -p "$here/out"
+summary=(); bad=0
+for batch in "$@"; do
+ log="$here/out/sweep-$batch.log"
+ echo "== batch $batch: $(batch_peers "$batch")"
+ # shellcheck disable=SC2046
+ if ! "$here/run.sh" up $(batch_peers "$batch") 2>&1 | tee "$log"; then
+ summary+=("$batch the pasture did not start (out/sweep-$batch.log)"); bad=1
+ "$here/run.sh" down || true
+ continue
+ fi
+ # shellcheck disable=SC2046
+ "$here/interop.sh" $(batch_scenarios "$batch") 2>&1 | tee -a "$log"
+ status=${PIPESTATUS[0]}
+ "$here/run.sh" down || true
+ passed=$(grep -c '^ ok ' "$log"); failed=$(grep -c '^ FAIL ' "$log"); xfailed=$(grep -c '^ xf ' "$log")
+ line="$batch ok $passed ko $failed xf $xfailed ($(batch_scenarios "$batch"))"
+ if ! grep -qE '^[0-9]+ passed, [0-9]+ failed, [0-9]+ expected failures$' "$log"; then
+ line="$line stopped before its end (exit $status)"; bad=1
+ fi
+ [ "$failed" = 0 ] || bad=1
+ summary+=("$line")
+done
+
+echo; echo "sweep:"
+printf ' %s\n' "${summary[@]}"
+[ "$bad" = 0 ]
diff --git a/tools/pasture/town.sh b/tools/pasture/town.sh
index 26ef9f6..ea1814a 100755
--- a/tools/pasture/town.sh
+++ b/tools/pasture/town.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# The town: a fake community seeded across every pasture peer, then checked for coherence (see town/town.py).
-# usage: tools/pasture/town.sh [args] commands: selftest, seed, check, report, backlog, gaps, drive, stored, seen,
+# usage: tools/pasture/town.sh [args] commands: selftest, seed, check, report, backlog, drive, stored, seen,
# renew (its accounts signed in again)
exec python3 "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/town/town.py" "$@"
diff --git a/tools/pasture/town/backlog.py b/tools/pasture/town/backlog.py
index 7132823..609b11c 100644
--- a/tools/pasture/town/backlog.py
+++ b/tools/pasture/town/backlog.py
@@ -1,5 +1,5 @@
-"""docs/INTEROP-BACKLOG.md from the latest town results, the bash scenarios and decePub's e2e cells: new failures (to fix
-or to record in gaps.json), known gaps by phase, known gaps now passing (close them), and the scenarios' failures.
+"""docs/INTEROP-BACKLOG.md from the latest town results and the bash scenarios: new failures (to fix or to record in
+gaps.json), known gaps by phase, known gaps now passing (close them), and the scenarios' failures.
town.sh backlog [run] [--write]"""
import json
@@ -27,9 +27,8 @@ def main(args):
with open(os.path.join(run_dir, "results.json")) as f:
result = json.load(f)
gaps = {g["id"]: g for g in check.load_gaps()}
- client = check.classify(check.latest_client([dict(c) for c in load_jsonl(os.path.join(seed.OUT, "client.jsonl"))]), list(gaps.values()))
scenarios = load_jsonl(os.path.join(seed.OUT, "scenarios.jsonl"))
- checks = result["checks"] + client
+ checks = result["checks"]
new = defaultdict(list)
known = defaultdict(list)
diff --git a/tools/pasture/town/check.py b/tools/pasture/town/check.py
index 1ee7e19..4e5e162 100644
--- a/tools/pasture/town/check.py
+++ b/tools/pasture/town/check.py
@@ -478,21 +478,6 @@ def canary_hits(platform, canaries):
return sorted(c for c in canaries if c in dump)
-def latest_client(lines):
- """decePub's e2e cells (out/client.jsonl, appended by every run) as each cell's latest run left them: the rows from
- within an hour of the cell's newest, the newest per test. A run that picked other accounts leaves no stale failure."""
- from datetime import datetime, timedelta
- at = lambda c: datetime.fromisoformat(c["t"].replace("Z", "+00:00"))
- newest = {}
- for c in lines:
- newest[c["cell"]] = max(newest.get(c["cell"], at(c)), at(c))
- latest = {}
- for c in sorted(lines, key=at):
- if at(c) >= newest[c["cell"]] - timedelta(hours=1):
- latest[(c["cell"], c.get("ref"))] = c
- return list(latest.values())
-
-
def load_gaps():
try:
with open(os.path.join(HERE, "gaps.json")) as f:
diff --git a/tools/pasture/town/expect.py b/tools/pasture/town/expect.py
deleted file mode 100644
index 20e13c6..0000000
--- a/tools/pasture/town/expect.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""What the town's objects should look like to a reader, for decePub's e2e tests: for every object PrivaPub holds and some
-town persona may see, its ref, URI, PrivaPub id, origin, kind, visibility, the text marker, content warning, media alt
-texts, poll options, quoted and parent URIs, and the personas that may see it.
-
- town.sh expect [run] [--persona ]"""
-import json
-import sys
-
-import check
-import seed
-from dialects import driver
-
-
-def edited_cw(world, ref, args):
- """The warning an object shows now: an edit made before plans kept warnings replaced the post without one."""
- edit = next((s for s in world.planner.steps if s["verb"] == "edit" and s["args"]["target"] == ref), None)
- if edit is None or ref not in world.edited:
- return args.get("cw")
- return edit["args"].get("cw")
-
-
-def objects(world):
- steps = {s["ref"]: s for s in world.planner.steps if s.get("ref") and s["verb"] == "post"}
- held = driver("privapub").stored([o["uri"] for o in world.objects.values()])
- personas = [k for k in world.planner.accounts if k.startswith("privapub/")]
- out = []
- for ref, o in sorted(world.objects.items(), key=lambda kv: int(kv[0][1:])):
- row = held.get(o["uri"])
- if not row or not row.exists or row.deleted or ref in world.deleted:
- continue
- recipients = world.recipients(o)
- readers = [p.split("/")[1] for p in personas
- if p == o["author"] or p in recipients or o["visibility"] in ("public", "unlisted", "community")]
- if not readers:
- continue
- args = steps[ref]["args"]
- out.append({
- "ref": ref, "uri": o["uri"], "id": row.local_id, "origin": o["origin"], "author": o["author"],
- "kind": o["kind"], "visibility": o["visibility"], "marker": f"({ref})", "cw": edited_cw(world, ref, args),
- "alts": [m["alt"] for m in args.get("media", [])], "poll": (args.get("poll") or {}).get("options"),
- "quote": world.objects.get(o.get("quote") or "", {}).get("uri"),
- "parent": world.objects.get(o.get("parent") or "", {}).get("uri"),
- "edited": ref in world.edited, "title": args.get("title"), "readers": readers,
- "followers_of_author": sorted(p.split("/")[1] for p in world.follows.get(o["author"], set()) if p.startswith("privapub/")),
- })
- return out
-
-
-def main(args):
- name = next((a for a in args if not a.startswith("--") and not a.startswith("privapub/")), "village")
- persona = None
- if "--persona" in args:
- persona = args[args.index("--persona") + 1].split("/")[-1]
- world = check.World(seed.run_dir(name))
- items = objects(world)
- if persona:
- items = [i for i in items if persona in i["readers"]]
- print(json.dumps(items, ensure_ascii=False))
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main(sys.argv[1:]))
diff --git a/tools/pasture/town/gaps.json b/tools/pasture/town/gaps.json
index f473317..57e0d38 100644
--- a/tools/pasture/town/gaps.json
+++ b/tools/pasture/town/gaps.json
@@ -75,7 +75,7 @@
"code": "misskey-dev/misskey packages/backend/src/core/NoteDeleteService.ts: only repliesCount is decremented on delete",
"opened": "2026-10-05",
"status": "open",
- "note": "the renote row is gone after our Undo (checked in its database); renoteCount stays up. Sharkey decrements it."
+ "note": "the renote row is gone after our Undo (checked in its database); renoteCount stays up. Sharkey decrements it. Its match named decePub's e2e cells (client.action.boost), deleted 2026-10-17, so it matches nothing until a town cell checks Misskey's renote count."
},
{
"id": "G-0006",
diff --git a/tools/pasture/town/gen.py b/tools/pasture/town/gen.py
index 667c3f9..e638078 100644
--- a/tools/pasture/town/gen.py
+++ b/tools/pasture/town/gen.py
@@ -1,4 +1,4 @@
-"""The plan: a fake community drawn from a spec and its seed, the same on every run (tests/test_gen.py pins its hash).
+"""The plan: a fake community drawn from a spec and its seed, the same on every run.
A plan is a list of steps, each {n, phase, round, actor, verb, args, ref}. Accounts are named "/",
objects by a ref ("p12"); the seeder turns refs into URIs as it goes. Phases run in order:
diff --git a/tools/pasture/town/report.py b/tools/pasture/town/report.py
index 5f80405..40b0dfa 100644
--- a/tools/pasture/town/report.py
+++ b/tools/pasture/town/report.py
@@ -12,7 +12,7 @@ import gen
DIRS = ["in", "out", "via", "control", "local"]
DIR_LABEL = {"in": "into PrivaPub", "out": "from PrivaPub", "via": "through PrivaPub", "control": "peer to peer",
"local": "on its own server"}
-GROUPS = [("client", "decePub"), ("deliver", "Delivery"), ("relay", "Replies passed on"), ("confine", "Confinement"),
+GROUPS = [("deliver", "Delivery"), ("relay", "Replies passed on"), ("confine", "Confinement"),
("see", "Seen by the audience"), ("hide", "Hidden from outsiders"), ("count", "Counts"), ("thread", "Threads"),
("edit", "Edits"), ("delete", "Deletes"), ("graph", "Follows"), ("privacy", "Privacy")]
STATUS_ORDER = ["fail", "xpass", "xfail", "pass"]
@@ -84,7 +84,7 @@ def render(result, scenarios, title):
cells = result["cells"]
totals = Counter(result["totals"])
observers = [p for p in gen.HOSTS if any(k.split("|")[2] == p for k in cells)] + \
- [o for o in ("*", "decepub") if any(k.split("|")[2] == o for k in cells)]
+ [o for o in ("*",) if any(k.split("|")[2] == o for k in cells)]
features = sorted({k.split("|")[0] for k in cells})
out = [f"{html.escape(title)}",
'',
@@ -169,19 +169,6 @@ def main(args):
if os.path.exists(path):
with open(path) as f:
scenarios = [json.loads(line) for line in f if line.strip()]
- # a client's cells, when any, join the servers' in the same matrix, under the observer "decepub"
- client = os.path.join(seed.OUT, "client.jsonl")
- if os.path.exists(client):
- import check
- with open(client) as f:
- lines = [json.loads(line) for line in f if line.strip()]
- checks = check.classify(check.latest_client(lines), check.load_gaps())
- for cell, v in check.summarise(checks).items():
- result["cells"][cell] = v
- from collections import Counter
- totals = Counter(result["totals"])
- totals.update(Counter(c["status"] for c in checks))
- result["totals"] = totals
page = render(result, scenarios, "PrivaPub Town Matrix")
target = os.path.join(run_dir, "report.html")
with open(target, "w") as f:
diff --git a/tools/pasture/town/tests/__init__.py b/tools/pasture/town/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tools/pasture/town/tests/test_town.py b/tools/pasture/town/tests/test_town.py
deleted file mode 100644
index fb4095c..0000000
--- a/tools/pasture/town/tests/test_town.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""The town's own logic, with no pasture: python3 -m unittest discover -s tools/pasture/town/tests"""
-import os
-import sys
-import unittest
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-import check # noqa: E402
-import gen # noqa: E402
-from core.rng import Rng # noqa: E402
-
-
-class RngTests(unittest.TestCase):
- def test_the_same_seed_and_name_give_the_same_stream(self):
- a, b = Rng(7, "x"), Rng(7, "x")
- self.assertEqual([a.next64() for _ in range(5)], [b.next64() for _ in range(5)])
-
- def test_substreams_do_not_disturb_each_other(self):
- root = Rng(7)
- before = Rng(7).sub("graph").next64()
- root.sub("content").next64()
- self.assertEqual(before, root.sub("graph").next64())
-
- def test_below_stays_in_range(self):
- r = Rng(1)
- self.assertTrue(all(0 <= r.below(3) < 3 for _ in range(1000)))
-
-
-class PlanTests(unittest.TestCase):
- def test_a_spec_always_plans_the_same(self):
- spec = gen.load("village")
- self.assertEqual(gen.digest(gen.plan(spec)), gen.digest(gen.plan(spec)))
-
- def test_every_post_names_its_marker_and_every_reply_a_parent_planned_before_it(self):
- planner = gen.Planner(gen.load("village"))
- steps = planner.plan()
- made = set()
- for s in steps:
- if s["verb"] != "post":
- continue
- self.assertIn(f"({s['ref']})", s["args"]["text"])
- if s["args"].get("reply_to"):
- self.assertIn(s["args"]["reply_to"], made)
- made.add(s["ref"])
-
- def test_lemmy_accounts_never_follow_people_and_platforms_get_only_what_they_can_do(self):
- steps = gen.plan(gen.load("village"))
- for s in steps:
- platform = s["actor"].split("/")[0]
- if s["verb"] == "follow":
- self.assertNotEqual("lemmy", platform)
- if s["verb"] in ("react", "edit", "vote", "boost"):
- self.assertIn({"react": "react", "edit": "edit", "vote": "vote", "boost": "boost"}[s["verb"]], gen.CAPS[platform])
-
- def test_a_stored_plan_rebuilds_the_same_world(self):
- planner = gen.Planner(gen.load("village"))
- planner.plan()
- stored = gen.Stored(gen.snapshot(planner))
- self.assertEqual(planner.objects.keys(), stored.objects.keys())
- self.assertEqual(planner.objects["p1"]["mentions"], stored.objects["p1"]["mentions"])
-
-
-class GapTests(unittest.TestCase):
- def test_a_known_gap_turns_failures_into_xfail_and_passes_into_xpass(self):
- gaps = [{"id": "G-9", "match": {"feature": r"count\.like\..*", "observer": "privapub"}, "status": "open"}]
- checks = check.classify([
- {"feature": "count.like.followers", "observer": "privapub", "ok": False},
- {"feature": "count.like.public", "observer": "privapub", "ok": True},
- {"feature": "count.like.public", "observer": "gts", "ok": False},
- ], gaps)
- self.assertEqual(["xfail", "xpass", "fail"], [c["status"] for c in checks])
-
- def test_a_closed_gap_matches_nothing(self):
- gaps = [{"id": "G-9", "match": {"feature": ".*"}, "status": "closed"}]
- self.assertEqual("fail", check.classify([{"feature": "x", "ok": False}], gaps)[0]["status"])
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tools/pasture/town/town.py b/tools/pasture/town/town.py
index 1426993..68e0dab 100644
--- a/tools/pasture/town/town.py
+++ b/tools/pasture/town/town.py
@@ -59,7 +59,7 @@ def main(argv):
s = state.session(args[0], args[1])
print(json.dumps(driver(args[0]).seen(s, args[2:])))
return 0
- if cmd in ("plan", "seed", "check", "report", "backlog", "gaps", "expect", "load"):
+ if cmd in ("plan", "seed", "check", "report", "backlog"):
module = __import__(cmd if cmd not in ("plan",) else "gen")
return module.main(args)
print(f"unknown command {cmd}", file=sys.stderr)