diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f02d5..6282944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ v1.7 - Fix FLEXIAPI-278 Complete and reorganize the Markdown documentation - Fix FLEXIAPI-233 Add External Accounts (new version) - Fix FLEXIAPI-277 Restrict authorized ini keys that can be set to prevent conflict with the existing ones set in the UI +- Fix FLEXIAPI-272 Add Space based email server integration v1.6 ---- diff --git a/flexiapi/app/ExternalAccount.php b/flexiapi/app/ExternalAccount.php index 453f20c..08600d7 100644 --- a/flexiapi/app/ExternalAccount.php +++ b/flexiapi/app/ExternalAccount.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php b/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php new file mode 100644 index 0000000..3abed1a --- /dev/null +++ b/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php @@ -0,0 +1,60 @@ + $space, + 'emailServer' => $space->emailServer ?? new SpaceEmailServer + ]); + } + + public function store(CreateUpdate $request, int $spaceId) + { + $space = Space::findOrFail($spaceId); + $emailServer = $space->emailServer ?? new SpaceEmailServer; + + $emailServer->space_id = $space->id; + $emailServer->host = $request->get('host'); + $emailServer->port = $request->get('port'); + $emailServer->username = $request->get('username'); + $emailServer->password = $request->get('password'); + $emailServer->from_address = $request->get('from_address') ?? null; + $emailServer->from_name = $request->get('from_name') ?? null; + $emailServer->signature = $request->get('signature') ?? null; + + $emailServer->save(); + + return redirect()->route('admin.spaces.integration', $spaceId); + } + + public function delete(int $spaceId) + { + $space = Space::findOrFail($spaceId); + + return view('admin.space.email_server.delete', [ + 'space' => $space + ]); + } + + public function destroy(int $spaceId) + { + $space = Space::findOrFail($spaceId); + $space->emailServer->delete(); + + return redirect()->route('admin.spaces.integration', $spaceId); + } +} diff --git a/flexiapi/app/Http/Controllers/Admin/SpaceController.php b/flexiapi/app/Http/Controllers/Admin/SpaceController.php index deaa90c..47f7881 100644 --- a/flexiapi/app/Http/Controllers/Admin/SpaceController.php +++ b/flexiapi/app/Http/Controllers/Admin/SpaceController.php @@ -104,6 +104,13 @@ class SpaceController extends Controller ]); } + public function integration(Space $space) + { + return view('admin.space.integration', [ + 'space' => $space + ]); + } + public function configurationUpdate(Request $request, Space $space) { $space = $this->setConfiguration($request, $space); diff --git a/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php b/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php new file mode 100644 index 0000000..11f9f2f --- /dev/null +++ b/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php @@ -0,0 +1,43 @@ +firstOrFail()->emailServer()->firstOrFail(); + } + + public function store(CreateUpdate $request, string $host) + { + $space = Space::where('host', $host)->firstOrFail(); + $emailServer = $space->emailServer ?? new SpaceEmailServer; + + $emailServer->space_id = $space->id; + $emailServer->host = $request->get('host'); + $emailServer->port = $request->get('port'); + $emailServer->username = $request->get('username'); + $emailServer->password = $request->get('password'); + $emailServer->from_address = $request->get('from_address') ?? null; + $emailServer->from_name = $request->get('from_name') ?? null; + $emailServer->signature = $request->get('signature') ?? null; + + $emailServer->save(); + + return $emailServer; + } + + public function destroy(string $host) + { + $space = Space::where('host', $host)->firstOrFail(); + return $space->emailServer->delete(); + } +} diff --git a/flexiapi/app/Http/Kernel.php b/flexiapi/app/Http/Kernel.php index 67d41cf..8bee932 100644 --- a/flexiapi/app/Http/Kernel.php +++ b/flexiapi/app/Http/Kernel.php @@ -90,7 +90,7 @@ class Kernel extends HttpKernel 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, - 'space.expired' => \App\Http\Middleware\IsSpaceExpired::class, + 'space.check' => \App\Http\Middleware\SpaceCheck::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'localization' => \App\Http\Middleware\Localization::class, diff --git a/flexiapi/app/Http/Middleware/IsSpaceExpired.php b/flexiapi/app/Http/Middleware/SpaceCheck.php similarity index 53% rename from flexiapi/app/Http/Middleware/IsSpaceExpired.php rename to flexiapi/app/Http/Middleware/SpaceCheck.php index dbe0936..6f3e3a9 100644 --- a/flexiapi/app/Http/Middleware/IsSpaceExpired.php +++ b/flexiapi/app/Http/Middleware/SpaceCheck.php @@ -7,7 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; use Symfony\Component\HttpFoundation\Response; -class IsSpaceExpired +class SpaceCheck { public function handle(Request $request, Closure $next): Response { @@ -29,6 +29,25 @@ class IsSpaceExpired abort(403, 'The related Space has expired'); } + // Custom email integration + if ($space->emailServer) { + $config = [ + 'driver' => config('mail.driver'), + 'encryption' => config('mail.encryption'), + 'host' => $space->emailServer->host, + 'port' => $space->emailServer->port, + 'from' => [ + 'address' => $space->emailServer->from_address, + 'name' => $space->emailServer->from_name + ], + 'username' => $space->emailServer->username, + 'password' => $space->emailServer->password, + 'signature' => $space->emailServer->signature ?? config('mail.signature') + ]; + + Config::set('mail', $config); + } + return $next($request); } diff --git a/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php b/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php new file mode 100644 index 0000000..ce34ced --- /dev/null +++ b/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php @@ -0,0 +1,38 @@ +. +*/ + +namespace App\Http\Requests\EmailServer; + +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; + +use App\EmailServer; +use App\Rules\Domain; + +class CreateUpdate extends FormRequest +{ + public function rules() + { + return [ + 'host' => ['required', new Domain()], + 'port' => 'required|integer', + 'from_address' => 'nullable|email', + ]; + } +} diff --git a/flexiapi/app/PhoneCountry.php b/flexiapi/app/PhoneCountry.php index aa51092..ed18a0d 100644 --- a/flexiapi/app/PhoneCountry.php +++ b/flexiapi/app/PhoneCountry.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/ResetPasswordEmailToken.php b/flexiapi/app/ResetPasswordEmailToken.php index 10241d9..ecb1181 100644 --- a/flexiapi/app/ResetPasswordEmailToken.php +++ b/flexiapi/app/ResetPasswordEmailToken.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/Space.php b/flexiapi/app/Space.php index 93463e7..b1ba02a 100644 --- a/flexiapi/app/Space.php +++ b/flexiapi/app/Space.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -11,6 +27,8 @@ class Space extends Model { use HasFactory; + protected $with = ['emailServer']; + public const FORBIDDEN_KEYS = [ 'disable_chat_feature', 'disable_meetings_feature', @@ -60,6 +78,11 @@ class Space extends Model return $this->accounts()->where('admin', true); } + public function emailServer() + { + return $this->hasOne(SpaceEmailServer::class); + } + public function scopeNotFull(Builder $query) { return $query->where('max_accounts', 0) diff --git a/flexiapi/app/SpaceEmailServer.php b/flexiapi/app/SpaceEmailServer.php new file mode 100644 index 0000000..e31668d --- /dev/null +++ b/flexiapi/app/SpaceEmailServer.php @@ -0,0 +1,34 @@ +. +*/ +namespace App; + +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; + +class SpaceEmailServer extends Model +{ + use HasFactory; + + protected $hidden = ['space_id']; + + public function space() + { + return $this->belongsTo(Space::class); + } +} diff --git a/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php b/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php new file mode 100644 index 0000000..f2e4aee --- /dev/null +++ b/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php @@ -0,0 +1,36 @@ +id(); + + $table->string('host', 64); + $table->integer('port'); + $table->string('username', 128)->nullable(); + $table->string('password', 128)->nullable(); + $table->string('from_address', 128)->nullable(); + $table->string('from_name', 128)->nullable(); + $table->string('signature', 256)->nullable(); + + $table->bigInteger('space_id')->unsigned(); + $table->foreign('space_id')->references('id') + ->on('spaces')->onDelete('cascade'); + + $table->unique('space_id'); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('space_email_servers'); + } +}; diff --git a/flexiapi/lang/fr.json b/flexiapi/lang/fr.json index c7ba0d9..f85f6d0 100644 --- a/flexiapi/lang/fr.json +++ b/flexiapi/lang/fr.json @@ -63,6 +63,7 @@ "Edit": "Éditer", "Email registration": "Inscription par email", "Email": "Email", + "Email Server": "Serveur Mail", "Empty": "Vide", "Enable the web interface": "Activer l'interface web", "Enabled": "Activé", @@ -89,6 +90,7 @@ "In ini format, will complete the other settings": "Au format ini, complètera les autres paramètres", "In lowercase letters": "En minuscules", "Information": "Informations", + "Integration": "Intégration", "Intercom features": "Fonctionnalités d'interphonie", "It might actually disable this page, be careful": "Cette page pourrait être désactivée, faites attention", "Key": "Clef", diff --git a/flexiapi/public/css/form.css b/flexiapi/public/css/form.css index 6d1dcef..2297532 100644 --- a/flexiapi/public/css/form.css +++ b/flexiapi/public/css/form.css @@ -35,6 +35,10 @@ p .btn { text-align: right; } +p .btn.oppose { + margin-right: 0; +} + .btn[disabled] { color: var(--main-5); border-color: var(--main-5); diff --git a/flexiapi/public/css/style.css b/flexiapi/public/css/style.css index 5c230e4..f850e34 100644 --- a/flexiapi/public/css/style.css +++ b/flexiapi/public/css/style.css @@ -342,6 +342,23 @@ content section { box-sizing: border-box; } +content:has(section.documentation) { + margin: 0; +} + +content section.documentation { + max-width: calc(100% - 35rem); + margin: 0; + margin-left: 35rem; +} + +@media screen and (max-width: 800px) { + content section.documentation { + max-width: 100%; + margin-left: 0; + } +} + form section { margin: initial; max-width: initial; @@ -787,14 +804,29 @@ select.list_toggle { /** Specific elements */ .table-of-contents { - max-width: 40%; - float: right; + max-width: 33rem; + max-height: calc(100vh - 19rem); + overflow-y: scroll; + position: fixed; + top: 11rem; + left: 0; + padding: 2rem; + box-sizing: border-box; + background-color: var(--grey-1); + border-radius: 0 1rem 1rem 0; +} + +@media screen and (max-width: 800px) { + .table-of-contents { + display: none; + } } .card { background-color: var(--grey-1); border-radius: 1rem; padding: 1.5rem; + box-sizing: border-box; margin-bottom: 1rem; overflow: hidden; } diff --git a/flexiapi/resources/views/account/documentation.blade.php b/flexiapi/resources/views/account/documentation.blade.php index 3f0ee58..4434565 100644 --- a/flexiapi/resources/views/account/documentation.blade.php +++ b/flexiapi/resources/views/account/documentation.blade.php @@ -1,8 +1,8 @@ @extends('layouts.main', ['welcome' => true]) @section('content') -
+ @if ($space->emailServer)
+ {{ $space->emailServer->host}}
+ @endif
+ @if ($space->emailServer)
+ {{ __('Edit') }}
+ {{ __('Delete') }}
+ @else
+ {{ __('Configure') }}
+ @endif
+