pp-planer/app/Http/Controllers/BookmarkletController.php
Thorsten Bus ae42b48753 feat(songs): per-song sections + section editing; fix CCLI import bugs
Refactor lyric storage so each song owns its sections instead of sharing
global labels. Adds song_sections (per song+label) owning song_slides;
labels stay global ProPresenter group tags (name/color/macro). Arrangements
now reference sections, so editing/importing one song no longer corrupts
others that share a label name.

- New: song_sections table + migration with safe backfill; SongSection,
  SongArrangementSection models; SongSectionController (edit/add/delete
  sections, immediate persistence) wired into SongEditModal.
- Refactor writers/readers: CcliImport, ProImport, SongService,
  ArrangementController, SongController, ProExport, PDF, Translation
  (translation reset now section-scoped), CCLI pairing.
- CCLI import fixes: parse SongSelect copy-icon format (German "Vers"
  abbrev + trailing author), fill empty CTS-synced songs instead of
  blocking as duplicate, distinct label colors per section kind,
  import&edit/existing-song open the edit modal (no 404/405), teleport
  paste dialog above assign dialog, preview shows section content,
  correct SongSelect search URL, copy-icon instructions.
- Bookmarklet clicks #generalCopyLyricsButton and captures clipboard;
  serves correct host from request.
- Export: embed key-visual/background under fixed bundle-relative names.
- Tests updated for the section model; new section + isolation coverage.
2026-05-31 14:45:47 +02:00

75 lines
2.4 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Config;
final class BookmarkletController extends Controller
{
public function show(Request $request): Response
{
$appUrl = rtrim($request->getSchemeAndHttpHost(), '/');
if ($appUrl === '') {
$appUrl = rtrim((string) Config::get('app.url', ''), '/');
}
$bookmarkletScript = <<<'BOOKMARKLET'
(function(){
var APP_URL = '__APP_URL__';
if(!location.hostname.includes('songselect.ccli.com')){
alert('Bitte öffne dieses Lesezeichen auf einer SongSelect Liedseite (songselect.ccli.com).');
return;
}
function send(text){
var ccliMatch = (text || '').match(/CCLI[\s#-]*(\d+)/i);
var payload = {
title: '',
author: '',
ccliId: ccliMatch ? ccliMatch[1] : '',
sourceUrl: location.href,
rawText: text || ''
};
var encoded = btoa(unescape(encodeURIComponent(JSON.stringify(payload))));
window.open(APP_URL + '/songs/import-from-ccli-paste?prefill=' + encoded, '_blank');
}
var btn = document.querySelector('#generalCopyLyricsButton');
if(!btn){
alert('Kopier-Symbol nicht gefunden. Bitte öffne die Liedtext-Ansicht auf SongSelect und versuche es erneut.');
return;
}
var captured = null;
function onCopy(e){
try { captured = e.clipboardData.getData('text/plain'); } catch(err) {}
}
document.addEventListener('copy', onCopy, true);
btn.click();
setTimeout(function(){
document.removeEventListener('copy', onCopy, true);
if(captured && captured.trim()){
send(captured);
return;
}
if(navigator.clipboard && navigator.clipboard.readText){
navigator.clipboard.readText().then(function(text){ send(text); })
.catch(function(){ alert('Liedtext konnte nicht aus der Zwischenablage gelesen werden. Bitte kopiere ihn manuell und nutze „Aus CCLI importieren".'); });
} else {
alert('Liedtext konnte nicht gelesen werden. Bitte kopiere ihn manuell und nutze „Aus CCLI importieren".');
}
}, 250);
})();
BOOKMARKLET;
$bookmarkletScript = str_replace('__APP_URL__', $appUrl, $bookmarkletScript);
$singleLine = 'javascript:'.preg_replace('/\s+/', ' ', $bookmarkletScript);
return response($singleLine, 200, [
'Content-Type' => 'text/javascript; charset=utf-8',
'Cache-Control' => 'public, max-age=3600',
]);
}
}