From ef8a52f02c4afa10dbc5fccafb6030b1aedc936c Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Sun, 6 Sep 2026 21:34:04 +0200 Subject: [PATCH] fix(usage): introduce consented v4 without changing historical reports --- backend/__tests__/routes/adminUsage.test.js | 8 +- .../services/usageCoverageInventory.test.js | 14 +- .../usageIngressCompatibility.test.js | 4 +- backend/__tests__/services/usageV3.test.js | 99 +- backend/src/routes/adminUsage.js | 2 +- backend/src/usage/UsageService.js | 38 +- backend/src/usage/expandedSnapshot.js | 9 +- backend/src/usage/features.v3.json | 12 +- backend/src/usage/features.v4.json | 1531 ++++++++++++++ backend/src/usage/schema.cjs | 16 +- docs/FEATURE_COVERAGE.md | 73 +- docs/PRODUCT_USAGE.md | 23 +- docs/usage-coverage.v3.json | 6 +- docs/usage-coverage.v4.json | 1813 +++++++++++++++++ .../src/features/settings/UsageCatalog.tsx | 2 +- .../__tests__/ProductUsageTab.test.tsx | 6 +- .../features/settings/usageFeatures.v3.json | 12 +- .../features/settings/usageFeatures.v4.json | 1531 ++++++++++++++ frontend/src/i18n/locales/de.json | 20 +- frontend/src/i18n/locales/en.json | 20 +- frontend/src/services/productUsage.service.ts | 4 +- 21 files changed, 5046 insertions(+), 197 deletions(-) create mode 100644 backend/src/usage/features.v4.json create mode 100644 docs/usage-coverage.v4.json create mode 100644 frontend/src/features/settings/usageFeatures.v4.json diff --git a/backend/__tests__/routes/adminUsage.test.js b/backend/__tests__/routes/adminUsage.test.js index 6d515052..d69b504d 100644 --- a/backend/__tests__/routes/adminUsage.test.js +++ b/backend/__tests__/routes/adminUsage.test.js @@ -201,13 +201,13 @@ test('public/gallery paths and failed/unauthenticated admin operations never set expect(JSON.stringify(service.markUsed.mock.calls)).not.toContain('42'); }); -test('consent upgrade accepts exactly the explicit v2 choice, never extra fields', async () => { - for (const data of [{}, { consent_version: 'usage-consent.v1' }, { consent_version: 'usage-consent.v2', user: 'PRIVATE' }]) +test.each(['usage-consent.v2', 'usage-consent.v3', 'usage-consent.v4'])('consent upgrade accepts exactly the explicit %s choice, never extra fields', async (consent_version) => { + for (const data of [{}, { consent_version: 'usage-consent.v1' }, { consent_version: 'usage-consent.v5' }, { consent_version, user: 'PRIVATE' }]) await request(app).post('/api/admin/usage/consent').set('Authorization', `Bearer ${token('admin')}`).send(data).expect(400); expect(service.command).not.toHaveBeenCalled(); await request(app).post('/api/admin/usage/consent').set('Authorization', `Bearer ${token('admin')}`) - .send({ consent_version: 'usage-consent.v2' }).expect(200); - expect(service.command).toHaveBeenCalledWith('consent', { consent_version: 'usage-consent.v2' }); + .send({ consent_version }).expect(200); + expect(service.command).toHaveBeenCalledWith('consent', { consent_version }); }); test('only a backup that writes to the configured destination flags S3', () => { diff --git a/backend/__tests__/services/usageCoverageInventory.test.js b/backend/__tests__/services/usageCoverageInventory.test.js index f421fd32..e201a014 100644 --- a/backend/__tests__/services/usageCoverageInventory.test.js +++ b/backend/__tests__/services/usageCoverageInventory.test.js @@ -1,8 +1,8 @@ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); -const catalog = require('../../src/usage/features.v3.json'); -const inventory = require('../../../docs/usage-coverage.v3.json'); +const catalog = require('../../src/usage/features.v4.json'); +const inventory = require('../../../docs/usage-coverage.v4.json'); const protocol = require('../../src/usage/schema.cjs'); const { RULES_V2, capabilityKeys } = require('../../src/usage/capabilityRules'); const { acceptedUpload, capabilityEvidence } = require('../../src/usage/capabilityEvidence'); @@ -52,16 +52,22 @@ test('all current settings tabs have an explicit scope decision', () => { } }); -test('v1 wire validation is immutable; catalog, UI and translated descriptions agree', () => { +test('v1/v2/v3 wire validation is immutable; v4 catalog, UI and translated descriptions agree', () => { expect(crypto.createHash('sha256').update(JSON.stringify(protocol.envelopeSchemas['usage.v1'].properties)).digest('hex')) .toBe('cc8d0a865d21e36d2b24d23ca6aa8dd8d48000cb17aef83996786f70755bc922'); expect(crypto.createHash('sha256').update(JSON.stringify(protocol.envelopeSchemas['usage.v2'].properties)).digest('hex')) .toBe('159821cf45c1951016d33a4ed9ca55a0a7ee1b60dd715b803fcfed33e5c8a846'); expect(protocol.FEATURE_KEYS).toHaveLength(86); + expect(crypto.createHash('sha256').update(JSON.stringify(protocol.envelopeSchemas['usage.v3'].properties)).digest('hex')) + .toBe('93214702c79f47823f154544ebad6612dd313604f69e60b86de4c0e4c904571a'); + expect(protocol.FEATURE_KEYS).toContain('gallery_downloads_restricted'); + expect(protocol.FEATURE_KEYS).not.toContain('gallery_downloads'); + expect(protocol.ALL_FEATURE_KEYS).toHaveLength(87); + expect(protocol.ALL_FEATURE_KEYS).toContain('gallery_downloads'); expect(protocol.LEGACY_FEATURE_KEYS).toHaveLength(19); expect(inventory.configuration_only).toHaveLength(23); const frontend = path.resolve(__dirname, '../../../frontend'); - expect(JSON.parse(fs.readFileSync(path.join(frontend, 'src/features/settings/usageFeatures.v3.json')))).toEqual(catalog); + expect(JSON.parse(fs.readFileSync(path.join(frontend, 'src/features/settings/usageFeatures.v4.json')))).toEqual(catalog); for (const lang of ['en', 'de']) { const translated = JSON.parse(fs.readFileSync(path.join(frontend, `src/i18n/locales/${lang}.json`))).productUsage.catalog; for (const [key, value] of Object.entries(catalog.features)) { diff --git a/backend/__tests__/services/usageIngressCompatibility.test.js b/backend/__tests__/services/usageIngressCompatibility.test.js index 03e9531f..0fc3bb5d 100644 --- a/backend/__tests__/services/usageIngressCompatibility.test.js +++ b/backend/__tests__/services/usageIngressCompatibility.test.js @@ -7,13 +7,13 @@ const signHistorical = (packet, id) => { return { ...signed, signature: crypto.sign(null, Buffer.from(p.canonical(signed)), id.private_key).toString('base64url') }; }; -describe.each(['usage.v1', 'usage.v2', 'usage.v3'])('%s receiver compatibility never loosens the PicPeak sender', version => { +describe.each(['usage.v1', 'usage.v2', 'usage.v3', 'usage.v4'])('%s receiver compatibility never loosens the PicPeak sender', version => { test('complete original reports still sign and verify', () => { const id = p.generateIdentity(); const packet = p.makePacket(id, 'report', 1, { picpeak_version: '1.0.0', report_date: '2026-09-06', generated_at: new Date(now).toISOString(), features: p.emptyFeatures(version), gallery_layouts: [], - ...(version === 'usage.v3' ? { inventory: { galleries: 0, photos: 0 } } : {}), + ...(['usage.v3', 'usage.v4'].includes(version) ? { inventory: { galleries: 0, photos: 0 } } : {}), }, version); const envelope = p.signPacket(packet, id, new Date(now)); expect(p.verifyEnvelope(envelope, now)).toEqual(packet); diff --git a/backend/__tests__/services/usageV3.test.js b/backend/__tests__/services/usageV3.test.js index 16e5f251..13fd0a7c 100644 --- a/backend/__tests__/services/usageV3.test.js +++ b/backend/__tests__/services/usageV3.test.js @@ -62,7 +62,7 @@ for (const engine of ['sqlite3', ...(process.env.PICPEAK_PG_TEST_URL ? ['pg'] : expect(queries.filter(sql => /from ["`]photos["`]/.test(sql))).toEqual([expect.stringMatching(/select count\(\*\)/)]); expect(JSON.stringify(report)).not.toContain('PRIVATE'); const identity = p.generateIdentity(); - const envelope = p.signPacket(p.makePacket(identity, 'report', 1, report), identity, new Date(now)); + const envelope = p.signPacket(p.makePacket(identity, 'report', 1, report, 'usage.v3'), identity, new Date(now)); expect(p.verifyEnvelope(envelope, now).payload).toEqual(report); await db('photos').where({ id: 1 }).delete(); await db('events').where({ id: 1 }).delete(); @@ -126,69 +126,6 @@ for (const engine of ['sqlite3', ...(process.env.PICPEAK_PG_TEST_URL ? ['pg'] : expect((await snap({})).gallery_folders.configured).toBe(false); }); - test('gallery_downloads_restricted counts galleries with downloads switched off, and v2 keeps its old key', async () => { - // allow_downloads ships true, so the v2 key was true on every install - // with a gallery. Only switching downloads off is a decision. - const snap = version => expandSnapshot(db, { features: p.emptyFeatures('usage.v1'), flags: {}, used: new Set(), now, version }); - expect((await snap('usage.v3')).gallery_downloads_restricted).toEqual({ configured: false }); - expect(await snap('usage.v3')).not.toHaveProperty('gallery_downloads'); - await db('events').insert([{ allow_downloads: true }, { allow_downloads: true }]); - expect((await snap('usage.v3')).gallery_downloads_restricted.configured).toBe(false); - expect((await snap('usage.v2')).gallery_downloads).toEqual({ configured: true }); - expect(await snap('usage.v2')).not.toHaveProperty('gallery_downloads_restricted'); - await db('events').insert({ allow_downloads: false }); - expect((await snap('usage.v3')).gallery_downloads_restricted.configured).toBe(true); - expect((await snap('usage.v2')).gallery_downloads.configured).toBe(true); - }); - - test('a report queued under the replaced catalog is rebuilt in place, keeping its packet id', async () => { - const identity = p.generateIdentity(); - const posted = []; - const service = new UsageService(db, { - now: () => now, secret: 'v3-test-only-secret'.repeat(3), endpoint: 'http://127.0.0.1:9/', - fetch: async (_url, init) => { posted.push(JSON.parse(init.body).packet); throw new Error('collector unreachable'); }, - }); - service.binding = async () => 'b'.repeat(64); - const report = (features) => ({ - picpeak_version: '1.0.0', report_date: '2026-09-05', generated_at: new Date(now).toISOString(), - features, gallery_layouts: [], inventory: { galleries: 0, photos: 0 }, - }); - // The v3 catalog as it stood before gallery_downloads_restricted replaced gallery_downloads. - const { gallery_downloads_restricted, ...rest } = p.emptyFeatures('usage.v3'); - const stale = { ...rest, gallery_downloads: gallery_downloads_restricted }; - const seed = (payload) => db('product_usage_state').where({ id: 1 }).update({ - status: 'active', installation_id: identity.installation_id, public_key: identity.public_key, - private_key_encrypted: service.encrypt(identity.private_key), instance_binding: 'b'.repeat(64), - sequence: 1, last_error: null, attempts: 3, next_attempt_at: now + 60_000, - pending_packet: JSON.stringify(p.makePacket(identity, 'report', 2, payload, 'usage.v3')), - }); - - await seed(report(stale)); - const queued = JSON.parse((await db('product_usage_state').where({ id: 1 }).first()).pending_packet); - await service.deliver(await db('product_usage_state').where({ id: 1 }).first()); - // Sent once, under the current catalog, as the same packet. - expect(posted).toHaveLength(1); - expect(posted[0].packet_id).toBe(queued.packet_id); - expect(posted[0].sequence).toBe(2); - expect(posted[0].payload.features).toHaveProperty('gallery_downloads_restricted'); - expect(posted[0].payload.features).not.toHaveProperty('gallery_downloads'); - // The rebuilt packet is what stays queued for the ordinary retry path. - let row = await db('product_usage_state').where({ id: 1 }).first(); - const retained = JSON.parse(row.pending_packet); - expect(retained.packet_id).toBe(queued.packet_id); - expect(retained.payload.features).toHaveProperty('gallery_downloads_restricted'); - expect(row.status).toBe('active'); - expect(row.last_error).toBe('DELIVERY_FAILED'); - - // Narrow: a report that still validates is sent as queued, payload untouched. - await seed(report(p.emptyFeatures('usage.v3'))); - await service.deliver(await db('product_usage_state').where({ id: 1 }).first()); - expect(posted).toHaveLength(2); - expect(posted[1].payload.report_date).toBe('2026-09-05'); - row = await db('product_usage_state').where({ id: 1 }).first(); - expect(JSON.parse(row.pending_packet).payload.report_date).toBe('2026-09-05'); - }); - test('ML recognition is already represented without querying faces or results', async () => { await db('feature_flags').insert({ key: 'faces', value: true }); await client.markUsed(['face_recognition']); @@ -197,5 +134,39 @@ for (const engine of ['sqlite3', ...(process.env.PICPEAK_PG_TEST_URL ? ['pg'] : expect((await client.snapshot()).features.face_recognition).toEqual({ configured: false, used: true }); // No faces, people, embeddings or recognition-result tables exist in this fixture. }); + + test.each([ + [[], false, false], [[true], true, false], [[false], false, true], + [[true, false], true, true], [[null], false, false], + ])('v4 measures explicit restrictions independently from legacy allowed downloads: %p', async (values, allowed, restricted) => { + if (values.length) await db('events').insert(values.map(allow_downloads => ({ allow_downloads }))); + const queries = []; + db.on('query', q => queries.push(q)); + for (const version of ['usage.v1', 'usage.v2', 'usage.v3', 'usage.v4']) { + await db('product_usage_state').where({ id: 1 }).update({ consent_version: p.CONSENT_VERSIONS[version] }); + queries.length = 0; + const report = await client.preview(); + const downloadQueries = queries.filter(q => /where ["`]allow_downloads["`] =/.test(q.sql)); + if (version === 'usage.v4') { + expect(report.features.gallery_downloads_restricted).toEqual({ configured: restricted }); + expect(report.features).not.toHaveProperty('gallery_downloads'); + expect(report.inventory).toEqual({ galleries: values.length, photos: 0 }); + expect(downloadQueries).toHaveLength(1); + expect(downloadQueries[0].sql).toMatch(/select 1 as present/); + expect(Number(downloadQueries[0].bindings[0])).toBe(0); + } else { + expect(report.features).not.toHaveProperty('gallery_downloads_restricted'); + if (version === 'usage.v1') expect(downloadQueries).toHaveLength(0); + else { + expect(report.features.gallery_downloads).toEqual({ configured: allowed }); + expect(downloadQueries).toHaveLength(1); + expect(Number(downloadQueries[0].bindings[0])).toBe(1); + } + } + const identity = p.generateIdentity(); + const envelope = p.signPacket(p.makePacket(identity, 'report', 1, report, version), identity, new Date(now)); + expect(p.verifyEnvelope(envelope, now).payload).toEqual(report); + } + }); }); } diff --git a/backend/src/routes/adminUsage.js b/backend/src/routes/adminUsage.js index 1be02f29..5bd419c9 100644 --- a/backend/src/routes/adminUsage.js +++ b/backend/src/routes/adminUsage.js @@ -82,7 +82,7 @@ router.post( router.post( '/consent', wrap(async (req, res) => { - if (!req.body || Object.keys(req.body).length !== 1 || !['usage.v2', 'usage.v3'].includes(schemaForConsent(req.body.consent_version))) + if (!req.body || Object.keys(req.body).length !== 1 || !['usage.v2', 'usage.v3', 'usage.v4'].includes(schemaForConsent(req.body.consent_version))) throw new ValidationError('Explicit usage consent is required'); res.json(await service.command('consent', { consent_version: req.body.consent_version })); }) diff --git a/backend/src/usage/UsageService.js b/backend/src/usage/UsageService.js index 50595359..cb8318de 100644 --- a/backend/src/usage/UsageService.js +++ b/backend/src/usage/UsageService.js @@ -539,6 +539,9 @@ class UsageService { return value; } async deliver(state) { + // A lost receipt may mean this packet is already stored by the collector. + // Preserve its original schema, date and payload across binary upgrades; + // only re-sign transport metadata, never rebuild under the same packet ID. const packet = JSON.parse(state.pending_packet); if ( packet.action !== 'delete' && @@ -564,31 +567,14 @@ class UsageService { }); return null; } - const identity = { - public_key: state.public_key, - private_key: this.decrypt(state.private_key_encrypted) - }; - let envelope; - try { - envelope = signPacket(packet, identity, new Date(this.now())); - } catch (error) { - // A report queued under a catalog this build no longer ships — the - // upgrade replaced a key in the same wire version — fails local - // validation before anything is sent, and retrying cannot repair it. - // Left as it was it blocked every operation behind it for good. A - // report's payload is derived state, so rebuild it from the current - // snapshot in place. The packet ID and sequence are kept: a re-signed - // retry must reuse them so a lost acknowledgement does not duplicate - // data. Reports only — a stale registration, deletion or command is a - // genuine conflict and keeps the handling below. - if (error.code !== 'INVALID_PACKET' || packet.action !== 'report') throw error; - packet.payload = await this.snapshot(packet.schema_version); - state.pending_packet = JSON.stringify(packet); - await this.db('product_usage_state') - .where({ id: 1, status: 'active' }) - .update({ pending_packet: state.pending_packet }); - envelope = signPacket(packet, identity, new Date(this.now())); - } + const envelope = signPacket( + packet, + { + public_key: state.public_key, + private_key: this.decrypt(state.private_key_encrypted) + }, + new Date(this.now()) + ); // Last check before anything leaves. The guard at the top of this // method runs before the binding lookup above, which is asynchronous — // so a withdrawal that COMPLETED during it would previously still have @@ -974,7 +960,7 @@ class UsageService { generated_at: now, features: expanded, gallery_layouts: [...layouts].sort(), - ...(version === 'usage.v3' ? { inventory: await require('./inventorySnapshot').inventorySnapshot(this.db) } : {}) + ...(['usage.v3', 'usage.v4'].includes(version) ? { inventory: await require('./inventorySnapshot').inventorySnapshot(this.db) } : {}) }; } diff --git a/backend/src/usage/expandedSnapshot.js b/backend/src/usage/expandedSnapshot.js index b00019c6..46428d12 100644 --- a/backend/src/usage/expandedSnapshot.js +++ b/backend/src/usage/expandedSnapshot.js @@ -85,12 +85,7 @@ async function expandSnapshot(db, { features, flags, used, now, version = 'usage gallery_guest_uploads: 'allow_user_uploads', gallery_client_access: 'client_access_enabled', gallery_watermarks: 'watermark_downloads' })) result[key].configured = await enabled('events', column); - // allow_downloads ships true — column default and the create route both set - // it — so "at least one gallery allows downloads" is true on every install - // with a gallery and says nothing. v2 consented to that key under that - // description, so v2 keeps sending it unchanged. v3 asks the question that - // is actually a decision: has anyone switched downloads off. - if (version === 'usage.v3') { + if (version === 'usage.v4') { result.gallery_downloads_restricted.configured = await exists('events', ['allow_downloads'], (query) => query.where('allow_downloads', formatBoolean(false))); } else { @@ -127,7 +122,7 @@ async function expandSnapshot(db, { features, flags, used, now, version = 'usage query.where({ feedback_enabled: formatBoolean(true), [column]: formatBoolean(true) })); result.gallery_guest_accounts.configured = await exists('event_feedback_settings', ['feedback_enabled', 'identity_mode'], (query) => query.where('feedback_enabled', formatBoolean(true)).whereIn('identity_mode', ['guest', 'shared'])); - if (version === 'usage.v3') { + if (['usage.v3', 'usage.v4'].includes(version)) { result.gallery_folders.configured = await exists('photo_categories', ['is_folder', 'event_id'], (query) => query.where('is_folder', formatBoolean(true)).where((q) => q.whereNull('event_id').orWhereIn('event_id', db('events').select('id')))); result.transfer_upload_links.configured = Boolean(effective.transfers) && await exists('transfers', diff --git a/backend/src/usage/features.v3.json b/backend/src/usage/features.v3.json index 3ac1646e..acb1de0b 100644 --- a/backend/src/usage/features.v3.json +++ b/backend/src/usage/features.v3.json @@ -1182,18 +1182,18 @@ }, "used": null }, - "gallery_downloads_restricted": { + "gallery_downloads": { "category": "gallery_configuration", - "since": "usage.v3", + "since": "usage.v2", "measurement": "configuration", "configuration": "configuration", "name": { - "en": "Gallery downloads restricted", - "de": "Galerie-Downloads eingeschränkt" + "en": "Gallery downloads allowed", + "de": "Galerie-Downloads erlaubt" }, "configured": { - "en": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts.", - "de": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." }, "used": null }, diff --git a/backend/src/usage/features.v4.json b/backend/src/usage/features.v4.json new file mode 100644 index 00000000..f8aecf3c --- /dev/null +++ b/backend/src/usage/features.v4.json @@ -0,0 +1,1531 @@ +{ + "schema_version": "usage.v4", + "consent_version": "usage-consent.v4", + "features": { + "crm": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "clients", + "name": { + "en": "Client management", + "de": "Kundenverwaltung" + }, + "configured": { + "en": "The clients capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter clients ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_quotes": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "quotes", + "name": { + "en": "Quotes", + "de": "Angebote" + }, + "configured": { + "en": "The quotes capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter quotes ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_invoices": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "bills", + "name": { + "en": "Invoices", + "de": "Rechnungen" + }, + "configured": { + "en": "The bills capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter bills ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_contracts": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "contracts", + "name": { + "en": "Contracts", + "de": "Verträge" + }, + "configured": { + "en": "The contracts capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter contracts ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_projects": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "projects", + "name": { + "en": "Projects", + "de": "Projekte" + }, + "configured": { + "en": "The projects capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter projects ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_calendar": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "calendar", + "name": { + "en": "Admin calendar", + "de": "Admin-Kalender" + }, + "configured": { + "en": "The calendar capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter calendar ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_hours": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "hoursLogging", + "name": { + "en": "Hours logging", + "de": "Zeiterfassung" + }, + "configured": { + "en": "The hoursLogging capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter hoursLogging ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "customer_portal": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "customerPortal", + "name": { + "en": "Customer portal", + "de": "Kundenportal" + }, + "configured": { + "en": "The customerPortal capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter customerPortal ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting": { + "category": "accounting", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "accounting", + "name": { + "en": "Accounting", + "de": "Buchhaltung" + }, + "configured": { + "en": "The accounting capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter accounting ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "workflows": { + "category": "automation", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "workflows", + "name": { + "en": "Workflows", + "de": "Workflows" + }, + "configured": { + "en": "The workflows capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter workflows ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "newsletters": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "newsletters", + "name": { + "en": "Newsletters", + "de": "Newsletter" + }, + "configured": { + "en": "The newsletters capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter newsletters ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "face_recognition": { + "category": "gallery", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "faces", + "name": { + "en": "ML face recognition", + "de": "ML-Gesichtserkennung" + }, + "configured": { + "en": "The faces capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter faces ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "custom_css": { + "category": "appearance", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Custom CSS", + "de": "Eigenes CSS" + }, + "configured": { + "en": "Custom CSS is configured globally or applied through a gallery/theme/template; CSS text is not sent.", + "de": "Eigenes CSS ist global oder über Galerie/Theme/Vorlage eingerichtet; CSS-Inhalte werden nicht gesendet." + }, + "used": { + "en": "Applied CSS observed after consent, without observing visitors.", + "de": "Angewendetes CSS nach Zustimmung festgestellt, ohne Besucher zu beobachten." + } + }, + "oauth": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin SSO", + "de": "Admin-SSO" + }, + "configured": { + "en": "Admin OIDC is enabled and issuer/client configuration is present; no provider or credential values.", + "de": "Admin-OIDC ist aktiviert und die Anbieter-/Client-Konfiguration vorhanden; keine Anbieter- oder Zugangsdaten." + }, + "used": { + "en": "Successful admin SSO login; no account, identity-provider or session details.", + "de": "Erfolgreiche Admin-SSO-Anmeldung; keine Konto-, Anbieter- oder Sitzungsdetails." + } + }, + "smtp": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "SMTP delivery", + "de": "SMTP-Versand" + }, + "configured": { + "en": "An outgoing SMTP host is configured; no host, account, address or credentials.", + "de": "Ein ausgehender SMTP-Host ist konfiguriert; keine Hosts, Konten, Adressen oder Zugangsdaten." + }, + "used": { + "en": "A successful explicitly initiated admin SMTP test/send; no recipients or messages.", + "de": "Erfolgreicher ausdrücklich ausgelöster Admin-SMTP-Test/-Versand; keine Empfänger oder Nachrichten." + } + }, + "whatsapp": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "WhatsApp integration", + "de": "WhatsApp-Integration" + }, + "configured": { + "en": "The WhatsApp capability is enabled and a usable configuration is present; no phone number, token or template.", + "de": "Die WhatsApp-Funktion ist aktiviert und eine nutzbare Konfiguration vorhanden; keine Telefonnummer, Tokens oder Vorlagen." + }, + "used": { + "en": "Successful admin integration test; no recipient, message or delivery history.", + "de": "Erfolgreicher Admin-Integrationstest; keine Empfänger, Nachrichten oder Zustellverläufe." + } + }, + "backup": { + "category": "operations", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Backups", + "de": "Sicherungen" + }, + "configured": { + "en": "A full or database backup schedule is enabled; no schedule, path, storage sizes or backup names.", + "de": "Ein Voll- oder Datenbanksicherungsplan ist aktiviert; keine Zeitpläne, Pfade, Speichergrößen oder Sicherungsnamen." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "s3_storage": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 storage", + "de": "S3-Speicher" + }, + "configured": { + "en": "S3 is configured for media or backups; no bucket, endpoint, credentials or object keys.", + "de": "S3 ist für Medien oder Sicherungen konfiguriert; keine Buckets, Endpunkte, Zugangsdaten oder Objektschlüssel." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "share_mounts": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "External folders", + "de": "Externe Ordner" + }, + "configured": { + "en": "At least one gallery uses an external folder; only existence, no folder paths or gallery identifiers.", + "de": "Mindestens eine Galerie verwendet einen externen Ordner; nur Existenz, keine Ordnerpfade oder Galeriekennungen." + }, + "used": { + "en": "An admin initiated an accepted external-folder import; no scanned paths, files or counts.", + "de": "Ein Admin hat einen angenommenen Import aus einem externen Ordner ausgelöst; keine Pfade, Dateien oder Anzahlen." + } + }, + "galleries": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery management", + "de": "Galerieverwaltung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Media management", + "de": "Medienverwaltung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_exports": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Admin media export", + "de": "Admin-Medienexport" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_processing": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Media maintenance tools", + "de": "Medien-Wartungswerkzeuge" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "archive_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery archives", + "de": "Galeriearchive" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_sharing": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery sharing and QR", + "de": "Galeriefreigabe und QR" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "short_links": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Short links", + "de": "Kurzlinks" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_categories": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photo categories", + "de": "Fotokategorien" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "event_types": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Event types and presets", + "de": "Ereignistypen und Vorlagen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "slideshow": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "slideshow", + "name": { + "en": "Live slideshow", + "de": "Live-Diashow" + }, + "configured": { + "en": "The slideshow capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter slideshow ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "transfers": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "transfers", + "name": { + "en": "PicTransfer", + "de": "PicTransfer" + }, + "configured": { + "en": "The transfers capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter transfers ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "video_uploads": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin video uploads", + "de": "Admin-Video-Uploads" + }, + "configured": { + "en": "Video extensions are allowed in global upload settings; no uploaded-file metadata.", + "de": "Videoformate sind in den globalen Upload-Einstellungen erlaubt; keine Metadaten hochgeladener Dateien." + }, + "used": { + "en": "At least one admin video file was successfully stored/accepted; no names, formats, lengths, sizes or processing/visitor history.", + "de": "Mindestens eine Admin-Videodatei wurde erfolgreich gespeichert/angenommen; keine Namen, Formate, Längen, Größen oder Verarbeitungs-/Besucherverläufe." + } + }, + "camera_raw_uploads": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin camera RAW uploads", + "de": "Admin-Kamera-RAW-Uploads" + }, + "configured": { + "en": "Camera RAW (DNG) is allowed in global upload settings; no camera models or EXIF.", + "de": "Kamera-RAW (DNG) ist in den globalen Upload-Einstellungen erlaubt; keine Kameramodelle oder EXIF-Daten." + }, + "used": { + "en": "At least one admin camera RAW upload was stored/accepted; only the capability bit, no filename or metadata.", + "de": "Mindestens ein Admin-Kamera-RAW-Upload wurde gespeichert/angenommen; nur das Capability-Bit, keine Dateinamen oder Metadaten." + } + }, + "messaging": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "messaging", + "name": { + "en": "Messaging tools", + "de": "Nachrichtenwerkzeuge" + }, + "configured": { + "en": "The messaging capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter messaging ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "incoming_mail": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "IMAP intake", + "de": "IMAP-Empfang" + }, + "configured": { + "en": "Incoming mail is enabled and an IMAP configuration is present; no mailbox, server, folders or credentials.", + "de": "Eingehende E-Mails sind aktiviert und eine IMAP-Konfiguration vorhanden; keine Postfächer, Server, Ordner oder Zugangsdaten." + }, + "used": { + "en": "A successful explicit admin connection test or non-skipped manual poll; no background intake, messages, attachments or counts.", + "de": "Erfolgreicher expliziter Admin-Verbindungstest oder nicht übersprungener manueller Abruf; kein Hintergrundempfang, keine Nachrichten, Anhänge oder Anzahlen." + } + }, + "reminder_emails": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "flag": "reminderEmails", + "name": { + "en": "Automatic event reminders", + "de": "Automatische Ereigniserinnerungen" + }, + "configured": { + "en": "The reminderEmails capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter reminderEmails ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": null + }, + "email_templates": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Email templates", + "de": "E-Mail-Vorlagen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "email_webhook": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Email webhook transport", + "de": "E-Mail-Webhook-Transport" + }, + "configured": { + "en": "Both email webhook settings are present; no URL or secret.", + "de": "Beide E-Mail-Webhook-Einstellungen sind vorhanden; keine URL oder Geheimnisse." + }, + "used": { + "en": "Successful explicitly initiated admin send/test through the webhook transport; no recipients, messages or automatic deliveries.", + "de": "Erfolgreicher ausdrücklich ausgelöster Admin-Versand/-Test über den Webhook-Transport; keine Empfänger, Nachrichten oder automatischen Zustellungen." + } + }, + "accounting_incoming_invoices": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "incomingInvoices", + "name": { + "en": "Incoming invoices", + "de": "Eingangsrechnungen" + }, + "configured": { + "en": "The incomingInvoices capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter incomingInvoices ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_expenses": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "expenses", + "name": { + "en": "Expenses", + "de": "Ausgaben" + }, + "configured": { + "en": "The expenses capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter expenses ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_tax_report": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "taxReport", + "name": { + "en": "Tax reports", + "de": "Steuerberichte" + }, + "configured": { + "en": "The taxReport capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter taxReport ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_ledger": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "accounting", + "name": { + "en": "Ledger and accounting export", + "de": "Kontenplan und Buchhaltungsexport" + }, + "configured": { + "en": "The accounting capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter accounting ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_installments": { + "category": "crm", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Installment-plan tools", + "de": "Ratenplan-Werkzeuge" + }, + "configured": { + "en": "Quotes or invoices are enabled; no actual payment plans, amounts or statuses are inspected.", + "de": "Angebote oder Rechnungen sind aktiviert; tatsächliche Ratenpläne, Beträge oder Zahlungsstatus werden nicht geprüft." + }, + "used": { + "en": "An admin saved an installment plan; no dates, amounts, currencies, payment status or document IDs.", + "de": "Ein Admin hat einen Ratenplan gespeichert; keine Termine, Beträge, Währungen, Zahlungsstatus oder Dokumentkennungen." + } + }, + "document_templates": { + "category": "crm", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Document presets and blocks", + "de": "Dokumentvorlagen und Bausteine" + }, + "configured": { + "en": "Quotes or contracts are enabled, making document presets/blocks available; no template content.", + "de": "Angebote oder Verträge sind aktiviert und stellen Dokumentvorlagen/-bausteine bereit; keine Vorlageninhalte." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "cms": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "CMS pages", + "de": "CMS-Seiten" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "public_site": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Public landing page", + "de": "Öffentliche Startseite" + }, + "configured": { + "en": "The public landing-page setting is enabled; no page HTML, texts, domains or visitors.", + "de": "Die Einstellung für die öffentliche Startseite ist aktiviert; keine HTML-Inhalte, Texte, Domains oder Besucher." + }, + "used": null + }, + "branding": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Branding settings", + "de": "Branding-Einstellungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "seo_customization": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "SEO settings", + "de": "SEO-Einstellungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "admin_management": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "userManagement", + "name": { + "en": "Admin and role management", + "de": "Admin- und Rollenverwaltung" + }, + "configured": { + "en": "The userManagement capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter userManagement ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "api_integration": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "HTTP API integration", + "de": "HTTP-API-Integration" + }, + "configured": { + "en": "An unrevoked, unexpired API credential exists; no tokens, names, scopes or owner data.", + "de": "Ein nicht widerrufener und nicht abgelaufener API-Zugang existiert; keine Tokens, Namen, Berechtigungswerte oder Inhaberdaten." + }, + "used": { + "en": "Successful authenticated HTTP API capability call; only this bit, never URLs, request values, token/owner IDs or call counts. Does not trigger a report.", + "de": "Erfolgreicher authentifizierter HTTP-API-Funktionsaufruf; nur dieses Bit, niemals URLs, Requestwerte, Token-/Inhaberkennungen oder Aufrufzahlen. Löst keinen Report aus." + } + }, + "webhooks": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Outbound webhooks", + "de": "Ausgehende Webhooks" + }, + "configured": { + "en": "At least one active webhook is configured; no destinations, subscriptions, secrets or delivery logs.", + "de": "Mindestens ein aktiver Webhook ist konfiguriert; keine Ziele, Abonnements, Geheimnisse oder Zustellprotokolle." + }, + "used": { + "en": "Successful explicit admin webhook test/replay; no automatic or visitor-triggered deliveries.", + "de": "Erfolgreicher expliziter Admin-Webhook-Test/-Replay; keine automatischen oder durch Besucher ausgelösten Zustellungen." + } + }, + "restore": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Restore", + "de": "Wiederherstellung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "portable_backup": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Portable PicPeak export/import", + "de": "Portabler PicPeak-Export/Import" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "database_backup": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Database backups", + "de": "Datenbanksicherungen" + }, + "configured": { + "en": "Scheduled database backups are enabled; no schedules, file names or database contents.", + "de": "Geplante Datenbanksicherungen sind aktiviert; keine Zeitpläne, Dateinamen oder Datenbankinhalte." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "s3_photo_storage": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 media storage", + "de": "S3-Medienspeicher" + }, + "configured": { + "en": "S3 is the configured media backend and required credentials are present; no values are sent.", + "de": "S3 ist als Medienspeicher konfiguriert und erforderliche Zugangsdaten sind vorhanden; keine Werte werden gesendet." + }, + "used": { + "en": "Successful admin media storage/accepted upload to S3; no buckets, objects or sizes.", + "de": "Erfolgreiche Admin-Medienspeicherung/angenommener Upload nach S3; keine Buckets, Objekte oder Größen." + } + }, + "s3_backups": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 backup destination", + "de": "S3-Sicherungsziel" + }, + "configured": { + "en": "The configured backup destination is S3 with a bucket present; no bucket or credentials.", + "de": "Das konfigurierte Sicherungsziel ist S3 und ein Bucket ist angegeben; kein Bucketname oder Zugangsdaten." + }, + "used": { + "en": "An admin started a backup to the configured S3 destination or a successful S3 test upload; local exports never imply S3 use.", + "de": "Ein Admin hat eine Sicherung zum konfigurierten S3-Ziel oder einen erfolgreichen S3-Testupload gestartet; lokale Exporte implizieren keine S3-Nutzung." + } + }, + "analytics_dashboard": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "analytics", + "name": { + "en": "Existing analytics module", + "de": "Bestehendes Analytics-Modul" + }, + "configured": { + "en": "The analytics capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter analytics ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "feedback_moderation": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Feedback moderation", + "de": "Feedback-Moderation" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "guest_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Guest administration tools", + "de": "Gastverwaltungswerkzeuge" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_feedback_likes": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery likes enabled", + "de": "Galerie-Likes aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_ratings": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery star ratings enabled", + "de": "Galerie-Sternebewertungen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_comments": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery comments enabled", + "de": "Galerie-Kommentare aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_favorites": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery favorites enabled", + "de": "Galerie-Favoriten aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_reactions": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery reactions enabled", + "de": "Galerie-Reaktionen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_color_labels": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery color labels enabled", + "de": "Galerie-Farblabels aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_guest_accounts": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Guest identities enabled", + "de": "Gastidentitäten aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_guest_uploads": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Guest uploads enabled", + "de": "Gast-Uploads aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_downloads_restricted": { + "category": "gallery_configuration", + "since": "usage.v4", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery downloads restricted", + "de": "Galerie-Downloads eingeschränkt" + }, + "configured": { + "en": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts.", + "de": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "download_resolution_picker": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Download resolution picker enabled", + "de": "Download-Auflösungswahl aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_client_access": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Client access enabled", + "de": "Client-Zugang aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_watermarks": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Watermarks enabled", + "de": "Wasserzeichen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_image_protection": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Image protection enabled", + "de": "Bildschutz aktiviert" + }, + "configured": { + "en": "Enabled beyond the shipped defaults — a stronger protection level, canvas rendering, or right-click disabled — globally or on at least one gallery; only existence across the installation, never gallery IDs or counts.", + "de": "Über die Auslieferungsvorgaben hinaus aktiviert — höhere Schutzstufe, Canvas-Rendering oder deaktivierter Rechtsklick — global oder in mindestens einer Galerie; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_reveal": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery reveal enabled", + "de": "Galerie-Enthüllung aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_expiration": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery expiration configured", + "de": "Galerieablauf konfiguriert" + }, + "configured": { + "en": "At least one gallery has an expiry configured; no dates, gallery IDs or counts.", + "de": "Mindestens eine Galerie hat einen Ablauf konfiguriert; keine Daten, Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "photo_xmp_export": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "XMP export", + "de": "XMP-Export" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin successfully generated an XMP export; no sidecars, filenames, ratings, selections or counts.", + "de": "Ein Admin hat erfolgreich einen XMP-Export erstellt; keine Sidecars, Dateinamen, Bewertungen, Auswahlen oder Anzahlen." + } + }, + "photo_replacement": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photo replacement", + "de": "Fotoersetzung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin upload actually replaced a photo successfully; no filenames, matching values, IDs or counts.", + "de": "Ein Admin-Upload hat tatsächlich erfolgreich ein Foto ersetzt; keine Dateinamen, Abgleichwerte, Kennungen oder Anzahlen." + } + }, + "photo_admin_marks": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photographer marks", + "de": "Fotografenmarkierungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin successfully saved their own photo mark; no rating, color, photo or admin identity.", + "de": "Ein Admin hat eine eigene Fotomarkierung erfolgreich gespeichert; keine Bewertung, Farbe, Foto- oder Admin-Identität." + } + }, + "gallery_folders": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery folders configured", + "de": "Galerieordner eingerichtet" + }, + "configured": { + "en": "An applicable global or gallery category is configured as a folder; no names, contents, counts or visitor activity.", + "de": "Eine anwendbare globale oder Galerie-Kategorie ist als Ordner eingerichtet; keine Namen, Inhalte, Anzahlen oder Besucheraktivität." + }, + "used": null + }, + "transfer_upload_links": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "PicTransfer upload links enabled", + "de": "PicTransfer-Uploadlinks aktiviert" + }, + "configured": { + "en": "PicTransfer is enabled and a non-deleted transfer allows unexpired uploads; no links, tokens, dates, recipients or uploads.", + "de": "PicTransfer ist aktiviert und ein nicht gelöschter Transfer erlaubt noch gültige Uploads; keine Links, Tokens, Daten, Empfänger oder Uploads." + }, + "used": null + }, + "workflow_automation_enabled": { + "category": "automation", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Workflow automation enabled", + "de": "Workflow-Automation aktiviert" + }, + "configured": { + "en": "The workflows module and at least one workflow are enabled; no names, graphs, triggers, decisions or runs.", + "de": "Das Workflow-Modul und mindestens ein Workflow sind aktiviert; keine Namen, Graphen, Auslöser, Entscheidungen oder Durchläufe." + }, + "used": null + }, + "s3_auto_import": { + "category": "integration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "S3 automatic import enabled", + "de": "Automatischer S3-Import aktiviert" + }, + "configured": { + "en": "S3 media storage is configured and STORAGE_AUTO_IMPORT is enabled; no bucket, prefix, credentials, polling or imported objects.", + "de": "S3-Medienspeicher ist eingerichtet und STORAGE_AUTO_IMPORT aktiviert; keine Buckets, Präfixe, Zugangsdaten, Abfragen oder importierten Objekte." + }, + "used": null + }, + "crm_invoice_import": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "flag", + "name": { + "en": "Invoice import", + "de": "Rechnungsimport" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully imported an existing invoice; no PDF, invoice number, amount, currency, customer or payment status.", + "de": "Ein Admin hat eine bestehende Rechnung erfolgreich importiert; keine PDF, Rechnungsnummer, Beträge, Währung, Kunden oder Zahlungsstände." + }, + "flag": "bills" + }, + "crm_combined_billing": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "capability", + "name": { + "en": "Combined billing", + "de": "Kombinierte Abrechnung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully created a combined bill; no hours, expenses, customer, documents or financial values.", + "de": "Ein Admin hat erfolgreich eine kombinierte Abrechnung erstellt; keine Stunden, Ausgaben, Kunden, Dokumente oder Finanzwerte." + } + }, + "crm_monthly_billing_manual": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "flag", + "name": { + "en": "Manual monthly billing", + "de": "Manuelle Monatsabrechnung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully released a monthly draft for delivery; actual email delivery is not measured. No scheduler activity, customer, cadence or invoice values.", + "de": "Ein Admin hat einen Monatsentwurf erfolgreich zum Versand freigegeben; die tatsächliche E-Mail-Zustellung wird nicht gemessen. Keine Scheduler-Aktivität, Kunden, Intervalle oder Rechnungswerte." + }, + "flag": "bills" + }, + "crm_document_conversion": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "capability", + "name": { + "en": "Document conversion", + "de": "Dokumentumwandlung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully converted a quote or contract into a document or gallery; no content, links, acceptance states or automatic workflows.", + "de": "Ein Admin hat ein Angebot oder einen Vertrag erfolgreich in ein Dokument oder eine Galerie umgewandelt; keine Inhalte, Verknüpfungen, Annahmestände oder automatischen Workflows." + } + }, + "gallery_capture_date_sort": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Capture-date sorting configured", + "de": "Sortierung nach Aufnahmezeit eingerichtet" + }, + "configured": { + "en": "A gallery defaults to sorting by capture date; no capture dates, EXIF or visitor sorting actions.", + "de": "Eine Galerie sortiert standardmäßig nach Aufnahmezeit; keine Aufnahmedaten, EXIF oder Sortieraktionen von Besuchern." + }, + "used": null + }, + "download_original_filenames": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Original download filenames enabled", + "de": "Originaldateinamen für Downloads aktiviert" + }, + "configured": { + "en": "The original-download-filenames switch is enabled; no filenames or downloads are read or sent.", + "de": "Der Schalter für Originaldateinamen beim Download ist aktiviert; keine Dateinamen oder Downloads werden gelesen oder gesendet." + }, + "used": null + } + }, + "inventory": { + "galleries": { + "name": { + "en": "Stored galleries", + "de": "Gespeicherte Galerien" + }, + "description": { + "en": "Current number of gallery records, including drafts, inactive and archived galleries. Deleted galleries are excluded. One total for the installation, no breakdown or identifiers.", + "de": "Aktuelle Anzahl gespeicherter Galerien einschließlich Entwürfen, inaktiven und archivierten Galerien. Gelöschte Galerien zählen nicht. Eine Gesamtzahl der Installation, ohne Aufschlüsselung oder Kennungen." + } + }, + "photos": { + "name": { + "en": "Stored photo records", + "de": "Gespeicherte Fotoeinträge" + }, + "description": { + "en": "Current number of non-video photo records, including RAW, guest uploads and records of archived galleries. One total for the installation; not unique files, thumbnails, processing success or photo contents. Deleted records are excluded.", + "de": "Aktuelle Anzahl der Fotoeinträge ohne Videos, einschließlich RAW, Gast-Uploads und Einträgen archivierter Galerien. Eine Gesamtzahl der Installation; keine eindeutigen Dateien, Vorschaubilder, Verarbeitungserfolge oder Fotoinhalte. Gelöschte Einträge zählen nicht." + } + } + } +} diff --git a/backend/src/usage/schema.cjs b/backend/src/usage/schema.cjs index 0a8074ce..685e6d7f 100644 --- a/backend/src/usage/schema.cjs +++ b/backend/src/usage/schema.cjs @@ -2,10 +2,10 @@ // Vendored byte-identical in PicPeak. Existing wire versions stay immutable; // every expansion requires explicit consent to its own version. -const CATALOG = require("./features.v3.json"); -const CATALOGS = { "usage.v2": require("./features.v2.json"), "usage.v3": CATALOG }; -const CONSENT_VERSIONS = { "usage.v1": "usage-consent.v1", "usage.v2": "usage-consent.v2", "usage.v3": "usage-consent.v3" }; -const CURRENT_SCHEMA_VERSION = "usage.v3"; +const CATALOG = require("./features.v4.json"); +const CATALOGS = { "usage.v2": require("./features.v2.json"), "usage.v3": require("./features.v3.json"), "usage.v4": CATALOG }; +const CONSENT_VERSIONS = { "usage.v1": "usage-consent.v1", "usage.v2": "usage-consent.v2", "usage.v3": "usage-consent.v3", "usage.v4": "usage-consent.v4" }; +const CURRENT_SCHEMA_VERSION = "usage.v4"; const CURRENT_CONSENT_VERSION = CONSENT_VERSIONS[CURRENT_SCHEMA_VERSION]; const schemaForConsent = (consent) => Object.keys(CONSENT_VERSIONS).find((version) => CONSENT_VERSIONS[version] === consent); const schemaRank = (version) => Object.keys(CONSENT_VERSIONS).indexOf(version); @@ -19,6 +19,10 @@ const LEGACY_FEATURE_KEYS = [ "whatsapp", "backup", "s3_storage", "share_mounts", ]; const FEATURE_KEYS = Object.keys(CATALOG.features); +// Historical questions remain independently visible after a newer schema +// stops asking them. Never rename or invert a retained report's values. +const ALL_FEATURES = Object.assign({}, ...Object.values(CATALOGS).map(c => c.features)); +const ALL_FEATURE_KEYS = Object.keys(ALL_FEATURES); const LAYOUTS = ["grid", "masonry", "carousel", "timeline", "mosaic", "gallery-premium", "gallery-story", "other"]; const object = (properties, required = Object.keys(properties)) => ({ type: "object", additionalProperties: false, properties, required, @@ -45,7 +49,7 @@ const report = (version) => object({ key, object({ configured: boolean, ...(observesUse(key, version) ? { used: boolean } : {}) }) ]))), gallery_layouts: { type: "array", uniqueItems: true, maxItems: LAYOUTS.length, items: { enum: LAYOUTS } }, - ...(version === "usage.v3" ? { inventory: object(Object.fromEntries(INVENTORY_KEYS.map(key => [key, + ...(["usage.v3", "usage.v4"].includes(version) ? { inventory: object(Object.fromEntries(INVENTORY_KEYS.map(key => [key, { type: "integer", minimum: 0, maximum: MAX_INVENTORY_COUNT } ]))) } : {}), }); @@ -109,7 +113,7 @@ const ingressEnvelopeSchemas = Object.fromEntries(Object.entries(envelopeSchemas return [version, schema]; })); module.exports = { - FEATURE_KEYS, LEGACY_FEATURE_KEYS, LAYOUTS, CATALOG, CATALOGS, CONSENT_VERSIONS, + FEATURE_KEYS, ALL_FEATURES, ALL_FEATURE_KEYS, LEGACY_FEATURE_KEYS, LAYOUTS, CATALOG, CATALOGS, CONSENT_VERSIONS, schemaForConsent, schemaRank, INVENTORY_KEYS, MAX_INVENTORY_COUNT, CURRENT_SCHEMA_VERSION, CURRENT_CONSENT_VERSION, featureKeysFor, observesUse, emptyFeatures, envelopeSchema, envelopeSchemas, ingressEnvelopeSchemas, payloads, payloadsByVersion, diff --git a/docs/FEATURE_COVERAGE.md b/docs/FEATURE_COVERAGE.md index 977578ec..2ade3488 100644 --- a/docs/FEATURE_COVERAGE.md +++ b/docs/FEATURE_COVERAGE.md @@ -1,19 +1,21 @@ -# Product-usage coverage: usage.v3 +# Product-usage coverage: usage.v4 Reviewed PicPeak baseline: a5ff9264 (3.124.1-beta.0), plus the usage integration. The inventory covers 81 backend route families (80 product families plus usage), all 26 feature flags and all current settings tabs. This is capability coverage, -not instrumentation of every UI field. Source of truth: `usage-coverage.v3.json`. -The prior `usage-coverage.v2.json` and v2 wire catalog remain available unchanged. +not instrumentation of every UI field. Source of truth: `usage-coverage.v4.json`. +The prior v2/v3 inventories and all v1/v2/v3 wire catalogs/schemas remain unchanged. ## Data scope There are 86 capabilities: the original 19 in v1, 54 added in v2, and 13 added in -v3. 63 have configured/used booleans; 23 are configuration-only and omit `used`. +v3. v4 replaces `gallery_downloads` with `gallery_downloads_restricted`; the active +catalog remains at 86. 63 have configured/used booleans; 23 are configuration-only +and omit `used`. Historical views retain both questions separately (87 total keys). ML face recognition was already included: only effective availability and a successful authenticated admin capability operation, never biometric results. -v3 additionally reports exactly two installation-wide integers under `inventory`: +v3 and v4 report exactly two installation-wide integers under `inventory`: current gallery records and non-video photo records. Counts include drafts and retained archive records. They are not uploads, unique files or processing-success counts. No grouping by gallery, customer, user, content, media format or source. @@ -36,25 +38,22 @@ records can include guest uploads without observing individual upload actions. ## Consent and version transition -- v1 and v2 keep their exact wire schemas and feature allowlists. Updating code - does not grant consent or collect v3 markers/inventory for an older participant. -- The local EN/DE dialog lists all 86 capabilities and both inventory definitions. - An unchecked checkbox requires explicit consent to `usage-consent.v3`. -- A signed v3 consent command upgrades v1 or v2 without changing identity/history. - Prior queued operations finish first. Only a matching accepted receipt changes - local consent and atomically clears previous local usage markers. Lost receipts - remain retryable; a withdrawal always wins over a late upgrade receipt. -- Consent cannot downgrade. Older clients may continue sending their already - consented older report schema. Old reports retain their original raw envelopes. -- Collector first, client second. Older collectors reject v3 rather than accepting - undisclosed fields. No second report on the same UTC day; the first v3 report - may be on the next day of admin activity. -- Summary/history count the latest report per reporter (per period for history). - Missing older fields are unknown. Feature denominators use only supplied fields. - Inventory has `{ total, reported }` per key; zero with `reported=0` means unknown, - while zero with a positive denominator is a reported empty inventory. Never sum - every daily report as if it were a different installation. Opt-out removes - current and historical contributions, including these totals. +- v1/v2/v3 retain their exact sender and receiver contracts. Updating code does + not grant consent to v4 or start collecting the restricted-downloads signal. +- v4 asks whether at least one gallery has downloads disabled, instead of the + v2/v3 question whether at least one gallery allows them. These questions are + not complements: mixed galleries can make both true. Never invert old values. +- The EN/DE dialog explains the change and all 86 capabilities plus both totals. + An unchecked checkbox requires explicit `usage-consent.v4` consent. +- Prior queued packets finish unchanged: preserve packet ID, sequence, payload, + logical day and digest. Only issue time, nonce and signature change on retry. + Never rebuild an existing packet with a new snapshot under its old packet ID. +- Signed v4 consent can upgrade v1/v2/v3 without changing identity or history. + Only the matching accepted receipt changes local scope and resets local markers. + Lost receipts remain retryable; opt-out always wins over a late response. +- Collector first, client second. v1/v2/v3 remain accepted even after v4 consent. + Missing fields stay unknown. Historical aggregation uses the union of known + questions, while the active v4 sender still has exactly 86 fields. ## Every reported capability @@ -129,7 +128,7 @@ Definitions are shipped byte-identically in both applications as | `gallery_feedback_color_labels` — Gallery color labels enabled / Galerie-Farblabels aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | | `gallery_guest_accounts` — Guest identities enabled / Gastidentitäten aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | | `gallery_guest_uploads` — Guest uploads enabled / Gast-Uploads aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | -| `gallery_downloads_restricted` — Gallery downloads restricted / Galerie-Downloads eingeschränkt | usage.v3 | At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts. Replaces v2's `gallery_downloads`, which was true on every installation with a gallery because downloads ship enabled. | Not collected: configuration only. | +| `gallery_downloads_restricted` — Gallery downloads restricted / Galerie-Downloads eingeschränkt | usage.v4 | At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts. | Not collected (configuration only). | | `download_resolution_picker` — Download resolution picker enabled / Download-Auflösungswahl aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | | `gallery_client_access` — Client access enabled / Client-Zugang aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | | `gallery_watermarks` — Watermarks enabled / Wasserzeichen aktiviert | usage.v2 | Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts. | Not collected: configuration only. | @@ -167,11 +166,11 @@ Definitions are shipped byte-identically in both applications as | `adminBackup.js` | partial: `backup`, `portable_backup`, `restore`, `s3_storage`, `s3_backups` | Admin backup initiation, portable export/import and successful S3 roundtrip test. Local export never implies S3; names, schedules, sizes, contents and history excluded. | | `adminBusinessProfile.js` | excluded: no telemetry | Business identity/bank/tax-address configuration and VAT-code helper surface are not separate usage signals. Billing/accounting capabilities are covered without profiling the business. | | `adminCalendar.js` | partial: `crm`, `crm_calendar` | Authenticated admin calendar retrieval is capability use; no calendar entries, dates, recurrence, availability or bookings. | -| `adminCategories.js` | partial: `gallery_categories`, `gallery_folders` | Admin category CRUD; no names, descriptions, colors or ordering values. v3 adds only: gallery_folders. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminCategories.js` | partial: `gallery_categories`, `gallery_folders` | Admin category CRUD; no names, descriptions, colors or ordering values. v3 adds only: gallery_folders. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminCMS.js` | partial: `cms` | Admin CMS page CRUD only. Public page traffic, slug, HTML, text, links and media excluded. | -| `adminContracts.js` | partial: `crm`, `crm_contracts`, `document_templates`, `crm_document_conversion` | Admin contract/block operations only; no legal text, signatures, signing parties or customer signing events. v3 adds only: crm_document_conversion. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminContracts.js` | partial: `crm`, `crm_contracts`, `document_templates`, `crm_document_conversion` | Admin contract/block operations only; no legal text, signatures, signing parties or customer signing events. v3 adds only: crm_document_conversion. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminCssTemplates.js` | configuration: `custom_css` | Only existence of enabled applied CSS and locally observed application, not editing/viewing templates or any CSS text. | -| `adminCustomers.js` | partial: `crm`, `crm_hours`, `customer_portal`, `crm_combined_billing`, `crm_monthly_billing_manual` | Successful admin CRM/hour-entry/invitation operations only. No customer/account names, IDs, rates, billed hours, payment state or portal behavior. v3 adds only: crm_combined_billing, crm_monthly_billing_manual. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminCustomers.js` | partial: `crm`, `crm_hours`, `customer_portal`, `crm_combined_billing`, `crm_monthly_billing_manual` | Successful admin CRM/hour-entry/invitation operations only. No customer/account names, IDs, rates, billed hours, payment state or portal behavior. v3 adds only: crm_combined_billing, crm_monthly_billing_manual. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminDashboard.js` | partial: `analytics_dashboard` | Admin analytics capability endpoint only; no stats, activities, health/CRM polls, underlying visitor data or dashboard values. | | `adminDatabaseBackup.js` | partial: `backup`, `database_backup` | Admin database-backup initiation plus schedule-enabled boolean, no file data/history. | | `adminDeals.js` | partial: `crm`, `crm_installments` | Admin installment-plan changes only. No actual plans, invoice links, amounts, paid states or deal reporting. | @@ -179,7 +178,7 @@ Definitions are shipped byte-identically in both applications as | `adminEmail.js` | partial: `messaging`, `incoming_mail`, `smtp`, `email_templates`, `email_webhook`, `reminder_emails` | Admin message operation/template edit, actual successful manual send/test transport and non-skipped manual IMAP poll/test. Reminder flag configuration only. No automated sends/polls, received-message or recipient data, queue/log reads, mailbox addresses or templates. | | `adminEventRename.js` | partial: `galleries` | Successful rename only, not validate-rename. No former/new names or identifiers. | | `adminEvents/archiveBulk.js` | partial: `galleries`, `archive_management`, `photo_exports` | Admin archive/delete/restore/download initiation only; filenames, histories, storage sizes and polling excluded. | -| `adminEvents/crud.js` | partial: `galleries`, `gallery_guest_uploads`, `gallery_downloads_restricted`, `gallery_client_access`, `gallery_watermarks`, `gallery_reveal`, `gallery_expiration`, `gallery_sharing`, `custom_css`, `gallery_capture_date_sort` | Admin creation/edit/publish etc. set galleries; sharing has its own fixed key. Guest/download/protection/reveal/expiry are configuration only; themes contribute controlled layouts and CSS presence. No gallery metadata or guest action history. v3 adds only: gallery_capture_date_sort, and replaces gallery_downloads with gallery_downloads_restricted (downloads ship enabled, so only switching them off is a decision). Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminEvents/crud.js` | partial: `galleries`, `gallery_guest_uploads`, `gallery_downloads_restricted`, `gallery_client_access`, `gallery_watermarks`, `gallery_reveal`, `gallery_expiration`, `gallery_sharing`, `custom_css`, `gallery_capture_date_sort` | Admin creation/edit/publish etc. set galleries; sharing has its own fixed key. Guest/download/protection/reveal/expiry are configuration only; themes contribute controlled layouts and CSS presence. No gallery metadata or guest action history. v3 adds only: gallery_capture_date_sort. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminEvents/downloadResolutions.js` | configuration: `download_resolution_picker` | Only whether a picker is configured globally or in a gallery. No chosen resolution, download event or counts. | | `adminEvents/faces.js` | partial: `face_recognition` | Effective flag plus successful admin faces/people operation. No health polling, embeddings, names, groups, detections or visitor searches. | | `adminEvents/helpers.js` | composition: no telemetry | Router composition / helpers; decisions are recorded for each mounted family. | @@ -195,30 +194,30 @@ Definitions are shipped byte-identically in both applications as | `adminFeedback.js` | partial: `feedback_moderation`, `gallery_feedback_likes`, `gallery_feedback_ratings`, `gallery_feedback_comments`, `gallery_feedback_favorites`, `gallery_feedback_reactions`, `gallery_feedback_color_labels`, `gallery_guest_accounts` | Admin moderation/word-filter operations only. Visitor feedback is not observed. Master-enabled per-gallery feedback-option booleans only; no contents, ratings, likes, colors, identities or word lists. | | `adminGuests.js` | partial: `guest_management` | Admin guest management/export initiation only. No guest names, invitations, tokens, contact data, guest counts or visitor interactions. | | `adminImageSecurity.js` | configuration: `gallery_image_protection` | Only gallery/global technical protection configuration existence. No security events, blocked IPs, request counts, threat scores or admin monitoring access. | -| `adminInvoices.js` | partial: `crm`, `crm_invoices`, `crm_invoice_import` | Admin invoice operations only; no amounts, VAT/customer/payment values or payment-check responses. v3 adds only: crm_invoice_import. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminInvoices.js` | partial: `crm`, `crm_invoices`, `crm_invoice_import` | Admin invoice operations only; no amounts, VAT/customer/payment values or payment-check responses. v3 adds only: crm_invoice_import. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminLedger.js` | partial: `accounting`, `accounting_ledger` | Admin ledger-account/VAT/mapping edits and ledger export initiation only; no account/currency/VAT identifiers or exported records. | | `adminNewsletters.js` | partial: `newsletters` | Admin campaign changes/test/queue/cancel only. Recipient resolution, previews, subscriptions/unsubscribes, delivery/open/click data and automatic sending excluded. | | `adminNotifications.js` | excluded: no telemetry | Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose. | | `adminPhotoDimensions.js` | partial: `photo_processing` | Admin repair/regenerate/configuration initiation, never status polling or processing totals. | -| `adminPhotoExport.js` | partial: `photo_exports`, `photo_xmp_export` | Admin export initiation only; export filters, selected files, sizes and contents excluded. v3 adds only: photo_xmp_export. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | -| `adminPhotos.js` | partial: `photo_management`, `photo_exports`, `photo_processing`, `video_uploads`, `camera_raw_uploads`, `s3_storage`, `s3_photo_storage`, `photo_replacement`, `photo_admin_marks` | Successful admin edits/exports and accepted upload evidence only. Chunk init/status, failed uploads and public downloads excluded. Only video/RAW/S3 booleans survive, never file metadata/EXIF/content. v3 adds only: photo_replacement, photo_admin_marks. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminPhotoExport.js` | partial: `photo_exports`, `photo_xmp_export` | Admin export initiation only; export filters, selected files, sizes and contents excluded. v3 adds only: photo_xmp_export. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | +| `adminPhotos.js` | partial: `photo_management`, `photo_exports`, `photo_processing`, `video_uploads`, `camera_raw_uploads`, `s3_storage`, `s3_photo_storage`, `photo_replacement`, `photo_admin_marks` | Successful admin edits/exports and accepted upload evidence only. Chunk init/status, failed uploads and public downloads excluded. Only video/RAW/S3 booleans survive, never file metadata/EXIF/content. v3 adds only: photo_replacement, photo_admin_marks. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminProjects.js` | partial: `crm`, `crm_projects` | Admin project operations only; project/person names, business performance, metadata and totals excluded. | -| `adminQuotes.js` | partial: `crm`, `crm_quotes`, `document_templates`, `crm_document_conversion` | Admin quote/preset operations only; no quote content, prices, customer acceptance or signatures. v3 adds only: crm_document_conversion. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminQuotes.js` | partial: `crm`, `crm_quotes`, `document_templates`, `crm_document_conversion` | Admin quote/preset operations only; no quote content, prices, customer acceptance or signatures. v3 adds only: crm_document_conversion. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminRestore.js` | partial: `restore` | Admin restore initiation only, never file selection, content, progress, errors or timing. | | `adminRoles.js` | partial: `admin_management` | Admin account/role management capability; no names, permissions, role labels, password reset operations or active-user counts. Auth/self-profile endpoints excluded. | -| `adminSettings.js` | partial: `custom_css`, `oauth`, `smtp`, `backup`, `s3_storage`, `video_uploads`, `camera_raw_uploads`, `public_site`, `branding`, `seo_customization`, `slideshow`, `download_resolution_picker`, `gallery_watermarks`, `database_backup`, `s3_auto_import`, `download_original_filenames` | Only specified configuration presence/booleans and explicit branding/SEO/slideshow operations. Generic settings reads, security policies, passwords, storage data, SMTP/OIDC credentials, custom HTML/CSS/SEO values excluded. v3 adds only: s3_auto_import, download_original_filenames. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminSettings.js` | partial: `custom_css`, `oauth`, `smtp`, `backup`, `s3_storage`, `video_uploads`, `camera_raw_uploads`, `public_site`, `branding`, `seo_customization`, `slideshow`, `download_resolution_picker`, `gallery_watermarks`, `database_backup`, `s3_auto_import`, `download_original_filenames` | Only specified configuration presence/booleans and explicit branding/SEO/slideshow operations. Generic settings reads, security policies, passwords, storage data, SMTP/OIDC credentials, custom HTML/CSS/SEO values excluded. v3 adds only: s3_auto_import, download_original_filenames. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminShortUrls.js` | partial: `gallery_sharing`, `short_links` | Admin short-link creation/deletion only; link/token/click metadata excluded. | | `adminSystem.js` | excluded: no telemetry | Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose. | | `adminSystemHealth.js` | excluded: no telemetry | Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose. | | `adminTaxReport.js` | partial: `accounting`, `accounting_tax_report` | Admin tax report generation/export only; no totals, dates, tax regimes, geography or currency. | | `adminThumbnails.js` | partial: `photo_processing` | Admin repair/regenerate/configuration initiation, never status polling or processing totals. | -| `adminTransfers.js` | partial: `transfers`, `transfer_upload_links` | Admin transfer CRUD/files/link management/download only. Public recipients, received-file data, upload and download statistics excluded. v3 adds only: transfer_upload_links. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminTransfers.js` | partial: `transfers`, `transfer_upload_links` | Admin transfer CRUD/files/link management/download only. Public recipients, received-file data, upload and download statistics excluded. v3 adds only: transfer_upload_links. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `adminUsage.js` | excluded: no telemetry | Consent, inspection, export, feedback, voting, deletion and abandoning an unsignable deletion are explicit protocol operations; not product-use signals. Activity only triggers a due fixed report. | | `adminUsers.js` | partial: `admin_management` | Admin account/role management capability; no names, permissions, role labels, password reset operations or active-user counts. Auth/self-profile endpoints excluded. | | `adminVatCodes.js` | excluded: no telemetry | Business identity/bank/tax-address configuration and VAT-code helper surface are not separate usage signals. Billing/accounting capabilities are covered without profiling the business. | | `adminWebhooks.js` | partial: `webhooks` | Active configuration existence plus successful admin manual test/replay enqueue. Actual network delivery/results/subscriptions/destinations excluded. | | `adminWhatsapp.js` | partial: `whatsapp` | Effective configured sender and successful manual test only. No automated deliveries, phone numbers, templates or delivery statuses. | -| `adminWorkflows.js` | partial: `workflows`, `workflow_automation_enabled` | Admin workflow authoring/approval/test initiation only. Runtime triggers, payloads, execution frequency/results and public approvals excluded. v3 adds only: workflow_automation_enabled. Exact definitions are in features.v3.json; configuration-only signals never observe the public surface. | +| `adminWorkflows.js` | partial: `workflows`, `workflow_automation_enabled` | Admin workflow authoring/approval/test initiation only. Runtime triggers, payloads, execution frequency/results and public approvals excluded. v3 adds only: workflow_automation_enabled. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface. | | `analyticsTrackerProxy.js` | excluded: no telemetry | Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers. | | `auth.js` | partial: `oauth` | Only successful admin OIDC callback sets oauth. Password/gallery authentication, MFA, account claims and provider details excluded. | | `customer.js` | excluded: no telemetry | Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers. | @@ -247,6 +246,6 @@ Definitions are shipped byte-identically in both applications as - Automated newsletter, reminder, WhatsApp, webhook and IMAP jobs - Security/audit logs, biometric embeddings and recognition results - Operational health, migration, update and polling metrics -- Business/customer/user identities, geography, financial amounts and document contents; only explicit v3 inventory totals are permitted. +- Business/customer/user identities, geography, financial amounts and document contents; only explicit v3/v4 inventory totals are permitted. - Disabled calendarBooking and internal crmDevelopment; hosted future product #1111 - Image fragmentation: removed from current PicPeak, not a live capability diff --git a/docs/PRODUCT_USAGE.md b/docs/PRODUCT_USAGE.md index ac008881..66256bcd 100644 --- a/docs/PRODUCT_USAGE.md +++ b/docs/PRODUCT_USAGE.md @@ -1,23 +1,28 @@ # Optional product usage and feedback (#1110) -Current scope: **usage.v3**. The expanded catalog contains 86 capabilities +Current scope: **usage.v4**. v4 replaces `gallery_downloads` with +`gallery_downloads_restricted` after explicit `usage-consent.v4` consent. +All v1/v2/v3 schemas and queued packets remain immutable. Historical views +keep both questions separate; neither can be inferred by inverting the other. + +The inventory and other capabilities are unchanged from v3. The expanded catalog contains 86 capabilities (including ML face recognition and invoice import) and exactly two inventory totals: stored gallery records and non-video photo records, including drafts and retained archive records. No content, identifiers, per-gallery breakdowns, biometric results, financial values or visitor actions. -Existing v1/v2 participants retain their previous scope until explicit signed -v3 consent is confirmed. New count queries and markers do not run before that -confirmation. Collector must be deployed first. v1/v2 wire schemas and raw +Existing v1/v2/v3 participants retain their previous scope until explicit signed +v4 consent is confirmed. The new restriction query does not run before that +confirmation. Collector must be deployed first. v1/v2/v3 wire schemas and raw history remain unchanged. See [current coverage](FEATURE_COVERAGE.md) for all -definitions and [v3 inventory](usage-coverage.v3.json) for code boundaries. +definitions and [v4 inventory](usage-coverage.v4.json) for code boundaries. The sections below also document the historical v1/v2 implementation. Any statements excluding all gallery/photo counts describe those earlier versions; v3 adds only the two installation totals above. Backward compatibility is required for future changes. The collector continues -to accept v1/v2/v3 reports, including omitted or null measurements, using their +to accept v1/v2/v3/v4 reports, including omitted or null measurements, using their declared schema and original reporting day. Missing values remain unknown in aggregates and histories. PicPeak still emits complete reports through the unchanged sender schemas; only reception is more tolerant. Consent, field @@ -205,8 +210,8 @@ Public voting uses a backend-authorized 15-minute session, never the lookup hash ## Contract -The closed v1/v2 schemas are in `backend/src/usage/schema.cjs`, with signing in -`protocol.cjs`. Keep these and `features.v2.json` byte-identical to the collector's `protocol/` copies. +The closed v1/v2/v3/v4 schemas are in `backend/src/usage/schema.cjs`, with signing in +`protocol.cjs`. Keep these and all versioned `features.v*.json` catalogs byte-identical to the collector's `protocol/` copies. The collector serves its schema and complete source archive publicly. Aggregate projections and the complete dataset are accessible to participating installations only; raw reports require the installation's confidential lookup @@ -224,7 +229,7 @@ cursor. Deletion removes the source publication; operators must also remove any externally copied content and follow the documented backup/log policies. Used flags represent successful allowlisted admin capability calls since -consent to the current schema (v1: joining; v2: joining or explicit upgrade), +consent to the current schema (v1: joining; v2/v3/v4: joining or explicit upgrade), not visitor behavior or counts. OAuth marks successful admin SSO; applied CSS is observed during report generation. Gallery layouts are controlled enums extracted from event themes without IDs or counts. Other signals use the diff --git a/docs/usage-coverage.v3.json b/docs/usage-coverage.v3.json index d95d6474..b57150ba 100644 --- a/docs/usage-coverage.v3.json +++ b/docs/usage-coverage.v3.json @@ -22,7 +22,7 @@ "signals": [ "galleries", "gallery_guest_uploads", - "gallery_downloads_restricted", + "gallery_downloads", "gallery_client_access", "gallery_watermarks", "gallery_image_protection", @@ -569,7 +569,7 @@ "signals": [ "galleries", "gallery_guest_uploads", - "gallery_downloads_restricted", + "gallery_downloads", "gallery_client_access", "gallery_watermarks", "gallery_reveal", @@ -1774,7 +1774,7 @@ "gallery_feedback_color_labels", "gallery_guest_accounts", "gallery_guest_uploads", - "gallery_downloads_restricted", + "gallery_downloads", "download_resolution_picker", "gallery_client_access", "gallery_watermarks", diff --git a/docs/usage-coverage.v4.json b/docs/usage-coverage.v4.json new file mode 100644 index 00000000..a0d63c5a --- /dev/null +++ b/docs/usage-coverage.v4.json @@ -0,0 +1,1813 @@ +{ + "settings_tabs": { + "usage": { + "signals": [], + "reason": "Explicit consent/report inspection/feedback is not itself adoption telemetry." + }, + "features": { + "signals": [], + "reason": "Only the allowlisted effective feature booleans; no settings visit/save marker." + }, + "general": { + "signals": [ + "video_uploads", + "camera_raw_uploads", + "public_site", + "custom_css", + "download_original_filenames" + ], + "reason": "General technical upload/public-site/CSS configuration only; no title, URLs, limits, times, HTML or identity. v3 also reports two installation inventory totals separately, with explicit consent." + }, + "events": { + "signals": [ + "galleries", + "gallery_guest_uploads", + "gallery_downloads_restricted", + "gallery_client_access", + "gallery_watermarks", + "gallery_image_protection", + "gallery_reveal", + "gallery_expiration", + "gallery_capture_date_sort" + ], + "reason": "Gallery operations and disclosed configuration only; no event/customer values or visitor use. v3 also reports two installation inventory totals separately, with explicit consent." + }, + "eventTypes": { + "signals": [ + "event_types" + ], + "reason": "General admin event-type capability; no names or preset contents." + }, + "branding": { + "signals": [ + "branding", + "gallery_watermarks" + ], + "reason": "Branding operation and watermark configuration only; no branding text, logos or colors." + }, + "categories": { + "signals": [ + "gallery_categories", + "gallery_folders" + ], + "reason": "Category management capability only; no names/order/category membership." + }, + "thumbnails": { + "signals": [ + "photo_processing" + ], + "reason": "Admin processing settings/regeneration initiation only; no image data or progress." + }, + "downloads": { + "signals": [ + "download_resolution_picker" + ], + "reason": "Configuration boolean only; no actual download/selection behavior or resolution values." + }, + "styling": { + "signals": [ + "custom_css" + ], + "reason": "Presence/application only plus controlled gallery-layout enums, never CSS/theme values." + }, + "cms": { + "signals": [ + "cms", + "public_site" + ], + "reason": "Admin page editing capability/public-site enabled only; no HTML, slugs or traffic." + }, + "email": { + "signals": [ + "smtp", + "incoming_mail", + "messaging", + "email_templates", + "email_webhook" + ], + "reason": "Configuration and documented manual admin capability operations only; messages, recipients, automatic activity and mailbox values excluded." + }, + "moderation": { + "signals": [ + "feedback_moderation" + ], + "reason": "Admin moderation/word-filter capability, never feedback content or visitor behavior." + }, + "security": { + "signals": [], + "reason": "Excluded password/MFA/session/rate-limit/security profiles and operations." + }, + "sso": { + "signals": [ + "oauth" + ], + "reason": "Enabled/config-present and successful admin callback only; no claims/provider details." + }, + "imageSecurity": { + "signals": [ + "gallery_image_protection" + ], + "reason": "Configuration presence only; no blocked-IP/security analytics or monitoring history." + }, + "seo": { + "signals": [ + "seo_customization" + ], + "reason": "Admin SEO configuration operation only; no meta tags, URLs, robots or verification tokens." + }, + "apiTokens": { + "signals": [ + "api_integration" + ], + "reason": "Valid credential presence and one successful scoped API capability bit; no tokens/scopes/owner metadata." + }, + "webhooks": { + "signals": [ + "webhooks" + ], + "reason": "Active configuration and manual test/replay enqueue only; no delivery data." + }, + "status": { + "signals": [], + "reason": "Excluded operational health, diagnostics, resource data, update and storage polling." + }, + "analytics": { + "signals": [ + "analytics_dashboard" + ], + "reason": "Analytics capability and admin aggregate-view use only; no embedded analytics results/tracker IDs or visitors." + }, + "backup": { + "signals": [ + "backup", + "database_backup", + "portable_backup", + "restore", + "s3_backups" + ], + "reason": "Schedule presence/manual capability initiation only, no histories, sizes, paths or files." + }, + "businessProfile": { + "signals": [], + "reason": "Excluded business identity, bank accounts and addresses." + }, + "crm": { + "signals": [ + "crm", + "crm_quotes", + "crm_invoices", + "crm_projects", + "crm_hours", + "customer_portal", + "crm_installments", + "crm_invoice_import", + "crm_combined_billing", + "crm_monthly_billing_manual", + "crm_document_conversion" + ], + "reason": "Only coarse module capabilities; no policies/amounts/customer/payment values." + }, + "contracts": { + "signals": [ + "crm_contracts", + "document_templates" + ], + "reason": "Admin contract/template capability only; no legal text or signatures." + }, + "reminderTemplates": { + "signals": [ + "reminder_emails", + "email_templates" + ], + "reason": "Reminder flag configuration and admin template editing only; no automatic reminder sends/recipients/content." + }, + "accounting": { + "signals": [ + "accounting", + "accounting_incoming_invoices", + "accounting_expenses", + "accounting_tax_report", + "accounting_ledger" + ], + "reason": "Only module capabilities, no tax codes, rates, balances or business identity." + }, + "whatsapp": { + "signals": [ + "whatsapp" + ], + "reason": "Configured integration plus manual test only; no phone numbers, tokens or automatic delivery." + }, + "slideshow": { + "signals": [ + "slideshow" + ], + "reason": "Admin setup capability only; no kiosk viewers, slide progress or photos." + } + }, + "reviewed_picpeak_base": "a5ff9264 (3.124.1-beta.0)", + "schema_version": "usage.v3", + "purpose": "Product capability prioritization and installation gallery/photo totals; no contents, identities, per-entity breakdowns, action frequencies or visitor observations.", + "route_families": { + "acceptInvite.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "POST /:token" + ] + }, + "admin.js": { + "decision": "composition", + "signals": [], + "reason": "Router composition / helpers; decisions are recorded for each mounted family.", + "route_signatures": [] + }, + "adminApiTokens.js": { + "decision": "configuration", + "signals": [ + "api_integration" + ], + "reason": "Only existence of a valid credential; no marker from token listing/creation, no scope, owner, token, expiry date or last-used time.", + "route_signatures": [ + "GET /", + "POST /", + "DELETE /:id" + ] + }, + "adminArchives.js": { + "decision": "partial", + "signals": [ + "galleries", + "archive_management", + "photo_exports" + ], + "reason": "Admin archive/delete/restore/download initiation only; filenames, histories, storage sizes and polling excluded.", + "route_signatures": [ + "GET /", + "GET /:id", + "POST /:id/restore", + "GET /:id/download", + "DELETE /:id" + ] + }, + "adminAuth.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /profile", + "PUT /profile", + "POST /change-password", + "POST /logout", + "GET /mfa/status", + "POST /mfa/setup", + "POST /mfa/enable", + "POST /mfa/disable", + "POST /mfa/recovery-codes" + ] + }, + "adminBackup.js": { + "decision": "partial", + "signals": [ + "backup", + "portable_backup", + "restore", + "s3_storage", + "s3_backups" + ], + "reason": "Admin backup initiation, portable export/import and successful S3 roundtrip test. Local export never implies S3; names, schedules, sizes, contents and history excluded.", + "route_signatures": [ + "GET /config", + "PUT /config", + "GET /status", + "POST /run", + "GET /picpeak/export", + "POST /picpeak/import", + "GET /runs/:id", + "GET /files", + "DELETE /cleanup", + "POST /test-connection", + "GET /manifest/:backupRunId", + "POST /manifest/validate", + "GET /manifest/:backupRunId/download", + "GET /manifests/:backupId", + "GET /manifests/:backupId/download", + "POST /manifests/validate", + "GET /s3/buckets", + "GET /s3/files", + "DELETE /s3/cleanup", + "POST /s3/test-upload", + "GET /download/:backupId", + "GET /checksums", + "POST /estimate" + ] + }, + "adminBusinessProfile.js": { + "decision": "excluded", + "signals": [], + "reason": "Business identity/bank/tax-address configuration and VAT-code helper surface are not separate usage signals. Billing/accounting capabilities are covered without profiling the business.", + "route_signatures": [ + "GET /", + "GET /logo-diagnostic", + "POST /logo", + "DELETE /logo", + "PUT /", + "GET /bank-accounts", + "POST /bank-accounts", + "PUT /bank-accounts/:id", + "DELETE /bank-accounts/:id" + ] + }, + "adminCalendar.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_calendar" + ], + "reason": "Authenticated admin calendar retrieval is capability use; no calendar entries, dates, recurrence, availability or bookings.", + "route_signatures": [ + "GET /items" + ] + }, + "adminCategories.js": { + "decision": "partial", + "signals": [ + "gallery_categories", + "gallery_folders" + ], + "reason": "Admin category CRUD; no names, descriptions, colors or ordering values. v3 adds only: gallery_folders. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /global", + "GET /event/:eventId", + "POST /", + "PUT /:id", + "PUT /:id/hero", + "DELETE /:id", + "POST /reorder", + "DELETE /reorder/:eventId", + "POST /reorder-global" + ] + }, + "adminCMS.js": { + "decision": "partial", + "signals": [ + "cms" + ], + "reason": "Admin CMS page CRUD only. Public page traffic, slug, HTML, text, links and media excluded.", + "route_signatures": [ + "GET /pages", + "GET /pages/:slug", + "PUT /pages/:slug", + "POST /pages/:slug/logo", + "DELETE /pages/:slug/logo" + ] + }, + "adminContracts.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_contracts", + "document_templates", + "crm_document_conversion" + ], + "reason": "Admin contract/block operations only; no legal text, signatures, signing parties or customer signing events. v3 adds only: crm_document_conversion. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /blocks", + "POST /blocks", + "PUT /blocks/:id", + "DELETE /blocks/:id", + "GET /", + "POST /", + "GET /:id", + "PUT /:id", + "POST /:id/send", + "POST /:id/cancel", + "POST /:id/convert-to-event", + "POST /:id/convert-to-invoice", + "POST /:id/resend-signed", + "POST /:id/restamp-signatures", + "POST /:id/countersign", + "POST /:id/upload-signed-pdf", + "GET /:id/pdf", + "GET /:id/signed-pdf", + "GET /:id/audit-trail", + "GET /:id/verify-integrity", + "GET /:id/preview" + ] + }, + "adminCssTemplates.js": { + "decision": "configuration", + "signals": [ + "custom_css" + ], + "reason": "Only existence of enabled applied CSS and locally observed application, not editing/viewing templates or any CSS text.", + "route_signatures": [ + "GET /", + "GET /enabled", + "GET /:slotNumber", + "PUT /:slotNumber", + "POST /:slotNumber/reset" + ] + }, + "adminCustomers.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_hours", + "customer_portal", + "crm_combined_billing", + "crm_monthly_billing_manual" + ], + "reason": "Successful admin CRM/hour-entry/invitation operations only. No customer/account names, IDs, rates, billed hours, payment state or portal behavior. v3 adds only: crm_combined_billing, crm_monthly_billing_manual. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /", + "GET /search", + "GET /invitations", + "POST /invite", + "DELETE /invitations/:id", + "POST /", + "POST /:id/send-invite", + "GET /:id", + "PUT /:id", + "POST /:id/deactivate", + "POST /:id/reactivate", + "POST /:id/erase", + "POST /:id/password-reset", + "PUT /:id/events", + "GET /hour-entries/unbilled-summary", + "GET /:id/hour-entries", + "POST /:id/hour-entries", + "PUT /:id/hour-entries/:entryId", + "DELETE /:id/hour-entries/:entryId", + "POST /:id/hour-entries/bill", + "POST /:id/bill-combined", + "POST /:id/trigger-monthly-bill", + "GET /:id/monthly-draft" + ] + }, + "adminDashboard.js": { + "decision": "partial", + "signals": [ + "analytics_dashboard" + ], + "reason": "Admin analytics capability endpoint only; no stats, activities, health/CRM polls, underlying visitor data or dashboard values.", + "route_signatures": [ + "GET /stats", + "GET /activity", + "GET /health", + "GET /analytics", + "GET /crm-stats" + ] + }, + "adminDatabaseBackup.js": { + "decision": "partial", + "signals": [ + "backup", + "database_backup" + ], + "reason": "Admin database-backup initiation plus schedule-enabled boolean, no file data/history.", + "route_signatures": [ + "GET /status", + "PUT /config", + "POST /backup", + "GET /progress", + "GET /history", + "DELETE /cleanup", + "POST /test", + "GET /checksums" + ] + }, + "adminDeals.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_installments" + ], + "reason": "Admin installment-plan changes only. No actual plans, invoice links, amounts, paid states or deal reporting.", + "route_signatures": [ + "GET /:uuid/documents", + "PUT /:uuid/installment-plan" + ] + }, + "adminDev.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /email-templates", + "POST /send-test-email" + ] + }, + "adminEmail.js": { + "decision": "partial", + "signals": [ + "messaging", + "incoming_mail", + "smtp", + "email_templates", + "email_webhook", + "reminder_emails" + ], + "reason": "Admin message operation/template edit, actual successful manual send/test transport and non-skipped manual IMAP poll/test. Reminder flag configuration only. No automated sends/polls, received-message or recipient data, queue/log reads, mailbox addresses or templates.", + "route_signatures": [ + "GET /config", + "POST /config", + "GET /incoming-config", + "POST /incoming-config", + "POST /incoming-config/folders", + "POST /incoming-config/test", + "POST /incoming-config/roundtrip", + "POST /incoming-config/poll", + "GET /received", + "GET /received/:id", + "POST /item/:kind/:id/state", + "DELETE /item/:kind/:id", + "GET /accounts", + "GET /identities", + "POST /accounts", + "POST /accounts/test", + "POST /test", + "POST /flush-queue", + "GET /queue", + "GET /queue/:id", + "POST /send", + "GET /templates", + "GET /templates/:key", + "PUT /templates/:key", + "POST /templates", + "POST /templates/:key/preview" + ] + }, + "adminEventRename.js": { + "decision": "partial", + "signals": [ + "galleries" + ], + "reason": "Successful rename only, not validate-rename. No former/new names or identifiers.", + "route_signatures": [ + "POST /:eventId/rename", + "POST /:eventId/validate-rename" + ] + }, + "adminEvents/archiveBulk.js": { + "decision": "partial", + "signals": [ + "galleries", + "archive_management", + "photo_exports" + ], + "reason": "Admin archive/delete/restore/download initiation only; filenames, histories, storage sizes and polling excluded.", + "route_signatures": [ + "POST /:id/archive", + "POST /bulk-archive", + "POST /bulk-delete" + ] + }, + "adminEvents/crud.js": { + "decision": "partial", + "signals": [ + "galleries", + "gallery_guest_uploads", + "gallery_downloads_restricted", + "gallery_client_access", + "gallery_watermarks", + "gallery_reveal", + "gallery_expiration", + "gallery_sharing", + "custom_css", + "gallery_capture_date_sort" + ], + "reason": "Admin creation/edit/publish etc. set galleries; sharing has its own fixed key. Guest/download/protection/reveal/expiry are configuration only; themes contribute controlled layouts and CSS presence. No gallery metadata or guest action history. v3 adds only: gallery_capture_date_sort. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "POST /", + "GET /", + "GET /:id", + "POST /:id/send-gallery-email", + "POST /:id/publish", + "POST /:id/duplicate", + "PUT /:id", + "POST /:id/reveal", + "DELETE /:id", + "POST /:id/toggle-status", + "POST /:id/extend" + ] + }, + "adminEvents/downloadResolutions.js": { + "decision": "configuration", + "signals": [ + "download_resolution_picker" + ], + "reason": "Only whether a picker is configured globally or in a gallery. No chosen resolution, download event or counts.", + "route_signatures": [ + "GET /:id/download-resolutions", + "PATCH /:id/download-resolutions" + ] + }, + "adminEvents/faces.js": { + "decision": "partial", + "signals": [ + "face_recognition" + ], + "reason": "Effective flag plus successful admin faces/people operation. No health polling, embeddings, names, groups, detections or visitor searches.", + "route_signatures": [ + "GET /faces/health", + "GET /:id/faces", + "PATCH /:id/faces", + "GET /:id/people", + "GET /:id/people/suggestions", + "POST /:id/people/suggestions/dismiss", + "PATCH /:id/people/:personId", + "POST /:id/people/merge", + "POST /:id/people/:personId/split", + "GET /:id/people/:personId/faces", + "POST /:id/faces/rescan", + "POST /:id/faces/recluster", + "GET /faces/auto-categories", + "PUT /faces/auto-categories", + "POST /:id/faces/categorize", + "DELETE /:id/faces/categorize", + "DELETE /:id/faces" + ] + }, + "adminEvents/helpers.js": { + "decision": "composition", + "signals": [], + "reason": "Router composition / helpers; decisions are recorded for each mounted family.", + "route_signatures": [] + }, + "adminEvents/index.js": { + "decision": "composition", + "signals": [], + "reason": "Router composition / helpers; decisions are recorded for each mounted family.", + "route_signatures": [] + }, + "adminEvents/logo.js": { + "decision": "partial", + "signals": [ + "branding" + ], + "reason": "Successful admin logo operation only; image/filename/content excluded.", + "route_signatures": [ + "POST /:id/logo", + "DELETE /:id/logo" + ] + }, + "adminEvents/qr.js": { + "decision": "partial", + "signals": [ + "gallery_sharing" + ], + "reason": "Admin QR generation only; no scans, tokens or URLs.", + "route_signatures": [ + "GET /:id/qr", + "GET /:id/qr-print" + ] + }, + "adminEvents/resets.js": { + "decision": "partial", + "signals": [ + "galleries", + "gallery_sharing" + ], + "reason": "Admin gallery reset/sharing capability only; no password, recipient, token or reset statistics.", + "route_signatures": [ + "POST /:id/reset-password", + "POST /:id/resend-email" + ] + }, + "adminEvents/slideshow.js": { + "decision": "partial", + "signals": [ + "slideshow" + ], + "reason": "Admin generate/disable/configure only, never kiosk viewers or slide advances.", + "route_signatures": [ + "POST /:id/slideshow/generate", + "POST /:id/slideshow/disable", + "PATCH /:id/slideshow" + ] + }, + "adminEventTypes.js": { + "decision": "partial", + "signals": [ + "event_types" + ], + "reason": "Admin event-type CRUD; preset contents/names excluded.", + "route_signatures": [ + "GET /", + "GET /active", + "GET /:id", + "POST /", + "PUT /:id", + "DELETE /:id", + "POST /reorder" + ] + }, + "adminExpenses.js": { + "decision": "partial", + "signals": [ + "accounting", + "accounting_expenses", + "accounting_incoming_invoices" + ], + "reason": "Admin expense/inbound-invoice operations; no financial values, suppliers, mileage/location, dates, receipt files or OCR text.", + "route_signatures": [ + "GET /categories", + "POST /categories", + "PATCH /categories/:id", + "DELETE /categories/:id", + "POST /inbound", + "GET /inbound", + "GET /inbound/pending-summary", + "POST /inbound/bill-pending", + "GET /inbound/by-customer/:customerAccountId", + "GET /inbound/:id/file", + "GET /inbound/:id/page/:n", + "GET /inbound/:id", + "PATCH /inbound/:id", + "POST /inbound/:id/categorize", + "POST /inbound/:id/rebill", + "POST /inbound/:id/supplier-payment", + "GET /", + "POST /", + "GET /:id/proof", + "GET /:id", + "PATCH /:id", + "POST /:id/invoice", + "POST /:id/paid" + ] + }, + "adminExternalMedia.js": { + "decision": "partial", + "signals": [ + "share_mounts" + ], + "reason": "Only admin import operation; status/list/browse are not use. Snapshot checks external-path presence, never reports a path.", + "route_signatures": [ + "GET /list", + "POST /events/:id/import-external" + ] + }, + "adminFeatureFlags.js": { + "decision": "configuration", + "signals": [ + "crm", + "crm_quotes", + "crm_invoices", + "crm_contracts", + "crm_projects", + "crm_calendar", + "crm_hours", + "customer_portal", + "accounting", + "workflows", + "newsletters", + "face_recognition", + "slideshow", + "transfers", + "messaging", + "reminder_emails", + "accounting_incoming_invoices", + "accounting_expenses", + "accounting_tax_report", + "accounting_ledger", + "admin_management", + "analytics_dashboard" + ], + "reason": "Only allowlisted effective capability booleans. No marker from reading or saving feature flags. Disabled roadmap/developer flags excluded.", + "route_signatures": [ + "GET /", + "PUT /" + ] + }, + "adminFeedback.js": { + "decision": "partial", + "signals": [ + "feedback_moderation", + "gallery_feedback_likes", + "gallery_feedback_ratings", + "gallery_feedback_comments", + "gallery_feedback_favorites", + "gallery_feedback_reactions", + "gallery_feedback_color_labels", + "gallery_guest_accounts" + ], + "reason": "Admin moderation/word-filter operations only. Visitor feedback is not observed. Master-enabled per-gallery feedback-option booleans only; no contents, ratings, likes, colors, identities or word lists.", + "route_signatures": [ + "GET /events/:eventId/feedback-settings", + "PUT /events/:eventId/feedback-settings", + "GET /events/:eventId/feedback", + "PUT /feedback/:feedbackId/:action", + "DELETE /feedback/:feedbackId", + "GET /events/:eventId/feedback-analytics", + "GET /events/:eventId/feedback/export", + "GET /feedback/pending-moderation", + "GET /word-filters", + "POST /word-filters", + "PUT /word-filters/:id", + "DELETE /word-filters/:id" + ] + }, + "adminGuests.js": { + "decision": "partial", + "signals": [ + "guest_management" + ], + "reason": "Admin guest management/export initiation only. No guest names, invitations, tokens, contact data, guest counts or visitor interactions.", + "route_signatures": [ + "GET /events/:eventId/guests", + "GET /events/:eventId/guests/aggregate", + "GET /events/:eventId/guests/invites", + "POST /events/:eventId/guests/invites", + "DELETE /events/:eventId/guests/invites/:inviteId", + "GET /events/:eventId/guests/export-all", + "GET /events/:eventId/guests/:guestId", + "GET /events/:eventId/guests/:guestId/export", + "DELETE /events/:eventId/guests/:guestId", + "POST /events/:eventId/guests/:keepId/merge" + ] + }, + "adminImageSecurity.js": { + "decision": "configuration", + "signals": [ + "gallery_image_protection" + ], + "reason": "Only gallery/global technical protection configuration existence. No security events, blocked IPs, request counts, threat scores or admin monitoring access.", + "route_signatures": [ + "GET /settings", + "PUT /settings", + "GET /dashboard", + "GET /logs", + "GET /events/:eventId/access-logs", + "POST /block-ip", + "DELETE /logs/cleanup", + "GET /export" + ] + }, + "adminInvoices.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_invoices", + "crm_invoice_import" + ], + "reason": "Admin invoice operations only; no amounts, VAT/customer/payment values or payment-check responses. v3 adds only: crm_invoice_import. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /", + "GET /:id", + "POST /", + "POST /import", + "PUT /:id", + "GET /:id/rebill-proofs", + "POST /:id/send", + "POST /:id/mark-paid", + "POST /:id/send-reminder", + "POST /:id/test-payment-check", + "POST /:id/reissue", + "POST /:id/release-for-delivery", + "POST /:id/cancel", + "GET /:id/pdf", + "POST /preview", + "GET /:id/payment-log" + ] + }, + "adminLedger.js": { + "decision": "partial", + "signals": [ + "accounting", + "accounting_ledger" + ], + "reason": "Admin ledger-account/VAT/mapping edits and ledger export initiation only; no account/currency/VAT identifiers or exported records.", + "route_signatures": [ + "GET /accounts", + "POST /accounts", + "PATCH /accounts/:id", + "DELETE /accounts/:id", + "GET /vat-codes", + "POST /vat-codes", + "PATCH /vat-codes/:id", + "DELETE /vat-codes/:id", + "GET /mappings", + "PATCH /mappings/category/:id", + "PATCH /mappings/settings", + "GET /export" + ] + }, + "adminNewsletters.js": { + "decision": "partial", + "signals": [ + "newsletters" + ], + "reason": "Admin campaign changes/test/queue/cancel only. Recipient resolution, previews, subscriptions/unsubscribes, delivery/open/click data and automatic sending excluded.", + "route_signatures": [ + "GET /", + "GET /:id", + "GET /:id/recipients", + "POST /", + "PUT /:id", + "DELETE /:id", + "POST /:id/preview", + "POST /:id/recipients/resolve", + "POST /:id/test", + "POST /:id/queue", + "POST /:id/cancel" + ] + }, + "adminNotifications.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /", + "PUT /:id/read", + "PUT /read-all", + "DELETE /clear-all" + ] + }, + "adminPhotoDimensions.js": { + "decision": "partial", + "signals": [ + "photo_processing" + ], + "reason": "Admin repair/regenerate/configuration initiation, never status polling or processing totals.", + "route_signatures": [ + "POST /repair-dimensions", + "GET /repair-dimensions/status", + "POST /repair-capture-dates", + "GET /repair-capture-dates/status", + "POST /repair-orientation", + "GET /repair-orientation/status" + ] + }, + "adminPhotoExport.js": { + "decision": "partial", + "signals": [ + "photo_exports", + "photo_xmp_export" + ], + "reason": "Admin export initiation only; export filters, selected files, sizes and contents excluded. v3 adds only: photo_xmp_export. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /:eventId/filtered", + "GET /:eventId/filter-summary", + "POST /:eventId/export", + "GET /export-formats" + ] + }, + "adminPhotos.js": { + "decision": "partial", + "signals": [ + "photo_management", + "photo_exports", + "photo_processing", + "video_uploads", + "camera_raw_uploads", + "s3_storage", + "s3_photo_storage", + "photo_replacement", + "photo_admin_marks" + ], + "reason": "Successful admin edits/exports and accepted upload evidence only. Chunk init/status, failed uploads and public downloads excluded. Only video/RAW/S3 booleans survive, never file metadata/EXIF/content. v3 adds only: photo_replacement, photo_admin_marks. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "POST /:eventId/upload", + "GET /uploads/:upload_id/status", + "GET /uploads/:upload_id/stream", + "POST /photos/:photoId/retry", + "DELETE /:eventId/photos/:photoId", + "PUT /:eventId/photos/:photoId/mark", + "PATCH /:eventId/photos/:photoId", + "POST /:eventId/photos/bulk-delete", + "POST /:eventId/photos/bulk-update", + "GET /:eventId/photos/:photoId/download", + "GET /:eventId/photos", + "GET /:eventId/photo/:photoId", + "GET /:eventId/thumbnail/:photoId", + "GET /:eventId/preview/:photoId", + "GET /:eventId/debug", + "POST /:eventId/chunked-upload/init", + "POST /:eventId/chunked-upload/:uploadId/chunk/:chunkIndex", + "POST /:eventId/chunked-upload/:uploadId/complete", + "GET /:eventId/chunked-upload/:uploadId/status", + "DELETE /:eventId/chunked-upload/:uploadId" + ] + }, + "adminProjects.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_projects" + ], + "reason": "Admin project operations only; project/person names, business performance, metadata and totals excluded.", + "route_signatures": [ + "GET /", + "POST /", + "GET /:id", + "PUT /:id", + "POST /:id/events", + "POST /:id/quotes", + "POST /:id/contracts", + "GET /:id/overview", + "GET /email/:emailId/preview", + "POST /email/:emailId/resend", + "POST /email/:emailId/cancel", + "POST /email/:emailId/retry", + "POST /email/:emailId/send-now" + ] + }, + "adminQuotes.js": { + "decision": "partial", + "signals": [ + "crm", + "crm_quotes", + "document_templates", + "crm_document_conversion" + ], + "reason": "Admin quote/preset operations only; no quote content, prices, customer acceptance or signatures. v3 adds only: crm_document_conversion. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /", + "GET /:id", + "POST /", + "PUT /:id", + "POST /:id/send", + "POST /:id/duplicate", + "POST /:id/accept", + "POST /:id/decline", + "POST /:id/convert", + "POST /:id/convert-to-invoice", + "POST /:id/convert-to-contract", + "GET /:id/pdf", + "POST /preview", + "GET /presets/line-items", + "POST /presets/line-items", + "PUT /presets/line-items/:id", + "DELETE /presets/line-items/:id", + "GET /presets/payment-terms", + "POST /presets/payment-terms", + "PUT /presets/payment-terms/:id", + "DELETE /presets/payment-terms/:id", + "GET /presets/payment-net-days", + "POST /presets/payment-net-days", + "PUT /presets/payment-net-days/:id", + "DELETE /presets/payment-net-days/:id", + "GET /presets/payment-timing", + "POST /presets/payment-timing", + "PUT /presets/payment-timing/:id", + "DELETE /presets/payment-timing/:id" + ] + }, + "adminRestore.js": { + "decision": "partial", + "signals": [ + "restore" + ], + "reason": "Admin restore initiation only, never file selection, content, progress, errors or timing.", + "route_signatures": [ + "GET /status", + "POST /validate", + "POST /start", + "GET /progress", + "GET /run/:id", + "GET /run/:id/report", + "GET /available-backups", + "POST /list-backups", + "GET /settings", + "PUT /settings" + ] + }, + "adminRoles.js": { + "decision": "partial", + "signals": [ + "admin_management" + ], + "reason": "Admin account/role management capability; no names, permissions, role labels, password reset operations or active-user counts. Auth/self-profile endpoints excluded.", + "route_signatures": [ + "GET /", + "GET /permissions", + "POST /", + "POST /:id/clone", + "PUT /:id", + "DELETE /:id" + ] + }, + "adminSettings.js": { + "decision": "partial", + "signals": [ + "custom_css", + "oauth", + "smtp", + "backup", + "s3_storage", + "video_uploads", + "camera_raw_uploads", + "public_site", + "branding", + "seo_customization", + "slideshow", + "download_resolution_picker", + "gallery_watermarks", + "database_backup", + "s3_auto_import", + "download_original_filenames" + ], + "reason": "Only specified configuration presence/booleans and explicit branding/SEO/slideshow operations. Generic settings reads, security policies, passwords, storage data, SMTP/OIDC credentials, custom HTML/CSS/SEO values excluded. v3 adds only: s3_auto_import, download_original_filenames. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /", + "GET /:type", + "GET /customer-surface", + "PUT /customer-surface", + "PUT /accounting", + "PUT /slideshow", + "GET /downloads", + "PUT /downloads", + "GET /sso", + "PUT /sso", + "POST /sso/test", + "GET /:type", + "GET /password/complexity", + "PUT /branding", + "POST /logo", + "DELETE /logo", + "POST /branding/watermark-logo", + "PUT /theme", + "PUT /general", + "PUT /security", + "PUT /analytics", + "PUT /seo", + "GET /storage/info", + "POST /favicon", + "PUT /security/rate-limit", + "GET /public-site/default", + "POST /public-site/reset" + ] + }, + "adminShortUrls.js": { + "decision": "partial", + "signals": [ + "gallery_sharing", + "short_links" + ], + "reason": "Admin short-link creation/deletion only; link/token/click metadata excluded.", + "route_signatures": [ + "GET /events/:eventId/short-urls", + "POST /events/:eventId/short-urls", + "DELETE /short-urls/:id" + ] + }, + "adminSystem.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /version", + "GET /updates", + "GET /updates/whatsnew", + "POST /updates/whatsnew/seen", + "GET /updates/changelog", + "GET /updates/instructions", + "GET /status", + "GET /database", + "GET /updates/notifications", + "PUT /updates/notifications", + "POST /updates/notifications/send", + "POST /updates/notifications/check" + ] + }, + "adminSystemHealth.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /backup-integrity", + "GET /backup-coverage", + "GET /failures", + "POST /failures/email/:id/retry", + "DELETE /failures/email/:id" + ] + }, + "adminTaxReport.js": { + "decision": "partial", + "signals": [ + "accounting", + "accounting_tax_report" + ], + "reason": "Admin tax report generation/export only; no totals, dates, tax regimes, geography or currency.", + "route_signatures": [ + "GET /", + "GET /pdf", + "GET /csv" + ] + }, + "adminThumbnails.js": { + "decision": "partial", + "signals": [ + "photo_processing" + ], + "reason": "Admin repair/regenerate/configuration initiation, never status polling or processing totals.", + "route_signatures": [ + "GET /settings", + "PUT /settings", + "POST /regenerate", + "POST /regenerate-previews", + "GET /regenerate/status" + ] + }, + "adminTransfers.js": { + "decision": "partial", + "signals": [ + "transfers", + "transfer_upload_links" + ], + "reason": "Admin transfer CRUD/files/link management/download only. Public recipients, received-file data, upload and download statistics excluded. v3 adds only: transfer_upload_links. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /", + "POST /", + "GET /:id", + "PATCH /:id", + "DELETE /:id", + "POST /:id/files", + "DELETE /:id/files/:fileId", + "POST /:id/upload-files", + "DELETE /:id/extra-files/:extraId", + "GET /:id/extra-files/:extraId/download", + "POST /:id/upload-link", + "DELETE /:id/upload-link", + "GET /:id/download", + "GET /:id/uploads/:uploadId/download" + ] + }, + "adminUsage.js": { + "decision": "excluded", + "signals": [], + "reason": "Consent, inspection, export, feedback, voting, deletion and abandoning an unsignable deletion are explicit protocol operations; not product-use signals. Activity only triggers a due fixed report.", + "route_signatures": [ + "POST /activity", + "GET /", + "POST /dismiss", + "POST /enable", + "POST /consent", + "POST /disable", + "POST /abandon", + "POST /retry", + "GET /preview", + "GET /export", + "PUT /feedback-preferences", + "POST /feedback", + "POST /vote", + "POST /portal-session" + ] + }, + "adminUsers.js": { + "decision": "partial", + "signals": [ + "admin_management" + ], + "reason": "Admin account/role management capability; no names, permissions, role labels, password reset operations or active-user counts. Auth/self-profile endpoints excluded.", + "route_signatures": [ + "GET /me/permissions", + "GET /", + "GET /roles", + "GET /invitations", + "POST /invite", + "DELETE /invitations/:id", + "GET /:id", + "PUT /:id", + "POST /:id/deactivate", + "POST /:id/activate", + "DELETE /:id", + "POST /:id/reset-password" + ] + }, + "adminVatCodes.js": { + "decision": "excluded", + "signals": [], + "reason": "Business identity/bank/tax-address configuration and VAT-code helper surface are not separate usage signals. Billing/accounting capabilities are covered without profiling the business.", + "route_signatures": [ + "GET /" + ] + }, + "adminWebhooks.js": { + "decision": "partial", + "signals": [ + "webhooks" + ], + "reason": "Active configuration existence plus successful admin manual test/replay enqueue. Actual network delivery/results/subscriptions/destinations excluded.", + "route_signatures": [ + "GET /", + "POST /", + "GET /:id", + "PUT /:id", + "DELETE /:id", + "POST /:id/test", + "GET /:id/deliveries", + "GET /:id/deliveries/:deliveryId", + "POST /:id/deliveries/:deliveryId/replay" + ] + }, + "adminWhatsapp.js": { + "decision": "partial", + "signals": [ + "whatsapp" + ], + "reason": "Effective configured sender and successful manual test only. No automated deliveries, phone numbers, templates or delivery statuses.", + "route_signatures": [ + "GET /config", + "PUT /config", + "POST /test" + ] + }, + "adminWorkflows.js": { + "decision": "partial", + "signals": [ + "workflows", + "workflow_automation_enabled" + ], + "reason": "Admin workflow authoring/approval/test initiation only. Runtime triggers, payloads, execution frequency/results and public approvals excluded. v3 adds only: workflow_automation_enabled. Exact definitions are in features.v4.json; configuration-only signals never observe the public surface.", + "route_signatures": [ + "GET /approvals", + "POST /approvals/:id/:action", + "GET /runs/:runId/steps", + "GET /:id/runs", + "POST /:id/test-run", + "GET /", + "GET /:id", + "POST /", + "PUT /:id", + "PATCH /:id/enabled", + "DELETE /:id" + ] + }, + "analyticsTrackerProxy.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [] + }, + "auth.js": { + "decision": "partial", + "signals": [ + "oauth" + ], + "reason": "Only successful admin OIDC callback sets oauth. Password/gallery authentication, MFA, account claims and provider details excluded.", + "route_signatures": [ + "POST /admin/login", + "POST /admin/login/mfa", + "POST /logout", + "POST /gallery/verify", + "POST /gallery/:slug/client-login", + "POST /gallery/share-login", + "POST /gallery/logout", + "GET /session", + "POST /admin/change-password", + "POST /password-strength", + "GET /admin/sso/login", + "GET /admin/sso/callback" + ] + }, + "customer.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /events", + "GET /events/:slug/access-token", + "GET /profile", + "PUT /profile", + "GET /profile/marketing", + "PUT /profile/marketing", + "POST /profile/password", + "GET /quotes", + "GET /invoices", + "GET /quotes/:id/pdf", + "GET /invoices/:id/pdf", + "GET /contracts", + "GET /contracts/:id/pdf" + ] + }, + "customerAuth.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "POST /login", + "POST /logout", + "GET /session", + "GET /invite/:token", + "POST /accept-invite", + "GET /password-reset/:token", + "POST /password-reset" + ] + }, + "gallery.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /resolve/:identifier", + "GET /:slug/verify-token/:token", + "GET /:slug/info", + "GET /:slug/show/:token/session", + "GET /:slug/show/:token/state", + "GET /:slug/photos", + "GET /:slug/people", + "PATCH /:slug/photos/:photoId/visibility", + "PATCH /:slug/photos/visibility/bulk", + "GET /:slug/download/:photoId", + "GET /:slug/download-all", + "POST /:slug/download-selected", + "POST /:slug/download-jobs", + "GET /:slug/download-jobs/:token", + "GET /:slug/download-jobs/:token/file", + "POST /:slug/photo/:photoId/view", + "GET /:slug/photo/:photoId", + "GET /:slug/thumbnail/:photoId", + "GET /:slug/hero/:photoId", + "GET /:slug/preview/:photoId", + "GET /:slug/stats", + "POST /:eventId/upload", + "GET /:slug/uploads/status", + "GET /:slug/css-template" + ] + }, + "galleryFeedback.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:slug/feedback-settings", + "GET /:slug/photos/:photoId/feedback", + "POST /:slug/photos/:photoId/feedback", + "GET /:slug/feedback-summary", + "GET /:slug/my-feedback" + ] + }, + "galleryGuests.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "POST /:slug/guest", + "GET /:slug/guest/me", + "DELETE /:slug/guest/me", + "POST /:slug/guest/recover", + "POST /:slug/guest/verify", + "POST /:slug/guest/redeem" + ] + }, + "protectedImages.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:slug/photo/:photoId/view", + "POST /:slug/photo/:photoId/generate-secure-token", + "POST /:slug/photo/:photoId/generate-url", + "GET /:slug/photo/:photoId/signed/:token" + ] + }, + "publicCMS.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /pages/:slug" + ] + }, + "publicContracts.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "POST /:token/sign", + "POST /:token/upload-signed-pdf", + "GET /:token/pdf" + ] + }, + "publicFonts.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /" + ] + }, + "publicNewsletter.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /unsubscribe/:token", + "POST /unsubscribe/:token" + ] + }, + "publicPaymentCheck.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "POST /:token" + ] + }, + "publicQuotes.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "POST /:token/respond" + ] + }, + "publicSettings.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /" + ] + }, + "publicTransfer.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "GET /:token/download", + "GET /:token/download/:fileId" + ] + }, + "publicTransferUpload.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token", + "POST /:token" + ] + }, + "publicWorkflowApprovals.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "GET /:token/:action", + "POST /:token/:action" + ] + }, + "secureImages.js": { + "decision": "excluded", + "signals": [], + "reason": "Public/customer/gallery/visitor surface or existing optional third-party analytics proxy: no product-usage middleware, callbacks, counters or report triggers.", + "route_signatures": [ + "POST /:slug/generate-token", + "GET /:slug/secure/:photoId/:token", + "GET /:slug/secure-download/:photoId/:token", + "GET /security/stats" + ] + }, + "setup.js": { + "decision": "excluded", + "signals": [], + "reason": "Bootstrap, passwords/MFA/session/profile, per-person notifications, developer helpers and operational health/update/log polling are outside the prioritization purpose.", + "route_signatures": [ + "GET /status", + "POST /verify-token", + "POST /admin", + "POST /complete" + ] + }, + "v1/events.js": { + "decision": "partial", + "signals": [ + "api_integration" + ], + "reason": "Single bit after successful admin-owned scoped API authentication. No request/response values; API requests do not trigger reports.", + "route_signatures": [ + "POST /events", + "GET /events", + "GET /event-types", + "GET /events/:id", + "POST /events/:id/photos", + "GET /events/:id/share-link", + "GET /events/:id/photos" + ] + } + }, + "feature_flags": { + "accounting": { + "signals": [ + "accounting", + "accounting_ledger" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "analytics": { + "signals": [ + "analytics_dashboard" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "bills": { + "signals": [ + "crm_invoices", + "crm_invoice_import", + "crm_monthly_billing_manual" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "calendar": { + "signals": [ + "crm_calendar" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "calendarBooking": { + "signals": [], + "reason": "Excluded: disabled roadmap placeholder, not an implemented booking capability." + }, + "clients": { + "signals": [ + "crm" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "contracts": { + "signals": [ + "crm_contracts" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "crmDevelopment": { + "signals": [], + "reason": "Excluded: internal development/test helpers, not product adoption." + }, + "customerPortal": { + "signals": [ + "customer_portal" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "expenses": { + "signals": [ + "accounting_expenses" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "faces": { + "signals": [ + "face_recognition" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "galleries": { + "signals": [ + "galleries" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "hoursLogging": { + "signals": [ + "crm_hours" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "incomingInvoices": { + "signals": [ + "accounting_incoming_invoices", + "crm_combined_billing" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "incomingMail": { + "signals": [ + "incoming_mail" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "messaging": { + "signals": [ + "messaging" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "newsletters": { + "signals": [ + "newsletters" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "projects": { + "signals": [ + "crm_projects" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "quotes": { + "signals": [ + "crm_quotes", + "crm_document_conversion" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "reminderEmails": { + "signals": [ + "reminder_emails" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "slideshow": { + "signals": [ + "slideshow" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "taxReport": { + "signals": [ + "accounting_tax_report" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "transfers": { + "signals": [ + "transfers", + "transfer_upload_links" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "userManagement": { + "signals": [ + "admin_management" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "whatsapp": { + "signals": [ + "whatsapp" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + }, + "workflows": { + "signals": [ + "workflows", + "workflow_automation_enabled" + ], + "reason": "Only effective configuration boolean; dependency rules apply, no flag values/history beyond this boolean." + } + }, + "excluded_runtime": [ + "Gallery/customer/public events and optional website analytics", + "Automated newsletter, reminder, WhatsApp, webhook and IMAP jobs", + "Security/audit logs, biometric embeddings and recognition results", + "Operational health, migration, update and polling metrics", + "Business/customer/user identities, geography, financial amounts and document contents; only explicit v3 inventory totals are permitted.", + "Disabled calendarBooking and internal crmDevelopment; hosted future product #1111", + "Image fragmentation: removed from current PicPeak, not a live capability" + ], + "configuration_only": [ + "reminder_emails", + "public_site", + "gallery_feedback_likes", + "gallery_feedback_ratings", + "gallery_feedback_comments", + "gallery_feedback_favorites", + "gallery_feedback_reactions", + "gallery_feedback_color_labels", + "gallery_guest_accounts", + "gallery_guest_uploads", + "gallery_downloads_restricted", + "download_resolution_picker", + "gallery_client_access", + "gallery_watermarks", + "gallery_image_protection", + "gallery_reveal", + "gallery_expiration", + "gallery_folders", + "transfer_upload_links", + "workflow_automation_enabled", + "s3_auto_import", + "gallery_capture_date_sort", + "download_original_filenames" + ], + "inventory_totals": { + "galleries": { + "name": { + "en": "Stored galleries", + "de": "Gespeicherte Galerien" + }, + "description": { + "en": "Current number of gallery records, including drafts, inactive and archived galleries. Deleted galleries are excluded. One total for the installation, no breakdown or identifiers.", + "de": "Aktuelle Anzahl gespeicherter Galerien einschließlich Entwürfen, inaktiven und archivierten Galerien. Gelöschte Galerien zählen nicht. Eine Gesamtzahl der Installation, ohne Aufschlüsselung oder Kennungen." + } + }, + "photos": { + "name": { + "en": "Stored photo records", + "de": "Gespeicherte Fotoeinträge" + }, + "description": { + "en": "Current number of non-video photo records, including RAW, guest uploads and records of archived galleries. One total for the installation; not unique files, thumbnails, processing success or photo contents. Deleted records are excluded.", + "de": "Aktuelle Anzahl der Fotoeinträge ohne Videos, einschließlich RAW, Gast-Uploads und Einträgen archivierter Galerien. Eine Gesamtzahl der Installation; keine eindeutigen Dateien, Vorschaubilder, Verarbeitungserfolge oder Fotoinhalte. Gelöschte Einträge zählen nicht." + } + } + } +} diff --git a/frontend/src/features/settings/UsageCatalog.tsx b/frontend/src/features/settings/UsageCatalog.tsx index c64c0686..61ea6472 100644 --- a/frontend/src/features/settings/UsageCatalog.tsx +++ b/frontend/src/features/settings/UsageCatalog.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import catalog from './usageFeatures.v3.json'; +import catalog from './usageFeatures.v4.json'; /** Local, static disclosure: opening it never contacts the collector. */ export function UsageCatalog() { diff --git a/frontend/src/features/settings/__tests__/ProductUsageTab.test.tsx b/frontend/src/features/settings/__tests__/ProductUsageTab.test.tsx index 70673e08..32515507 100644 --- a/frontend/src/features/settings/__tests__/ProductUsageTab.test.tsx +++ b/frontend/src/features/settings/__tests__/ProductUsageTab.test.tsx @@ -69,7 +69,7 @@ beforeEach(() => { }; }); afterEach(cleanup); -it('shows every v2 signal locally before participation, without collector calls', async () => { +it('shows every v4 signal locally before participation, without collector calls', async () => { mount(); await screen.findByText('productUsage.catalogTitle'); expect(screen.getAllByRole('heading', { level: 4, hidden: true })).toHaveLength(87); @@ -77,8 +77,8 @@ it('shows every v2 signal locally before participation, without collector calls' expect(service.preview).not.toHaveBeenCalled(); expect(service.upgradeConsent).not.toHaveBeenCalled(); }); -it('existing v1 requires renewed unchecked consent; cancellation keeps v1 unchanged', async () => { - vi.mocked(service.status).mockResolvedValue({ ...status, status: 'active', consent_update_available: true }); +it.each(['usage.v1', 'usage.v2', 'usage.v3'])('existing %s requires renewed unchecked consent; cancellation keeps its scope unchanged', async (schema_version) => { + vi.mocked(service.status).mockResolvedValue({ ...status, status: 'active', schema_version, consent_update_available: true }); vi.mocked(service.upgradeConsent).mockResolvedValue({ delivered: false, queued: true, state: { ...status, status: 'active', pending_action: 'consent' } }); mount(); fireEvent.click(await screen.findByText('productUsage.reviewUpgrade')); diff --git a/frontend/src/features/settings/usageFeatures.v3.json b/frontend/src/features/settings/usageFeatures.v3.json index 3ac1646e..acb1de0b 100644 --- a/frontend/src/features/settings/usageFeatures.v3.json +++ b/frontend/src/features/settings/usageFeatures.v3.json @@ -1182,18 +1182,18 @@ }, "used": null }, - "gallery_downloads_restricted": { + "gallery_downloads": { "category": "gallery_configuration", - "since": "usage.v3", + "since": "usage.v2", "measurement": "configuration", "configuration": "configuration", "name": { - "en": "Gallery downloads restricted", - "de": "Galerie-Downloads eingeschränkt" + "en": "Gallery downloads allowed", + "de": "Galerie-Downloads erlaubt" }, "configured": { - "en": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts.", - "de": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." }, "used": null }, diff --git a/frontend/src/features/settings/usageFeatures.v4.json b/frontend/src/features/settings/usageFeatures.v4.json new file mode 100644 index 00000000..f8aecf3c --- /dev/null +++ b/frontend/src/features/settings/usageFeatures.v4.json @@ -0,0 +1,1531 @@ +{ + "schema_version": "usage.v4", + "consent_version": "usage-consent.v4", + "features": { + "crm": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "clients", + "name": { + "en": "Client management", + "de": "Kundenverwaltung" + }, + "configured": { + "en": "The clients capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter clients ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_quotes": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "quotes", + "name": { + "en": "Quotes", + "de": "Angebote" + }, + "configured": { + "en": "The quotes capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter quotes ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_invoices": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "bills", + "name": { + "en": "Invoices", + "de": "Rechnungen" + }, + "configured": { + "en": "The bills capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter bills ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_contracts": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "contracts", + "name": { + "en": "Contracts", + "de": "Verträge" + }, + "configured": { + "en": "The contracts capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter contracts ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_projects": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "projects", + "name": { + "en": "Projects", + "de": "Projekte" + }, + "configured": { + "en": "The projects capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter projects ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_calendar": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "calendar", + "name": { + "en": "Admin calendar", + "de": "Admin-Kalender" + }, + "configured": { + "en": "The calendar capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter calendar ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_hours": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "hoursLogging", + "name": { + "en": "Hours logging", + "de": "Zeiterfassung" + }, + "configured": { + "en": "The hoursLogging capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter hoursLogging ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "customer_portal": { + "category": "crm", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "customerPortal", + "name": { + "en": "Customer portal", + "de": "Kundenportal" + }, + "configured": { + "en": "The customerPortal capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter customerPortal ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting": { + "category": "accounting", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "accounting", + "name": { + "en": "Accounting", + "de": "Buchhaltung" + }, + "configured": { + "en": "The accounting capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter accounting ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "workflows": { + "category": "automation", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "workflows", + "name": { + "en": "Workflows", + "de": "Workflows" + }, + "configured": { + "en": "The workflows capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter workflows ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "newsletters": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "newsletters", + "name": { + "en": "Newsletters", + "de": "Newsletter" + }, + "configured": { + "en": "The newsletters capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter newsletters ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "face_recognition": { + "category": "gallery", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "faces", + "name": { + "en": "ML face recognition", + "de": "ML-Gesichtserkennung" + }, + "configured": { + "en": "The faces capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter faces ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "custom_css": { + "category": "appearance", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Custom CSS", + "de": "Eigenes CSS" + }, + "configured": { + "en": "Custom CSS is configured globally or applied through a gallery/theme/template; CSS text is not sent.", + "de": "Eigenes CSS ist global oder über Galerie/Theme/Vorlage eingerichtet; CSS-Inhalte werden nicht gesendet." + }, + "used": { + "en": "Applied CSS observed after consent, without observing visitors.", + "de": "Angewendetes CSS nach Zustimmung festgestellt, ohne Besucher zu beobachten." + } + }, + "oauth": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin SSO", + "de": "Admin-SSO" + }, + "configured": { + "en": "Admin OIDC is enabled and issuer/client configuration is present; no provider or credential values.", + "de": "Admin-OIDC ist aktiviert und die Anbieter-/Client-Konfiguration vorhanden; keine Anbieter- oder Zugangsdaten." + }, + "used": { + "en": "Successful admin SSO login; no account, identity-provider or session details.", + "de": "Erfolgreiche Admin-SSO-Anmeldung; keine Konto-, Anbieter- oder Sitzungsdetails." + } + }, + "smtp": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "SMTP delivery", + "de": "SMTP-Versand" + }, + "configured": { + "en": "An outgoing SMTP host is configured; no host, account, address or credentials.", + "de": "Ein ausgehender SMTP-Host ist konfiguriert; keine Hosts, Konten, Adressen oder Zugangsdaten." + }, + "used": { + "en": "A successful explicitly initiated admin SMTP test/send; no recipients or messages.", + "de": "Erfolgreicher ausdrücklich ausgelöster Admin-SMTP-Test/-Versand; keine Empfänger oder Nachrichten." + } + }, + "whatsapp": { + "category": "communication", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "WhatsApp integration", + "de": "WhatsApp-Integration" + }, + "configured": { + "en": "The WhatsApp capability is enabled and a usable configuration is present; no phone number, token or template.", + "de": "Die WhatsApp-Funktion ist aktiviert und eine nutzbare Konfiguration vorhanden; keine Telefonnummer, Tokens oder Vorlagen." + }, + "used": { + "en": "Successful admin integration test; no recipient, message or delivery history.", + "de": "Erfolgreicher Admin-Integrationstest; keine Empfänger, Nachrichten oder Zustellverläufe." + } + }, + "backup": { + "category": "operations", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Backups", + "de": "Sicherungen" + }, + "configured": { + "en": "A full or database backup schedule is enabled; no schedule, path, storage sizes or backup names.", + "de": "Ein Voll- oder Datenbanksicherungsplan ist aktiviert; keine Zeitpläne, Pfade, Speichergrößen oder Sicherungsnamen." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "s3_storage": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 storage", + "de": "S3-Speicher" + }, + "configured": { + "en": "S3 is configured for media or backups; no bucket, endpoint, credentials or object keys.", + "de": "S3 ist für Medien oder Sicherungen konfiguriert; keine Buckets, Endpunkte, Zugangsdaten oder Objektschlüssel." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "share_mounts": { + "category": "integration", + "since": "usage.v1", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "External folders", + "de": "Externe Ordner" + }, + "configured": { + "en": "At least one gallery uses an external folder; only existence, no folder paths or gallery identifiers.", + "de": "Mindestens eine Galerie verwendet einen externen Ordner; nur Existenz, keine Ordnerpfade oder Galeriekennungen." + }, + "used": { + "en": "An admin initiated an accepted external-folder import; no scanned paths, files or counts.", + "de": "Ein Admin hat einen angenommenen Import aus einem externen Ordner ausgelöst; keine Pfade, Dateien oder Anzahlen." + } + }, + "galleries": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery management", + "de": "Galerieverwaltung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Media management", + "de": "Medienverwaltung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_exports": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Admin media export", + "de": "Admin-Medienexport" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "photo_processing": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Media maintenance tools", + "de": "Medien-Wartungswerkzeuge" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "archive_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery archives", + "de": "Galeriearchive" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_sharing": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Gallery sharing and QR", + "de": "Galeriefreigabe und QR" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "short_links": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Short links", + "de": "Kurzlinks" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_categories": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photo categories", + "de": "Fotokategorien" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "event_types": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Event types and presets", + "de": "Ereignistypen und Vorlagen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "slideshow": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "slideshow", + "name": { + "en": "Live slideshow", + "de": "Live-Diashow" + }, + "configured": { + "en": "The slideshow capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter slideshow ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "transfers": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "transfers", + "name": { + "en": "PicTransfer", + "de": "PicTransfer" + }, + "configured": { + "en": "The transfers capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter transfers ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "video_uploads": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin video uploads", + "de": "Admin-Video-Uploads" + }, + "configured": { + "en": "Video extensions are allowed in global upload settings; no uploaded-file metadata.", + "de": "Videoformate sind in den globalen Upload-Einstellungen erlaubt; keine Metadaten hochgeladener Dateien." + }, + "used": { + "en": "At least one admin video file was successfully stored/accepted; no names, formats, lengths, sizes or processing/visitor history.", + "de": "Mindestens eine Admin-Videodatei wurde erfolgreich gespeichert/angenommen; keine Namen, Formate, Längen, Größen oder Verarbeitungs-/Besucherverläufe." + } + }, + "camera_raw_uploads": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Admin camera RAW uploads", + "de": "Admin-Kamera-RAW-Uploads" + }, + "configured": { + "en": "Camera RAW (DNG) is allowed in global upload settings; no camera models or EXIF.", + "de": "Kamera-RAW (DNG) ist in den globalen Upload-Einstellungen erlaubt; keine Kameramodelle oder EXIF-Daten." + }, + "used": { + "en": "At least one admin camera RAW upload was stored/accepted; only the capability bit, no filename or metadata.", + "de": "Mindestens ein Admin-Kamera-RAW-Upload wurde gespeichert/angenommen; nur das Capability-Bit, keine Dateinamen oder Metadaten." + } + }, + "messaging": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "messaging", + "name": { + "en": "Messaging tools", + "de": "Nachrichtenwerkzeuge" + }, + "configured": { + "en": "The messaging capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter messaging ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "incoming_mail": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "IMAP intake", + "de": "IMAP-Empfang" + }, + "configured": { + "en": "Incoming mail is enabled and an IMAP configuration is present; no mailbox, server, folders or credentials.", + "de": "Eingehende E-Mails sind aktiviert und eine IMAP-Konfiguration vorhanden; keine Postfächer, Server, Ordner oder Zugangsdaten." + }, + "used": { + "en": "A successful explicit admin connection test or non-skipped manual poll; no background intake, messages, attachments or counts.", + "de": "Erfolgreicher expliziter Admin-Verbindungstest oder nicht übersprungener manueller Abruf; kein Hintergrundempfang, keine Nachrichten, Anhänge oder Anzahlen." + } + }, + "reminder_emails": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "flag": "reminderEmails", + "name": { + "en": "Automatic event reminders", + "de": "Automatische Ereigniserinnerungen" + }, + "configured": { + "en": "The reminderEmails capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter reminderEmails ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": null + }, + "email_templates": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Email templates", + "de": "E-Mail-Vorlagen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "email_webhook": { + "category": "communication", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Email webhook transport", + "de": "E-Mail-Webhook-Transport" + }, + "configured": { + "en": "Both email webhook settings are present; no URL or secret.", + "de": "Beide E-Mail-Webhook-Einstellungen sind vorhanden; keine URL oder Geheimnisse." + }, + "used": { + "en": "Successful explicitly initiated admin send/test through the webhook transport; no recipients, messages or automatic deliveries.", + "de": "Erfolgreicher ausdrücklich ausgelöster Admin-Versand/-Test über den Webhook-Transport; keine Empfänger, Nachrichten oder automatischen Zustellungen." + } + }, + "accounting_incoming_invoices": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "incomingInvoices", + "name": { + "en": "Incoming invoices", + "de": "Eingangsrechnungen" + }, + "configured": { + "en": "The incomingInvoices capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter incomingInvoices ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_expenses": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "expenses", + "name": { + "en": "Expenses", + "de": "Ausgaben" + }, + "configured": { + "en": "The expenses capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter expenses ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_tax_report": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "taxReport", + "name": { + "en": "Tax reports", + "de": "Steuerberichte" + }, + "configured": { + "en": "The taxReport capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter taxReport ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "accounting_ledger": { + "category": "accounting", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "accounting", + "name": { + "en": "Ledger and accounting export", + "de": "Kontenplan und Buchhaltungsexport" + }, + "configured": { + "en": "The accounting capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter accounting ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "crm_installments": { + "category": "crm", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Installment-plan tools", + "de": "Ratenplan-Werkzeuge" + }, + "configured": { + "en": "Quotes or invoices are enabled; no actual payment plans, amounts or statuses are inspected.", + "de": "Angebote oder Rechnungen sind aktiviert; tatsächliche Ratenpläne, Beträge oder Zahlungsstatus werden nicht geprüft." + }, + "used": { + "en": "An admin saved an installment plan; no dates, amounts, currencies, payment status or document IDs.", + "de": "Ein Admin hat einen Ratenplan gespeichert; keine Termine, Beträge, Währungen, Zahlungsstatus oder Dokumentkennungen." + } + }, + "document_templates": { + "category": "crm", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Document presets and blocks", + "de": "Dokumentvorlagen und Bausteine" + }, + "configured": { + "en": "Quotes or contracts are enabled, making document presets/blocks available; no template content.", + "de": "Angebote oder Verträge sind aktiviert und stellen Dokumentvorlagen/-bausteine bereit; keine Vorlageninhalte." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "cms": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "CMS pages", + "de": "CMS-Seiten" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "public_site": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Public landing page", + "de": "Öffentliche Startseite" + }, + "configured": { + "en": "The public landing-page setting is enabled; no page HTML, texts, domains or visitors.", + "de": "Die Einstellung für die öffentliche Startseite ist aktiviert; keine HTML-Inhalte, Texte, Domains oder Besucher." + }, + "used": null + }, + "branding": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Branding settings", + "de": "Branding-Einstellungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "seo_customization": { + "category": "appearance", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "SEO settings", + "de": "SEO-Einstellungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "admin_management": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "userManagement", + "name": { + "en": "Admin and role management", + "de": "Admin- und Rollenverwaltung" + }, + "configured": { + "en": "The userManagement capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter userManagement ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "api_integration": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "HTTP API integration", + "de": "HTTP-API-Integration" + }, + "configured": { + "en": "An unrevoked, unexpired API credential exists; no tokens, names, scopes or owner data.", + "de": "Ein nicht widerrufener und nicht abgelaufener API-Zugang existiert; keine Tokens, Namen, Berechtigungswerte oder Inhaberdaten." + }, + "used": { + "en": "Successful authenticated HTTP API capability call; only this bit, never URLs, request values, token/owner IDs or call counts. Does not trigger a report.", + "de": "Erfolgreicher authentifizierter HTTP-API-Funktionsaufruf; nur dieses Bit, niemals URLs, Requestwerte, Token-/Inhaberkennungen oder Aufrufzahlen. Löst keinen Report aus." + } + }, + "webhooks": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Outbound webhooks", + "de": "Ausgehende Webhooks" + }, + "configured": { + "en": "At least one active webhook is configured; no destinations, subscriptions, secrets or delivery logs.", + "de": "Mindestens ein aktiver Webhook ist konfiguriert; keine Ziele, Abonnements, Geheimnisse oder Zustellprotokolle." + }, + "used": { + "en": "Successful explicit admin webhook test/replay; no automatic or visitor-triggered deliveries.", + "de": "Erfolgreicher expliziter Admin-Webhook-Test/-Replay; keine automatischen oder durch Besucher ausgelösten Zustellungen." + } + }, + "restore": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Restore", + "de": "Wiederherstellung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "portable_backup": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Portable PicPeak export/import", + "de": "Portabler PicPeak-Export/Import" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "database_backup": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "Database backups", + "de": "Datenbanksicherungen" + }, + "configured": { + "en": "Scheduled database backups are enabled; no schedules, file names or database contents.", + "de": "Geplante Datenbanksicherungen sind aktiviert; keine Zeitpläne, Dateinamen oder Datenbankinhalte." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "s3_photo_storage": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 media storage", + "de": "S3-Medienspeicher" + }, + "configured": { + "en": "S3 is the configured media backend and required credentials are present; no values are sent.", + "de": "S3 ist als Medienspeicher konfiguriert und erforderliche Zugangsdaten sind vorhanden; keine Werte werden gesendet." + }, + "used": { + "en": "Successful admin media storage/accepted upload to S3; no buckets, objects or sizes.", + "de": "Erfolgreiche Admin-Medienspeicherung/angenommener Upload nach S3; keine Buckets, Objekte oder Größen." + } + }, + "s3_backups": { + "category": "integration", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "presence", + "name": { + "en": "S3 backup destination", + "de": "S3-Sicherungsziel" + }, + "configured": { + "en": "The configured backup destination is S3 with a bucket present; no bucket or credentials.", + "de": "Das konfigurierte Sicherungsziel ist S3 und ein Bucket ist angegeben; kein Bucketname oder Zugangsdaten." + }, + "used": { + "en": "An admin started a backup to the configured S3 destination or a successful S3 test upload; local exports never imply S3 use.", + "de": "Ein Admin hat eine Sicherung zum konfigurierten S3-Ziel oder einen erfolgreichen S3-Testupload gestartet; lokale Exporte implizieren keine S3-Nutzung." + } + }, + "analytics_dashboard": { + "category": "operations", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "flag", + "flag": "analytics", + "name": { + "en": "Existing analytics module", + "de": "Bestehendes Analytics-Modul" + }, + "configured": { + "en": "The analytics capability switch is effectively enabled; only a boolean.", + "de": "Der Funktionsschalter analytics ist effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "feedback_moderation": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Feedback moderation", + "de": "Feedback-Moderation" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "guest_management": { + "category": "gallery", + "since": "usage.v2", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Guest administration tools", + "de": "Gastverwaltungswerkzeuge" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "A documented successful authenticated admin capability operation was observed since consent to this schema. No actor, operation history, parameters or counts.", + "de": "Eine dokumentierte erfolgreiche authentifizierte Admin-Funktionsoperation wurde seit Zustimmung zu diesem Schema beobachtet. Keine Person, Operationshistorie, Parameter oder Anzahlen." + } + }, + "gallery_feedback_likes": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery likes enabled", + "de": "Galerie-Likes aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_ratings": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery star ratings enabled", + "de": "Galerie-Sternebewertungen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_comments": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery comments enabled", + "de": "Galerie-Kommentare aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_favorites": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery favorites enabled", + "de": "Galerie-Favoriten aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_reactions": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery reactions enabled", + "de": "Galerie-Reaktionen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_feedback_color_labels": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery color labels enabled", + "de": "Galerie-Farblabels aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_guest_accounts": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Guest identities enabled", + "de": "Gastidentitäten aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_guest_uploads": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Guest uploads enabled", + "de": "Gast-Uploads aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_downloads_restricted": { + "category": "gallery_configuration", + "since": "usage.v4", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery downloads restricted", + "de": "Galerie-Downloads eingeschränkt" + }, + "configured": { + "en": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts.", + "de": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "download_resolution_picker": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Download resolution picker enabled", + "de": "Download-Auflösungswahl aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_client_access": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Client access enabled", + "de": "Client-Zugang aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_watermarks": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Watermarks enabled", + "de": "Wasserzeichen aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_image_protection": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Image protection enabled", + "de": "Bildschutz aktiviert" + }, + "configured": { + "en": "Enabled beyond the shipped defaults — a stronger protection level, canvas rendering, or right-click disabled — globally or on at least one gallery; only existence across the installation, never gallery IDs or counts.", + "de": "Über die Auslieferungsvorgaben hinaus aktiviert — höhere Schutzstufe, Canvas-Rendering oder deaktivierter Rechtsklick — global oder in mindestens einer Galerie; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_reveal": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery reveal enabled", + "de": "Galerie-Enthüllung aktiviert" + }, + "configured": { + "en": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts.", + "de": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "gallery_expiration": { + "category": "gallery_configuration", + "since": "usage.v2", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery expiration configured", + "de": "Galerieablauf konfiguriert" + }, + "configured": { + "en": "At least one gallery has an expiry configured; no dates, gallery IDs or counts.", + "de": "Mindestens eine Galerie hat einen Ablauf konfiguriert; keine Daten, Galeriekennungen oder Anzahlen." + }, + "used": null + }, + "photo_xmp_export": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "XMP export", + "de": "XMP-Export" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin successfully generated an XMP export; no sidecars, filenames, ratings, selections or counts.", + "de": "Ein Admin hat erfolgreich einen XMP-Export erstellt; keine Sidecars, Dateinamen, Bewertungen, Auswahlen oder Anzahlen." + } + }, + "photo_replacement": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photo replacement", + "de": "Fotoersetzung" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin upload actually replaced a photo successfully; no filenames, matching values, IDs or counts.", + "de": "Ein Admin-Upload hat tatsächlich erfolgreich ein Foto ersetzt; keine Dateinamen, Abgleichwerte, Kennungen oder Anzahlen." + } + }, + "photo_admin_marks": { + "category": "gallery", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "builtin", + "name": { + "en": "Photographer marks", + "de": "Fotografenmarkierungen" + }, + "configured": { + "en": "Built-in capability is available; this is not evidence of use.", + "de": "Integrierte Funktion ist verfügbar; dies ist kein Nutzungsnachweis." + }, + "used": { + "en": "An admin successfully saved their own photo mark; no rating, color, photo or admin identity.", + "de": "Ein Admin hat eine eigene Fotomarkierung erfolgreich gespeichert; keine Bewertung, Farbe, Foto- oder Admin-Identität." + } + }, + "gallery_folders": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Gallery folders configured", + "de": "Galerieordner eingerichtet" + }, + "configured": { + "en": "An applicable global or gallery category is configured as a folder; no names, contents, counts or visitor activity.", + "de": "Eine anwendbare globale oder Galerie-Kategorie ist als Ordner eingerichtet; keine Namen, Inhalte, Anzahlen oder Besucheraktivität." + }, + "used": null + }, + "transfer_upload_links": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "PicTransfer upload links enabled", + "de": "PicTransfer-Uploadlinks aktiviert" + }, + "configured": { + "en": "PicTransfer is enabled and a non-deleted transfer allows unexpired uploads; no links, tokens, dates, recipients or uploads.", + "de": "PicTransfer ist aktiviert und ein nicht gelöschter Transfer erlaubt noch gültige Uploads; keine Links, Tokens, Daten, Empfänger oder Uploads." + }, + "used": null + }, + "workflow_automation_enabled": { + "category": "automation", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Workflow automation enabled", + "de": "Workflow-Automation aktiviert" + }, + "configured": { + "en": "The workflows module and at least one workflow are enabled; no names, graphs, triggers, decisions or runs.", + "de": "Das Workflow-Modul und mindestens ein Workflow sind aktiviert; keine Namen, Graphen, Auslöser, Entscheidungen oder Durchläufe." + }, + "used": null + }, + "s3_auto_import": { + "category": "integration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "S3 automatic import enabled", + "de": "Automatischer S3-Import aktiviert" + }, + "configured": { + "en": "S3 media storage is configured and STORAGE_AUTO_IMPORT is enabled; no bucket, prefix, credentials, polling or imported objects.", + "de": "S3-Medienspeicher ist eingerichtet und STORAGE_AUTO_IMPORT aktiviert; keine Buckets, Präfixe, Zugangsdaten, Abfragen oder importierten Objekte." + }, + "used": null + }, + "crm_invoice_import": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "flag", + "name": { + "en": "Invoice import", + "de": "Rechnungsimport" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully imported an existing invoice; no PDF, invoice number, amount, currency, customer or payment status.", + "de": "Ein Admin hat eine bestehende Rechnung erfolgreich importiert; keine PDF, Rechnungsnummer, Beträge, Währung, Kunden oder Zahlungsstände." + }, + "flag": "bills" + }, + "crm_combined_billing": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "capability", + "name": { + "en": "Combined billing", + "de": "Kombinierte Abrechnung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully created a combined bill; no hours, expenses, customer, documents or financial values.", + "de": "Ein Admin hat erfolgreich eine kombinierte Abrechnung erstellt; keine Stunden, Ausgaben, Kunden, Dokumente oder Finanzwerte." + } + }, + "crm_monthly_billing_manual": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "flag", + "name": { + "en": "Manual monthly billing", + "de": "Manuelle Monatsabrechnung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully released a monthly draft for delivery; actual email delivery is not measured. No scheduler activity, customer, cadence or invoice values.", + "de": "Ein Admin hat einen Monatsentwurf erfolgreich zum Versand freigegeben; die tatsächliche E-Mail-Zustellung wird nicht gemessen. Keine Scheduler-Aktivität, Kunden, Intervalle oder Rechnungswerte." + }, + "flag": "bills" + }, + "crm_document_conversion": { + "category": "crm", + "since": "usage.v3", + "measurement": "configuration_and_use", + "configuration": "capability", + "name": { + "en": "Document conversion", + "de": "Dokumentumwandlung" + }, + "configured": { + "en": "The required product capabilities are effectively enabled; only a boolean.", + "de": "Die erforderlichen Produktfunktionen sind effektiv aktiviert; nur ein Wahrheitswert." + }, + "used": { + "en": "An admin successfully converted a quote or contract into a document or gallery; no content, links, acceptance states or automatic workflows.", + "de": "Ein Admin hat ein Angebot oder einen Vertrag erfolgreich in ein Dokument oder eine Galerie umgewandelt; keine Inhalte, Verknüpfungen, Annahmestände oder automatischen Workflows." + } + }, + "gallery_capture_date_sort": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Capture-date sorting configured", + "de": "Sortierung nach Aufnahmezeit eingerichtet" + }, + "configured": { + "en": "A gallery defaults to sorting by capture date; no capture dates, EXIF or visitor sorting actions.", + "de": "Eine Galerie sortiert standardmäßig nach Aufnahmezeit; keine Aufnahmedaten, EXIF oder Sortieraktionen von Besuchern." + }, + "used": null + }, + "download_original_filenames": { + "category": "gallery_configuration", + "since": "usage.v3", + "measurement": "configuration", + "configuration": "configuration", + "name": { + "en": "Original download filenames enabled", + "de": "Originaldateinamen für Downloads aktiviert" + }, + "configured": { + "en": "The original-download-filenames switch is enabled; no filenames or downloads are read or sent.", + "de": "Der Schalter für Originaldateinamen beim Download ist aktiviert; keine Dateinamen oder Downloads werden gelesen oder gesendet." + }, + "used": null + } + }, + "inventory": { + "galleries": { + "name": { + "en": "Stored galleries", + "de": "Gespeicherte Galerien" + }, + "description": { + "en": "Current number of gallery records, including drafts, inactive and archived galleries. Deleted galleries are excluded. One total for the installation, no breakdown or identifiers.", + "de": "Aktuelle Anzahl gespeicherter Galerien einschließlich Entwürfen, inaktiven und archivierten Galerien. Gelöschte Galerien zählen nicht. Eine Gesamtzahl der Installation, ohne Aufschlüsselung oder Kennungen." + } + }, + "photos": { + "name": { + "en": "Stored photo records", + "de": "Gespeicherte Fotoeinträge" + }, + "description": { + "en": "Current number of non-video photo records, including RAW, guest uploads and records of archived galleries. One total for the installation; not unique files, thumbnails, processing success or photo contents. Deleted records are excluded.", + "de": "Aktuelle Anzahl der Fotoeinträge ohne Videos, einschließlich RAW, Gast-Uploads und Einträgen archivierter Galerien. Eine Gesamtzahl der Installation; keine eindeutigen Dateien, Vorschaubilder, Verarbeitungserfolge oder Fotoinhalte. Gelöschte Einträge zählen nicht." + } + } + } +} diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index faa31286..1d450d62 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -1,17 +1,17 @@ { "productUsage": { - "fields": "usage.v3-Berichte enthalten einen Installationsfingerabdruck, PicPeak-Version, UTC-Berichtsdatum und Erstellungszeit, Schema-/Signaturmetadaten, feste Galerie-Layouts, 86 Funktionssignale (63 Konfiguriert/Genutzt-Paare und 23 reine Konfigurationswerte) sowie zwei Gesamtzahlen der Installation: gespeicherte Galerien und Fotoeinträge ohne Videos. Entwürfe, archivierte Galerien und deren erhaltene Fotoeinträge zählen mit. Der Katalog unten erklärt jedes Feld. Keine Aktionsanzahlen oder Besucherbeobachtung.", - "catalogTitle": "Vollständiger Katalog: 86 Funktionssignale und 2 Bestandszahlen (usage.v3)", + "fields": "usage.v4-Berichte enthalten einen Installationsfingerabdruck, PicPeak-Version, UTC-Berichtsdatum und Erstellungszeit, Schema-/Signaturmetadaten, feste Galerie-Layouts, 86 Funktionssignale (63 Konfiguriert/Genutzt-Paare und 23 reine Konfigurationswerte) sowie zwei Gesamtzahlen der Installation: gespeicherte Galerien und Fotoeinträge ohne Videos. Entwürfe, archivierte Galerien und deren erhaltene Fotoeinträge zählen mit. Der Katalog unten erklärt jedes Feld. Keine Aktionsanzahlen oder Besucherbeobachtung.", + "catalogTitle": "Vollständiger Katalog: 86 Funktionssignale und 2 Bestandszahlen (usage.v4)", "catalogExplanation": "Konfiguriert beschreibt die aktuelle technische Verfügbarkeit oder Einrichtung. Integriert bedeutet verfügbar, nicht genutzt. Genutzt ist ein installationsweites Ja/Nein seit Zustimmung zum Berichtsschema; angenommene Aufträge gelten als gestartet, nicht zwingend abgeschlossen. Reine Konfigurationssignale enthalten kein Genutzt-Feld. Der Bestand enthält nur aktuelle Gesamtzahlen der Galerie-/Fotoeinträge, ohne Aufschlüsselung nach Galerien. Nutzungsmarker speichern keine Personen, Objektkennungen, Aktionszeiten oder Häufigkeiten.", "catalogSearch": "Funktionsname oder Schlüssel suchen", "catalogEmpty": "Keine passenden Funktionen.", "configuredLabel": "Konfiguriert", "usedLabel": "Genutzt", "configurationOnly": "Nur Konfiguration — tatsächliche Nutzung wird nicht erfasst.", - "versionDisclosure": "Diese Zustimmung gilt für usage.v3 / usage-consent.v3. Bestehende v1- und v2-Teilnahmen behalten ihre bisherigen 19 bzw. 73 Fähigkeiten ohne Bestandszahlen bis zur ausdrücklichen Erweiterung. Identität und Rohhistorie bleiben erhalten; lokale Nutzungsmarker beginnen erst nach Bestätigung durch den Collector neu. Höchstens ein Bericht pro UTC-Tag wird angenommen, der erste v3-Bericht kann daher am nächsten aktiven Tag folgen. Neue Marker und Bestandszahlen werden vor Bestätigung nicht erfasst.", + "versionDisclosure": "Diese Zustimmung gilt für usage.v4 / usage-consent.v4. Sie ersetzt die Frage „Erlaubt mindestens eine Galerie Downloads?“ durch „Hat mindestens eine Galerie Downloads abgeschaltet?“. Bestehende v1/v2/v3-Teilnahmen behalten ihren bisherigen Umfang bis zur ausdrücklichen Erweiterung. Identität und Rohhistorie bleiben erhalten; wartende Pakete werden vor dem Upgrade unverändert zugestellt. Lokale Nutzungsmarker beginnen erst nach Bestätigung neu. Der erste v4-Bericht kann am nächsten aktiven UTC-Tag folgen. Das neue Signal wird vor Bestätigung nicht erfasst.", "currentSchema": "Aktuelles Berichtsschema: {{schema}}", - "reviewUpgrade": "Erweiterten Umfang von usage.v3 prüfen", - "upgrade": "usage.v3 ausdrücklich zustimmen", + "reviewUpgrade": "Erweiterten Umfang von usage.v4 prüfen", + "upgrade": "usage.v4 ausdrücklich zustimmen", "upgradeExplanation": "Deine bestehende Teilnahme behält ihren bisherigen Umfang. Prüfe den erweiterten Katalog und die beiden Bestandszahlen, bevor du dich entscheidest. Ablehnen beendet die Teilnahme nicht.", "upgradePending": "Die signierte Erweiterung wartet auf Bestätigung. Es wird nur der bisher bestätigte Umfang erfasst. Wiederhole den Versuch, wenn der Collector erreichbar ist, oder deaktiviere die Teilnahme zum Stoppen und Löschen.", "catalog": { @@ -335,9 +335,9 @@ "name": "Gast-Uploads aktiviert", "configured": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." }, - "gallery_downloads_restricted": { - "name": "Galerie-Downloads eingeschränkt", - "configured": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." + "gallery_downloads": { + "name": "Galerie-Downloads erlaubt", + "configured": "In der betreffenden Galerie-/Globalkonfiguration aktiviert; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." }, "download_resolution_picker": { "name": "Download-Auflösungswahl aktiviert", @@ -421,6 +421,10 @@ "download_original_filenames": { "name": "Originaldateinamen für Downloads aktiviert", "configured": "Der Schalter für Originaldateinamen beim Download ist aktiviert; keine Dateinamen oder Downloads werden gelesen oder gesendet." + }, + "gallery_downloads_restricted": { + "name": "Galerie-Downloads eingeschränkt", + "configured": "Mindestens eine Galerie hat Downloads abgeschaltet; nur installationsweite Existenz, niemals Galeriekennungen oder Anzahlen." } }, "auditTitle": "Export- und Löschquittungen", diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index f20b1681..1b09a0ec 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -1,17 +1,17 @@ { "productUsage": { - "fields": "usage.v3 reports contain an installation fingerprint, PicPeak version, UTC report date and generation time, schema/signing metadata, controlled gallery layouts, 86 fixed capability signals (63 configured/used pairs and 23 configuration-only booleans), and two installation totals: stored galleries and photo records excluding videos. Drafts and archived galleries and their retained photo records are included. The catalog below defines every field. There are no action counts or visitor observations.", - "catalogTitle": "Full catalog: 86 capability signals and 2 inventory totals (usage.v3)", + "fields": "usage.v4 reports contain an installation fingerprint, PicPeak version, UTC report date and generation time, schema/signing metadata, controlled gallery layouts, 86 fixed capability signals (63 configured/used pairs and 23 configuration-only booleans), and two installation totals: stored galleries and photo records excluding videos. Drafts and archived galleries and their retained photo records are included. The catalog below defines every field. There are no action counts or visitor observations.", + "catalogTitle": "Full catalog: 86 capability signals and 2 inventory totals (usage.v4)", "catalogExplanation": "Configured describes current technical availability or configuration. Built-in means available, not used. Used is one installation-wide yes/no bit since consent to the reporting schema; accepted jobs mean initiated, not necessarily completed. Configuration-only capabilities omit used. Inventory contains only current gallery/photo record totals, with no per-gallery breakdown. No actor, entity identifier, action time or frequency is stored in usage markers.", "catalogSearch": "Search capability name or key", "catalogEmpty": "No matching capabilities.", "configuredLabel": "Configured", "usedLabel": "Used", "configurationOnly": "Configuration only — actual use is not collected.", - "versionDisclosure": "This consent covers usage.v3 / usage-consent.v3. Existing v1 and v2 participants retain their previous 19 or 73 capabilities without inventory totals until they explicitly upgrade. Identity and raw history remain; local usage markers restart only after the collector confirms the upgrade. At most one report per UTC day is accepted, so the first v3 report may be on the next active day. New markers and totals are not collected before confirmation.", + "versionDisclosure": "This consent covers usage.v4 / usage-consent.v4. It replaces the question “does any gallery allow downloads?” with “does any gallery have downloads switched off?”. Existing v1/v2/v3 participants keep their exact previous scope until they explicitly upgrade. Identity and raw history remain; pending packets are delivered unchanged before an upgrade. Local usage markers restart only after confirmation. The first v4 report may be on the next active UTC day. The new signal is never collected before confirmation.", "currentSchema": "Current reporting schema: {{schema}}", - "reviewUpgrade": "Review expanded usage.v3 scope", - "upgrade": "Explicitly agree to usage.v3", + "reviewUpgrade": "Review expanded usage.v4 scope", + "upgrade": "Explicitly agree to usage.v4", "upgradeExplanation": "Your existing participation keeps its current scope. Review the expanded catalog and the two inventory totals before deciding whether to upgrade. Declining does not end participation.", "upgradePending": "The signed consent upgrade is pending confirmation. Only the previously accepted scope is collected. Retry when the collector is available, or disable participation to stop and delete.", "catalog": { @@ -335,9 +335,9 @@ "name": "Guest uploads enabled", "configured": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts." }, - "gallery_downloads_restricted": { - "name": "Gallery downloads restricted", - "configured": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts." + "gallery_downloads": { + "name": "Gallery downloads allowed", + "configured": "Enabled in applicable gallery/global configuration; only existence across the installation, never gallery IDs or counts." }, "download_resolution_picker": { "name": "Download resolution picker enabled", @@ -421,6 +421,10 @@ "download_original_filenames": { "name": "Original download filenames enabled", "configured": "The original-download-filenames switch is enabled; no filenames or downloads are read or sent." + }, + "gallery_downloads_restricted": { + "name": "Gallery downloads restricted", + "configured": "At least one gallery has downloads switched off; only existence across the installation, never gallery IDs or counts." } }, "auditTitle": "Export and deletion receipts", diff --git a/frontend/src/services/productUsage.service.ts b/frontend/src/services/productUsage.service.ts index cfeb3993..2ce0021f 100644 --- a/frontend/src/services/productUsage.service.ts +++ b/frontend/src/services/productUsage.service.ts @@ -49,12 +49,12 @@ export const productUsageService = { async enable(): Promise { return ( await api.post('/admin/usage/enable', { - consent_version: 'usage-consent.v3' + consent_version: 'usage-consent.v4' }) ).data; }, async upgradeConsent(): Promise<{ delivered: boolean; queued: boolean; state: UsageStatus }> { - return (await api.post('/admin/usage/consent', { consent_version: 'usage-consent.v3' })).data; + return (await api.post('/admin/usage/consent', { consent_version: 'usage-consent.v4' })).data; }, async disable(): Promise { return (await api.post('/admin/usage/disable')).data;