Add mailgateway

This commit is contained in:
philipp lang 2024-10-20 21:19:07 +02:00
parent 716f7e695a
commit 6188a8de4e
10 changed files with 511 additions and 34 deletions

View File

@ -4,7 +4,6 @@ namespace App\Setting;
use App\Fileshare\FileshareSettings;
use App\Form\FormSettings;
use App\Mailgateway\MailgatewaySettings;
use Modules\Module\ModuleSettings;
use App\Prevention\PreventionSettings;
use App\Setting\Data\SettingSynthesizer;
@ -35,7 +34,6 @@ class SettingServiceProvider extends ServiceProvider
{
app(SettingFactory::class)->register(ModuleSettings::class);
app(SettingFactory::class)->register(InvoiceSettings::class);
app(SettingFactory::class)->register(MailgatewaySettings::class);
app(SettingFactory::class)->register(NamiSettings::class);
app(SettingFactory::class)->register(FormSettings::class);
app(SettingFactory::class)->register(FileshareSettings::class);

View File

@ -0,0 +1,119 @@
<?php
namespace Modules\Mailgateway\Components;
use App\Mailgateway\Models\Mailgateway;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\On;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Form extends Component
{
public string $id = '';
public string $name = '';
public string $domain = '';
public array $params = [];
#[Validate('required')]
public ?string $cls = null;
public Collection $types;
public function rules()
{
return [
'name' => 'required|string|max:255',
'domain' => 'required|string|max:255',
'cls' => ['required', 'string', 'max:255', Rule::in(app('mail-gateways'))],
'params' => 'present|array',
...$this->cls ? collect($this->cls::rules($this->id ? 'updateValidator' : 'storeValidator'))->mapWithKeys(fn ($rules, $key) => ["params.{$key}" => $rules]) : [],
];
}
public function validationAttributes(): array
{
return [
'cls' => 'Typ',
'name' => 'Beschreibung',
'domain' => 'Domain',
...$this->cls ? collect($this->cls::fieldNames())->mapWithKeys(fn ($attribute, $key) => ["params.{$key}" => $attribute]) : [],
];
}
public function mount(?string $model = null): void
{
$this->types = app('mail-gateways')->map(fn ($gateway) => [
'name' => $gateway::name(),
'id' => $gateway,
]);
$model = Mailgateway::find($model);
if ($model) {
$this->id = $model->id;
$this->name = $model->name;
$this->domain = $model->domain;
$this->cls = get_class($model->type);
$this->params = (array) $model->type;
}
}
public function updatedType(string $type): void
{
$this->params = $type::defaults();
}
public function fields(): array
{
return $this->cls ? $this->cls::fields() : [];
}
#[On('onStoreFromModal')]
public function onSave(): void
{
$this->validate();
if (!app($this->cls)->setParams($this->params)->works()) {
throw ValidationException::withMessages(['connection' => 'Verbindung fehlgeschlagen.']);
}
$payload = [
'name' => $this->name,
'domain' => $this->domain,
'type' => ['cls' => $this->cls, 'params' => $this->params],
];
if ($this->id) {
Mailgateway::find($this->id)->update($payload);
} else {
Mailgateway::create($payload);
}
$this->dispatch('closeModal');
$this->dispatch('refresh');
$this->dispatch('success', 'Erfolgreich gespeichert.');
}
public function render()
{
return <<<'HTML'
<div>
<form class="grid grid-cols-2 gap-3">
<x-form::text name="name" wire:model="name" label="Beschreibung" required />
<x-form::text name="domain" wire:model="domain" label="Domain" required />
<x-form::select name="cls" wire:model.live="cls" label="Typ" :options="$types" required />
@foreach($this->fields() as $index => $field)
<x-form::text
wire:key="index"
wire:model="params.{{$field['name']}}"
:label="$field['label']"
:type="$field['type']"
:name="$field['name']"
:required="str_contains('required', $field['storeValidator'])"
></x-form::text>
@endforeach
</form>
</div>
HTML;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Mailgateway\Components;
use App\Mailgateway\Models\Mailgateway;
use Livewire\Component;
use Modules\Mailgateway\MailgatewaySettings;
class SettingView extends Component
{
public string $settingClass = MailgatewaySettings::class;
public $listeners = ['refresh' => '$refresh'];
public function render()
{
return view('mailgateway::setting-view', [
'data' => Mailgateway::get(),
]);
}
}

View File

@ -0,0 +1,37 @@
<x-page::setting-layout :active="$settingClass">
<div>
<x-ui::table>
<thead>
<th>Bezeichnung</th>
<th>Domain</th>
<th>Typ</th>
<th>Prüfung</th>
<th>Aktion</th>
</thead>
<x-ui::action wire:click.prevent="$dispatch('openModal', {component: 'modules.mailgateway.components.form', props: {model: ''}, title: 'Verbindung erstellen'})" icon="plus" variant="danger">Neu</x-ui::action>
@foreach ($data as $index => $gateway)
<tr wire:key="$index">
<td>{{ $gateway->name }}</td>
<td>{{ $gateway->domain }}</td>
<td>{{ $gateway->type::name() }}</td>
<td>
<x-ui::boolean-display :value="$gateway->type->works()"
hint="Verbindungsstatus"
right="Verbindung erfolgreich"
wrong="Verbindung fehlgeschlagen"
></x-ui::boolean-display>
</td>
<td>
<x-ui::action wire:click="$dispatch('openModal', {
component: 'modules.mailgateway.components.form',
props: {model: '{{$gateway->id}}'},
title: 'Verbindung {{$gateway->name}} bearbeiten'}
)" icon="pencil" variant="warning">Bearbeiten</x-ui::action>
</td>
</tr>
@endforeach
</x-ui::table>
</div>
</x-page::setting-layout>

View File

@ -0,0 +1,39 @@
<?php
namespace Tests\Feature\Mailgateway;
use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\LocalType;
use App\Mailgateway\Types\MailmanType;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Mailgateway\Components\SettingView;
use Tests\RequestFactories\MailmanTypeRequest;
use Tests\TestCase;
uses(DatabaseTransactions::class);
uses(TestCase::class);
it('test it can view index page', function () {
test()->login()->loginNami();
test()->get('/setting/mailgateway')->assertSeeLivewire(SettingView::class);
});
it('test it displays local gateways', function () {
test()->withoutExceptionHandling()->login()->loginNami();
Mailgateway::factory()->type(LocalType::class, [])->name('Lore')->domain('example.com')->create();
Livewire::test(SettingView::class)
->assertSeeHtml('example.com')
->assertSeeHtml('Lore')
->assertSeeHtml('Lokal')
->assertSeeHtml('Verbindung erfolgreich');
});
it('displays mailman gateways', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->succeeds()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']);
Mailgateway::factory()->type(MailmanType::class, $typeParams)->create();
Livewire::test(SettingView::class)->assertSeeHtml('Verbindung erfolgreich');
});

View File

@ -0,0 +1,37 @@
<?php
namespace Modules\Mailgateway;
use App\Setting\SettingFactory;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Modules\Mailgateway\Components\SettingView;
class MailgatewayServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
app(SettingFactory::class)->register(MailgatewaySettings::class);
app(Router::class)->middleware(['web', 'auth:web'])->group(function ($router) {
$router->get('/setting/mailgateway', SettingView::class)->name('setting.mailgateway');
});
View::addNamespace('mailgateway', __DIR__ . '/Components');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Modules\Mailgateway;
use App\Setting\LocalSettings;
class MailgatewaySettings extends LocalSettings
{
public static function group(): string
{
return 'mailgateway';
}
public static function title(): string
{
return 'E-Mail-Verbindungen';
}
/**
* @inheritdoc
*/
public function viewData(): array
{
return [];
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace Tests\Feature\Mailgateway;
use App\Mailgateway\Types\LocalType;
use App\Mailgateway\Types\MailmanType;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Mailgateway\Components\Form;
use Tests\RequestFactories\MailmanTypeRequest;
use Tests\TestCase;
uses(DatabaseTransactions::class);
uses(TestCase::class);
it('test it saves a mail gateway', function () {
test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class)
->set('name', 'lala')
->set('domain', 'example.com')
->set('cls', LocalType::class)
->call('onSave')
->assertDispatched('closeModal')
->assertDispatched('refresh')
->assertDispatched('success');
$this->assertDatabaseHas('mailgateways', [
'domain' => 'example.com',
'name' => 'lala',
'type' => json_encode([
'cls' => LocalType::class,
'params' => [],
]),
]);
});
it('validates type', function () {
test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class)
->set('cls', '')
->assertHasErrors(['cls' => 'required']);
});
it('test it validates mail gateway', function (array $attributes, array $errors) {
test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class)
->set('name', 'lala')
->set('domain', 'example.com')
->set('cls', LocalType::class)
->setArray($attributes)
->call('onSave')
->assertHasErrors($errors)
->assertNotDispatched('closeModal')
->assertNotDispatched('refresh')
->assertNotDispatched('success');
})->with([
[['name' => ''], ['name' => 'required']],
[['domain' => ''], ['domain' => 'required']],
]);
it('test it validates mailman type', function (array $attributes, array $errors) {
test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class)
->set('name', 'lala')
->set('domain', 'example.com')
->set('cls', MailmanType::class)
->set('params.url', 'exampl.com')
->set('params.user', '::user::')
->set('params.password', 'password')
->setArray($attributes)
->call('onSave')
->assertHasErrors($errors)
->assertNotDispatched('closeModal');
})->with([
[['params.url' => ''], ['params.url' => 'required']],
[['params.user' => ''], ['params.user' => 'required']],
[['params.password' => ''], ['params.password' => 'required']],
[['params.owner' => ''], ['params.owner' => 'required']],
[['params.owner' => 'aaa'], ['params.owner' => 'email']],
]);
it('test it stores mailman gateway', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->succeeds()->create(['url' => 'https://example.com', 'user' => 'user', 'password' => 'secret', 'owner' => 'owner@example.com']);
Livewire::test(Form::class)
->setArray([
'name' => 'lala',
'domain' => 'https://example.com',
'cls' => MailmanType::class,
'params' => $typeParams
])
->call('onSave')
->assertDispatched('closeModal');
$this->assertDatabaseHas('mailgateways', [
'type' => json_encode([
'cls' => MailmanType::class,
'params' => $typeParams,
]),
'name' => 'lala',
'domain' => 'https://example.com',
]);
});
it('test it checks mailman connection', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->fails()->create(['url' => 'https://example.com', 'user' => 'user', 'password' => 'secret', 'owner' => 'owner@example.com']);
Livewire::test(Form::class)
->setArray([
'name' => 'lala',
'domain' => 'https://example.com',
'cls' => MailmanType::class,
'params' => $typeParams
])
->call('onSave')
->assertHasErrors('connection')
->assertNotDispatched('closeModal');
$this->assertDatabaseCount('mailgateways', 0);
});

View File

@ -0,0 +1,104 @@
<?php
namespace Tests\Feature\Mailgateway;
use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\LocalType;
use App\Mailgateway\Types\MailmanType;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Mailgateway\Components\Form;
use Phake;
use Tests\RequestFactories\MailmanTypeRequest;
use Tests\TestCase;
uses(DatabaseTransactions::class);
uses(TestCase::class);
it('test it sets attributes for mailman', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']);
$mailgateway = Mailgateway::factory()->type(MailmanType::class, $typeParams)->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['model' => $mailgateway->id])
->assertSet('id', $mailgateway->id)
->assertSet('name', '::name::')
->assertSet('domain', 'example.com')
->assertSet('cls', MailmanType::class)
->assertSet('params.url', 'https://mailman.example.com')
->assertSet('params.user', 'user')
->assertSet('params.password', 'password')
->assertSet('params.owner', 'owner');
});
it('test it sets attributes for local', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$mailgateway = Mailgateway::factory()->type(LocalType::class, [])->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['model' => $mailgateway->id])
->assertSet('name', '::name::')
->assertSet('domain', 'example.com')
->assertSet('cls', LocalType::class)
->assertSet('params', []);
});
it('test it validates type', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$mailgateway = Mailgateway::factory()->type(LocalType::class, [])->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['model' => $mailgateway->id])
->set('cls', '')
->assertHasErrors(['cls' => 'required']);
});
it('test it updates a mailman gateway without updating password', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->succeeds()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner@example.com']);
$mailgateway = Mailgateway::factory()->type(MailmanType::class, $typeParams)->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['model' => $mailgateway->id])
->set('name', '::newname::')
->call('onSave')
->assertHasNoErrors();
$this->assertDatabaseCount('mailgateways', 1);
$this->assertDatabaseHas('mailgateways', [
'name' => '::newname::',
'type' => json_encode(['cls' => MailmanType::class, 'params' => $typeParams]),
]);
});
it('test it updates a mailman gateway with password', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner@example.com']);
$newTypeParams = MailmanTypeRequest::new()->succeeds()->create(['url' => 'https://mailman.example.com', 'user' => 'newuser', 'password' => 'password', 'owner' => 'owner@example.com']);
$mailgateway = Mailgateway::factory()->type(MailmanType::class, $typeParams)->create();
Livewire::test(Form::class, ['model' => $mailgateway->id])
->set('params.user', 'newuser')
->call('onSave')
->assertHasNoErrors();
$this->assertDatabaseCount('mailgateways', 1);
$this->assertDatabaseHas('mailgateways', [
'type' => json_encode(['cls' => MailmanType::class, 'params' => $newTypeParams]),
]);
});
it('test it checks mailgateway connection when updating', function () {
test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner@example.com']);
MailmanTypeRequest::new()->fails()->create(['url' => 'https://mailman.example.com', 'user' => 'newuser', 'password' => 'password', 'owner' => 'owner@example.com']);
$mailgateway = Mailgateway::factory()->type(MailmanType::class, $typeParams)->create();
Livewire::test(Form::class, ['model' => $mailgateway->id])
->set('params.user', 'newuser')
->call('onSave')
->assertHasErrors('connection');
});

View File

@ -1,32 +0,0 @@
<template>
<div v-tooltip="longLabel" class="flex space-x-2 items-center">
<div class="border-2 rounded-full w-5 h-5 flex items-center justify-center" :class="value ? (dark ? 'border-green-500' : 'border-green-700') : dark ? 'border-red-500' : 'border-red-700'">
<ui-sprite :src="value ? 'check' : 'close'" :class="value ? (dark ? 'text-green-600' : 'text-green-800') : dark ? 'text-red-600' : 'text-red-800'" class="w-3 h-3 flex-none"></ui-sprite>
</div>
<div class="text-gray-400 text-xs" v-text="label"></div>
</div>
</template>
<script>
export default {
props: {
value: {
required: true,
type: Boolean,
},
label: {
type: String,
default: () => '',
},
longLabel: {
default: function () {
return null;
},
},
dark: {
type: Boolean,
default: () => false,
},
},
};
</script>