- Configurable ticket numbering (Admin > Konfiguracja > Ogólne): admin-set
  prefix and minimum zero-padded length for the ticket number.
- "Ukryj kolejność zgłoszeń": an opt-in mode that displays a stable,
  HMAC-derived checksum instead of the sequential ticket number, so it gives
  no indication of ticket volume or creation order. Ticket URLs switch to
  the same checksum when this is on, so a link and the number on the page it
  points to always match. The REST API is unaffected — pinned to `id`
  regardless of this setting. Search now also matches by checksum.
- Fixed: attachments no longer show an inline image thumbnail in the
  message thread — every attachment (images included) shows as just its
  filename, opening in a new tab on click.
- Docs: README/ARCHITECTURE/wiki updated for all of the above.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 09:57:39 +02:00
parent ab90abcaa3
commit 63178b366e
21 changed files with 368 additions and 42 deletions

View File

@@ -0,0 +1,60 @@
<?php
use App\Models\ApiClient;
use App\Models\User;
use App\Support\Settings;
use Laravel\Sanctum\Sanctum;
test('a ticket is assigned a stable, unique checksum on creation', function () {
seedStatusesAndPriorities();
$ticket = makeTicket();
expect($ticket->checksum)->not->toBeNull()
->and($ticket->checksum)->toMatch('/^\d{6}$/')
->and($ticket->fresh()->checksum)->toBe($ticket->checksum);
});
test('with obfuscation off, the ticket URL and the displayed number both use the raw sequential number', function () {
seedStatusesAndPriorities();
Settings::set('ticket_number_obfuscate', '0');
$operator = User::query()->create(['name' => 'Op', 'email' => 'op@example.com', 'roles' => ['operator']]);
$ticket = makeTicket(['number' => '1042']);
$url = route('operator.ticket', $ticket);
expect($url)->toContain('/1042')
->and($ticket->displayNumber())->toBe('#1042');
$this->actingAs($operator)->get($url)->assertOk();
});
test('with obfuscation on, the ticket URL and the displayed number both use the checksum, and the raw number no longer resolves', function () {
seedStatusesAndPriorities();
$operator = User::query()->create(['name' => 'Op', 'email' => 'op@example.com', 'roles' => ['operator']]);
$ticket = makeTicket(['number' => '1042']);
Settings::set('ticket_number_obfuscate', '1');
$url = route('operator.ticket', $ticket);
expect($url)->toContain($ticket->checksum)
->and($url)->not->toContain('/1042')
->and($ticket->displayNumber())->toBe('#'.$ticket->checksum);
$this->actingAs($operator)->get($url)->assertOk();
$this->actingAs($operator)->get('/operator/tickets/1042')->assertNotFound();
});
test('the API still binds tickets by numeric id regardless of the obfuscation setting', function () {
seedStatusesAndPriorities();
Settings::set('ticket_number_obfuscate', '1');
$ticket = makeTicket();
$client = ApiClient::factory()->create();
Sanctum::actingAs($client, ['tickets:read']);
$this->getJson("/api/v1/tickets/{$ticket->id}")->assertOk()->assertJsonPath('data.id', $ticket->id);
});