Resolves a batch of bugs and feature requests across songs, services, settings and export: Songs & sections - Every song now carries permanent, empty, locked PREFIX (COPYRIGHT) and POSTFIX (BLANK) sections, deduplicated on import; locked sections cannot be edited or deleted via UI or API. - Song edit modal: explicit Speichern/Schließen with dirty-tracking, editable section headline (combobox + custom values), and a fix for the 419 CSRF errors after CCLI "Importieren & Bearbeiten" (token read fresh per request). - CCLI bookmarklet "Importieren & Bearbeiten" now opens the edit dialog. Service schedule & arrangements - Fixed assigned songs showing no sections (slides loaded for all arrangements, not just the default). - Added "Song entfernen / neu zuordnen" to reassign an assigned song. - Worship-leader arrangement is created/selected lazily when the arrangement dialog opens (only when not user-overridden); the leader is resolved from the "Lobpreis" agenda item, and manual create/clone names are prefixed with the leader name. Navigation - "/" redirects to the next upcoming service's edit page (or the list). - Service titles link to the edit page. Settings - Renamed "Makro-Import"/"Label-Import" menu items; fixed drag-and-drop imports (were downloading the dropped file); added label-import hint; made the panel scrollable. - Nametag now uses a single MacroPicker; added song prefix/postfix label defaults (COPYRIGHT #24B34C / BLANK #000000); new "Export-Dateien" menu to upload prefix/postfix .pro files added to every export. Export - Filenames/playlist names are date-first ("YYYY-MM-DD <Title>"). - Keyvisual slide only for the first content-less item after real content; all other content-less items render as headlines. - New "Vorschau herunterladen" for non-finalized services (filename and import name prefixed "Vorschau" with export timestamp). - Uploaded prefix/postfix .pro files wrap every export. Tests updated to the new behavior; full suite green (569 passed).
93 lines
3.2 KiB
PHP
93 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\ExportProFile;
|
|
use App\Models\Label;
|
|
use App\Models\Macro;
|
|
use App\Models\MacroAssignment;
|
|
use App\Models\MacroCollection;
|
|
use App\Models\Setting;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class SettingsController extends Controller
|
|
{
|
|
private const AGENDA_KEYS = [
|
|
'agenda_start_title',
|
|
'agenda_end_title',
|
|
'agenda_announcement_position',
|
|
'agenda_sermon_matching',
|
|
'default_translation_language',
|
|
'current_key_visual',
|
|
'current_background',
|
|
'namenseinblender_macro_name',
|
|
'namenseinblender_macro_uuid',
|
|
'namenseinblender_macro_collection_name',
|
|
'namenseinblender_macro_collection_uuid',
|
|
'namenseinblender_macro_id',
|
|
'song_prefix_label_id',
|
|
'song_postfix_label_id',
|
|
];
|
|
|
|
public function index(): Response
|
|
{
|
|
$copyright = Label::firstOrCreate(['name' => 'COPYRIGHT'], ['color' => '#24B34C']);
|
|
$blank = Label::firstOrCreate(['name' => 'BLANK'], ['color' => '#000000']);
|
|
|
|
if (Setting::get('song_prefix_label_id') === null) {
|
|
Setting::set('song_prefix_label_id', (string) $copyright->id);
|
|
}
|
|
|
|
if (Setting::get('song_postfix_label_id') === null) {
|
|
Setting::set('song_postfix_label_id', (string) $blank->id);
|
|
}
|
|
|
|
$settings = [];
|
|
foreach (self::AGENDA_KEYS as $key) {
|
|
$settings[$key] = Setting::get($key);
|
|
}
|
|
|
|
return Inertia::render('Settings', [
|
|
'settings' => $settings,
|
|
'assignments' => MacroAssignment::with(['macro', 'label'])->orderBy('part_type')->orderBy('order')->get(),
|
|
'macros' => Macro::with('collections')->orderBy('name')->get(),
|
|
'labels' => Label::orderBy('name')->get(),
|
|
'collections' => MacroCollection::with('macros')->orderBy('name')->get(),
|
|
'last_macros_import' => [
|
|
'at' => Setting::get('macros_last_imported_at'),
|
|
'filename' => Setting::get('macros_last_imported_filename'),
|
|
],
|
|
'last_labels_import' => [
|
|
'at' => Setting::get('labels_last_imported_at'),
|
|
'filename' => Setting::get('labels_last_imported_filename'),
|
|
],
|
|
'export_pro_files' => [
|
|
'prefix' => ExportProFile::prefix()->orderBy('order')->get(['id', 'original_name', 'order']),
|
|
'postfix' => ExportProFile::postfix()->orderBy('order')->get(['id', 'original_name', 'order']),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'key' => ['required', 'string', Rule::in(self::AGENDA_KEYS)],
|
|
'value' => ['nullable', 'string', 'max:500'],
|
|
]);
|
|
|
|
if ($validated['key'] === 'default_translation_language') {
|
|
validator($validated, [
|
|
'value' => ['nullable', Rule::in(['DE', 'EN', 'FR', 'ES', 'NL', 'IT'])],
|
|
])->validate();
|
|
}
|
|
|
|
Setting::set($validated['key'], $validated['value']);
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
}
|