Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,9 @@ jobs:
matrix:
include:
- suite: ui
specs: tests/e2e/collection.spec.ts tests/e2e/event.spec.ts tests/e2e/gallery.spec.ts
specs: tests/e2e/collection.spec.ts tests/e2e/event.spec.ts tests/e2e/gallery.spec.ts tests/e2e/review-viewer.spec.ts
- suite: api
specs: tests/e2e/ecosystem.spec.ts tests/e2e/regression.spec.ts tests/e2e/security.spec.ts
specs: tests/e2e/ecosystem.spec.ts tests/e2e/regression.spec.ts tests/e2e/security.spec.ts tests/e2e/review-identity.spec.ts
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -272,11 +272,11 @@ jobs:
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Chromium and runner dependencies
- name: Install browsers and runner dependencies
run: |
if ! timeout --kill-after=30s 5m npx playwright install --with-deps chromium; then
if ! timeout --kill-after=30s 5m npx playwright install --with-deps chromium firefox webkit; then
echo "::warning::System dependency installation stalled; using runner-provided browser libraries."
npx playwright install chromium
npx playwright install chromium firefox webkit
fi
- run: make dev-up
- run: make occ CMD="app:list --enabled --output=json" | grep -q '"proofing_gallery"'
Expand All @@ -287,6 +287,7 @@ jobs:
run: |
./scripts/test-context-agent.sh
./scripts/test-user-migration.sh
docker compose exec -T --user www-data nextcloud php /var/www/html/custom_apps/proofing_gallery/tests/smoke/CollaborationSelectionSync.php
- name: Verify protected public routes
if: matrix.suite == 'api'
env:
Expand Down
23 changes: 13 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

## Unreleased

## 0.9.2 — 2026-09-07

- make the mobile gallery and lightbox easier to use with more reliable overlays,
touch interaction, navigation, and action controls
- make success feedback and file-conflict resolution clearer and more accessible,
including improved screen-reader announcements and a simpler conflict dialog
- improve project settings and reusable preset controls with updated Nextcloud
interface components
- harden client-side input handling against case and whitespace variants of
malicious `onload` attributes
- separate general feedback and pin conversations beside the image without
dimming the artwork, with labelled review colors and localized comment headers
- attribute signed-in reviews to Nextcloud accounts while retaining guest
authorship, private ratings, link restrictions, and private selection updates
- identify pin replies by their root comment rather than matching coordinates;
retain conversations when their initial comment is deleted
- invalidate live collaboration cursors after account or guest data erasure so
open viewers refresh without exposing deleted identity data
- keep annotations attached during zoom and support bounded touch panning
- add account attribution and private event recipient migrations; complete the
database upgrade before serving the updated application
- accept empty settings during user migration and fail migration smoke checks
with a nonzero exit status

## 0.9.1 — 2026-09-06

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Automation, Privacy, and History workspaces.
and server-rendered preview watermarks
- likes, color states, comments, image annotations, named selections, and
CSV/plain-text exports
- separate general comments and expandable pin conversations, with panels beside
the selected detail, color-labelled review states, and account-aware authorship
- individual downloads, selected ZIP files, and printable contact sheets
- resumable guest uploads to a hidden moderation inbox
- user/group gallery managers, activity filters, and opt-in event digests for
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Vorlagen und Nextcloud-Integrationen, sofern aktiviert. Administrations-,
Galerie-, Link- und Event-Wellen-Regeln werden gemeinsam ausgewertet, sodass
ein öffentlicher Link nur erlauben kann, was alle geltenden Regeln zulassen.
]]></description>
<version>0.9.2</version>
<version>0.10.0</version>
<licence>AGPL-3.0-or-later</licence>
<author>soerennb</author>
<namespace>ProofingGallery</namespace>
Expand Down
55 changes: 55 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,47 @@ PHP routes, templates, or controller constructors, run
`docker compose restart nextcloud` to clear PHP OPcache before validating the
change.

## Persistent remote test tenant

A Docker-capable Linux VM can be used as a persistent manual-QA tenant when
Docker is not installed on the editing workstation. Keep source and build
dependencies in a staging directory outside the live Nextcloud Compose tree.
Build and test in disposable containers, then copy only a successful runtime
tree into the tenant's `custom_apps/proofing_gallery` directory.

Before deployment, record `occ status`, the enabled-app list, migration state,
and container health. Keep the previous app directory only during the immediate
HTTP, `occ`, log, and browser checks. Restore it only if that deployment fails;
delete it as soon as the replacement passes so the development tenant retains
only the latest verified app build. Never reset the tenant's Compose volumes as
part of an app deployment.

Keep rollback copies outside `custom_apps`; Nextcloud scans every directory in
that path and a backup containing the same app ID can cause duplicate-app
discovery. App Store packages intentionally omit development-only `scripts/`,
so run migration diagnostics from the source staging tree rather than assuming
they are installed with the runtime package.

The persistent tenant is for manual integration and browser QA only. Do not
run this repository's Playwright global setup against it: the current E2E
fixtures assume the disposable `admin` / `admin` tenant, rewrite that user's
preferences, and remove prior `E2E` fixtures. Use `npm run test:e2e` only with
the repository's isolated loopback Compose stack, or refactor the harness for
a dedicated test identity before targeting another tenant.

For frontend-only UI work, a persistent pinned Node container may run
`npm run watch` against the staging checkout. Run the affected Vitest files
during each small iteration and copy only generated `build`, `css`, and `js`
assets into the installed development app. Stable entry assets must be served
without long-lived caching on that development host so a refresh discovers
new content-hashed chunks. Run the complete lint, unit, build, package, PHP,
and compatibility gates at milestones and before publication. Never use the
frontend-only path for PHP, routes, migrations, or dependency changes.

Machine-specific addresses, SSH key locations, credentials, and deployment
commands belong in an ignored local runbook under `.local/`, never in tracked
documentation.

## Documentation sources and builds

The English and German user and administrator guides under `docs/en/` and
Expand Down Expand Up @@ -123,6 +164,13 @@ verify desktop and 390 px mobile layouts, scroll reachability, horizontal
overflow, media hit testing, rows below the hero, and side and bottom filmstrip
placement inside the viewport.

For feedback panels, test the overflow model rather than requiring every item
to fit at once. A history of 20 or more comments must scroll inside the panel;
headers, comment text, identity labels, edit controls, and the composer must not
be clipped, overlapped, or made unreachable at short viewport heights. Selecting
an image annotation may keep its marker visible for context, but the comment
workflow must remain usable without relying on the marker staying on screen.

Playwright global setup creates and later supersedes its own E2E gallery.
Snapshots are intentionally versioned. Update them only after reviewing the
rendered images, preferably through the isolation-preserving wrapper:
Expand Down Expand Up @@ -204,6 +252,13 @@ used for this gate without modifying its application code.

## Database changes

Point replies use `parentId` referencing a root comment in the same gallery,
file and visible feedback scope. Nested replies and foreign/private roots are
rejected. The server copies the root annotation; client-supplied reply coordinates
are not authoritative. Responses expose `threadId` independently of coordinates,
so coincident pins remain distinct. Deleted roots retain a text-free tombstone
while replies survive, including when their original author is deleted.

Add a new monotonically increasing migration; do not modify released
migrations. Use Nextcloud's schema abstraction exclusively and rerun all three
database engines. Keep controllers thin and put authorization and domain rules
Expand Down
10 changes: 9 additions & 1 deletion docs/PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,15 @@ and workflow labels even if those values exist in Files or an XMP sidecar.

## Visibility and retention

Private proofing exposes a guest's feedback only to that guest and gallery
Private proofing exposes an author's feedback only to that guest or account and gallery
managers. Collaborative proofing deliberately shares feedback with other
reviewers. The chosen policy should be communicated before inviting guests.

Signed-in feedback records the existing Nextcloud UID. Historical guest
authorship is not transferred on sign-in. Owner-originated selection events
retain the owner's UID and a separate private account recipient where needed;
neither identity field is added to the public event response.

Guest identities expire after 30 days unless renewed by product behavior.
Scheduled cleanup removes expired identities, abandoned chunks, stale derived
previews, and old internal activity in bounded batches. Owners can revoke the
Expand All @@ -72,6 +77,9 @@ identity and contributions and can erase those records from the gallery. The
mutation requires both the guest cookie and its independent nonce. Gallery
exports omit session hashes, nonces, public-link and verification tokens,
unsubscribe tokens, encrypted email ciphertext, and Live Push password hashes.
Open collaboration viewers receive a privacy-neutral reset signal and
re-hydrate their permitted state after such an erasure; the signal contains no
deleted identity or contribution data.

After archiving, an owner may schedule deletion of that gallery's app records
with a 30-day cancellation period. The dry-run reports affected row categories
Expand Down
21 changes: 21 additions & 0 deletions docs/USER-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,27 @@ pick, reject, label, comment, annotate, and save named selections. Guests do
not need Nextcloud accounts. Their identity and mutation token are stored in a
private browser session; clearing site data ends access to private feedback.

If you are signed in to Nextcloud, new feedback uses your account identity.
Earlier guest comments remain linked to their original guest identity; signing
in does not claim them. Account identity does not bypass gallery or link rules.
Uploads still require a guest session; signed-in reviewers can open the link
in a private browser window when they need to upload files.

**Feedback** opens **General comments**, newest first beneath the input.
The **Pins** tab groups point conversations into expandable rows. Expand a row
to read its replies, or use its separate open button to show the conversation
beside the pin. These panels do not dim the image. **Review state** shows both
the configured state name and its color indicator.
Each pin has its own conversation, even when two pins share the same position.
Replies remain available when the original comment is deleted; its text is
replaced with a deletion notice.

Zoom with the viewer controls, mouse wheel, or a touch pinch. On desktop, hold
the right mouse button and drag the zoomed image to pan. A right-click without
dragging keeps the browser context menu. Pins retain their image-relative
positions while zooming and panning. On touchscreens, drag with one finger
after zooming to reach the image edges. Dragging does not create a new pin.

Click or tap an image to place a numbered point and open its comment editor.
For keyboard placement, choose **Add point comment**, move the point with the
arrow keys, press Enter to write, or Escape to cancel. Unpinned comments remain
Expand Down
14 changes: 14 additions & 0 deletions docs/de/administrationshandbuch.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ Aufbewahrung. Die Regeln werden serverseitig erzwungen und sperren kritische
Funktionen bei Unsicherheit. Native Nextcloud-Regeln für Freigaben, Passwörter,
Ablauf und Uploads bleiben maßgeblich und werden niemals gelockert.

Öffentliche Prüfer können Gastidentitäten oder angemeldete Nextcloud-Konten
verwenden. Konto-UIDs bestimmen die Urheberschaft, nicht zusätzliche Rechte.
Frühere Gastbeiträge werden nicht anhand von Namen oder E-Mail-Adressen
übertragen. Privates Feedback bleibt auf den Autor und berechtigte
Galerieverwalter beschränkt. Eigentümeränderungen an einer Kontoauswahl werden
nur an den zulässigen Empfängerkreis übermittelt. Gast-Uploads bleiben ein
separater, Gästen vorbehaltener Ablauf.

Die Migration der Kontozuordnung behält bei doppelten Konto-Feedbackzeilen die
höchste ID, bevor sie Eindeutigkeitsregeln anlegt; Gastkommentare und Punkte
werden nicht neu zugeordnet. Eine folgende Migration ergänzt eine optionale
Empfänger-UID für Ereignisse, getrennt vom auslösenden Eigentümer. Schließe das
normale Datenbank-Upgrade ab, bevor der neue Anwendungscode bereitgestellt wird.

Prüfe Freigabe-, Mail- und Gruppenrichtlinien vor der Einführung. Aktiviere
Gast-Downloads und -Uploads nur bei Bedarf. Richte Grenzen nach PHP, Proxy,
Speicher und Worker-Kapazität aus, nicht nach Browservalidierung.
Expand Down
22 changes: 22 additions & 0 deletions docs/de/benutzerhandbuch.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,28 @@ Auswahlen speichern. Ein Nextcloud-Konto ist nicht nötig. Identität und
Änderungstoken liegen in einer privaten Browsersitzung; gelöschte Website-Daten
beenden den Zugriff auf privates Feedback.

Wenn du bei Nextcloud angemeldet bist, gehört neues Feedback zu deinem Konto.
Frühere Gastkommentare bleiben ihrer ursprünglichen Gastidentität zugeordnet;
die Anmeldung übernimmt sie nicht. Galerie- und Linkregeln gelten weiterhin.
Uploads benötigen weiterhin eine Gastsitzung. Öffne dafür als angemeldeter
Benutzer den Link in einem privaten Browserfenster.

**Feedback** öffnet **Allgemeine Kommentare**, mit den neuesten Beiträgen direkt
unter dem Eingabefeld. **Markierungen** gruppiert Punktgespräche in aufklappbaren
Zeilen. Klappe eine Zeile zum Lesen auf oder öffne das Gespräch über dessen
separate Schaltfläche neben dem Punkt. Diese Fenster verdunkeln das Bild nicht.
**Prüfstatus** zeigt den konfigurierten Statusnamen zusammen mit seiner Farbe.
Jeder Punkt hat ein eigenes Gespräch, auch bei identischen Bildkoordinaten.
Antworten bleiben beim Löschen des ursprünglichen Kommentars erhalten; dessen
Text wird durch einen Löschhinweis ersetzt.

Zoome über die Bedienelemente, das Mausrad oder eine Zwei-Finger-Geste. Am Desktop
kannst du das vergrößerte Bild mit gedrückter rechter Maustaste verschieben.
Ein Rechtsklick ohne Ziehen öffnet weiterhin das Browser-Kontextmenü. Punkte
bleiben beim Zoomen und Verschieben an derselben bildrelativen Position.
Auf Touchscreens kannst du nach dem Zoomen mit einem Finger bis zu den
Bildrändern verschieben. Dabei wird kein neuer Punkt gesetzt.

Klicke oder tippe direkt auf ein Bild, um einen nummerierten Punkt zu setzen
und den zugehörigen Kommentar zu schreiben. Wähle für die Tastaturplatzierung
**Punktkommentar hinzufügen**, verschiebe den Punkt mit den Pfeiltasten, drücke
Expand Down
13 changes: 13 additions & 0 deletions docs/en/admin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ the server, including existing galleries where a capability must fail closed.
Native Nextcloud sharing, password, expiration, and upload restrictions remain
authoritative; this app never weakens them.

Public reviewers may use guest identities or existing signed-in Nextcloud
accounts. Account UIDs establish authorship, not additional permissions. Earlier
guest contributions are not reassigned by matching names or email addresses.
Private feedback stays private to its author and authorized gallery managers;
owner changes to an account's selection are delivered only to the permitted
review audience. Guest uploads remain a separate guest-only workflow.

The account-attribution migration retains the highest-ID duplicate account
feedback row before adding uniqueness constraints; guest comments and pins are
not reassigned. A subsequent migration adds a nullable event recipient UID so
owner attribution remains distinct from private account delivery. Complete the
normal database upgrade before serving the new application code.

Review public-link, mail, and group policy before onboarding users. Keep guest
downloads and uploads disabled unless required. Set limits according to PHP,
proxy, storage, and worker capacity rather than relying on browser validation.
Expand Down
21 changes: 21 additions & 0 deletions docs/en/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,27 @@ pick, reject, label, comment, annotate, and save named selections. Guests do
not need Nextcloud accounts. Their identity and mutation token are stored in a
private browser session; clearing site data ends access to private feedback.

If you are signed in to Nextcloud, new feedback uses your account identity.
Earlier guest comments remain linked to their original guest identity; signing
in does not claim them. Account identity does not bypass gallery or link rules.
Uploads still require a guest session; signed-in reviewers can open the link
in a private browser window when they need to upload files.

**Feedback** opens **General comments**, newest first beneath the input.
The **Pins** tab groups point conversations into expandable rows. Expand a row
to read its replies, or use its separate open button to show the conversation
beside the pin. These panels do not dim the image. **Review state** shows both
the configured state name and its color indicator.
Each pin has its own conversation, even when two pins share the same position.
Replies remain available when the original comment is deleted; its text is
replaced with a deletion notice.

Zoom with the viewer controls, mouse wheel, or a touch pinch. On desktop, hold
the right mouse button and drag the zoomed image to pan. A right-click without
dragging keeps the browser context menu. Pins retain their image-relative
positions while zooming and panning. On touchscreens, drag with one finger
after zooming to reach the image edges. Dragging does not create a new pin.

Click or tap an image to place a numbered point and open its comment editor.
For keyboard placement, choose **Add point comment**, move the point with the
arrow keys, press Enter to write, or Escape to cancel. Unpinned comments remain
Expand Down
5 changes: 5 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export default [
'vue/custom-event-name-casing': ['error', 'kebab-case'],
'vue/define-macros-order': 'off',
'vue/first-attribute-linebreak': 'off',
'vue/no-bare-strings-in-template': ['error', {
// Numbers, icons, punctuation, units and protocol tokens are not prose.
// Everything containing translatable words must use the l10n helpers.
allowlist: ['/^[^\\p{L}]+$/u', '/^[A-Z]$/u', 'Ø', 'TXT', 'px'],
}],
'vue/v-on-event-hyphenation': ['error', 'always'],
},
},
Expand Down
Loading
Loading