Files
2026-07-17 19:13:08 -04:00

76 lines
2.7 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXM\ChronoManager\Import;
use Generator;
use InvalidArgumentException;
use KTXF\Resource\Identifier\CollectionIdentifier;
use KTXM\ChronoManager\Manager;
use KTXM\ChronoManager\Conversion\IcalDecoder;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VEvent;
use Sabre\VObject\Component\VJournal;
use Sabre\VObject\Component\VTodo;
use Sabre\VObject\Parser\MimeDir;
use Sabre\VObject\Reader;
use Throwable;
/** Imports the three RFC 5545 calendar object types into Chrono entities. */
class ImportService {
public function __construct(
private readonly Manager $manager,
private readonly IcalDecoder $decoder,
) {}
/**
* @param resource $source readable, seekable iCalendar resource
* @return Generator<int, ImportEvent>
*/
public function import($source, CollectionIdentifier $target, ImportOptions $options, string $tenantId, string $userId): Generator {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source: must be a file resource');
}
try {
rewind($source);
$calendar = Reader::read($source, MimeDir::OPTION_FORGIVING | MimeDir::OPTION_IGNORE_INVALID_LINES);
if (!$calendar instanceof VCalendar) {
throw new InvalidArgumentException('Supplied input is not an iCalendar document');
}
} catch (Throwable $e) {
if ($options->getCounts()) {
yield new ImportCountEvent(0);
}
yield new ImportObjectEvent(null, ImportDisposition::Error, ['Malformed iCalendar: ' . $e->getMessage()]);
return;
}
$components = array_values(array_filter(
$calendar->children(),
static fn($component): bool => $component instanceof VEvent
|| $component instanceof VTodo
|| $component instanceof VJournal,
));
if ($options->getCounts()) {
yield new ImportCountEvent(count($components));
}
foreach ($components as $component) {
$uid = isset($component->UID) ? (trim((string)$component->UID) ?: null) : null;
try {
$this->manager->entityCreate($tenantId, $userId, $target, $this->decoder->fromComponent($component));
yield new ImportObjectEvent($uid, ImportDisposition::Created);
} catch (Throwable $e) {
yield new ImportObjectEvent($uid, ImportDisposition::Error, [$e->getMessage()]);
if ($options->getErrors() === ImportOptions::ERROR_FAIL) {
return;
}
}
}
}
}