181 lines
7.2 KiB
PHP
181 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Admin\Concerns\ScopedToTenant;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\AuditLog;
|
|
use App\Models\DirectoryUser;
|
|
use App\Models\Template;
|
|
use App\Models\TemplateVersion;
|
|
use App\Signature\SignatureBuilder;
|
|
use App\Signature\TemplateRenderer;
|
|
use App\Signature\TemplateSanitizer;
|
|
use App\Signature\VariableMap;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\View\View;
|
|
|
|
/**
|
|
* Templates : les éditeurs modifient un brouillon, les administrateurs le publient.
|
|
* L'API ne sert que le contenu publié (html_full / html_short).
|
|
*/
|
|
class TemplateController extends Controller
|
|
{
|
|
use ScopedToTenant;
|
|
|
|
public function __construct(private readonly TemplateSanitizer $sanitizer) {}
|
|
|
|
public function index(): View
|
|
{
|
|
$templates = Template::where('tenant_id', $this->tenantId())
|
|
->withCount('assignments')
|
|
->orderByDesc('is_default')->orderBy('name')
|
|
->get();
|
|
|
|
return view('admin.templates.index', compact('templates'));
|
|
}
|
|
|
|
public function create(): View
|
|
{
|
|
return view('admin.templates.edit', [
|
|
'template' => new Template([
|
|
'draft_html_full' => file_get_contents(database_path('seeders/templates/agents-full.html')),
|
|
'draft_html_short' => file_get_contents(database_path('seeders/templates/agents-short.html')),
|
|
]),
|
|
'versions' => collect(),
|
|
'variables' => VariableMap::FIELDS,
|
|
'previewAgent' => $this->defaultPreviewAgent(),
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$data = $this->validated($request);
|
|
$template = Template::create($data + ['tenant_id' => $this->tenantId()]);
|
|
AuditLog::record('admin', $this->actor(), 'template.create', $template, ['name' => $template->name]);
|
|
|
|
return redirect()->route('admin.templates.edit', $template)->with('status', 'Template créé (brouillon non publié).');
|
|
}
|
|
|
|
public function edit(Template $template): View
|
|
{
|
|
$this->ensureTenant($template);
|
|
|
|
return view('admin.templates.edit', [
|
|
'template' => $template,
|
|
'versions' => $template->versions()->with('publisher:id,name')->orderByDesc('version')->limit(20)->get(),
|
|
'variables' => VariableMap::FIELDS,
|
|
'previewAgent' => $this->defaultPreviewAgent(),
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, Template $template): RedirectResponse
|
|
{
|
|
$this->ensureTenant($template);
|
|
$data = $this->validated($request);
|
|
$template->fill($data);
|
|
$changed = array_keys($template->getDirty());
|
|
$template->save();
|
|
AuditLog::record('admin', $this->actor(), 'template.update', $template, ['fields' => $changed]);
|
|
|
|
return back()->with('status', 'Brouillon enregistré.');
|
|
}
|
|
|
|
/** Aperçu du brouillon en cours de saisie (non enregistré) sur un agent réel. */
|
|
public function preview(Request $request, SignatureBuilder $builder): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'html' => ['nullable', 'string', 'max:100000'],
|
|
'variant' => ['required', 'in:full,short'],
|
|
'agent_id' => ['nullable', 'integer'],
|
|
]);
|
|
|
|
$agent = isset($data['agent_id'])
|
|
? DirectoryUser::where('tenant_id', $this->tenantId())->find($data['agent_id'])
|
|
: $this->defaultPreviewAgent();
|
|
abort_unless($agent, 422, 'Aucun agent pour l\'aperçu.');
|
|
|
|
$html = $this->sanitizer->clean((string) ($data['html'] ?? ''));
|
|
|
|
return response()->json([
|
|
'html' => $builder->preview($agent, $html, $data['variant']),
|
|
'agent' => $agent->display_name.' — '.$agent->mail,
|
|
'variables' => (new TemplateRenderer)->variables($html),
|
|
]);
|
|
}
|
|
|
|
public function publish(Template $template): RedirectResponse
|
|
{
|
|
$this->ensureTenant($template);
|
|
abort_if(blank($template->draft_html_full) && blank($template->html_full), 422, 'Le template complet est vide.');
|
|
|
|
$template->publish(auth()->user());
|
|
AuditLog::record('admin', $this->actor(), 'template.publish', $template, ['version' => $template->published_version]);
|
|
|
|
return back()->with('status', "Version {$template->published_version} publiée : elle est servie immédiatement par l'add-in.");
|
|
}
|
|
|
|
/** Recharge une version publiée dans le brouillon (il faut republier ensuite). */
|
|
public function restore(Template $template, TemplateVersion $version): RedirectResponse
|
|
{
|
|
$this->ensureTenant($template);
|
|
abort_unless($version->template_id === $template->id, 404);
|
|
|
|
$template->update(['draft_html_full' => $version->html_full, 'draft_html_short' => $version->html_short]);
|
|
AuditLog::record('admin', $this->actor(), 'template.restore', $template, ['version' => $version->version]);
|
|
|
|
return back()->with('status', "Version {$version->version} rechargée dans le brouillon. Publiez pour l'appliquer.");
|
|
}
|
|
|
|
public function makeDefault(Template $template): RedirectResponse
|
|
{
|
|
$this->ensureTenant($template);
|
|
abort_if(blank($template->html_full), 422, 'Publiez le template avant d\'en faire le template par défaut.');
|
|
|
|
DB::transaction(function () use ($template) {
|
|
Template::where('tenant_id', $this->tenantId())->update(['is_default' => false]);
|
|
$template->update(['is_default' => true]);
|
|
});
|
|
AuditLog::record('admin', $this->actor(), 'template.default', $template);
|
|
|
|
return back()->with('status', "« {$template->name} » est maintenant le template par défaut.");
|
|
}
|
|
|
|
public function destroy(Template $template): RedirectResponse
|
|
{
|
|
$this->ensureTenant($template);
|
|
abort_if($template->is_default, 422, 'Le template par défaut ne peut pas être supprimé.');
|
|
|
|
AuditLog::record('admin', $this->actor(), 'template.delete', $template, ['name' => $template->name]);
|
|
$template->delete(); // les affectations sont supprimées en cascade
|
|
|
|
return redirect()->route('admin.templates.index')->with('status', 'Template supprimé.');
|
|
}
|
|
|
|
private function validated(Request $request): array
|
|
{
|
|
$data = $request->validate([
|
|
'name' => ['required', 'string', 'max:120'],
|
|
'description' => ['nullable', 'string', 'max:500'],
|
|
'draft_html_full' => ['required', 'string', 'max:100000'],
|
|
'draft_html_short' => ['nullable', 'string', 'max:100000'],
|
|
]);
|
|
$data['draft_html_full'] = $this->sanitizer->clean($data['draft_html_full']);
|
|
$data['draft_html_short'] = isset($data['draft_html_short']) ? $this->sanitizer->clean($data['draft_html_short']) : null;
|
|
|
|
return $data;
|
|
}
|
|
|
|
/** Agent utilisé par défaut pour l'aperçu : l'administrateur lui-même. */
|
|
private function defaultPreviewAgent(): ?DirectoryUser
|
|
{
|
|
$user = auth()->user();
|
|
|
|
return DirectoryUser::where('tenant_id', $user->tenant_id)->where('entra_oid', $user->entra_oid)->first()
|
|
?? DirectoryUser::where('tenant_id', $user->tenant_id)->orderBy('display_name')->first();
|
|
}
|
|
}
|