pp-planer/app/Services/ProBundleExportService.php
Thorsten Bus 5b35afb31d feat(export): add probundle export for service slide blocks
- Create ProBundleExportService with generateBundle() method
- Generate flat ZIP with .pro file + image files at root level
- Add downloadBundle() method to ServiceController
- Add services.download-bundle route
- Add .probundle download buttons to Information, Moderation, Sermon blocks
- Add 3 tests verifying ZIP structure and validation
- All tests pass (206/206, 1129 assertions)
2026-03-02 22:18:33 +01:00

118 lines
3.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Service;
use InvalidArgumentException;
use Illuminate\Support\Facades\Storage;
use ProPresenter\Parser\ProFileGenerator;
use ZipArchive;
class ProBundleExportService
{
private const ALLOWED_BLOCK_TYPES = ['information', 'moderation', 'sermon'];
public function generateBundle(Service $service, string $blockType): string
{
if (! in_array($blockType, self::ALLOWED_BLOCK_TYPES, true)) {
throw new InvalidArgumentException('Ungültiger Blocktyp für .probundle Export.');
}
$slides = $service->slides()
->where('type', $blockType)
->orderBy('sort_order')
->get();
$tempDir = sys_get_temp_dir().'/probundle-export-'.uniqid();
mkdir($tempDir, 0755, true);
$groupName = ucfirst($blockType);
$slideData = [];
$copiedImagePaths = [];
foreach ($slides as $slide) {
$sourcePath = Storage::disk('public')->path('slides/'.$slide->stored_filename);
if (! file_exists($sourcePath)) {
continue;
}
$imageFilename = basename($slide->stored_filename);
$tempImagePath = $tempDir.'/'.$imageFilename;
copy($sourcePath, $tempImagePath);
$copiedImagePaths[] = $tempImagePath;
$slideData[] = [
'text' => $slide->original_filename ?? '',
'media' => $imageFilename,
'format' => 'JPG',
];
}
$groups = [
[
'name' => $groupName,
'color' => [0, 0, 0, 1],
'slides' => $slideData,
],
];
$arrangements = [
[
'name' => 'normal',
'groupNames' => [$groupName],
],
];
$proFilePath = $tempDir.'/'.$blockType.'.pro';
ProFileGenerator::generateAndWrite($proFilePath, $groupName, $groups, $arrangements);
$bundlePath = sys_get_temp_dir().'/'.uniqid($blockType.'-').'.probundle';
$zip = new ZipArchive();
$openResult = $zip->open($bundlePath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($openResult !== true) {
$this->deleteDirectory($tempDir);
throw new InvalidArgumentException('Konnte .probundle nicht erstellen.');
}
$zip->addFile($proFilePath, basename($proFilePath));
foreach ($copiedImagePaths as $imagePath) {
$zip->addFile($imagePath, basename($imagePath));
}
$zip->close();
$this->deleteDirectory($tempDir);
return $bundlePath;
}
private function deleteDirectory(string $dir): void
{
if (! is_dir($dir)) {
return;
}
$entries = scandir($dir);
if ($entries === false) {
return;
}
foreach ($entries as $entry) {
if ($entry === '.' || $entry === '..') {
continue;
}
$path = $dir.'/'.$entry;
if (is_dir($path)) {
$this->deleteDirectory($path);
} else {
@unlink($path);
}
}
@rmdir($dir);
}
}