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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions config/ipdr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/*
* The session records database. An empty value takes the app's own MySQL connection setting; the database name
* defaults to ispgrid_ipdr. Read here, not in the service provider, so the values survive `config:cache`.
*/
return [
'database' => [
'host' => env('IPDR_DB_HOST'),
'port' => env('IPDR_DB_PORT'),
'database' => env('IPDR_DB_DATABASE'),
'username' => env('IPDR_DB_USERNAME'),
'password' => env('IPDR_DB_PASSWORD'),
],
];
3 changes: 2 additions & 1 deletion resources/views/livewire/session-records/session-records.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Validation\Rule;
use Ispgrid\Ipdr\Archive\CopySessions;
use Ispgrid\Ipdr\Archive\KeepDays;
use Ispgrid\Ipdr\Search\CsvCell;
use Ispgrid\Ipdr\Search\SessionRows;
use Ispgrid\Ipdr\Search\SessionSearch;
use Livewire\Attributes\Computed;
Expand Down Expand Up @@ -82,7 +83,7 @@ public function export(): StreamedResponse
fputcsv($output, $header, escape: '');

foreach ($rows as $row) {
fputcsv($output, [$row['username'], $row['customer'], $row['mobile'], $row['address'], $row['mac'], $row['ip'], $row['nas'], $row['start'], $row['end'], $row['duration'], $row['down'], $row['up']], escape: '');
fputcsv($output, array_map(CsvCell::safe(...), [$row['username'], $row['customer'], $row['mobile'], $row['address'], $row['mac'], $row['ip'], $row['nas'], $row['start'], $row['end'], $row['duration'], $row['down'], $row['up']]), escape: '');
}

fclose($output);
Expand Down
11 changes: 4 additions & 7 deletions src/IpdrServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ class IpdrServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->mergeConfigFrom(__DIR__.'/../config/ipdr.php', 'ipdr');

/** @var array<string, mixed> $base */
$base = config('database.connections.mysql');
$own = array_filter((array) config('ipdr.database', []), fn (mixed $value): bool => $value !== null && $value !== '');
$default = $this->app->environment('testing') ? 'ispgrid_ipdr_test' : 'ispgrid_ipdr';

config(['database.connections.ipdr' => array_merge($base, [
'host' => env('IPDR_DB_HOST', $base['host']),
'port' => env('IPDR_DB_PORT', $base['port']),
'database' => env('IPDR_DB_DATABASE', $default),
'username' => env('IPDR_DB_USERNAME', $base['username']),
'password' => env('IPDR_DB_PASSWORD', $base['password']),
])]);
config(['database.connections.ipdr' => array_merge($base, ['database' => $default], $own)]);
}

public function boot(): void
Expand Down
17 changes: 17 additions & 0 deletions src/Search/CsvCell.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Ispgrid\Ipdr\Search;

/**
* A CSV cell a spreadsheet will not run: text starting with =, +, -, @, tab or CR (a customer named
* "=HYPERLINK(...)") gets a leading apostrophe; numbers stay numbers.
*/
final class CsvCell
{
public static function safe(string|int|float|null $value): string
{
$text = (string) $value;

return $text !== '' && ! is_numeric($text) && in_array($text[0], ['=', '+', '-', '@', "\t", "\r"], true) ? "'".$text : $text;
}
}
10 changes: 10 additions & 0 deletions tests/Feature/DatabaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Ispgrid\Ipdr\IpdrServiceProvider;

it('creates the archive tables in their own database', function () {
expect(DB::connection('ipdr')->getDatabaseName())->toBe('ispgrid_ipdr_test')
Expand All @@ -10,6 +11,15 @@
->and(Schema::connection('ipdr')->hasTable('ipdr_state'))->toBeTrue();
});

it('takes its database settings from its config file, so they survive config:cache', function () {
config(['ipdr.database.database' => 'ispgrid_ipdr_other', 'ipdr.database.host' => '']);

(new IpdrServiceProvider(app()))->register();

expect(config('database.connections.ipdr.database'))->toBe('ispgrid_ipdr_other')
->and(config('database.connections.ipdr.host'))->toBe(config('database.connections.mysql.host'));
});

it('adds the Search session records permission', function () {
expect(DB::table('permissions')->where('name', 'search-session-records')->exists())->toBeTrue();
});
13 changes: 13 additions & 0 deletions tests/Feature/SearchPageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,16 @@ function closedSessionWithIp(string $username): void

expect(Activity::query()->where('event', 'session-records-exported')->count())->toBe(1);
});

it('exports a name a spreadsheet would run as a formula as plain text', function () {
$this->actingAs(User::factory()->create());
Account::factory()->create(['username' => 'asha', 'fullname' => '=HYPERLINK("http://x.test")']);
closedSessionWithIp('asha');

$page = Livewire::test('ipdr::session-records')
->set('by', 'ip')->set('value', '100.64.10.7')->set('at', '2026-10-15T10:00')
->set('reference', 'R1')->set('reason', 'Test')
->call('export');

expect(base64_decode((string) data_get($page->effects, 'download.content')))->toContain('"\'=HYPERLINK(""http://x.test"")"');
});