designtopack/public/kirby/src/Content/ContentStorage.php
2024-07-10 16:10:33 +02:00

314 lines
7.6 KiB
PHP

<?php
namespace Kirby\Content;
use Generator;
use Kirby\Cms\ModelWithContent;
use Kirby\Cms\Page;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
/**
* Wrapper for the ContentStorageHandler to
* bundle some business logic that should not
* be included in the handlers themselves but
* also not in the general code calling the storage
* methods
*
* @internal
* @since 4.0.0
*
* @package Kirby Content
* @author Lukas Bestle <lukas@getkirby.com>
* @author Nico Hoffmann <nico@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class ContentStorage
{
protected ContentStorageHandler $handler;
public function __construct(
protected ModelWithContent $model,
string $handler = PlainTextContentStorageHandler::class
) {
$this->handler = new $handler($model);
}
/**
* Magic caller for handler methods
*/
public function __call(string $name, array $args): mixed
{
return $this->handler->$name(...$args);
}
/**
* Returns generator for all existing versions-languages combinations
*
* @return Generator<string|string>
* @todo 4.0.0 consider more descpritive name
*/
public function all(): Generator
{
foreach ($this->model->kirby()->languages()->codes() as $lang) {
foreach ($this->dynamicVersions() as $version) {
if ($this->exists($version, $lang) === true) {
yield $version => $lang;
}
}
}
}
/**
* Returns the absolute path to the content file
* @internal eventually should only exists in PlainTextContentStorage,
* when not relying anymore on language helper
*
* @param string $lang Code `'default'` in a single-lang installation
*
* @throws \Kirby\Exception\LogicException If the model type doesn't have a known content filename
*/
public function contentFile(
string $version,
string $lang,
bool $force = false
): string {
$lang = $this->language($lang, $force);
return $this->handler->contentFile($version, $lang);
}
/**
* Adapts all versions when converting languages
* @internal
*/
public function convertLanguage(string $from, string $to): void
{
$from = $this->language($from, true);
$to = $this->language($to, true);
foreach ($this->dynamicVersions() as $version) {
$this->handler->move($version, $from, $version, $to);
}
}
/**
* Creates a new version
*
* @param string|null $lang Code `'default'` in a single-lang installation
* @param array<string, string> $fields Content fields
*/
public function create(
string $versionType,
string|null $lang,
array $fields
): void {
$lang = $this->language($lang);
$this->handler->create($versionType, $lang, $fields);
}
/**
* Returns the default version identifier for the model
* @internal
*/
public function defaultVersion(): string
{
if (
$this->model instanceof Page === true &&
$this->model->isDraft() === true
) {
return 'changes';
}
return 'published';
}
/**
* Deletes an existing version in an idempotent way if it was already deleted
*
* @param string $lang Code `'default'` in a single-lang installation
*/
public function delete(
string $version,
string|null $lang = null,
bool $force = false
): void {
$lang = $this->language($lang, $force);
$this->handler->delete($version, $lang);
}
/**
* Deletes all versions when deleting a language
* @internal
*/
public function deleteLanguage(string|null $lang): void
{
$lang = $this->language($lang, true);
foreach ($this->dynamicVersions() as $version) {
$this->handler->delete($version, $lang);
}
}
/**
* Returns all versions availalbe for the model that can be updated
* @internal
*/
public function dynamicVersions(): array
{
$versions = ['changes'];
if (
$this->model instanceof Page === false ||
$this->model->isDraft() === false
) {
$versions[] = 'published';
}
return $versions;
}
/**
* Checks if a version exists
*
* @param string|null $lang Code `'default'` in a single-lang installation;
* checks for "any language" if not provided
*/
public function exists(
string $version,
string|null $lang
): bool {
if ($lang !== null) {
$lang = $this->language($lang);
}
return $this->handler->exists($version, $lang);
}
/**
* Returns the modification timestamp of a version
* if it exists
*
* @param string $lang Code `'default'` in a single-lang installation
*/
public function modified(
string $version,
string|null $lang = null
): int|null {
$lang = $this->language($lang);
return $this->handler->modified($version, $lang);
}
/**
* Returns the stored content fields
*
* @param string $lang Code `'default'` in a single-lang installation
* @return array<string, string>
*
* @throws \Kirby\Exception\NotFoundException If the version does not exist
*/
public function read(
string $version,
string|null $lang = null
): array {
$lang = $this->language($lang);
$this->ensureExistingVersion($version, $lang);
return $this->handler->read($version, $lang);
}
/**
* Updates the modification timestamp of an existing version
*
* @param string $lang Code `'default'` in a single-lang installation
*
* @throws \Kirby\Exception\NotFoundException If the version does not exist
*/
public function touch(
string $version,
string|null $lang = null
): void {
$lang = $this->language($lang);
$this->ensureExistingVersion($version, $lang);
$this->handler->touch($version, $lang);
}
/**
* Touches all versions of a language
* @internal
*/
public function touchLanguage(string|null $lang): void
{
$lang = $this->language($lang, true);
foreach ($this->dynamicVersions() as $version) {
if ($this->exists($version, $lang) === true) {
$this->handler->touch($version, $lang);
}
}
}
/**
* Updates the content fields of an existing version
*
* @param string $lang Code `'default'` in a single-lang installation
* @param array<string, string> $fields Content fields
*
* @throws \Kirby\Exception\NotFoundException If the version does not exist
*/
public function update(
string $version,
string|null $lang = null,
array $fields = []
): void {
$lang = $this->language($lang);
$this->ensureExistingVersion($version, $lang);
$this->handler->update($version, $lang, $fields);
}
/**
* @throws \Kirby\Exception\NotFoundException If the version does not exist
*/
protected function ensureExistingVersion(
string $version,
string $lang
): void {
if ($this->exists($version, $lang) !== true) {
throw new NotFoundException('Version "' . $version . ' (' . $lang . ')" does not already exist');
}
}
/**
* Converts a "user-facing" language code to a "raw" language code to be
* used for storage
*
* @param bool $force If set to `true`, the language code is not validated
* @return string Language code
*/
protected function language(
string|null $languageCode = null,
bool $force = false
): string {
// in force mode, use the provided language code even in single-lang for
// compatibility with the previous behavior in `$model->contentFile()`
if ($force === true) {
return $languageCode ?? 'default';
}
// in multi-lang, …
if ($this->model->kirby()->multilang() === true) {
// look up the actual language object if possible
$language = $this->model->kirby()->language($languageCode);
// validate the language code
if ($language === null) {
throw new InvalidArgumentException('Invalid language: ' . $languageCode);
}
return $language->code();
}
// otherwise use hardcoded "default" code for single lang
return 'default';
}
}