Upgrade to 3.7.1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -19,160 +19,160 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Collection
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Api\Api
|
||||
*/
|
||||
protected $api;
|
||||
/**
|
||||
* @var \Kirby\Api\Api
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $model;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $select;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $select;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $view;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Collection constructor
|
||||
*
|
||||
* @param \Kirby\Api\Api $api
|
||||
* @param mixed|null $data
|
||||
* @param array $schema
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Api $api, $data, array $schema)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->data = $data;
|
||||
$this->model = $schema['model'] ?? null;
|
||||
$this->view = $schema['view'] ?? null;
|
||||
/**
|
||||
* Collection constructor
|
||||
*
|
||||
* @param \Kirby\Api\Api $api
|
||||
* @param mixed|null $data
|
||||
* @param array $schema
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Api $api, $data, array $schema)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->data = $data;
|
||||
$this->model = $schema['model'] ?? null;
|
||||
$this->view = $schema['view'] ?? null;
|
||||
|
||||
if ($data === null) {
|
||||
if (is_a($schema['default'] ?? null, 'Closure') === false) {
|
||||
throw new Exception('Missing collection data');
|
||||
}
|
||||
if ($data === null) {
|
||||
if (is_a($schema['default'] ?? null, 'Closure') === false) {
|
||||
throw new Exception('Missing collection data');
|
||||
}
|
||||
|
||||
$this->data = $schema['default']->call($this->api);
|
||||
}
|
||||
$this->data = $schema['default']->call($this->api);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($schema['type']) === true &&
|
||||
is_a($this->data, $schema['type']) === false
|
||||
) {
|
||||
throw new Exception('Invalid collection type');
|
||||
}
|
||||
}
|
||||
if (
|
||||
isset($schema['type']) === true &&
|
||||
is_a($this->data, $schema['type']) === false
|
||||
) {
|
||||
throw new Exception('Invalid collection type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array|null $keys
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function select($keys = null)
|
||||
{
|
||||
if ($keys === false) {
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string|array|null $keys
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function select($keys = null)
|
||||
{
|
||||
if ($keys === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = Str::split($keys);
|
||||
}
|
||||
if (is_string($keys)) {
|
||||
$keys = Str::split($keys);
|
||||
}
|
||||
|
||||
if ($keys !== null && is_array($keys) === false) {
|
||||
throw new Exception('Invalid select keys');
|
||||
}
|
||||
if ($keys !== null && is_array($keys) === false) {
|
||||
throw new Exception('Invalid select keys');
|
||||
}
|
||||
|
||||
$this->select = $keys;
|
||||
return $this;
|
||||
}
|
||||
$this->select = $keys;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->data as $item) {
|
||||
$model = $this->api->model($this->model, $item);
|
||||
foreach ($this->data as $item) {
|
||||
$model = $this->api->model($this->model, $item);
|
||||
|
||||
if ($this->view !== null) {
|
||||
$model = $model->view($this->view);
|
||||
}
|
||||
if ($this->view !== null) {
|
||||
$model = $model->view($this->view);
|
||||
}
|
||||
|
||||
if ($this->select !== null) {
|
||||
$model = $model->select($this->select);
|
||||
}
|
||||
if ($this->select !== null) {
|
||||
$model = $model->select($this->select);
|
||||
}
|
||||
|
||||
$result[] = $model->toArray();
|
||||
}
|
||||
$result[] = $model->toArray();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
if ($query = $this->api->requestQuery('query')) {
|
||||
$this->data = $this->data->query($query);
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
if ($query = $this->api->requestQuery('query')) {
|
||||
$this->data = $this->data->query($query);
|
||||
}
|
||||
|
||||
if (!$this->data->pagination()) {
|
||||
$this->data = $this->data->paginate([
|
||||
'page' => $this->api->requestQuery('page', 1),
|
||||
'limit' => $this->api->requestQuery('limit', 100)
|
||||
]);
|
||||
}
|
||||
if (!$this->data->pagination()) {
|
||||
$this->data = $this->data->paginate([
|
||||
'page' => $this->api->requestQuery('page', 1),
|
||||
'limit' => $this->api->requestQuery('limit', 100)
|
||||
]);
|
||||
}
|
||||
|
||||
$pagination = $this->data->pagination();
|
||||
$pagination = $this->data->pagination();
|
||||
|
||||
if ($select = $this->api->requestQuery('select')) {
|
||||
$this->select($select);
|
||||
}
|
||||
if ($select = $this->api->requestQuery('select')) {
|
||||
$this->select($select);
|
||||
}
|
||||
|
||||
if ($view = $this->api->requestQuery('view')) {
|
||||
$this->view($view);
|
||||
}
|
||||
if ($view = $this->api->requestQuery('view')) {
|
||||
$this->view($view);
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 200,
|
||||
'data' => $this->toArray(),
|
||||
'pagination' => [
|
||||
'page' => $pagination->page(),
|
||||
'total' => $pagination->total(),
|
||||
'offset' => $pagination->offset(),
|
||||
'limit' => $pagination->limit(),
|
||||
],
|
||||
'status' => 'ok',
|
||||
'type' => 'collection'
|
||||
];
|
||||
}
|
||||
return [
|
||||
'code' => 200,
|
||||
'data' => $this->toArray(),
|
||||
'pagination' => [
|
||||
'page' => $pagination->page(),
|
||||
'total' => $pagination->total(),
|
||||
'offset' => $pagination->offset(),
|
||||
'limit' => $pagination->limit(),
|
||||
],
|
||||
'status' => 'ok',
|
||||
'type' => 'collection'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $view
|
||||
* @return $this
|
||||
*/
|
||||
public function view(string $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $view
|
||||
* @return $this
|
||||
*/
|
||||
public function view(string $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,228 +21,228 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Model
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Api\Api
|
||||
*/
|
||||
protected $api;
|
||||
/**
|
||||
* @var \Kirby\Api\Api
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var array|mixed
|
||||
*/
|
||||
protected $fields;
|
||||
/**
|
||||
* @var array|mixed
|
||||
*/
|
||||
protected $fields;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $select;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $select;
|
||||
|
||||
/**
|
||||
* @var array|mixed
|
||||
*/
|
||||
protected $views;
|
||||
/**
|
||||
* @var array|mixed
|
||||
*/
|
||||
protected $views;
|
||||
|
||||
/**
|
||||
* Model constructor
|
||||
*
|
||||
* @param \Kirby\Api\Api $api
|
||||
* @param mixed $data
|
||||
* @param array $schema
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Api $api, $data, array $schema)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->data = $data;
|
||||
$this->fields = $schema['fields'] ?? [];
|
||||
$this->select = $schema['select'] ?? null;
|
||||
$this->views = $schema['views'] ?? [];
|
||||
/**
|
||||
* Model constructor
|
||||
*
|
||||
* @param \Kirby\Api\Api $api
|
||||
* @param mixed $data
|
||||
* @param array $schema
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Api $api, $data, array $schema)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->data = $data;
|
||||
$this->fields = $schema['fields'] ?? [];
|
||||
$this->select = $schema['select'] ?? null;
|
||||
$this->views = $schema['views'] ?? [];
|
||||
|
||||
if ($this->select === null && array_key_exists('default', $this->views)) {
|
||||
$this->view('default');
|
||||
}
|
||||
if ($this->select === null && array_key_exists('default', $this->views)) {
|
||||
$this->view('default');
|
||||
}
|
||||
|
||||
if ($data === null) {
|
||||
if (is_a($schema['default'] ?? null, 'Closure') === false) {
|
||||
throw new Exception('Missing model data');
|
||||
}
|
||||
if ($data === null) {
|
||||
if (is_a($schema['default'] ?? null, 'Closure') === false) {
|
||||
throw new Exception('Missing model data');
|
||||
}
|
||||
|
||||
$this->data = $schema['default']->call($this->api);
|
||||
}
|
||||
$this->data = $schema['default']->call($this->api);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($schema['type']) === true &&
|
||||
is_a($this->data, $schema['type']) === false
|
||||
) {
|
||||
throw new Exception(sprintf('Invalid model type "%s" expected: "%s"', get_class($this->data), $schema['type']));
|
||||
}
|
||||
}
|
||||
if (
|
||||
isset($schema['type']) === true &&
|
||||
is_a($this->data, $schema['type']) === false
|
||||
) {
|
||||
throw new Exception(sprintf('Invalid model type "%s" expected: "%s"', get_class($this->data), $schema['type']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $keys
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function select($keys = null)
|
||||
{
|
||||
if ($keys === false) {
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param null $keys
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function select($keys = null)
|
||||
{
|
||||
if ($keys === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = Str::split($keys);
|
||||
}
|
||||
if (is_string($keys)) {
|
||||
$keys = Str::split($keys);
|
||||
}
|
||||
|
||||
if ($keys !== null && is_array($keys) === false) {
|
||||
throw new Exception('Invalid select keys');
|
||||
}
|
||||
if ($keys !== null && is_array($keys) === false) {
|
||||
throw new Exception('Invalid select keys');
|
||||
}
|
||||
|
||||
$this->select = $keys;
|
||||
return $this;
|
||||
}
|
||||
$this->select = $keys;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selection(): array
|
||||
{
|
||||
$select = $this->select;
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selection(): array
|
||||
{
|
||||
$select = $this->select;
|
||||
|
||||
if ($select === null) {
|
||||
$select = array_keys($this->fields);
|
||||
}
|
||||
if ($select === null) {
|
||||
$select = array_keys($this->fields);
|
||||
}
|
||||
|
||||
$selection = [];
|
||||
$selection = [];
|
||||
|
||||
foreach ($select as $key => $value) {
|
||||
if (is_int($key) === true) {
|
||||
$selection[$value] = [
|
||||
'view' => null,
|
||||
'select' => null
|
||||
];
|
||||
continue;
|
||||
}
|
||||
foreach ($select as $key => $value) {
|
||||
if (is_int($key) === true) {
|
||||
$selection[$value] = [
|
||||
'view' => null,
|
||||
'select' => null
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($value) === true) {
|
||||
if ($value === 'any') {
|
||||
throw new Exception('Invalid sub view: "any"');
|
||||
}
|
||||
if (is_string($value) === true) {
|
||||
if ($value === 'any') {
|
||||
throw new Exception('Invalid sub view: "any"');
|
||||
}
|
||||
|
||||
$selection[$key] = [
|
||||
'view' => $value,
|
||||
'select' => null
|
||||
];
|
||||
$selection[$key] = [
|
||||
'view' => $value,
|
||||
'select' => null
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value) === true) {
|
||||
$selection[$key] = [
|
||||
'view' => null,
|
||||
'select' => $value
|
||||
];
|
||||
}
|
||||
}
|
||||
if (is_array($value) === true) {
|
||||
$selection[$key] = [
|
||||
'view' => null,
|
||||
'select' => $value
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $selection;
|
||||
}
|
||||
return $selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$select = $this->selection();
|
||||
$result = [];
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$select = $this->selection();
|
||||
$result = [];
|
||||
|
||||
foreach ($this->fields as $key => $resolver) {
|
||||
if (array_key_exists($key, $select) === false || is_a($resolver, 'Closure') === false) {
|
||||
continue;
|
||||
}
|
||||
foreach ($this->fields as $key => $resolver) {
|
||||
if (array_key_exists($key, $select) === false || is_a($resolver, 'Closure') === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $resolver->call($this->api, $this->data);
|
||||
$value = $resolver->call($this->api, $this->data);
|
||||
|
||||
if (is_object($value)) {
|
||||
$value = $this->api->resolve($value);
|
||||
}
|
||||
if (is_object($value)) {
|
||||
$value = $this->api->resolve($value);
|
||||
}
|
||||
|
||||
if (
|
||||
is_a($value, 'Kirby\Api\Collection') === true ||
|
||||
is_a($value, 'Kirby\Api\Model') === true
|
||||
) {
|
||||
$selection = $select[$key];
|
||||
if (
|
||||
is_a($value, 'Kirby\Api\Collection') === true ||
|
||||
is_a($value, 'Kirby\Api\Model') === true
|
||||
) {
|
||||
$selection = $select[$key];
|
||||
|
||||
if ($subview = $selection['view']) {
|
||||
$value->view($subview);
|
||||
}
|
||||
if ($subview = $selection['view']) {
|
||||
$value->view($subview);
|
||||
}
|
||||
|
||||
if ($subselect = $selection['select']) {
|
||||
$value->select($subselect);
|
||||
}
|
||||
if ($subselect = $selection['select']) {
|
||||
$value->select($subselect);
|
||||
}
|
||||
|
||||
$value = $value->toArray();
|
||||
}
|
||||
$value = $value->toArray();
|
||||
}
|
||||
|
||||
$result[$key] = $value;
|
||||
}
|
||||
$result[$key] = $value;
|
||||
}
|
||||
|
||||
ksort($result);
|
||||
ksort($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
$model = $this;
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
$model = $this;
|
||||
|
||||
if ($select = $this->api->requestQuery('select')) {
|
||||
$model = $model->select($select);
|
||||
}
|
||||
if ($select = $this->api->requestQuery('select')) {
|
||||
$model = $model->select($select);
|
||||
}
|
||||
|
||||
if ($view = $this->api->requestQuery('view')) {
|
||||
$model = $model->view($view);
|
||||
}
|
||||
if ($view = $this->api->requestQuery('view')) {
|
||||
$model = $model->view($view);
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 200,
|
||||
'data' => $model->toArray(),
|
||||
'status' => 'ok',
|
||||
'type' => 'model'
|
||||
];
|
||||
}
|
||||
return [
|
||||
'code' => 200,
|
||||
'data' => $model->toArray(),
|
||||
'status' => 'ok',
|
||||
'type' => 'model'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function view(string $name)
|
||||
{
|
||||
if ($name === 'any') {
|
||||
return $this->select(null);
|
||||
}
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function view(string $name)
|
||||
{
|
||||
if ($name === 'any') {
|
||||
return $this->select(null);
|
||||
}
|
||||
|
||||
if (isset($this->views[$name]) === false) {
|
||||
$name = 'default';
|
||||
if (isset($this->views[$name]) === false) {
|
||||
$name = 'default';
|
||||
|
||||
// try to fall back to the default view at least
|
||||
if (isset($this->views[$name]) === false) {
|
||||
throw new Exception(sprintf('The view "%s" does not exist', $name));
|
||||
}
|
||||
}
|
||||
// try to fall back to the default view at least
|
||||
if (isset($this->views[$name]) === false) {
|
||||
throw new Exception(sprintf('The view "%s" does not exist', $name));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->select($this->views[$name]);
|
||||
}
|
||||
return $this->select($this->views[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,72 +15,72 @@ use APCUIterator;
|
||||
*/
|
||||
class ApcuCache extends Cache
|
||||
{
|
||||
/**
|
||||
* Determines if an item exists in the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return apcu_exists($this->key($key));
|
||||
}
|
||||
/**
|
||||
* Determines if an item exists in the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return apcu_exists($this->key($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
return apcu_delete(new APCUIterator('!^' . preg_quote($this->options['prefix']) . '!'));
|
||||
} else {
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
return apcu_delete(new APCUIterator('!^' . preg_quote($this->options['prefix']) . '!'));
|
||||
} else {
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return apcu_delete($this->key($key));
|
||||
}
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return apcu_delete($this->key($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson(apcu_fetch($this->key($key)));
|
||||
}
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson(apcu_fetch($this->key($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return apcu_store($this->key($key), (new Value($value, $minutes))->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return apcu_store($this->key($key), (new Value($value, $minutes))->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,227 +16,227 @@ namespace Kirby\Cache;
|
||||
*/
|
||||
abstract class Cache
|
||||
{
|
||||
/**
|
||||
* Stores all options for the driver
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
/**
|
||||
* Stores all options for the driver
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Sets all parameters which are needed to connect to the cache storage
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
/**
|
||||
* Sets all parameters which are needed to connect to the cache storage
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function set(string $key, $value, int $minutes = 0): bool;
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function set(string $key, $value, int $minutes = 0): bool;
|
||||
|
||||
/**
|
||||
* Adds the prefix to the key if given
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function key(string $key): string
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$key = $this->options['prefix'] . '/' . $key;
|
||||
}
|
||||
/**
|
||||
* Adds the prefix to the key if given
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function key(string $key): string
|
||||
{
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$key = $this->options['prefix'] . '/' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
abstract public function retrieve(string $key);
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
abstract public function retrieve(string $key);
|
||||
|
||||
/**
|
||||
* Gets an item from the cache
|
||||
*
|
||||
* <code>
|
||||
* // get an item from the cache driver
|
||||
* $value = $cache->get('value');
|
||||
*
|
||||
* // return a default value if the requested item isn't cached
|
||||
* $value = $cache->get('value', 'default value');
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
// get the Value
|
||||
$value = $this->retrieve($key);
|
||||
/**
|
||||
* Gets an item from the cache
|
||||
*
|
||||
* <code>
|
||||
* // get an item from the cache driver
|
||||
* $value = $cache->get('value');
|
||||
*
|
||||
* // return a default value if the requested item isn't cached
|
||||
* $value = $cache->get('value', 'default value');
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
// get the Value
|
||||
$value = $this->retrieve($key);
|
||||
|
||||
// check for a valid cache value
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return $default;
|
||||
}
|
||||
// check for a valid cache value
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
// remove the item if it is expired
|
||||
if ($value->expires() > 0 && time() >= $value->expires()) {
|
||||
$this->remove($key);
|
||||
return $default;
|
||||
}
|
||||
// remove the item if it is expired
|
||||
if ($value->expires() > 0 && time() >= $value->expires()) {
|
||||
$this->remove($key);
|
||||
return $default;
|
||||
}
|
||||
|
||||
// return the pure value
|
||||
return $value->value();
|
||||
}
|
||||
// return the pure value
|
||||
return $value->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the expiration timestamp
|
||||
*
|
||||
* @param int $minutes
|
||||
* @return int
|
||||
*/
|
||||
protected function expiration(int $minutes = 0): int
|
||||
{
|
||||
// 0 = keep forever
|
||||
if ($minutes === 0) {
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Calculates the expiration timestamp
|
||||
*
|
||||
* @param int $minutes
|
||||
* @return int
|
||||
*/
|
||||
protected function expiration(int $minutes = 0): int
|
||||
{
|
||||
// 0 = keep forever
|
||||
if ($minutes === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// calculate the time
|
||||
return time() + ($minutes * 60);
|
||||
}
|
||||
// calculate the time
|
||||
return time() + ($minutes * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks when an item in the cache expires;
|
||||
* returns the expiry timestamp on success, null if the
|
||||
* item never expires and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|null|false
|
||||
*/
|
||||
public function expires(string $key)
|
||||
{
|
||||
// get the Value object
|
||||
$value = $this->retrieve($key);
|
||||
/**
|
||||
* Checks when an item in the cache expires;
|
||||
* returns the expiry timestamp on success, null if the
|
||||
* item never expires and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|null|false
|
||||
*/
|
||||
public function expires(string $key)
|
||||
{
|
||||
// get the Value object
|
||||
$value = $this->retrieve($key);
|
||||
|
||||
// check for a valid Value object
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return false;
|
||||
}
|
||||
// check for a valid Value object
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the expires timestamp
|
||||
return $value->expires();
|
||||
}
|
||||
// return the expires timestamp
|
||||
return $value->expires();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item in the cache is expired
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function expired(string $key): bool
|
||||
{
|
||||
$expires = $this->expires($key);
|
||||
/**
|
||||
* Checks if an item in the cache is expired
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function expired(string $key): bool
|
||||
{
|
||||
$expires = $this->expires($key);
|
||||
|
||||
if ($expires === null) {
|
||||
return false;
|
||||
} elseif (!is_int($expires)) {
|
||||
return true;
|
||||
} else {
|
||||
return time() >= $expires;
|
||||
}
|
||||
}
|
||||
if ($expires === null) {
|
||||
return false;
|
||||
} elseif (!is_int($expires)) {
|
||||
return true;
|
||||
} else {
|
||||
return time() >= $expires;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks when the cache has been created;
|
||||
* returns the creation timestamp on success
|
||||
* and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|false
|
||||
*/
|
||||
public function created(string $key)
|
||||
{
|
||||
// get the Value object
|
||||
$value = $this->retrieve($key);
|
||||
/**
|
||||
* Checks when the cache has been created;
|
||||
* returns the creation timestamp on success
|
||||
* and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|false
|
||||
*/
|
||||
public function created(string $key)
|
||||
{
|
||||
// get the Value object
|
||||
$value = $this->retrieve($key);
|
||||
|
||||
// check for a valid Value object
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return false;
|
||||
}
|
||||
// check for a valid Value object
|
||||
if (!is_a($value, 'Kirby\Cache\Value')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the expires timestamp
|
||||
return $value->created();
|
||||
}
|
||||
// return the expires timestamp
|
||||
return $value->created();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate version for Cache::created($key)
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|false
|
||||
*/
|
||||
public function modified(string $key)
|
||||
{
|
||||
return static::created($key);
|
||||
}
|
||||
/**
|
||||
* Alternate version for Cache::created($key)
|
||||
*
|
||||
* @param string $key
|
||||
* @return int|false
|
||||
*/
|
||||
public function modified(string $key)
|
||||
{
|
||||
return static::created($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an item exists in the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return $this->expired($key) === false;
|
||||
}
|
||||
/**
|
||||
* Determines if an item exists in the cache
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $key): bool
|
||||
{
|
||||
return $this->expired($key) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function remove(string $key): bool;
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function remove(string $key): bool;
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function flush(): bool;
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful;
|
||||
* this needs to be defined by the driver
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function flush(): bool;
|
||||
|
||||
/**
|
||||
* Returns all passed cache options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
/**
|
||||
* Returns all passed cache options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,217 +18,217 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class FileCache extends Cache
|
||||
{
|
||||
/**
|
||||
* Full root including prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
/**
|
||||
* Full root including prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Sets all parameters which are needed for the file cache
|
||||
*
|
||||
* @param array $options 'root' (required)
|
||||
* 'prefix' (default: none)
|
||||
* 'extension' (file extension for cache files, default: none)
|
||||
*/
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$defaults = [
|
||||
'root' => null,
|
||||
'prefix' => null,
|
||||
'extension' => null
|
||||
];
|
||||
/**
|
||||
* Sets all parameters which are needed for the file cache
|
||||
*
|
||||
* @param array $options 'root' (required)
|
||||
* 'prefix' (default: none)
|
||||
* 'extension' (file extension for cache files, default: none)
|
||||
*/
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$defaults = [
|
||||
'root' => null,
|
||||
'prefix' => null,
|
||||
'extension' => null
|
||||
];
|
||||
|
||||
parent::__construct(array_merge($defaults, $options));
|
||||
parent::__construct(array_merge($defaults, $options));
|
||||
|
||||
// build the full root including prefix
|
||||
$this->root = $this->options['root'];
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$this->root .= '/' . $this->options['prefix'];
|
||||
}
|
||||
// build the full root including prefix
|
||||
$this->root = $this->options['root'];
|
||||
if (empty($this->options['prefix']) === false) {
|
||||
$this->root .= '/' . $this->options['prefix'];
|
||||
}
|
||||
|
||||
// try to create the directory
|
||||
Dir::make($this->root, true);
|
||||
}
|
||||
// try to create the directory
|
||||
Dir::make($this->root, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full root including prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
/**
|
||||
* Returns the full root including prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path to a file for a given key
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function file(string $key): string
|
||||
{
|
||||
// strip out invalid characters in each path segment
|
||||
// split by slash or backslash
|
||||
$keyParts = [];
|
||||
foreach (preg_split('#([\/\\\\])#', $key, 0, PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||
switch ($part) {
|
||||
// forward slashes don't need special treatment
|
||||
case '/':
|
||||
break;
|
||||
/**
|
||||
* Returns the full path to a file for a given key
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function file(string $key): string
|
||||
{
|
||||
// strip out invalid characters in each path segment
|
||||
// split by slash or backslash
|
||||
$keyParts = [];
|
||||
foreach (preg_split('#([\/\\\\])#', $key, 0, PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||
switch ($part) {
|
||||
// forward slashes don't need special treatment
|
||||
case '/':
|
||||
break;
|
||||
|
||||
// backslashes get their own marker in the path
|
||||
// to differentiate the cache key from one with forward slashes
|
||||
case '\\':
|
||||
$keyParts[] = '_backslash';
|
||||
break;
|
||||
// backslashes get their own marker in the path
|
||||
// to differentiate the cache key from one with forward slashes
|
||||
case '\\':
|
||||
$keyParts[] = '_backslash';
|
||||
break;
|
||||
|
||||
// empty part means two slashes in a row;
|
||||
// special marker like for backslashes
|
||||
case '':
|
||||
$keyParts[] = '_empty';
|
||||
break;
|
||||
// empty part means two slashes in a row;
|
||||
// special marker like for backslashes
|
||||
case '':
|
||||
$keyParts[] = '_empty';
|
||||
break;
|
||||
|
||||
// an actual path segment
|
||||
default:
|
||||
// check if the segment only contains safe characters;
|
||||
// underscores are *not* safe to guarantee uniqueness
|
||||
// as they are used in the special cases
|
||||
if (preg_match('/^[a-zA-Z0-9-]+$/', $part) === 1) {
|
||||
$keyParts[] = $part;
|
||||
} else {
|
||||
$keyParts[] = Str::slug($part) . '_' . sha1($part);
|
||||
}
|
||||
}
|
||||
}
|
||||
// an actual path segment
|
||||
default:
|
||||
// check if the segment only contains safe characters;
|
||||
// underscores are *not* safe to guarantee uniqueness
|
||||
// as they are used in the special cases
|
||||
if (preg_match('/^[a-zA-Z0-9-]+$/', $part) === 1) {
|
||||
$keyParts[] = $part;
|
||||
} else {
|
||||
$keyParts[] = Str::slug($part) . '_' . sha1($part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->root . '/' . implode('/', $keyParts);
|
||||
$file = $this->root . '/' . implode('/', $keyParts);
|
||||
|
||||
if (isset($this->options['extension'])) {
|
||||
return $file . '.' . $this->options['extension'];
|
||||
} else {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
if (isset($this->options['extension'])) {
|
||||
return $file . '.' . $this->options['extension'];
|
||||
} else {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
$file = $this->file($key);
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
$file = $this->file($key);
|
||||
|
||||
return F::write($file, (new Value($value, $minutes))->toJson());
|
||||
}
|
||||
return F::write($file, (new Value($value, $minutes))->toJson());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
$file = $this->file($key);
|
||||
$value = F::read($file);
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
$file = $this->file($key);
|
||||
$value = F::read($file);
|
||||
|
||||
return $value ? Value::fromJson($value) : null;
|
||||
}
|
||||
return $value ? Value::fromJson($value) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks when the cache has been created;
|
||||
* returns the creation timestamp on success
|
||||
* and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function created(string $key)
|
||||
{
|
||||
// use the modification timestamp
|
||||
// as indicator when the cache has been created/overwritten
|
||||
clearstatcache();
|
||||
/**
|
||||
* Checks when the cache has been created;
|
||||
* returns the creation timestamp on success
|
||||
* and false if the item does not exist
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function created(string $key)
|
||||
{
|
||||
// use the modification timestamp
|
||||
// as indicator when the cache has been created/overwritten
|
||||
clearstatcache();
|
||||
|
||||
// get the file for this cache key
|
||||
$file = $this->file($key);
|
||||
return file_exists($file) ? filemtime($this->file($key)) : false;
|
||||
}
|
||||
// get the file for this cache key
|
||||
$file = $this->file($key);
|
||||
return file_exists($file) ? filemtime($this->file($key)) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
$file = $this->file($key);
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
$file = $this->file($key);
|
||||
|
||||
if (is_file($file) === true && F::remove($file) === true) {
|
||||
$this->removeEmptyDirectories(dirname($file));
|
||||
return true;
|
||||
}
|
||||
if (is_file($file) === true && F::remove($file) === true) {
|
||||
$this->removeEmptyDirectories(dirname($file));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes empty directories safely by checking each directory
|
||||
* up to the root directory
|
||||
*
|
||||
* @param string $dir
|
||||
* @return void
|
||||
*/
|
||||
protected function removeEmptyDirectories(string $dir): void
|
||||
{
|
||||
try {
|
||||
// ensure the path doesn't end with a slash for the next comparison
|
||||
$dir = rtrim($dir, '/\/');
|
||||
/**
|
||||
* Removes empty directories safely by checking each directory
|
||||
* up to the root directory
|
||||
*
|
||||
* @param string $dir
|
||||
* @return void
|
||||
*/
|
||||
protected function removeEmptyDirectories(string $dir): void
|
||||
{
|
||||
try {
|
||||
// ensure the path doesn't end with a slash for the next comparison
|
||||
$dir = rtrim($dir, '/\/');
|
||||
|
||||
// checks all directory segments until reaching the root directory
|
||||
while (Str::startsWith($dir, $this->root()) === true && $dir !== $this->root()) {
|
||||
$files = array_diff(scandir($dir) ?? [], ['.', '..']);
|
||||
// checks all directory segments until reaching the root directory
|
||||
while (Str::startsWith($dir, $this->root()) === true && $dir !== $this->root()) {
|
||||
$files = array_diff(scandir($dir) ?? [], ['.', '..']);
|
||||
|
||||
if (empty($files) === true && Dir::remove($dir) === true) {
|
||||
// continue with the next level up
|
||||
$dir = dirname($dir);
|
||||
} else {
|
||||
// no need to continue with the next level up as `$dir` was not deleted
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) { // @codeCoverageIgnore
|
||||
// silently stops the process
|
||||
}
|
||||
}
|
||||
if (empty($files) === true && Dir::remove($dir) === true) {
|
||||
// continue with the next level up
|
||||
$dir = dirname($dir);
|
||||
} else {
|
||||
// no need to continue with the next level up as `$dir` was not deleted
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) { // @codeCoverageIgnore
|
||||
// silently stops the process
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (Dir::remove($this->root) === true && Dir::make($this->root) === true) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
if (Dir::remove($this->root) === true && Dir::make($this->root) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,85 +15,85 @@ use Memcached as MemcachedExt;
|
||||
*/
|
||||
class MemCached extends Cache
|
||||
{
|
||||
/**
|
||||
* store for the memcache connection
|
||||
* @var \Memcached
|
||||
*/
|
||||
protected $connection;
|
||||
/**
|
||||
* store for the memcache connection
|
||||
* @var \Memcached
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Sets all parameters which are needed to connect to Memcached
|
||||
*
|
||||
* @param array $options 'host' (default: localhost)
|
||||
* 'port' (default: 11211)
|
||||
* 'prefix' (default: null)
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$defaults = [
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'prefix' => null,
|
||||
];
|
||||
/**
|
||||
* Sets all parameters which are needed to connect to Memcached
|
||||
*
|
||||
* @param array $options 'host' (default: localhost)
|
||||
* 'port' (default: 11211)
|
||||
* 'prefix' (default: null)
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$defaults = [
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'prefix' => null,
|
||||
];
|
||||
|
||||
parent::__construct(array_merge($defaults, $options));
|
||||
parent::__construct(array_merge($defaults, $options));
|
||||
|
||||
$this->connection = new MemcachedExt();
|
||||
$this->connection->addServer($this->options['host'], $this->options['port']);
|
||||
}
|
||||
$this->connection = new MemcachedExt();
|
||||
$this->connection->addServer($this->options['host'], $this->options['port']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return $this->connection->set($this->key($key), (new Value($value, $minutes))->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return $this->connection->set($this->key($key), (new Value($value, $minutes))->toJson(), $this->expiration($minutes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson($this->connection->get($this->key($key)));
|
||||
}
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return Value::fromJson($this->connection->get($this->key($key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return $this->connection->delete($this->key($key));
|
||||
}
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return $this->connection->delete($this->key($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful;
|
||||
* WARNING: Memcached only supports flushing the whole cache at once!
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
return $this->connection->flush();
|
||||
}
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful;
|
||||
* WARNING: Memcached only supports flushing the whole cache at once!
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
return $this->connection->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,70 +13,70 @@ namespace Kirby\Cache;
|
||||
*/
|
||||
class MemoryCache extends Cache
|
||||
{
|
||||
/**
|
||||
* Cache data
|
||||
* @var array
|
||||
*/
|
||||
protected $store = [];
|
||||
/**
|
||||
* Cache data
|
||||
* @var array
|
||||
*/
|
||||
protected $store = [];
|
||||
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
$this->store[$key] = new Value($value, $minutes);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
$this->store[$key] = new Value($value, $minutes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return $this->store[$key] ?? null;
|
||||
}
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return $this->store[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
if (isset($this->store[$key])) {
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
if (isset($this->store[$key])) {
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
$this->store = [];
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
$this->store = [];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,57 +13,57 @@ namespace Kirby\Cache;
|
||||
*/
|
||||
class NullCache extends Cache
|
||||
{
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Writes an item to the cache for a given number of minutes and
|
||||
* returns whether the operation was successful
|
||||
*
|
||||
* <code>
|
||||
* // put an item in the cache for 15 minutes
|
||||
* $cache->set('value', 'my value', 15);
|
||||
* </code>
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $minutes
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $key, $value, int $minutes = 0): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Internal method to retrieve the raw cache value;
|
||||
* needs to return a Value object or null if not found
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Value|null
|
||||
*/
|
||||
public function retrieve(string $key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Removes an item from the cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove(string $key): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Flushes the entire cache and returns
|
||||
* whether the operation was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function flush(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,136 +17,136 @@ use Throwable;
|
||||
*/
|
||||
class Value
|
||||
{
|
||||
/**
|
||||
* Cached value
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Cached value
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* the number of minutes until the value expires
|
||||
* @todo Rename this property to $expiry to reflect
|
||||
* both minutes and absolute timestamps
|
||||
* @var int
|
||||
*/
|
||||
protected $minutes;
|
||||
/**
|
||||
* the number of minutes until the value expires
|
||||
* @todo Rename this property to $expiry to reflect
|
||||
* both minutes and absolute timestamps
|
||||
* @var int
|
||||
*/
|
||||
protected $minutes;
|
||||
|
||||
/**
|
||||
* Creation timestamp
|
||||
* @var int
|
||||
*/
|
||||
protected $created;
|
||||
/**
|
||||
* Creation timestamp
|
||||
* @var int
|
||||
*/
|
||||
protected $created;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int $minutes the number of minutes until the value expires
|
||||
* or an absolute UNIX timestamp
|
||||
* @param int $created the UNIX timestamp when the value has been created
|
||||
*/
|
||||
public function __construct($value, int $minutes = 0, int $created = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->minutes = $minutes ?? 0;
|
||||
$this->created = $created ?? time();
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int $minutes the number of minutes until the value expires
|
||||
* or an absolute UNIX timestamp
|
||||
* @param int $created the UNIX timestamp when the value has been created
|
||||
*/
|
||||
public function __construct($value, int $minutes = 0, int $created = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->minutes = $minutes ?? 0;
|
||||
$this->created = $created ?? time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the creation date as UNIX timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function created(): int
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
/**
|
||||
* Returns the creation date as UNIX timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function created(): int
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expiration date as UNIX timestamp or
|
||||
* null if the value never expires
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function expires(): ?int
|
||||
{
|
||||
// 0 = keep forever
|
||||
if ($this->minutes === 0) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the expiration date as UNIX timestamp or
|
||||
* null if the value never expires
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function expires(): ?int
|
||||
{
|
||||
// 0 = keep forever
|
||||
if ($this->minutes === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->minutes > 1000000000) {
|
||||
// absolute timestamp
|
||||
return $this->minutes;
|
||||
}
|
||||
if ($this->minutes > 1000000000) {
|
||||
// absolute timestamp
|
||||
return $this->minutes;
|
||||
}
|
||||
|
||||
return $this->created + ($this->minutes * 60);
|
||||
}
|
||||
return $this->created + ($this->minutes * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value object from an array
|
||||
*
|
||||
* @param array $array
|
||||
* @return static
|
||||
*/
|
||||
public static function fromArray(array $array)
|
||||
{
|
||||
return new static($array['value'] ?? null, $array['minutes'] ?? 0, $array['created'] ?? null);
|
||||
}
|
||||
/**
|
||||
* Creates a value object from an array
|
||||
*
|
||||
* @param array $array
|
||||
* @return static
|
||||
*/
|
||||
public static function fromArray(array $array)
|
||||
{
|
||||
return new static($array['value'] ?? null, $array['minutes'] ?? 0, $array['created'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value object from a JSON string;
|
||||
* returns null on error
|
||||
*
|
||||
* @param string $json
|
||||
* @return static|null
|
||||
*/
|
||||
public static function fromJson(string $json)
|
||||
{
|
||||
try {
|
||||
$array = json_decode($json, true);
|
||||
/**
|
||||
* Creates a value object from a JSON string;
|
||||
* returns null on error
|
||||
*
|
||||
* @param string $json
|
||||
* @return static|null
|
||||
*/
|
||||
public static function fromJson(string $json)
|
||||
{
|
||||
try {
|
||||
$array = json_decode($json, true);
|
||||
|
||||
if (is_array($array)) {
|
||||
return static::fromArray($array);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (is_array($array)) {
|
||||
return static::fromArray($array);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a JSON string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->toArray());
|
||||
}
|
||||
/**
|
||||
* Converts the object to a JSON string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'created' => $this->created,
|
||||
'minutes' => $this->minutes,
|
||||
'value' => $this->value,
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'created' => $this->created,
|
||||
'minutes' => $this->minutes,
|
||||
'value' => $this->value,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pure value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
/**
|
||||
* Returns the pure value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,229 +17,229 @@ use Kirby\Form\Form;
|
||||
*/
|
||||
class Api extends BaseApi
|
||||
{
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
protected $kirby;
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* Execute an API call for the given path,
|
||||
* request method and optional request data
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string $method
|
||||
* @param array $requestData
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null, string $method = 'GET', array $requestData = [])
|
||||
{
|
||||
$this->setRequestMethod($method);
|
||||
$this->setRequestData($requestData);
|
||||
/**
|
||||
* Execute an API call for the given path,
|
||||
* request method and optional request data
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string $method
|
||||
* @param array $requestData
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null, string $method = 'GET', array $requestData = [])
|
||||
{
|
||||
$this->setRequestMethod($method);
|
||||
$this->setRequestData($requestData);
|
||||
|
||||
$this->kirby->setCurrentLanguage($this->language());
|
||||
$this->kirby->setCurrentLanguage($this->language());
|
||||
|
||||
$allowImpersonation = $this->kirby()->option('api.allowImpersonation', false);
|
||||
if ($user = $this->kirby->user(null, $allowImpersonation)) {
|
||||
$translation = $user->language();
|
||||
} else {
|
||||
$translation = $this->kirby->panelLanguage();
|
||||
}
|
||||
$this->kirby->setCurrentTranslation($translation);
|
||||
$allowImpersonation = $this->kirby()->option('api.allowImpersonation', false);
|
||||
if ($user = $this->kirby->user(null, $allowImpersonation)) {
|
||||
$translation = $user->language();
|
||||
} else {
|
||||
$translation = $this->kirby->panelLanguage();
|
||||
}
|
||||
$this->kirby->setCurrentTranslation($translation);
|
||||
|
||||
return parent::call($path, $method, $requestData);
|
||||
}
|
||||
return parent::call($path, $method, $requestData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $model
|
||||
* @param string $name
|
||||
* @param string|null $path
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\NotFoundException if the field type cannot be found or the field cannot be loaded
|
||||
*/
|
||||
public function fieldApi($model, string $name, string $path = null)
|
||||
{
|
||||
$field = Form::for($model)->field($name);
|
||||
/**
|
||||
* @param mixed $model
|
||||
* @param string $name
|
||||
* @param string|null $path
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\NotFoundException if the field type cannot be found or the field cannot be loaded
|
||||
*/
|
||||
public function fieldApi($model, string $name, string $path = null)
|
||||
{
|
||||
$field = Form::for($model)->field($name);
|
||||
|
||||
$fieldApi = new static(
|
||||
array_merge($this->propertyData, [
|
||||
'data' => array_merge($this->data(), ['field' => $field]),
|
||||
'routes' => $field->api(),
|
||||
]),
|
||||
);
|
||||
$fieldApi = new static(
|
||||
array_merge($this->propertyData, [
|
||||
'data' => array_merge($this->data(), ['field' => $field]),
|
||||
'routes' => $field->api(),
|
||||
]),
|
||||
);
|
||||
|
||||
return $fieldApi->call($path, $this->requestMethod(), $this->requestData());
|
||||
}
|
||||
return $fieldApi->call($path, $this->requestMethod(), $this->requestData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file object for the given
|
||||
* parent path and filename
|
||||
*
|
||||
* @param string|null $path Path to file's parent model
|
||||
* @param string $filename Filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the file cannot be found
|
||||
*/
|
||||
public function file(string $path = null, string $filename)
|
||||
{
|
||||
return Find::file($path, $filename);
|
||||
}
|
||||
/**
|
||||
* Returns the file object for the given
|
||||
* parent path and filename
|
||||
*
|
||||
* @param string|null $path Path to file's parent model
|
||||
* @param string $filename Filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the file cannot be found
|
||||
*/
|
||||
public function file(string $path = null, string $filename)
|
||||
{
|
||||
return Find::file($path, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model's object for the given path
|
||||
*
|
||||
* @param string $path Path to parent model
|
||||
* @return \Kirby\Cms\Model|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid
|
||||
* @throws \Kirby\Exception\NotFoundException if the model cannot be found
|
||||
*/
|
||||
public function parent(string $path)
|
||||
{
|
||||
return Find::parent($path);
|
||||
}
|
||||
/**
|
||||
* Returns the model's object for the given path
|
||||
*
|
||||
* @param string $path Path to parent model
|
||||
* @return \Kirby\Cms\Model|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid
|
||||
* @throws \Kirby\Exception\NotFoundException if the model cannot be found
|
||||
*/
|
||||
public function parent(string $path)
|
||||
{
|
||||
return Find::parent($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->kirby;
|
||||
}
|
||||
/**
|
||||
* Returns the Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->kirby;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language request header
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function language(): ?string
|
||||
{
|
||||
return $this->requestQuery('language') ?? $this->requestHeaders('x-language');
|
||||
}
|
||||
/**
|
||||
* Returns the language request header
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function language(): ?string
|
||||
{
|
||||
return $this->requestQuery('language') ?? $this->requestHeaders('x-language');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object for the given id
|
||||
*
|
||||
* @param string $id Page's id
|
||||
* @return \Kirby\Cms\Page|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the page cannot be found
|
||||
*/
|
||||
public function page(string $id)
|
||||
{
|
||||
return Find::page($id);
|
||||
}
|
||||
/**
|
||||
* Returns the page object for the given id
|
||||
*
|
||||
* @param string $id Page's id
|
||||
* @return \Kirby\Cms\Page|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the page cannot be found
|
||||
*/
|
||||
public function page(string $id)
|
||||
{
|
||||
return Find::page($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subpages for the given
|
||||
* parent. The subpages can be filtered
|
||||
* by status (draft, listed, unlisted, published, all)
|
||||
*
|
||||
* @param string|null $parentId
|
||||
* @param string|null $status
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function pages(string $parentId = null, string $status = null)
|
||||
{
|
||||
$parent = $parentId === null ? $this->site() : $this->page($parentId);
|
||||
/**
|
||||
* Returns the subpages for the given
|
||||
* parent. The subpages can be filtered
|
||||
* by status (draft, listed, unlisted, published, all)
|
||||
*
|
||||
* @param string|null $parentId
|
||||
* @param string|null $status
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function pages(string $parentId = null, string $status = null)
|
||||
{
|
||||
$parent = $parentId === null ? $this->site() : $this->page($parentId);
|
||||
|
||||
switch ($status) {
|
||||
case 'all':
|
||||
return $parent->childrenAndDrafts();
|
||||
case 'draft':
|
||||
case 'drafts':
|
||||
return $parent->drafts();
|
||||
case 'listed':
|
||||
return $parent->children()->listed();
|
||||
case 'unlisted':
|
||||
return $parent->children()->unlisted();
|
||||
case 'published':
|
||||
default:
|
||||
return $parent->children();
|
||||
}
|
||||
}
|
||||
switch ($status) {
|
||||
case 'all':
|
||||
return $parent->childrenAndDrafts();
|
||||
case 'draft':
|
||||
case 'drafts':
|
||||
return $parent->drafts();
|
||||
case 'listed':
|
||||
return $parent->children()->listed();
|
||||
case 'unlisted':
|
||||
return $parent->children()->unlisted();
|
||||
case 'published':
|
||||
default:
|
||||
return $parent->children();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for direct subpages of the
|
||||
* given parent
|
||||
*
|
||||
* @param string|null $parent
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function searchPages(string $parent = null)
|
||||
{
|
||||
$pages = $this->pages($parent, $this->requestQuery('status'));
|
||||
/**
|
||||
* Search for direct subpages of the
|
||||
* given parent
|
||||
*
|
||||
* @param string|null $parent
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function searchPages(string $parent = null)
|
||||
{
|
||||
$pages = $this->pages($parent, $this->requestQuery('status'));
|
||||
|
||||
if ($this->requestMethod() === 'GET') {
|
||||
return $pages->search($this->requestQuery('q'));
|
||||
}
|
||||
if ($this->requestMethod() === 'GET') {
|
||||
return $pages->search($this->requestQuery('q'));
|
||||
}
|
||||
|
||||
return $pages->query($this->requestBody());
|
||||
}
|
||||
return $pages->query($this->requestBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Session instance
|
||||
*
|
||||
* @param array $options Additional options, see the session component
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
public function session(array $options = [])
|
||||
{
|
||||
return $this->kirby->session(array_merge([
|
||||
'detect' => true
|
||||
], $options));
|
||||
}
|
||||
/**
|
||||
* Returns the current Session instance
|
||||
*
|
||||
* @param array $options Additional options, see the session component
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
public function session(array $options = [])
|
||||
{
|
||||
return $this->kirby->session(array_merge([
|
||||
'detect' => true
|
||||
], $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent Kirby instance
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Setter for the parent Kirby instance
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the site object
|
||||
*
|
||||
* @return \Kirby\Cms\Site
|
||||
*/
|
||||
public function site()
|
||||
{
|
||||
return $this->kirby->site();
|
||||
}
|
||||
/**
|
||||
* Returns the site object
|
||||
*
|
||||
* @return \Kirby\Cms\Site
|
||||
*/
|
||||
public function site()
|
||||
{
|
||||
return $this->kirby->site();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user object for the given id or
|
||||
* returns the current authenticated user if no
|
||||
* id is passed
|
||||
*
|
||||
* @param string|null $id User's id
|
||||
* @return \Kirby\Cms\User|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found
|
||||
*/
|
||||
public function user(string $id = null)
|
||||
{
|
||||
try {
|
||||
return Find::user($id);
|
||||
} catch (NotFoundException $e) {
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the user object for the given id or
|
||||
* returns the current authenticated user if no
|
||||
* id is passed
|
||||
*
|
||||
* @param string|null $id User's id
|
||||
* @return \Kirby\Cms\User|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found
|
||||
*/
|
||||
public function user(string $id = null)
|
||||
{
|
||||
try {
|
||||
return Find::user($id);
|
||||
} catch (NotFoundException $e) {
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users collection
|
||||
*
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->kirby->users();
|
||||
}
|
||||
/**
|
||||
* Returns the users collection
|
||||
*
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->kirby->users();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,122 +16,122 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
trait AppCaches
|
||||
{
|
||||
protected $caches = [];
|
||||
protected $caches = [];
|
||||
|
||||
/**
|
||||
* Returns a cache instance by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Cache
|
||||
*/
|
||||
public function cache(string $key)
|
||||
{
|
||||
if (isset($this->caches[$key]) === true) {
|
||||
return $this->caches[$key];
|
||||
}
|
||||
/**
|
||||
* Returns a cache instance by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cache\Cache
|
||||
*/
|
||||
public function cache(string $key)
|
||||
{
|
||||
if (isset($this->caches[$key]) === true) {
|
||||
return $this->caches[$key];
|
||||
}
|
||||
|
||||
// get the options for this cache type
|
||||
$options = $this->cacheOptions($key);
|
||||
// get the options for this cache type
|
||||
$options = $this->cacheOptions($key);
|
||||
|
||||
if ($options['active'] === false) {
|
||||
// use a dummy cache that does nothing
|
||||
return $this->caches[$key] = new NullCache();
|
||||
}
|
||||
if ($options['active'] === false) {
|
||||
// use a dummy cache that does nothing
|
||||
return $this->caches[$key] = new NullCache();
|
||||
}
|
||||
|
||||
$type = strtolower($options['type']);
|
||||
$types = $this->extensions['cacheTypes'] ?? [];
|
||||
$type = strtolower($options['type']);
|
||||
$types = $this->extensions['cacheTypes'] ?? [];
|
||||
|
||||
if (array_key_exists($type, $types) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'app.invalid.cacheType',
|
||||
'data' => ['type' => $type]
|
||||
]);
|
||||
}
|
||||
if (array_key_exists($type, $types) === false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'app.invalid.cacheType',
|
||||
'data' => ['type' => $type]
|
||||
]);
|
||||
}
|
||||
|
||||
$className = $types[$type];
|
||||
$className = $types[$type];
|
||||
|
||||
// initialize the cache class
|
||||
$cache = new $className($options);
|
||||
// initialize the cache class
|
||||
$cache = new $className($options);
|
||||
|
||||
// check if it is a usable cache object
|
||||
if (is_a($cache, 'Kirby\Cache\Cache') !== true) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'app.invalid.cacheType',
|
||||
'data' => ['type' => $type]
|
||||
]);
|
||||
}
|
||||
// check if it is a usable cache object
|
||||
if (is_a($cache, 'Kirby\Cache\Cache') !== true) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'app.invalid.cacheType',
|
||||
'data' => ['type' => $type]
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->caches[$key] = $cache;
|
||||
}
|
||||
return $this->caches[$key] = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache options by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
protected function cacheOptions(string $key): array
|
||||
{
|
||||
$options = $this->option($this->cacheOptionsKey($key), false);
|
||||
/**
|
||||
* Returns the cache options by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
protected function cacheOptions(string $key): array
|
||||
{
|
||||
$options = $this->option($this->cacheOptionsKey($key), false);
|
||||
|
||||
if ($options === false) {
|
||||
return [
|
||||
'active' => false
|
||||
];
|
||||
}
|
||||
if ($options === false) {
|
||||
return [
|
||||
'active' => false
|
||||
];
|
||||
}
|
||||
|
||||
$prefix = str_replace(['/', ':'], '_', $this->system()->indexUrl()) .
|
||||
'/' .
|
||||
str_replace('.', '/', $key);
|
||||
$prefix = str_replace(['/', ':'], '_', $this->system()->indexUrl()) .
|
||||
'/' .
|
||||
str_replace('.', '/', $key);
|
||||
|
||||
$defaults = [
|
||||
'active' => true,
|
||||
'type' => 'file',
|
||||
'extension' => 'cache',
|
||||
'root' => $this->root('cache'),
|
||||
'prefix' => $prefix
|
||||
];
|
||||
$defaults = [
|
||||
'active' => true,
|
||||
'type' => 'file',
|
||||
'extension' => 'cache',
|
||||
'root' => $this->root('cache'),
|
||||
'prefix' => $prefix
|
||||
];
|
||||
|
||||
if ($options === true) {
|
||||
return $defaults;
|
||||
} else {
|
||||
return array_merge($defaults, $options);
|
||||
}
|
||||
}
|
||||
if ($options === true) {
|
||||
return $defaults;
|
||||
} else {
|
||||
return array_merge($defaults, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of converting prefixed plugin cache setups
|
||||
* to the right cache key, while leaving regular cache
|
||||
* setups untouched.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function cacheOptionsKey(string $key): string
|
||||
{
|
||||
$prefixedKey = 'cache.' . $key;
|
||||
/**
|
||||
* Takes care of converting prefixed plugin cache setups
|
||||
* to the right cache key, while leaving regular cache
|
||||
* setups untouched.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function cacheOptionsKey(string $key): string
|
||||
{
|
||||
$prefixedKey = 'cache.' . $key;
|
||||
|
||||
if (isset($this->options[$prefixedKey])) {
|
||||
return $prefixedKey;
|
||||
}
|
||||
if (isset($this->options[$prefixedKey])) {
|
||||
return $prefixedKey;
|
||||
}
|
||||
|
||||
// plain keys without dots don't need further investigation
|
||||
// since they can never be from a plugin.
|
||||
if (strpos($key, '.') === false) {
|
||||
return $prefixedKey;
|
||||
}
|
||||
// plain keys without dots don't need further investigation
|
||||
// since they can never be from a plugin.
|
||||
if (strpos($key, '.') === false) {
|
||||
return $prefixedKey;
|
||||
}
|
||||
|
||||
// try to extract the plugin name
|
||||
$parts = explode('.', $key);
|
||||
$pluginName = implode('/', array_slice($parts, 0, 2));
|
||||
$pluginPrefix = implode('.', array_slice($parts, 0, 2));
|
||||
$cacheName = implode('.', array_slice($parts, 2));
|
||||
// try to extract the plugin name
|
||||
$parts = explode('.', $key);
|
||||
$pluginName = implode('/', array_slice($parts, 0, 2));
|
||||
$pluginPrefix = implode('.', array_slice($parts, 0, 2));
|
||||
$cacheName = implode('.', array_slice($parts, 2));
|
||||
|
||||
// check if such a plugin exists
|
||||
if ($this->plugin($pluginName)) {
|
||||
return empty($cacheName) === true ? $pluginPrefix . '.cache' : $pluginPrefix . '.cache.' . $cacheName;
|
||||
}
|
||||
// check if such a plugin exists
|
||||
if ($this->plugin($pluginName)) {
|
||||
return empty($cacheName) === true ? $pluginPrefix . '.cache' : $pluginPrefix . '.cache.' . $cacheName;
|
||||
}
|
||||
|
||||
return $prefixedKey;
|
||||
}
|
||||
return $prefixedKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,184 +22,184 @@ use Whoops\Run as Whoops;
|
||||
*/
|
||||
trait AppErrors
|
||||
{
|
||||
/**
|
||||
* Whoops instance cache
|
||||
*
|
||||
* @var \Whoops\Run
|
||||
*/
|
||||
protected $whoops;
|
||||
/**
|
||||
* Whoops instance cache
|
||||
*
|
||||
* @var \Whoops\Run
|
||||
*/
|
||||
protected $whoops;
|
||||
|
||||
/**
|
||||
* Registers the PHP error handler for CLI usage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleCliErrors(): void
|
||||
{
|
||||
$this->setWhoopsHandler(new PlainTextHandler());
|
||||
}
|
||||
/**
|
||||
* Registers the PHP error handler for CLI usage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleCliErrors(): void
|
||||
{
|
||||
$this->setWhoopsHandler(new PlainTextHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the PHP error handler
|
||||
* based on the environment
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleErrors(): void
|
||||
{
|
||||
if ($this->environment()->cli() === true) {
|
||||
$this->handleCliErrors();
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Registers the PHP error handler
|
||||
* based on the environment
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleErrors(): void
|
||||
{
|
||||
if ($this->environment()->cli() === true) {
|
||||
$this->handleCliErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->visitor()->prefersJson() === true) {
|
||||
$this->handleJsonErrors();
|
||||
return;
|
||||
}
|
||||
if ($this->visitor()->prefersJson() === true) {
|
||||
$this->handleJsonErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handleHtmlErrors();
|
||||
}
|
||||
$this->handleHtmlErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the PHP error handler for HTML output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleHtmlErrors(): void
|
||||
{
|
||||
$handler = null;
|
||||
/**
|
||||
* Registers the PHP error handler for HTML output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleHtmlErrors(): void
|
||||
{
|
||||
$handler = null;
|
||||
|
||||
if ($this->option('debug') === true) {
|
||||
if ($this->option('whoops', true) === true) {
|
||||
$handler = new PrettyPageHandler();
|
||||
$handler->setPageTitle('Kirby CMS Debugger');
|
||||
$handler->setResourcesPath(dirname(__DIR__, 2) . '/assets');
|
||||
$handler->addCustomCss('whoops.css');
|
||||
if ($this->option('debug') === true) {
|
||||
if ($this->option('whoops', true) === true) {
|
||||
$handler = new PrettyPageHandler();
|
||||
$handler->setPageTitle('Kirby CMS Debugger');
|
||||
$handler->setResourcesPath(dirname(__DIR__, 2) . '/assets');
|
||||
$handler->addCustomCss('whoops.css');
|
||||
|
||||
if ($editor = $this->option('editor')) {
|
||||
$handler->setEditor($editor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$handler = new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
$fatal = $this->option('fatal');
|
||||
if ($editor = $this->option('editor')) {
|
||||
$handler->setEditor($editor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$handler = new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
$fatal = $this->option('fatal');
|
||||
|
||||
if (is_a($fatal, 'Closure') === true) {
|
||||
echo $fatal($this, $exception);
|
||||
} else {
|
||||
include $this->root('kirby') . '/views/fatal.php';
|
||||
}
|
||||
if (is_a($fatal, 'Closure') === true) {
|
||||
echo $fatal($this, $exception);
|
||||
} else {
|
||||
include $this->root('kirby') . '/views/fatal.php';
|
||||
}
|
||||
|
||||
return Handler::QUIT;
|
||||
});
|
||||
}
|
||||
return Handler::QUIT;
|
||||
});
|
||||
}
|
||||
|
||||
if ($handler !== null) {
|
||||
$this->setWhoopsHandler($handler);
|
||||
} else {
|
||||
$this->unsetWhoopsHandler();
|
||||
}
|
||||
}
|
||||
if ($handler !== null) {
|
||||
$this->setWhoopsHandler($handler);
|
||||
} else {
|
||||
$this->unsetWhoopsHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the PHP error handler for JSON output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleJsonErrors(): void
|
||||
{
|
||||
$handler = new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
if (is_a($exception, 'Kirby\Exception\Exception') === true) {
|
||||
$httpCode = $exception->getHttpCode();
|
||||
$code = $exception->getCode();
|
||||
$details = $exception->getDetails();
|
||||
} elseif (is_a($exception, '\Throwable') === true) {
|
||||
$httpCode = 500;
|
||||
$code = $exception->getCode();
|
||||
$details = null;
|
||||
} else {
|
||||
$httpCode = 500;
|
||||
$code = 500;
|
||||
$details = null;
|
||||
}
|
||||
/**
|
||||
* Registers the PHP error handler for JSON output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handleJsonErrors(): void
|
||||
{
|
||||
$handler = new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
if (is_a($exception, 'Kirby\Exception\Exception') === true) {
|
||||
$httpCode = $exception->getHttpCode();
|
||||
$code = $exception->getCode();
|
||||
$details = $exception->getDetails();
|
||||
} elseif (is_a($exception, '\Throwable') === true) {
|
||||
$httpCode = 500;
|
||||
$code = $exception->getCode();
|
||||
$details = null;
|
||||
} else {
|
||||
$httpCode = 500;
|
||||
$code = 500;
|
||||
$details = null;
|
||||
}
|
||||
|
||||
if ($this->option('debug') === true) {
|
||||
echo Response::json([
|
||||
'status' => 'error',
|
||||
'exception' => get_class($exception),
|
||||
'code' => $code,
|
||||
'message' => $exception->getMessage(),
|
||||
'details' => $details,
|
||||
'file' => F::relativepath($exception->getFile(), $this->environment()->get('DOCUMENT_ROOT', '')),
|
||||
'line' => $exception->getLine(),
|
||||
], $httpCode);
|
||||
} else {
|
||||
echo Response::json([
|
||||
'status' => 'error',
|
||||
'code' => $code,
|
||||
'details' => $details,
|
||||
'message' => I18n::translate('error.unexpected'),
|
||||
], $httpCode);
|
||||
}
|
||||
if ($this->option('debug') === true) {
|
||||
echo Response::json([
|
||||
'status' => 'error',
|
||||
'exception' => get_class($exception),
|
||||
'code' => $code,
|
||||
'message' => $exception->getMessage(),
|
||||
'details' => $details,
|
||||
'file' => F::relativepath($exception->getFile(), $this->environment()->get('DOCUMENT_ROOT', '')),
|
||||
'line' => $exception->getLine(),
|
||||
], $httpCode);
|
||||
} else {
|
||||
echo Response::json([
|
||||
'status' => 'error',
|
||||
'code' => $code,
|
||||
'details' => $details,
|
||||
'message' => I18n::translate('error.unexpected'),
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
return Handler::QUIT;
|
||||
});
|
||||
return Handler::QUIT;
|
||||
});
|
||||
|
||||
$this->setWhoopsHandler($handler);
|
||||
$this->whoops()->sendHttpCode(false);
|
||||
}
|
||||
$this->setWhoopsHandler($handler);
|
||||
$this->whoops()->sendHttpCode(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Whoops with the specified handler
|
||||
*
|
||||
* @param Callable|\Whoops\Handler\HandlerInterface $handler
|
||||
* @return void
|
||||
*/
|
||||
protected function setWhoopsHandler($handler): void
|
||||
{
|
||||
$whoops = $this->whoops();
|
||||
$whoops->clearHandlers();
|
||||
$whoops->pushHandler($handler);
|
||||
$whoops->pushHandler($this->getExceptionHookWhoopsHandler());
|
||||
$whoops->register(); // will only do something if not already registered
|
||||
}
|
||||
/**
|
||||
* Enables Whoops with the specified handler
|
||||
*
|
||||
* @param Callable|\Whoops\Handler\HandlerInterface $handler
|
||||
* @return void
|
||||
*/
|
||||
protected function setWhoopsHandler($handler): void
|
||||
{
|
||||
$whoops = $this->whoops();
|
||||
$whoops->clearHandlers();
|
||||
$whoops->pushHandler($handler);
|
||||
$whoops->pushHandler($this->getExceptionHookWhoopsHandler());
|
||||
$whoops->register(); // will only do something if not already registered
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a callback handler for triggering the `system.exception` hook
|
||||
*
|
||||
* @return \Whoops\Handler\CallbackHandler
|
||||
*/
|
||||
protected function getExceptionHookWhoopsHandler(): CallbackHandler
|
||||
{
|
||||
return new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
$this->trigger('system.exception', compact('exception'));
|
||||
return Handler::DONE;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Initializes a callback handler for triggering the `system.exception` hook
|
||||
*
|
||||
* @return \Whoops\Handler\CallbackHandler
|
||||
*/
|
||||
protected function getExceptionHookWhoopsHandler(): CallbackHandler
|
||||
{
|
||||
return new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
$this->trigger('system.exception', compact('exception'));
|
||||
return Handler::DONE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the Whoops handlers and disables Whoops
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function unsetWhoopsHandler(): void
|
||||
{
|
||||
$whoops = $this->whoops();
|
||||
$whoops->clearHandlers();
|
||||
$whoops->unregister(); // will only do something if currently registered
|
||||
}
|
||||
/**
|
||||
* Clears the Whoops handlers and disables Whoops
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function unsetWhoopsHandler(): void
|
||||
{
|
||||
$whoops = $this->whoops();
|
||||
$whoops->clearHandlers();
|
||||
$whoops->unregister(); // will only do something if currently registered
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Whoops error handler instance
|
||||
*
|
||||
* @return \Whoops\Run
|
||||
*/
|
||||
protected function whoops()
|
||||
{
|
||||
if ($this->whoops !== null) {
|
||||
return $this->whoops;
|
||||
}
|
||||
/**
|
||||
* Returns the Whoops error handler instance
|
||||
*
|
||||
* @return \Whoops\Run
|
||||
*/
|
||||
protected function whoops()
|
||||
{
|
||||
if ($this->whoops !== null) {
|
||||
return $this->whoops;
|
||||
}
|
||||
|
||||
return $this->whoops = new Whoops();
|
||||
}
|
||||
return $this->whoops = new Whoops();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,205 +17,205 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
trait AppTranslations
|
||||
{
|
||||
protected $translations;
|
||||
protected $translations;
|
||||
|
||||
/**
|
||||
* Setup internationalization
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function i18n(): void
|
||||
{
|
||||
I18n::$load = function ($locale): array {
|
||||
$data = [];
|
||||
/**
|
||||
* Setup internationalization
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function i18n(): void
|
||||
{
|
||||
I18n::$load = function ($locale): array {
|
||||
$data = [];
|
||||
|
||||
if ($translation = $this->translation($locale)) {
|
||||
$data = $translation->data();
|
||||
}
|
||||
if ($translation = $this->translation($locale)) {
|
||||
$data = $translation->data();
|
||||
}
|
||||
|
||||
// inject translations from the current language
|
||||
if (
|
||||
$this->multilang() === true &&
|
||||
$language = $this->languages()->find($locale)
|
||||
) {
|
||||
$data = array_merge($data, $language->translations());
|
||||
}
|
||||
// inject translations from the current language
|
||||
if (
|
||||
$this->multilang() === true &&
|
||||
$language = $this->languages()->find($locale)
|
||||
) {
|
||||
$data = array_merge($data, $language->translations());
|
||||
}
|
||||
|
||||
|
||||
return $data;
|
||||
};
|
||||
return $data;
|
||||
};
|
||||
|
||||
// the actual locale is set using $app->setCurrentTranslation()
|
||||
I18n::$locale = function (): string {
|
||||
if ($this->multilang() === true) {
|
||||
return $this->defaultLanguage()->code();
|
||||
} else {
|
||||
return 'en';
|
||||
}
|
||||
};
|
||||
// the actual locale is set using $app->setCurrentTranslation()
|
||||
I18n::$locale = function (): string {
|
||||
if ($this->multilang() === true) {
|
||||
return $this->defaultLanguage()->code();
|
||||
} else {
|
||||
return 'en';
|
||||
}
|
||||
};
|
||||
|
||||
I18n::$fallback = function (): array {
|
||||
if ($this->multilang() === true) {
|
||||
// first try to fall back to the configured default language
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
$fallback = [$defaultCode];
|
||||
I18n::$fallback = function (): array {
|
||||
if ($this->multilang() === true) {
|
||||
// first try to fall back to the configured default language
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
$fallback = [$defaultCode];
|
||||
|
||||
// if the default language is specified with a country code
|
||||
// (e.g. `en-us`), also try with just the language code
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$fallback[] = $matches[1];
|
||||
}
|
||||
// if the default language is specified with a country code
|
||||
// (e.g. `en-us`), also try with just the language code
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$fallback[] = $matches[1];
|
||||
}
|
||||
|
||||
// fall back to the complete English translation
|
||||
// as a last resort
|
||||
$fallback[] = 'en';
|
||||
// fall back to the complete English translation
|
||||
// as a last resort
|
||||
$fallback[] = 'en';
|
||||
|
||||
return $fallback;
|
||||
} else {
|
||||
return ['en'];
|
||||
}
|
||||
};
|
||||
return $fallback;
|
||||
} else {
|
||||
return ['en'];
|
||||
}
|
||||
};
|
||||
|
||||
I18n::$translations = [];
|
||||
I18n::$translations = [];
|
||||
|
||||
// add slug rules based on config option
|
||||
if ($slugs = $this->option('slugs')) {
|
||||
// two ways that the option can be defined:
|
||||
// "slugs" => "de" or "slugs" => ["language" => "de"]
|
||||
if ($slugs = $slugs['language'] ?? $slugs ?? null) {
|
||||
Str::$language = Language::loadRules($slugs);
|
||||
}
|
||||
}
|
||||
}
|
||||
// add slug rules based on config option
|
||||
if ($slugs = $this->option('slugs')) {
|
||||
// two ways that the option can be defined:
|
||||
// "slugs" => "de" or "slugs" => ["language" => "de"]
|
||||
if ($slugs = $slugs['language'] ?? $slugs ?? null) {
|
||||
Str::$language = Language::loadRules($slugs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language code that will be used
|
||||
* for the Panel if no user is logged in or if
|
||||
* no language is configured for the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function panelLanguage(): string
|
||||
{
|
||||
if ($this->multilang() === true) {
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
/**
|
||||
* Returns the language code that will be used
|
||||
* for the Panel if no user is logged in or if
|
||||
* no language is configured for the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function panelLanguage(): string
|
||||
{
|
||||
if ($this->multilang() === true) {
|
||||
$defaultCode = $this->defaultLanguage()->code();
|
||||
|
||||
// extract the language code from a language that
|
||||
// contains the country code (e.g. `en-us`)
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$defaultCode = $matches[1];
|
||||
}
|
||||
} else {
|
||||
$defaultCode = 'en';
|
||||
}
|
||||
// extract the language code from a language that
|
||||
// contains the country code (e.g. `en-us`)
|
||||
if (preg_match('/^([a-z]{2})-[a-z]+$/i', $defaultCode, $matches) === 1) {
|
||||
$defaultCode = $matches[1];
|
||||
}
|
||||
} else {
|
||||
$defaultCode = 'en';
|
||||
}
|
||||
|
||||
return $this->option('panel.language', $defaultCode);
|
||||
}
|
||||
return $this->option('panel.language', $defaultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and set the current language if it exists
|
||||
* Otherwise fall back to the default language
|
||||
*
|
||||
* @internal
|
||||
* @param string|null $languageCode
|
||||
* @return \Kirby\Cms\Language|null
|
||||
*/
|
||||
public function setCurrentLanguage(string $languageCode = null)
|
||||
{
|
||||
if ($this->multilang() === false) {
|
||||
Locale::set($this->option('locale', 'en_US.utf-8'));
|
||||
return $this->language = null;
|
||||
}
|
||||
/**
|
||||
* Load and set the current language if it exists
|
||||
* Otherwise fall back to the default language
|
||||
*
|
||||
* @internal
|
||||
* @param string|null $languageCode
|
||||
* @return \Kirby\Cms\Language|null
|
||||
*/
|
||||
public function setCurrentLanguage(string $languageCode = null)
|
||||
{
|
||||
if ($this->multilang() === false) {
|
||||
Locale::set($this->option('locale', 'en_US.utf-8'));
|
||||
return $this->language = null;
|
||||
}
|
||||
|
||||
if ($language = $this->language($languageCode)) {
|
||||
$this->language = $language;
|
||||
} else {
|
||||
$this->language = $this->defaultLanguage();
|
||||
}
|
||||
if ($language = $this->language($languageCode)) {
|
||||
$this->language = $language;
|
||||
} else {
|
||||
$this->language = $this->defaultLanguage();
|
||||
}
|
||||
|
||||
if ($this->language) {
|
||||
Locale::set($this->language->locale());
|
||||
}
|
||||
if ($this->language) {
|
||||
Locale::set($this->language->locale());
|
||||
}
|
||||
|
||||
// add language slug rules to Str class
|
||||
Str::$language = $this->language->rules();
|
||||
// add language slug rules to Str class
|
||||
Str::$language = $this->language->rules();
|
||||
|
||||
return $this->language;
|
||||
}
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current translation
|
||||
*
|
||||
* @internal
|
||||
* @param string|null $translationCode
|
||||
* @return void
|
||||
*/
|
||||
public function setCurrentTranslation(string $translationCode = null): void
|
||||
{
|
||||
I18n::$locale = $translationCode ?? 'en';
|
||||
}
|
||||
/**
|
||||
* Set the current translation
|
||||
*
|
||||
* @internal
|
||||
* @param string|null $translationCode
|
||||
* @return void
|
||||
*/
|
||||
public function setCurrentTranslation(string $translationCode = null): void
|
||||
{
|
||||
I18n::$locale = $translationCode ?? 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific translation by locale
|
||||
*
|
||||
* @param string|null $locale Locale name or `null` for the current locale
|
||||
* @return \Kirby\Cms\Translation
|
||||
*/
|
||||
public function translation(?string $locale = null)
|
||||
{
|
||||
$locale = $locale ?? I18n::locale();
|
||||
$locale = basename($locale);
|
||||
/**
|
||||
* Load a specific translation by locale
|
||||
*
|
||||
* @param string|null $locale Locale name or `null` for the current locale
|
||||
* @return \Kirby\Cms\Translation
|
||||
*/
|
||||
public function translation(?string $locale = null)
|
||||
{
|
||||
$locale = $locale ?? I18n::locale();
|
||||
$locale = basename($locale);
|
||||
|
||||
// prefer loading them from the translations collection
|
||||
if (is_a($this->translations, 'Kirby\Cms\Translations') === true) {
|
||||
if ($translation = $this->translations()->find($locale)) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
// prefer loading them from the translations collection
|
||||
if (is_a($this->translations, 'Kirby\Cms\Translations') === true) {
|
||||
if ($translation = $this->translations()->find($locale)) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
// get injected translation data from plugins etc.
|
||||
$inject = $this->extensions['translations'][$locale] ?? [];
|
||||
// get injected translation data from plugins etc.
|
||||
$inject = $this->extensions['translations'][$locale] ?? [];
|
||||
|
||||
// inject current language translations
|
||||
if ($language = $this->language($locale)) {
|
||||
$inject = array_merge($inject, $language->translations());
|
||||
}
|
||||
// inject current language translations
|
||||
if ($language = $this->language($locale)) {
|
||||
$inject = array_merge($inject, $language->translations());
|
||||
}
|
||||
|
||||
// load from disk instead
|
||||
return Translation::load($locale, $this->root('i18n:translations') . '/' . $locale . '.json', $inject);
|
||||
}
|
||||
// load from disk instead
|
||||
return Translation::load($locale, $this->root('i18n:translations') . '/' . $locale . '.json', $inject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available translations
|
||||
*
|
||||
* @return \Kirby\Cms\Translations
|
||||
*/
|
||||
public function translations()
|
||||
{
|
||||
if (is_a($this->translations, 'Kirby\Cms\Translations') === true) {
|
||||
return $this->translations;
|
||||
}
|
||||
/**
|
||||
* Returns all available translations
|
||||
*
|
||||
* @return \Kirby\Cms\Translations
|
||||
*/
|
||||
public function translations()
|
||||
{
|
||||
if (is_a($this->translations, 'Kirby\Cms\Translations') === true) {
|
||||
return $this->translations;
|
||||
}
|
||||
|
||||
$translations = $this->extensions['translations'] ?? [];
|
||||
$translations = $this->extensions['translations'] ?? [];
|
||||
|
||||
// injects languages translations
|
||||
if ($languages = $this->languages()) {
|
||||
foreach ($languages as $language) {
|
||||
$languageCode = $language->code();
|
||||
$languageTranslations = $language->translations();
|
||||
// injects languages translations
|
||||
if ($languages = $this->languages()) {
|
||||
foreach ($languages as $language) {
|
||||
$languageCode = $language->code();
|
||||
$languageTranslations = $language->translations();
|
||||
|
||||
// merges language translations with extensions translations
|
||||
if (empty($languageTranslations) === false) {
|
||||
$translations[$languageCode] = array_merge(
|
||||
$translations[$languageCode] ?? [],
|
||||
$languageTranslations
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// merges language translations with extensions translations
|
||||
if (empty($languageTranslations) === false) {
|
||||
$translations[$languageCode] = array_merge(
|
||||
$translations[$languageCode] ?? [],
|
||||
$languageTranslations
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->translations = Translations::load($this->root('i18n:translations'), $translations);
|
||||
$this->translations = Translations::load($this->root('i18n:translations'), $translations);
|
||||
|
||||
return $this->translations;
|
||||
}
|
||||
return $this->translations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,128 +16,128 @@ use Throwable;
|
||||
*/
|
||||
trait AppUsers
|
||||
{
|
||||
/**
|
||||
* Cache for the auth auth layer
|
||||
*
|
||||
* @var Auth
|
||||
*/
|
||||
protected $auth;
|
||||
/**
|
||||
* Cache for the auth auth layer
|
||||
*
|
||||
* @var Auth
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Returns the Authentication layer class
|
||||
*
|
||||
* @internal
|
||||
* @return \Kirby\Cms\Auth
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
return $this->auth = $this->auth ?? new Auth($this);
|
||||
}
|
||||
/**
|
||||
* Returns the Authentication layer class
|
||||
*
|
||||
* @internal
|
||||
* @return \Kirby\Cms\Auth
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
return $this->auth = $this->auth ?? new Auth($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Become any existing user or disable the current user
|
||||
*
|
||||
* @param string|null $who User ID or email address,
|
||||
* `null` to use the actual user again,
|
||||
* `'kirby'` for a virtual admin user or
|
||||
* `'nobody'` to disable the actual user
|
||||
* @param Closure|null $callback Optional action function that will be run with
|
||||
* the permissions of the impersonated user; the
|
||||
* impersonation will be reset afterwards
|
||||
* @return mixed If called without callback: User that was impersonated;
|
||||
* if called with callback: Return value from the callback
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function impersonate(?string $who = null, ?Closure $callback = null)
|
||||
{
|
||||
$auth = $this->auth();
|
||||
/**
|
||||
* Become any existing user or disable the current user
|
||||
*
|
||||
* @param string|null $who User ID or email address,
|
||||
* `null` to use the actual user again,
|
||||
* `'kirby'` for a virtual admin user or
|
||||
* `'nobody'` to disable the actual user
|
||||
* @param Closure|null $callback Optional action function that will be run with
|
||||
* the permissions of the impersonated user; the
|
||||
* impersonation will be reset afterwards
|
||||
* @return mixed If called without callback: User that was impersonated;
|
||||
* if called with callback: Return value from the callback
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function impersonate(?string $who = null, ?Closure $callback = null)
|
||||
{
|
||||
$auth = $this->auth();
|
||||
|
||||
$userBefore = $auth->currentUserFromImpersonation();
|
||||
$userAfter = $auth->impersonate($who);
|
||||
$userBefore = $auth->currentUserFromImpersonation();
|
||||
$userAfter = $auth->impersonate($who);
|
||||
|
||||
if ($callback === null) {
|
||||
return $userAfter;
|
||||
}
|
||||
if ($callback === null) {
|
||||
return $userAfter;
|
||||
}
|
||||
|
||||
try {
|
||||
// bind the App object to the callback
|
||||
return $callback->call($this, $userAfter);
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
// ensure that the impersonation is *always* reset
|
||||
// to the original value, even if an error occurred
|
||||
$auth->impersonate($userBefore !== null ? $userBefore->id() : null);
|
||||
}
|
||||
}
|
||||
try {
|
||||
// bind the App object to the callback
|
||||
return $callback->call($this, $userAfter);
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
// ensure that the impersonation is *always* reset
|
||||
// to the original value, even if an error occurred
|
||||
$auth->impersonate($userBefore !== null ? $userBefore->id() : null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the currently active user id
|
||||
*
|
||||
* @param \Kirby\Cms\User|string $user
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function setUser($user = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set the currently active user id
|
||||
*
|
||||
* @param \Kirby\Cms\User|string $user
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function setUser($user = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create your own set of app users
|
||||
*
|
||||
* @param array|null $users
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function setUsers(array $users = null)
|
||||
{
|
||||
if ($users !== null) {
|
||||
$this->users = Users::factory($users, [
|
||||
'kirby' => $this
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Create your own set of app users
|
||||
*
|
||||
* @param array|null $users
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function setUsers(array $users = null)
|
||||
{
|
||||
if ($users !== null) {
|
||||
$this->users = Users::factory($users, [
|
||||
'kirby' => $this
|
||||
]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific user by id
|
||||
* or the current user if no id is given
|
||||
*
|
||||
* @param string|null $id
|
||||
* @param bool $allowImpersonation If set to false, only the actually
|
||||
* logged in user will be returned
|
||||
* (when `$id` is passed as `null`)
|
||||
* @return \Kirby\Cms\User|null
|
||||
*/
|
||||
public function user(?string $id = null, bool $allowImpersonation = true)
|
||||
{
|
||||
if ($id !== null) {
|
||||
return $this->users()->find($id);
|
||||
}
|
||||
/**
|
||||
* Returns a specific user by id
|
||||
* or the current user if no id is given
|
||||
*
|
||||
* @param string|null $id
|
||||
* @param bool $allowImpersonation If set to false, only the actually
|
||||
* logged in user will be returned
|
||||
* (when `$id` is passed as `null`)
|
||||
* @return \Kirby\Cms\User|null
|
||||
*/
|
||||
public function user(?string $id = null, bool $allowImpersonation = true)
|
||||
{
|
||||
if ($id !== null) {
|
||||
return $this->users()->find($id);
|
||||
}
|
||||
|
||||
if ($allowImpersonation === true && is_string($this->user) === true) {
|
||||
return $this->auth()->impersonate($this->user);
|
||||
} else {
|
||||
try {
|
||||
return $this->auth()->user(null, $allowImpersonation);
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($allowImpersonation === true && is_string($this->user) === true) {
|
||||
return $this->auth()->impersonate($this->user);
|
||||
} else {
|
||||
try {
|
||||
return $this->auth()->user(null, $allowImpersonation);
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all users
|
||||
*
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
if (is_a($this->users, 'Kirby\Cms\Users') === true) {
|
||||
return $this->users;
|
||||
}
|
||||
/**
|
||||
* Returns all users
|
||||
*
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
if (is_a($this->users, 'Kirby\Cms\Users') === true) {
|
||||
return $this->users;
|
||||
}
|
||||
|
||||
return $this->users = Users::load($this->root('accounts'), ['kirby' => $this]);
|
||||
}
|
||||
return $this->users = Users::load($this->root('accounts'), ['kirby' => $this]);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,48 +16,48 @@ use Kirby\Cms\User;
|
||||
*/
|
||||
abstract class Challenge
|
||||
{
|
||||
/**
|
||||
* Checks whether the challenge is available
|
||||
* for the passed user and purpose
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User the code will be generated for
|
||||
* @param string $mode Purpose of the code ('login', 'reset' or '2fa')
|
||||
* @return bool
|
||||
*/
|
||||
abstract public static function isAvailable(User $user, string $mode): bool;
|
||||
/**
|
||||
* Checks whether the challenge is available
|
||||
* for the passed user and purpose
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User the code will be generated for
|
||||
* @param string $mode Purpose of the code ('login', 'reset' or '2fa')
|
||||
* @return bool
|
||||
*/
|
||||
abstract public static function isAvailable(User $user, string $mode): bool;
|
||||
|
||||
/**
|
||||
* Generates a random one-time auth code and returns that code
|
||||
* for later verification
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to generate the code for
|
||||
* @param array $options Details of the challenge request:
|
||||
* - 'mode': Purpose of the code ('login', 'reset' or '2fa')
|
||||
* - 'timeout': Number of seconds the code will be valid for
|
||||
* @return string|null The generated and sent code or `null` in case
|
||||
* there was no code to generate by this algorithm
|
||||
*/
|
||||
abstract public static function create(User $user, array $options): ?string;
|
||||
/**
|
||||
* Generates a random one-time auth code and returns that code
|
||||
* for later verification
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to generate the code for
|
||||
* @param array $options Details of the challenge request:
|
||||
* - 'mode': Purpose of the code ('login', 'reset' or '2fa')
|
||||
* - 'timeout': Number of seconds the code will be valid for
|
||||
* @return string|null The generated and sent code or `null` in case
|
||||
* there was no code to generate by this algorithm
|
||||
*/
|
||||
abstract public static function create(User $user, array $options): ?string;
|
||||
|
||||
/**
|
||||
* Verifies the provided code against the created one;
|
||||
* default implementation that checks the code that was
|
||||
* returned from the `create()` method
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to check the code for
|
||||
* @param string $code Code to verify
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify(User $user, string $code): bool
|
||||
{
|
||||
$hash = $user->kirby()->session()->get('kirby.challenge.code');
|
||||
if (is_string($hash) !== true) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Verifies the provided code against the created one;
|
||||
* default implementation that checks the code that was
|
||||
* returned from the `create()` method
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to check the code for
|
||||
* @param string $code Code to verify
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify(User $user, string $code): bool
|
||||
{
|
||||
$hash = $user->kirby()->session()->get('kirby.challenge.code');
|
||||
if (is_string($hash) !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize the formatting in the user-provided code
|
||||
$code = str_replace(' ', '', $code);
|
||||
// normalize the formatting in the user-provided code
|
||||
$code = str_replace(' ', '', $code);
|
||||
|
||||
return password_verify($code, $hash);
|
||||
}
|
||||
return password_verify($code, $hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,60 +18,60 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class EmailChallenge extends Challenge
|
||||
{
|
||||
/**
|
||||
* Checks whether the challenge is available
|
||||
* for the passed user and purpose
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User the code will be generated for
|
||||
* @param string $mode Purpose of the code ('login', 'reset' or '2fa')
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAvailable(User $user, string $mode): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Checks whether the challenge is available
|
||||
* for the passed user and purpose
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User the code will be generated for
|
||||
* @param string $mode Purpose of the code ('login', 'reset' or '2fa')
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAvailable(User $user, string $mode): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random one-time auth code and returns that code
|
||||
* for later verification
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to generate the code for
|
||||
* @param array $options Details of the challenge request:
|
||||
* - 'mode': Purpose of the code ('login', 'reset' or '2fa')
|
||||
* - 'timeout': Number of seconds the code will be valid for
|
||||
* @return string The generated and sent code
|
||||
*/
|
||||
public static function create(User $user, array $options): string
|
||||
{
|
||||
$code = Str::random(6, 'num');
|
||||
/**
|
||||
* Generates a random one-time auth code and returns that code
|
||||
* for later verification
|
||||
*
|
||||
* @param \Kirby\Cms\User $user User to generate the code for
|
||||
* @param array $options Details of the challenge request:
|
||||
* - 'mode': Purpose of the code ('login', 'reset' or '2fa')
|
||||
* - 'timeout': Number of seconds the code will be valid for
|
||||
* @return string The generated and sent code
|
||||
*/
|
||||
public static function create(User $user, array $options): string
|
||||
{
|
||||
$code = Str::random(6, 'num');
|
||||
|
||||
// insert a space in the middle for easier readability
|
||||
$formatted = substr($code, 0, 3) . ' ' . substr($code, 3, 3);
|
||||
// insert a space in the middle for easier readability
|
||||
$formatted = substr($code, 0, 3) . ' ' . substr($code, 3, 3);
|
||||
|
||||
// use the login templates for 2FA
|
||||
$mode = $options['mode'];
|
||||
if ($mode === '2fa') {
|
||||
$mode = 'login';
|
||||
}
|
||||
// use the login templates for 2FA
|
||||
$mode = $options['mode'];
|
||||
if ($mode === '2fa') {
|
||||
$mode = 'login';
|
||||
}
|
||||
|
||||
$kirby = $user->kirby();
|
||||
$kirby->email([
|
||||
'from' => $kirby->option('auth.challenge.email.from', 'noreply@' . $kirby->url('index', true)->host()),
|
||||
'fromName' => $kirby->option('auth.challenge.email.fromName', $kirby->site()->title()),
|
||||
'to' => $user,
|
||||
'subject' => $kirby->option(
|
||||
'auth.challenge.email.subject',
|
||||
I18n::translate('login.email.' . $mode . '.subject', null, $user->language())
|
||||
),
|
||||
'template' => 'auth/' . $mode,
|
||||
'data' => [
|
||||
'user' => $user,
|
||||
'site' => $kirby->system()->title(),
|
||||
'code' => $formatted,
|
||||
'timeout' => round($options['timeout'] / 60)
|
||||
]
|
||||
]);
|
||||
$kirby = $user->kirby();
|
||||
$kirby->email([
|
||||
'from' => $kirby->option('auth.challenge.email.from', 'noreply@' . $kirby->url('index', true)->host()),
|
||||
'fromName' => $kirby->option('auth.challenge.email.fromName', $kirby->site()->title()),
|
||||
'to' => $user,
|
||||
'subject' => $kirby->option(
|
||||
'auth.challenge.email.subject',
|
||||
I18n::translate('login.email.' . $mode . '.subject', null, $user->language())
|
||||
),
|
||||
'template' => 'auth/' . $mode,
|
||||
'data' => [
|
||||
'user' => $user,
|
||||
'site' => $kirby->system()->title(),
|
||||
'code' => $formatted,
|
||||
'timeout' => round($options['timeout'] / 60)
|
||||
]
|
||||
]);
|
||||
|
||||
return $code;
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,201 +19,201 @@ use Kirby\Toolkit\Properties;
|
||||
*/
|
||||
class Status
|
||||
{
|
||||
use Properties;
|
||||
use Properties;
|
||||
|
||||
/**
|
||||
* Type of the active challenge
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challenge = null;
|
||||
/**
|
||||
* Type of the active challenge
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challenge = null;
|
||||
|
||||
/**
|
||||
* Challenge type to use as a fallback
|
||||
* when $challenge is `null`
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challengeFallback = null;
|
||||
/**
|
||||
* Challenge type to use as a fallback
|
||||
* when $challenge is `null`
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $challengeFallback = null;
|
||||
|
||||
/**
|
||||
* Email address of the current/pending user
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $email = null;
|
||||
/**
|
||||
* Email address of the current/pending user
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $email = null;
|
||||
|
||||
/**
|
||||
* Kirby instance for user lookup
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
/**
|
||||
* Kirby instance for user lookup
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* Authentication status:
|
||||
* `active|impersonated|pending|inactive`
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $status;
|
||||
/**
|
||||
* Authentication status:
|
||||
* `active|impersonated|pending|inactive`
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->status();
|
||||
}
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the active challenge
|
||||
*
|
||||
* @param bool $automaticFallback If set to `false`, no faked challenge is returned;
|
||||
* WARNING: never send the resulting `null` value to the
|
||||
* user to avoid leaking whether the pending user exists
|
||||
* @return string|null
|
||||
*/
|
||||
public function challenge(bool $automaticFallback = true): ?string
|
||||
{
|
||||
// never return a challenge type if the status doesn't match
|
||||
if ($this->status() !== 'pending') {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the type of the active challenge
|
||||
*
|
||||
* @param bool $automaticFallback If set to `false`, no faked challenge is returned;
|
||||
* WARNING: never send the resulting `null` value to the
|
||||
* user to avoid leaking whether the pending user exists
|
||||
* @return string|null
|
||||
*/
|
||||
public function challenge(bool $automaticFallback = true): ?string
|
||||
{
|
||||
// never return a challenge type if the status doesn't match
|
||||
if ($this->status() !== 'pending') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($automaticFallback === false) {
|
||||
return $this->challenge;
|
||||
} else {
|
||||
return $this->challenge ?? $this->challengeFallback;
|
||||
}
|
||||
}
|
||||
if ($automaticFallback === false) {
|
||||
return $this->challenge;
|
||||
} else {
|
||||
return $this->challenge ?? $this->challengeFallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email address of the current/pending user
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function email(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
/**
|
||||
* Returns the email address of the current/pending user
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function email(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string `active|impersonated|pending|inactive`
|
||||
*/
|
||||
public function status(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
/**
|
||||
* Returns the authentication status
|
||||
*
|
||||
* @return string `active|impersonated|pending|inactive`
|
||||
*/
|
||||
public function status(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all public status data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'challenge' => $this->challenge(),
|
||||
'email' => $this->email(),
|
||||
'status' => $this->status()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns an array with all public status data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'challenge' => $this->challenge(),
|
||||
'email' => $this->email(),
|
||||
'status' => $this->status()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently logged in user
|
||||
*
|
||||
* @return \Kirby\Cms\User
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
// for security, only return the user if they are
|
||||
// already logged in
|
||||
if (in_array($this->status(), ['active', 'impersonated']) !== true) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the currently logged in user
|
||||
*
|
||||
* @return \Kirby\Cms\User
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
// for security, only return the user if they are
|
||||
// already logged in
|
||||
if (in_array($this->status(), ['active', 'impersonated']) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->kirby->user($this->email());
|
||||
}
|
||||
return $this->kirby->user($this->email());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the active challenge
|
||||
*
|
||||
* @param string|null $challenge
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChallenge(?string $challenge = null)
|
||||
{
|
||||
$this->challenge = $challenge;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the type of the active challenge
|
||||
*
|
||||
* @param string|null $challenge
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChallenge(?string $challenge = null)
|
||||
{
|
||||
$this->challenge = $challenge;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the challenge type to use as
|
||||
* a fallback when $challenge is `null`
|
||||
*
|
||||
* @param string|null $challengeFallback
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChallengeFallback(?string $challengeFallback = null)
|
||||
{
|
||||
$this->challengeFallback = $challengeFallback;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the challenge type to use as
|
||||
* a fallback when $challenge is `null`
|
||||
*
|
||||
* @param string|null $challengeFallback
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChallengeFallback(?string $challengeFallback = null)
|
||||
{
|
||||
$this->challengeFallback = $challengeFallback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the email address of the current/pending user
|
||||
*
|
||||
* @param string|null $email
|
||||
* @return $this
|
||||
*/
|
||||
protected function setEmail(?string $email = null)
|
||||
{
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the email address of the current/pending user
|
||||
*
|
||||
* @param string|null $email
|
||||
* @return $this
|
||||
*/
|
||||
protected function setEmail(?string $email = null)
|
||||
{
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Kirby instance for user lookup
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the Kirby instance for user lookup
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication status
|
||||
*
|
||||
* @param string $status `active|impersonated|pending|inactive`
|
||||
* @return $this
|
||||
*/
|
||||
protected function setStatus(string $status)
|
||||
{
|
||||
if (in_array($status, ['active', 'impersonated', 'pending', 'inactive']) !== true) {
|
||||
throw new InvalidArgumentException([
|
||||
'data' => ['argument' => '$props[\'status\']', 'method' => 'Status::__construct']
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Sets the authentication status
|
||||
*
|
||||
* @param string $status `active|impersonated|pending|inactive`
|
||||
* @return $this
|
||||
*/
|
||||
protected function setStatus(string $status)
|
||||
{
|
||||
if (in_array($status, ['active', 'impersonated', 'pending', 'inactive']) !== true) {
|
||||
throw new InvalidArgumentException([
|
||||
'data' => ['argument' => '$props[\'status\']', 'method' => 'Status::__construct']
|
||||
]);
|
||||
}
|
||||
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,242 +20,246 @@ use Throwable;
|
||||
*/
|
||||
class Block extends Item
|
||||
{
|
||||
use HasMethods;
|
||||
use HasMethods;
|
||||
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Blocks';
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Blocks';
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Content
|
||||
*/
|
||||
protected $content;
|
||||
/**
|
||||
* @var \Kirby\Cms\Content
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isHidden;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isHidden;
|
||||
|
||||
/**
|
||||
* Registry with all block models
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $models = [];
|
||||
/**
|
||||
* Registry with all block models
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $models = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Proxy for content fields
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $method, array $args = [])
|
||||
{
|
||||
// block methods
|
||||
if ($this->hasMethod($method)) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
/**
|
||||
* Proxy for content fields
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $method, array $args = [])
|
||||
{
|
||||
// block methods
|
||||
if ($this->hasMethod($method)) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
|
||||
return $this->content()->get($method);
|
||||
}
|
||||
return $this->content()->get($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new block object
|
||||
*
|
||||
* @param array $params
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
parent::__construct($params);
|
||||
/**
|
||||
* Creates a new block object
|
||||
*
|
||||
* @param array $params
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
parent::__construct($params);
|
||||
|
||||
if (isset($params['type']) === false) {
|
||||
throw new InvalidArgumentException('The block type is missing');
|
||||
}
|
||||
if (isset($params['type']) === false) {
|
||||
throw new InvalidArgumentException('The block type is missing');
|
||||
}
|
||||
|
||||
// make sure the content is always defined as array to keep
|
||||
// at least a bit of backward compatibility with older fields
|
||||
if (is_array($params['content'] ?? null) === false) {
|
||||
$params['content'] = [];
|
||||
}
|
||||
// make sure the content is always defined as array to keep
|
||||
// at least a bit of backward compatibility with older fields
|
||||
if (is_array($params['content'] ?? null) === false) {
|
||||
$params['content'] = [];
|
||||
}
|
||||
|
||||
$this->content = $params['content'];
|
||||
$this->isHidden = $params['isHidden'] ?? false;
|
||||
$this->type = $params['type'];
|
||||
$this->content = $params['content'];
|
||||
$this->isHidden = $params['isHidden'] ?? false;
|
||||
$this->type = $params['type'];
|
||||
|
||||
// create the content object
|
||||
$this->content = new Content($this->content, $this->parent);
|
||||
}
|
||||
// create the content object
|
||||
$this->content = new Content($this->content, $this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
/**
|
||||
* Converts the object to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content object
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
/**
|
||||
* Returns the content object
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for the block snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function controller(): array
|
||||
{
|
||||
return [
|
||||
'block' => $this,
|
||||
'content' => $this->content(),
|
||||
// deprecated block data
|
||||
'data' => $this,
|
||||
'id' => $this->id(),
|
||||
'prev' => $this->prev(),
|
||||
'next' => $this->next()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Controller for the block snippet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function controller(): array
|
||||
{
|
||||
return [
|
||||
'block' => $this,
|
||||
'content' => $this->content(),
|
||||
// deprecated block data
|
||||
'data' => $this,
|
||||
'id' => $this->id(),
|
||||
'prev' => $this->prev(),
|
||||
'next' => $this->next()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the block to HTML and then
|
||||
* uses the Str::excerpt method to create
|
||||
* a non-formatted, shortened excerpt from it
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function excerpt(...$args)
|
||||
{
|
||||
return Str::excerpt($this->toHtml(), ...$args);
|
||||
}
|
||||
/**
|
||||
* Converts the block to HTML and then
|
||||
* uses the Str::excerpt method to create
|
||||
* a non-formatted, shortened excerpt from it
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function excerpt(...$args)
|
||||
{
|
||||
return Str::excerpt($this->toHtml(), ...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a block object with registering blocks models
|
||||
*
|
||||
* @param array $params
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
* @internal
|
||||
*/
|
||||
public static function factory(array $params)
|
||||
{
|
||||
$type = $params['type'] ?? null;
|
||||
/**
|
||||
* Constructs a block object with registering blocks models
|
||||
*
|
||||
* @param array $params
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
* @internal
|
||||
*/
|
||||
public static function factory(array $params)
|
||||
{
|
||||
$type = $params['type'] ?? null;
|
||||
|
||||
if (empty($type) === false && $class = (static::$models[$type] ?? null)) {
|
||||
$object = new $class($params);
|
||||
if (empty($type) === false && $class = (static::$models[$type] ?? null)) {
|
||||
$object = new $class($params);
|
||||
|
||||
if (is_a($object, 'Kirby\Cms\Block') === true) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
if (is_a($object, 'Kirby\Cms\Block') === true) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
// default model for blocks
|
||||
if ($class = (static::$models['Kirby\Cms\Block'] ?? null)) {
|
||||
$object = new $class($params);
|
||||
// default model for blocks
|
||||
if ($class = (static::$models['Kirby\Cms\Block'] ?? null)) {
|
||||
$object = new $class($params);
|
||||
|
||||
if (is_a($object, 'Kirby\Cms\Block') === true) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
if (is_a($object, 'Kirby\Cms\Block') === true) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return new static($params);
|
||||
}
|
||||
return new static($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the block is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->content()->toArray());
|
||||
}
|
||||
/**
|
||||
* Checks if the block is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->content()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the block is hidden
|
||||
* from being rendered in the frontend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this->isHidden;
|
||||
}
|
||||
/**
|
||||
* Checks if the block is hidden
|
||||
* from being rendered in the frontend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this->isHidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the block is not empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
/**
|
||||
* Checks if the block is not empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
/**
|
||||
* Returns the block type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'content' => $this->content()->toArray(),
|
||||
'id' => $this->id(),
|
||||
'isHidden' => $this->isHidden(),
|
||||
'type' => $this->type(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'content' => $this->content()->toArray(),
|
||||
'id' => $this->id(),
|
||||
'isHidden' => $this->isHidden(),
|
||||
'type' => $this->type(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the block to html first
|
||||
* and then places that inside a field
|
||||
* object. This can be used further
|
||||
* with all available field methods
|
||||
*
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function toField()
|
||||
{
|
||||
return new Field($this->parent(), $this->id(), $this->toHtml());
|
||||
}
|
||||
/**
|
||||
* Converts the block to html first
|
||||
* and then places that inside a field
|
||||
* object. This can be used further
|
||||
* with all available field methods
|
||||
*
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function toField()
|
||||
{
|
||||
return new Field($this->parent(), $this->id(), $this->toHtml());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the block to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml(): string
|
||||
{
|
||||
try {
|
||||
$kirby = $this->parent()->kirby();
|
||||
return (string)$kirby->snippet('blocks/' . $this->type(), $this->controller(), true);
|
||||
} catch (Throwable $e) {
|
||||
return '<p>Block error: "' . $e->getMessage() . '" in block type: "' . $this->type() . '"</p>';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts the block to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml(): string
|
||||
{
|
||||
try {
|
||||
$kirby = $this->parent()->kirby();
|
||||
return (string)$kirby->snippet('blocks/' . $this->type(), $this->controller(), true);
|
||||
} catch (Throwable $e) {
|
||||
if ($kirby->option('debug') === true) {
|
||||
return '<p>Block error: "' . $e->getMessage() . '" in block type: "' . $this->type() . '"</p>';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,126 +20,126 @@ use Throwable;
|
||||
*/
|
||||
class Blocks extends Items
|
||||
{
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Block';
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Block';
|
||||
|
||||
/**
|
||||
* Return HTML when the collection is
|
||||
* converted to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
/**
|
||||
* Return HTML when the collection is
|
||||
* converted to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the blocks to HTML and then
|
||||
* uses the Str::excerpt method to create
|
||||
* a non-formatted, shortened excerpt from it
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function excerpt(...$args)
|
||||
{
|
||||
return Str::excerpt($this->toHtml(), ...$args);
|
||||
}
|
||||
/**
|
||||
* Converts the blocks to HTML and then
|
||||
* uses the Str::excerpt method to create
|
||||
* a non-formatted, shortened excerpt from it
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function excerpt(...$args)
|
||||
{
|
||||
return Str::excerpt($this->toHtml(), ...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around the factory to
|
||||
* catch blocks from layouts
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$items = static::extractFromLayouts($items);
|
||||
/**
|
||||
* Wrapper around the factory to
|
||||
* catch blocks from layouts
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$items = static::extractFromLayouts($items);
|
||||
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull out blocks from layouts
|
||||
*
|
||||
* @param array $input
|
||||
* @return array
|
||||
*/
|
||||
protected static function extractFromLayouts(array $input): array
|
||||
{
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Pull out blocks from layouts
|
||||
*
|
||||
* @param array $input
|
||||
* @return array
|
||||
*/
|
||||
protected static function extractFromLayouts(array $input): array
|
||||
{
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// no columns = no layout
|
||||
if (array_key_exists('columns', $input[0]) === false) {
|
||||
return $input;
|
||||
}
|
||||
// no columns = no layout
|
||||
if (array_key_exists('columns', $input[0]) === false) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
$blocks = [];
|
||||
$blocks = [];
|
||||
|
||||
foreach ($input as $layout) {
|
||||
foreach (($layout['columns'] ?? []) as $column) {
|
||||
foreach (($column['blocks'] ?? []) as $block) {
|
||||
$blocks[] = $block;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($input as $layout) {
|
||||
foreach (($layout['columns'] ?? []) as $column) {
|
||||
foreach (($column['blocks'] ?? []) as $block) {
|
||||
$blocks[] = $block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given block type exists in the collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasType(string $type): bool
|
||||
{
|
||||
return $this->filterBy('type', $type)->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if a given block type exists in the collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasType(string $type): bool
|
||||
{
|
||||
return $this->filterBy('type', $type)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and sanitize various block formats
|
||||
*
|
||||
* @param array|string $input
|
||||
* @return array
|
||||
*/
|
||||
public static function parse($input): array
|
||||
{
|
||||
if (empty($input) === false && is_array($input) === false) {
|
||||
try {
|
||||
$input = Json::decode((string)$input);
|
||||
} catch (Throwable $e) {
|
||||
$parser = new Parsley((string)$input, new BlockSchema());
|
||||
$input = $parser->blocks();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse and sanitize various block formats
|
||||
*
|
||||
* @param array|string $input
|
||||
* @return array
|
||||
*/
|
||||
public static function parse($input): array
|
||||
{
|
||||
if (empty($input) === false && is_array($input) === false) {
|
||||
try {
|
||||
$input = Json::decode((string)$input);
|
||||
} catch (Throwable $e) {
|
||||
$parser = new Parsley((string)$input, new BlockSchema());
|
||||
$input = $parser->blocks();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all blocks to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml(): string
|
||||
{
|
||||
$html = [];
|
||||
/**
|
||||
* Convert all blocks to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml(): string
|
||||
{
|
||||
$html = [];
|
||||
|
||||
foreach ($this->data as $block) {
|
||||
$html[] = $block->toHtml();
|
||||
}
|
||||
foreach ($this->data as $block) {
|
||||
$html[] = $block->toHtml();
|
||||
}
|
||||
|
||||
return implode($html);
|
||||
}
|
||||
return implode($html);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,315 +24,316 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Collection extends BaseCollection
|
||||
{
|
||||
use HasMethods;
|
||||
use HasMethods;
|
||||
|
||||
/**
|
||||
* Stores the parent object, which is needed
|
||||
* in some collections to get the finder methods right.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* Stores the parent object, which is needed
|
||||
* in some collections to get the finder methods right.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Magic getter function
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $key, $arguments)
|
||||
{
|
||||
// collection methods
|
||||
if ($this->hasMethod($key) === true) {
|
||||
return $this->callMethod($key, $arguments);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Magic getter function
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $key, $arguments)
|
||||
{
|
||||
// collection methods
|
||||
if ($this->hasMethod($key) === true) {
|
||||
return $this->callMethod($key, $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Collection with the given objects
|
||||
*
|
||||
* @param array $objects
|
||||
* @param object|null $parent
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
/**
|
||||
* Creates a new Collection with the given objects
|
||||
*
|
||||
* @param array $objects
|
||||
* @param object|null $parent
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
foreach ($objects as $object) {
|
||||
$this->add($object);
|
||||
}
|
||||
}
|
||||
foreach ($objects as $object) {
|
||||
$this->add($object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal setter for each object in the Collection.
|
||||
* This takes care of Component validation and of setting
|
||||
* the collection prop on each object correctly.
|
||||
*
|
||||
* @param string $id
|
||||
* @param object $object
|
||||
*/
|
||||
public function __set(string $id, $object)
|
||||
{
|
||||
$this->data[$id] = $object;
|
||||
}
|
||||
/**
|
||||
* Internal setter for each object in the Collection.
|
||||
* This takes care of Component validation and of setting
|
||||
* the collection prop on each object correctly.
|
||||
*
|
||||
* @param string $id
|
||||
* @param object $object
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string $id, $object): void
|
||||
{
|
||||
$this->data[$id] = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single object or
|
||||
* an entire second collection to the
|
||||
* current collection
|
||||
*
|
||||
* @param mixed $object
|
||||
*/
|
||||
public function add($object)
|
||||
{
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
} elseif (is_object($object) === true && method_exists($object, 'id') === true) {
|
||||
$this->__set($object->id(), $object);
|
||||
} else {
|
||||
$this->append($object);
|
||||
}
|
||||
/**
|
||||
* Adds a single object or
|
||||
* an entire second collection to the
|
||||
* current collection
|
||||
*
|
||||
* @param mixed $object
|
||||
*/
|
||||
public function add($object)
|
||||
{
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
} elseif (is_object($object) === true && method_exists($object, 'id') === true) {
|
||||
$this->__set($object->id(), $object);
|
||||
} else {
|
||||
$this->append($object);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an element to the data array
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @param mixed $key Optional collection key, will be determined from the item if not given
|
||||
* @param mixed $item
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function append(...$args)
|
||||
{
|
||||
if (count($args) === 1) {
|
||||
// try to determine the key from the provided item
|
||||
if (is_object($args[0]) === true && is_callable([$args[0], 'id']) === true) {
|
||||
return parent::append($args[0]->id(), $args[0]);
|
||||
} else {
|
||||
return parent::append($args[0]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Appends an element to the data array
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @param mixed $key Optional collection key, will be determined from the item if not given
|
||||
* @param mixed $item
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function append(...$args)
|
||||
{
|
||||
if (count($args) === 1) {
|
||||
// try to determine the key from the provided item
|
||||
if (is_object($args[0]) === true && is_callable([$args[0], 'id']) === true) {
|
||||
return parent::append($args[0]->id(), $args[0]);
|
||||
} else {
|
||||
return parent::append($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::append(...$args);
|
||||
}
|
||||
return parent::append(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the items by a given field or callback. Returns a collection
|
||||
* with an item for each group and a collection for each group.
|
||||
*
|
||||
* @param string|Closure $field
|
||||
* @param bool $i Ignore upper/lowercase for group names
|
||||
* @return \Kirby\Cms\Collection
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
public function group($field, bool $i = true)
|
||||
{
|
||||
if (is_string($field) === true) {
|
||||
$groups = new Collection([], $this->parent());
|
||||
/**
|
||||
* Groups the items by a given field or callback. Returns a collection
|
||||
* with an item for each group and a collection for each group.
|
||||
*
|
||||
* @param string|Closure $field
|
||||
* @param bool $i Ignore upper/lowercase for group names
|
||||
* @return \Kirby\Cms\Collection
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
public function group($field, bool $i = true)
|
||||
{
|
||||
if (is_string($field) === true) {
|
||||
$groups = new Collection([], $this->parent());
|
||||
|
||||
foreach ($this->data as $key => $item) {
|
||||
$value = $this->getAttribute($item, $field);
|
||||
foreach ($this->data as $key => $item) {
|
||||
$value = $this->getAttribute($item, $field);
|
||||
|
||||
// make sure that there's always a proper value to group by
|
||||
if (!$value) {
|
||||
throw new InvalidArgumentException('Invalid grouping value for key: ' . $key);
|
||||
}
|
||||
// make sure that there's always a proper value to group by
|
||||
if (!$value) {
|
||||
throw new InvalidArgumentException('Invalid grouping value for key: ' . $key);
|
||||
}
|
||||
|
||||
// ignore upper/lowercase for group names
|
||||
if ($i) {
|
||||
$value = Str::lower($value);
|
||||
}
|
||||
// ignore upper/lowercase for group names
|
||||
if ($i) {
|
||||
$value = Str::lower($value);
|
||||
}
|
||||
|
||||
if (isset($groups->data[$value]) === false) {
|
||||
// create a new entry for the group if it does not exist yet
|
||||
$groups->data[$value] = new static([$key => $item]);
|
||||
} else {
|
||||
// add the item to an existing group
|
||||
$groups->data[$value]->set($key, $item);
|
||||
}
|
||||
}
|
||||
if (isset($groups->data[$value]) === false) {
|
||||
// create a new entry for the group if it does not exist yet
|
||||
$groups->data[$value] = new static([$key => $item]);
|
||||
} else {
|
||||
// add the item to an existing group
|
||||
$groups->data[$value]->set($key, $item);
|
||||
}
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
return parent::group($field, $i);
|
||||
}
|
||||
return parent::group($field, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given object or id
|
||||
* is in the collection
|
||||
*
|
||||
* @param string|object $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
{
|
||||
if (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
/**
|
||||
* Checks if the given object or id
|
||||
* is in the collection
|
||||
*
|
||||
* @param string|object $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
{
|
||||
if (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
|
||||
return parent::has($key);
|
||||
}
|
||||
return parent::has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct position detection for objects.
|
||||
* The method will automatically detect objects
|
||||
* or ids and then search accordingly.
|
||||
*
|
||||
* @param string|object $needle
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf($needle): int
|
||||
{
|
||||
if (is_string($needle) === true) {
|
||||
return array_search($needle, $this->keys());
|
||||
}
|
||||
/**
|
||||
* Correct position detection for objects.
|
||||
* The method will automatically detect objects
|
||||
* or ids and then search accordingly.
|
||||
*
|
||||
* @param string|object $needle
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf($needle): int
|
||||
{
|
||||
if (is_string($needle) === true) {
|
||||
return array_search($needle, $this->keys());
|
||||
}
|
||||
|
||||
return array_search($needle->id(), $this->keys());
|
||||
}
|
||||
return array_search($needle->id(), $this->keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Collection without the given element(s)
|
||||
*
|
||||
* @param mixed ...$keys any number of keys, passed as individual arguments
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function not(...$keys)
|
||||
{
|
||||
$collection = $this->clone();
|
||||
/**
|
||||
* Returns a Collection without the given element(s)
|
||||
*
|
||||
* @param mixed ...$keys any number of keys, passed as individual arguments
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function not(...$keys)
|
||||
{
|
||||
$collection = $this->clone();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (is_array($key) === true) {
|
||||
return $this->not(...$key);
|
||||
} elseif (is_a($key, 'Kirby\Toolkit\Collection') === true) {
|
||||
$collection = $collection->not(...$key->keys());
|
||||
} elseif (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (is_array($key) === true) {
|
||||
return $this->not(...$key);
|
||||
} elseif (is_a($key, 'Kirby\Toolkit\Collection') === true) {
|
||||
$collection = $collection->not(...$key->keys());
|
||||
} elseif (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
|
||||
unset($collection->{$key});
|
||||
}
|
||||
unset($collection->{$key});
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add pagination and return a sliced set of data.
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function paginate(...$arguments)
|
||||
{
|
||||
$this->pagination = Pagination::for($this, ...$arguments);
|
||||
/**
|
||||
* Add pagination and return a sliced set of data.
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function paginate(...$arguments)
|
||||
{
|
||||
$this->pagination = Pagination::for($this, ...$arguments);
|
||||
|
||||
// slice and clone the collection according to the pagination
|
||||
return $this->slice($this->pagination->offset(), $this->pagination->limit());
|
||||
}
|
||||
// slice and clone the collection according to the pagination
|
||||
return $this->slice($this->pagination->offset(), $this->pagination->limit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends an element to the data array
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @param mixed $key Optional collection key, will be determined from the item if not given
|
||||
* @param mixed $item
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function prepend(...$args)
|
||||
{
|
||||
if (count($args) === 1) {
|
||||
// try to determine the key from the provided item
|
||||
if (is_object($args[0]) === true && is_callable([$args[0], 'id']) === true) {
|
||||
return parent::prepend($args[0]->id(), $args[0]);
|
||||
} else {
|
||||
return parent::prepend($args[0]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prepends an element to the data array
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @param mixed $key Optional collection key, will be determined from the item if not given
|
||||
* @param mixed $item
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function prepend(...$args)
|
||||
{
|
||||
if (count($args) === 1) {
|
||||
// try to determine the key from the provided item
|
||||
if (is_object($args[0]) === true && is_callable([$args[0], 'id']) === true) {
|
||||
return parent::prepend($args[0]->id(), $args[0]);
|
||||
} else {
|
||||
return parent::prepend($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::prepend(...$args);
|
||||
}
|
||||
return parent::prepend(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a combination of filter, sort, not,
|
||||
* offset, limit, search and paginate on the collection.
|
||||
* Any part of the query is optional.
|
||||
*
|
||||
* @param array $arguments
|
||||
* @return static
|
||||
*/
|
||||
public function query(array $arguments = [])
|
||||
{
|
||||
$paginate = $arguments['paginate'] ?? null;
|
||||
$search = $arguments['search'] ?? null;
|
||||
/**
|
||||
* Runs a combination of filter, sort, not,
|
||||
* offset, limit, search and paginate on the collection.
|
||||
* Any part of the query is optional.
|
||||
*
|
||||
* @param array $arguments
|
||||
* @return static
|
||||
*/
|
||||
public function query(array $arguments = [])
|
||||
{
|
||||
$paginate = $arguments['paginate'] ?? null;
|
||||
$search = $arguments['search'] ?? null;
|
||||
|
||||
unset($arguments['paginate']);
|
||||
unset($arguments['paginate']);
|
||||
|
||||
$result = parent::query($arguments);
|
||||
$result = parent::query($arguments);
|
||||
|
||||
if (empty($search) === false) {
|
||||
if (is_array($search) === true) {
|
||||
$result = $result->search($search['query'] ?? null, $search['options'] ?? []);
|
||||
} else {
|
||||
$result = $result->search($search);
|
||||
}
|
||||
}
|
||||
if (empty($search) === false) {
|
||||
if (is_array($search) === true) {
|
||||
$result = $result->search($search['query'] ?? null, $search['options'] ?? []);
|
||||
} else {
|
||||
$result = $result->search($search);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($paginate) === false) {
|
||||
$result = $result->paginate($paginate);
|
||||
}
|
||||
if (empty($paginate) === false) {
|
||||
$result = $result->paginate($paginate);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an object
|
||||
*
|
||||
* @param mixed $key the name of the key
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
/**
|
||||
* Removes an object
|
||||
*
|
||||
* @param mixed $key the name of the key
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if (is_object($key) === true) {
|
||||
$key = $key->id();
|
||||
}
|
||||
|
||||
return parent::remove($key);
|
||||
}
|
||||
return parent::remove($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the collection
|
||||
*
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return self
|
||||
*/
|
||||
public function search(string $query = null, $params = [])
|
||||
{
|
||||
return Search::collection($this, $query, $params);
|
||||
}
|
||||
/**
|
||||
* Searches the collection
|
||||
*
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return self
|
||||
*/
|
||||
public function search(string $query = null, $params = [])
|
||||
{
|
||||
return Search::collection($this, $query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all objects in the collection
|
||||
* to an array. This can also take a callback
|
||||
* function to further modify the array result.
|
||||
*
|
||||
* @param \Closure|null $map
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return parent::toArray($map ?? fn ($object) => $object->toArray());
|
||||
}
|
||||
/**
|
||||
* Converts all objects in the collection
|
||||
* to an array. This can also take a callback
|
||||
* function to further modify the array result.
|
||||
*
|
||||
* @param \Closure|null $map
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return parent::toArray($map ?? fn ($object) => $object->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,120 +22,120 @@ use Kirby\Toolkit\Controller;
|
||||
*/
|
||||
class Collections
|
||||
{
|
||||
/**
|
||||
* Each collection is cached once it
|
||||
* has been called, to avoid further
|
||||
* processing on sequential calls to
|
||||
* the same collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
/**
|
||||
* Each collection is cached once it
|
||||
* has been called, to avoid further
|
||||
* processing on sequential calls to
|
||||
* the same collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* Store of all collections
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $collections = [];
|
||||
/**
|
||||
* Store of all collections
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $collections = [];
|
||||
|
||||
/**
|
||||
* Magic caller to enable something like
|
||||
* `$collections->myCollection()`
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
return $this->get($name, ...$arguments);
|
||||
}
|
||||
/**
|
||||
* Magic caller to enable something like
|
||||
* `$collections->myCollection()`
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
return $this->get($name, ...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a collection by name if registered
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
public function get(string $name, array $data = [])
|
||||
{
|
||||
// if not yet loaded
|
||||
if (isset($this->collections[$name]) === false) {
|
||||
$this->collections[$name] = $this->load($name);
|
||||
}
|
||||
/**
|
||||
* Loads a collection by name if registered
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
public function get(string $name, array $data = [])
|
||||
{
|
||||
// if not yet loaded
|
||||
if (isset($this->collections[$name]) === false) {
|
||||
$this->collections[$name] = $this->load($name);
|
||||
}
|
||||
|
||||
// if not yet cached
|
||||
if (
|
||||
isset($this->cache[$name]) === false ||
|
||||
$this->cache[$name]['data'] !== $data
|
||||
) {
|
||||
$controller = new Controller($this->collections[$name]);
|
||||
$this->cache[$name] = [
|
||||
'result' => $controller->call(null, $data),
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
// if not yet cached
|
||||
if (
|
||||
isset($this->cache[$name]) === false ||
|
||||
$this->cache[$name]['data'] !== $data
|
||||
) {
|
||||
$controller = new Controller($this->collections[$name]);
|
||||
$this->cache[$name] = [
|
||||
'result' => $controller->call(null, $data),
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
// return cloned object
|
||||
if (is_object($this->cache[$name]['result']) === true) {
|
||||
return clone $this->cache[$name]['result'];
|
||||
}
|
||||
// return cloned object
|
||||
if (is_object($this->cache[$name]['result']) === true) {
|
||||
return clone $this->cache[$name]['result'];
|
||||
}
|
||||
|
||||
return $this->cache[$name]['result'];
|
||||
}
|
||||
return $this->cache[$name]['result'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a collection exists
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
if (isset($this->collections[$name]) === true) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Checks if a collection exists
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
if (isset($this->collections[$name]) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->load($name);
|
||||
return true;
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->load($name);
|
||||
return true;
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads collection from php file in a
|
||||
* given directory or from plugin extension.
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
public function load(string $name)
|
||||
{
|
||||
$kirby = App::instance();
|
||||
/**
|
||||
* Loads collection from php file in a
|
||||
* given directory or from plugin extension.
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
public function load(string $name)
|
||||
{
|
||||
$kirby = App::instance();
|
||||
|
||||
// first check for collection file
|
||||
$file = $kirby->root('collections') . '/' . $name . '.php';
|
||||
// first check for collection file
|
||||
$file = $kirby->root('collections') . '/' . $name . '.php';
|
||||
|
||||
if (is_file($file) === true) {
|
||||
$collection = F::load($file);
|
||||
if (is_file($file) === true) {
|
||||
$collection = F::load($file);
|
||||
|
||||
if (is_a($collection, 'Closure')) {
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
if (is_a($collection, 'Closure')) {
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to collections from plugins
|
||||
$collections = $kirby->extensions('collections');
|
||||
// fallback to collections from plugins
|
||||
$collections = $kirby->extensions('collections');
|
||||
|
||||
if (isset($collections[$name]) === true) {
|
||||
return $collections[$name];
|
||||
}
|
||||
if (isset($collections[$name]) === true) {
|
||||
return $collections[$name];
|
||||
}
|
||||
|
||||
throw new NotFoundException('The collection cannot be found');
|
||||
}
|
||||
throw new NotFoundException('The collection cannot be found');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,254 +16,254 @@ use Kirby\Form\Form;
|
||||
*/
|
||||
class Content
|
||||
{
|
||||
/**
|
||||
* The raw data array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
/**
|
||||
* The raw data array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Cached field objects
|
||||
* Once a field is being fetched
|
||||
* it is added to this array for
|
||||
* later reuse
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = [];
|
||||
/**
|
||||
* Cached field objects
|
||||
* Once a field is being fetched
|
||||
* it is added to this array for
|
||||
* later reuse
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = [];
|
||||
|
||||
/**
|
||||
* A potential parent object.
|
||||
* Not necessarily needed. Especially
|
||||
* for testing, but field methods might
|
||||
* need it.
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* A potential parent object.
|
||||
* Not necessarily needed. Especially
|
||||
* for testing, but field methods might
|
||||
* need it.
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Magic getter for content fields
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
return $this->get($name);
|
||||
}
|
||||
/**
|
||||
* Magic getter for content fields
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
return $this->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Content object
|
||||
*
|
||||
* @param array|null $data
|
||||
* @param object|null $parent
|
||||
* @param bool $normalize Set to `false` if the input field keys are already lowercase
|
||||
*/
|
||||
public function __construct(array $data = [], $parent = null, bool $normalize = true)
|
||||
{
|
||||
if ($normalize === true) {
|
||||
$data = array_change_key_case($data, CASE_LOWER);
|
||||
}
|
||||
/**
|
||||
* Creates a new Content object
|
||||
*
|
||||
* @param array|null $data
|
||||
* @param object|null $parent
|
||||
* @param bool $normalize Set to `false` if the input field keys are already lowercase
|
||||
*/
|
||||
public function __construct(array $data = [], $parent = null, bool $normalize = true)
|
||||
{
|
||||
if ($normalize === true) {
|
||||
$data = array_change_key_case($data, CASE_LOWER);
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
$this->data = $data;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `self::data()` to improve
|
||||
* `var_dump` output
|
||||
*
|
||||
* @see self::data()
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Same as `self::data()` to improve
|
||||
* `var_dump` output
|
||||
*
|
||||
* @see self::data()
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the content to a new blueprint
|
||||
*
|
||||
* @param string $to
|
||||
* @return array
|
||||
*/
|
||||
public function convertTo(string $to): array
|
||||
{
|
||||
// prepare data
|
||||
$data = [];
|
||||
$content = $this;
|
||||
/**
|
||||
* Converts the content to a new blueprint
|
||||
*
|
||||
* @param string $to
|
||||
* @return array
|
||||
*/
|
||||
public function convertTo(string $to): array
|
||||
{
|
||||
// prepare data
|
||||
$data = [];
|
||||
$content = $this;
|
||||
|
||||
// blueprints
|
||||
$old = $this->parent->blueprint();
|
||||
$subfolder = dirname($old->name());
|
||||
$new = Blueprint::factory($subfolder . '/' . $to, $subfolder . '/default', $this->parent);
|
||||
// blueprints
|
||||
$old = $this->parent->blueprint();
|
||||
$subfolder = dirname($old->name());
|
||||
$new = Blueprint::factory($subfolder . '/' . $to, $subfolder . '/default', $this->parent);
|
||||
|
||||
// forms
|
||||
$oldForm = new Form(['fields' => $old->fields(), 'model' => $this->parent]);
|
||||
$newForm = new Form(['fields' => $new->fields(), 'model' => $this->parent]);
|
||||
// forms
|
||||
$oldForm = new Form(['fields' => $old->fields(), 'model' => $this->parent]);
|
||||
$newForm = new Form(['fields' => $new->fields(), 'model' => $this->parent]);
|
||||
|
||||
// fields
|
||||
$oldFields = $oldForm->fields();
|
||||
$newFields = $newForm->fields();
|
||||
// fields
|
||||
$oldFields = $oldForm->fields();
|
||||
$newFields = $newForm->fields();
|
||||
|
||||
// go through all fields of new template
|
||||
foreach ($newFields as $newField) {
|
||||
$name = $newField->name();
|
||||
$oldField = $oldFields->get($name);
|
||||
// go through all fields of new template
|
||||
foreach ($newFields as $newField) {
|
||||
$name = $newField->name();
|
||||
$oldField = $oldFields->get($name);
|
||||
|
||||
// field name and type matches with old template
|
||||
if ($oldField && $oldField->type() === $newField->type()) {
|
||||
$data[$name] = $content->get($name)->value();
|
||||
} else {
|
||||
$data[$name] = $newField->default();
|
||||
}
|
||||
}
|
||||
// field name and type matches with old template
|
||||
if ($oldField && $oldField->type() === $newField->type()) {
|
||||
$data[$name] = $content->get($name)->value();
|
||||
} else {
|
||||
$data[$name] = $newField->default();
|
||||
}
|
||||
}
|
||||
|
||||
// preserve existing fields
|
||||
return array_merge($this->data, $data);
|
||||
}
|
||||
// preserve existing fields
|
||||
return array_merge($this->data, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Returns the raw data array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all registered field objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
foreach ($this->data as $key => $value) {
|
||||
$this->get($key);
|
||||
}
|
||||
return $this->fields;
|
||||
}
|
||||
/**
|
||||
* Returns all registered field objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
foreach ($this->data as $key => $value) {
|
||||
$this->get($key);
|
||||
}
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either a single field object
|
||||
* or all registered fields
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return \Kirby\Cms\Field|array
|
||||
*/
|
||||
public function get(string $key = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->fields();
|
||||
}
|
||||
/**
|
||||
* Returns either a single field object
|
||||
* or all registered fields
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return \Kirby\Cms\Field|array
|
||||
*/
|
||||
public function get(string $key = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->fields();
|
||||
}
|
||||
|
||||
$key = strtolower($key);
|
||||
$key = strtolower($key);
|
||||
|
||||
if (isset($this->fields[$key])) {
|
||||
return $this->fields[$key];
|
||||
}
|
||||
if (isset($this->fields[$key])) {
|
||||
return $this->fields[$key];
|
||||
}
|
||||
|
||||
$value = $this->data()[$key] ?? null;
|
||||
$value = $this->data()[$key] ?? null;
|
||||
|
||||
return $this->fields[$key] = new Field($this->parent, $key, $value);
|
||||
}
|
||||
return $this->fields[$key] = new Field($this->parent, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a content field is set
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->data[strtolower($key)]) === true;
|
||||
}
|
||||
/**
|
||||
* Checks if a content field is set
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->data[strtolower($key)]) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all field keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->data());
|
||||
}
|
||||
/**
|
||||
* Returns all field keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of the content object
|
||||
* without the fields, specified by the
|
||||
* passed key(s)
|
||||
*
|
||||
* @param string ...$keys
|
||||
* @return static
|
||||
*/
|
||||
public function not(...$keys)
|
||||
{
|
||||
$copy = clone $this;
|
||||
$copy->fields = null;
|
||||
/**
|
||||
* Returns a clone of the content object
|
||||
* without the fields, specified by the
|
||||
* passed key(s)
|
||||
*
|
||||
* @param string ...$keys
|
||||
* @return static
|
||||
*/
|
||||
public function not(...$keys)
|
||||
{
|
||||
$copy = clone $this;
|
||||
$copy->fields = null;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
unset($copy->data[strtolower($key)]);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
unset($copy->data[strtolower($key)]);
|
||||
}
|
||||
|
||||
return $copy;
|
||||
}
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent
|
||||
* Site, Page, File or User object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent
|
||||
* Site, Page, File or User object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent model
|
||||
*
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Model $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set the parent model
|
||||
*
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Model $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data array
|
||||
*
|
||||
* @see self::data()
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->data();
|
||||
}
|
||||
/**
|
||||
* Returns the raw data array
|
||||
*
|
||||
* @see self::data()
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the content and returns
|
||||
* a cloned object
|
||||
*
|
||||
* @param array|null $content
|
||||
* @param bool $overwrite
|
||||
* @return $this
|
||||
*/
|
||||
public function update(array $content = null, bool $overwrite = false)
|
||||
{
|
||||
$content = array_change_key_case((array)$content, CASE_LOWER);
|
||||
$this->data = $overwrite === true ? $content : array_merge($this->data, $content);
|
||||
/**
|
||||
* Updates the content and returns
|
||||
* a cloned object
|
||||
*
|
||||
* @param array|null $content
|
||||
* @param bool $overwrite
|
||||
* @return $this
|
||||
*/
|
||||
public function update(array $content = null, bool $overwrite = false)
|
||||
{
|
||||
$content = array_change_key_case((array)$content, CASE_LOWER);
|
||||
$this->data = $overwrite === true ? $content : array_merge($this->data, $content);
|
||||
|
||||
// clear cache of Field objects
|
||||
$this->fields = [];
|
||||
// clear cache of Field objects
|
||||
$this->fields = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,216 +17,216 @@ use Kirby\Exception\PermissionException;
|
||||
*/
|
||||
class ContentLock
|
||||
{
|
||||
/**
|
||||
* Lock data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* Lock data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* The model to manage locking/unlocking for
|
||||
*
|
||||
* @var ModelWithContent
|
||||
*/
|
||||
protected $model;
|
||||
/**
|
||||
* The model to manage locking/unlocking for
|
||||
*
|
||||
* @var ModelWithContent
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
*/
|
||||
public function __construct(ModelWithContent $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->data = $this->kirby()->locks()->get($model);
|
||||
}
|
||||
/**
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
*/
|
||||
public function __construct(ModelWithContent $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->data = $this->kirby()->locks()->get($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the lock unconditionally
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function clearLock(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Clears the lock unconditionally
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function clearLock(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove lock
|
||||
unset($this->data['lock']);
|
||||
// remove lock
|
||||
unset($this->data['lock']);
|
||||
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets lock with the current user
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException
|
||||
*/
|
||||
public function create(): bool
|
||||
{
|
||||
// check if model is already locked by another user
|
||||
if (
|
||||
isset($this->data['lock']) === true &&
|
||||
$this->data['lock']['user'] !== $this->user()->id()
|
||||
) {
|
||||
$id = ContentLocks::id($this->model);
|
||||
throw new DuplicateException($id . ' is already locked');
|
||||
}
|
||||
/**
|
||||
* Sets lock with the current user
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException
|
||||
*/
|
||||
public function create(): bool
|
||||
{
|
||||
// check if model is already locked by another user
|
||||
if (
|
||||
isset($this->data['lock']) === true &&
|
||||
$this->data['lock']['user'] !== $this->user()->id()
|
||||
) {
|
||||
$id = ContentLocks::id($this->model);
|
||||
throw new DuplicateException($id . ' is already locked');
|
||||
}
|
||||
|
||||
$this->data['lock'] = [
|
||||
'user' => $this->user()->id(),
|
||||
'time' => time()
|
||||
];
|
||||
$this->data['lock'] = [
|
||||
'user' => $this->user()->id(),
|
||||
'time' => time()
|
||||
];
|
||||
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either `false` or array with `user`, `email`,
|
||||
* `time` and `unlockable` keys
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$data = $this->data['lock'] ?? [];
|
||||
/**
|
||||
* Returns either `false` or array with `user`, `email`,
|
||||
* `time` and `unlockable` keys
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$data = $this->data['lock'] ?? [];
|
||||
|
||||
if (empty($data) === false && $data['user'] !== $this->user()->id()) {
|
||||
if ($user = $this->kirby()->user($data['user'])) {
|
||||
$time = (int)($data['time']);
|
||||
if (empty($data) === false && $data['user'] !== $this->user()->id()) {
|
||||
if ($user = $this->kirby()->user($data['user'])) {
|
||||
$time = (int)($data['time']);
|
||||
|
||||
return [
|
||||
'user' => $user->id(),
|
||||
'email' => $user->email(),
|
||||
'time' => $time,
|
||||
'unlockable' => ($time + 60) <= time()
|
||||
];
|
||||
}
|
||||
return [
|
||||
'user' => $user->id(),
|
||||
'email' => $user->email(),
|
||||
'time' => $time,
|
||||
'unlockable' => ($time + 60) <= time()
|
||||
];
|
||||
}
|
||||
|
||||
// clear lock if user not found
|
||||
$this->clearLock();
|
||||
}
|
||||
// clear lock if user not found
|
||||
$this->clearLock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the model is locked by another user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocked(): bool
|
||||
{
|
||||
$lock = $this->get();
|
||||
/**
|
||||
* Returns if the model is locked by another user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocked(): bool
|
||||
{
|
||||
$lock = $this->get();
|
||||
|
||||
if ($lock !== false && $lock['user'] !== $this->user()->id()) {
|
||||
return true;
|
||||
}
|
||||
if ($lock !== false && $lock['user'] !== $this->user()->id()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the current user's lock has been removed by another user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUnlocked(): bool
|
||||
{
|
||||
$data = $this->data['unlock'] ?? [];
|
||||
/**
|
||||
* Returns if the current user's lock has been removed by another user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUnlocked(): bool
|
||||
{
|
||||
$data = $this->data['unlock'] ?? [];
|
||||
|
||||
return in_array($this->user()->id(), $data) === true;
|
||||
}
|
||||
return in_array($this->user()->id(), $data) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function kirby(): App
|
||||
{
|
||||
return $this->model->kirby();
|
||||
}
|
||||
/**
|
||||
* Returns the app instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
protected function kirby(): App
|
||||
{
|
||||
return $this->model->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes lock of current user
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function remove(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Removes lock of current user
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function remove(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if lock was set by another user
|
||||
if ($this->data['lock']['user'] !== $this->user()->id()) {
|
||||
throw new LogicException([
|
||||
'fallback' => 'The content lock can only be removed by the user who created it. Use unlock instead.',
|
||||
'httpCode' => 409
|
||||
]);
|
||||
}
|
||||
// check if lock was set by another user
|
||||
if ($this->data['lock']['user'] !== $this->user()->id()) {
|
||||
throw new LogicException([
|
||||
'fallback' => 'The content lock can only be removed by the user who created it. Use unlock instead.',
|
||||
'httpCode' => 409
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->clearLock();
|
||||
}
|
||||
return $this->clearLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unlock information for current user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function resolve(): bool
|
||||
{
|
||||
// if no unlocks exist, skip
|
||||
if (isset($this->data['unlock']) === false) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Removes unlock information for current user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function resolve(): bool
|
||||
{
|
||||
// if no unlocks exist, skip
|
||||
if (isset($this->data['unlock']) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove user from unlock array
|
||||
$this->data['unlock'] = array_diff(
|
||||
$this->data['unlock'],
|
||||
[$this->user()->id()]
|
||||
);
|
||||
// remove user from unlock array
|
||||
$this->data['unlock'] = array_diff(
|
||||
$this->data['unlock'],
|
||||
[$this->user()->id()]
|
||||
);
|
||||
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
return $this->kirby()->locks()->set($this->model, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current lock and adds lock user to unlock data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Removes current lock and adds lock user to unlock data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock(): bool
|
||||
{
|
||||
// if no lock exists, skip
|
||||
if (isset($this->data['lock']) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// add lock user to unlocked data
|
||||
$this->data['unlock'] ??= [];
|
||||
$this->data['unlock'][] = $this->data['lock']['user'];
|
||||
// add lock user to unlocked data
|
||||
$this->data['unlock'] ??= [];
|
||||
$this->data['unlock'][] = $this->data['lock']['user'];
|
||||
|
||||
return $this->clearLock();
|
||||
}
|
||||
return $this->clearLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns currently authenticated user;
|
||||
* throws exception if none is authenticated
|
||||
*
|
||||
* @return \Kirby\Cms\User
|
||||
* @throws \Kirby\Exception\PermissionException
|
||||
*/
|
||||
protected function user(): User
|
||||
{
|
||||
if ($user = $this->kirby()->user()) {
|
||||
return $user;
|
||||
}
|
||||
/**
|
||||
* Returns currently authenticated user;
|
||||
* throws exception if none is authenticated
|
||||
*
|
||||
* @return \Kirby\Cms\User
|
||||
* @throws \Kirby\Exception\PermissionException
|
||||
*/
|
||||
protected function user(): User
|
||||
{
|
||||
if ($user = $this->kirby()->user()) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new PermissionException('No user authenticated.');
|
||||
}
|
||||
throw new PermissionException('No user authenticated.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,211 +18,211 @@ use Kirby\Filesystem\F;
|
||||
*/
|
||||
class ContentLocks
|
||||
{
|
||||
/**
|
||||
* Data from the `.lock` files
|
||||
* that have been read so far
|
||||
* cached by `.lock` file path
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
/**
|
||||
* Data from the `.lock` files
|
||||
* that have been read so far
|
||||
* cached by `.lock` file path
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* PHP file handles for all currently
|
||||
* open `.lock` files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $handles = [];
|
||||
/**
|
||||
* PHP file handles for all currently
|
||||
* open `.lock` files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $handles = [];
|
||||
|
||||
/**
|
||||
* Closes the open file handles
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $file => $handle) {
|
||||
$this->closeHandle($file);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Closes the open file handles
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $file => $handle) {
|
||||
$this->closeHandle($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file lock and closes the file handle
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
protected function closeHandle(string $file)
|
||||
{
|
||||
if (isset($this->handles[$file]) === false) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Removes the file lock and closes the file handle
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
protected function closeHandle(string $file)
|
||||
{
|
||||
if (isset($this->handles[$file]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$handle = $this->handles[$file];
|
||||
$result = flock($handle, LOCK_UN) && fclose($handle);
|
||||
$handle = $this->handles[$file];
|
||||
$result = flock($handle, LOCK_UN) && fclose($handle);
|
||||
|
||||
if ($result !== true) {
|
||||
throw new Exception('Unexpected file system error.'); // @codeCoverageIgnore
|
||||
}
|
||||
if ($result !== true) {
|
||||
throw new Exception('Unexpected file system error.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
unset($this->handles[$file]);
|
||||
}
|
||||
unset($this->handles[$file]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to a model's lock file
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return string
|
||||
*/
|
||||
public static function file(ModelWithContent $model): string
|
||||
{
|
||||
return $model->contentFileDirectory() . '/.lock';
|
||||
}
|
||||
/**
|
||||
* Returns the path to a model's lock file
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return string
|
||||
*/
|
||||
public static function file(ModelWithContent $model): string
|
||||
{
|
||||
return $model->contentFileDirectory() . '/.lock';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lock/unlock data for the specified model
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return array
|
||||
*/
|
||||
public function get(ModelWithContent $model): array
|
||||
{
|
||||
$file = static::file($model);
|
||||
$id = static::id($model);
|
||||
/**
|
||||
* Returns the lock/unlock data for the specified model
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return array
|
||||
*/
|
||||
public function get(ModelWithContent $model): array
|
||||
{
|
||||
$file = static::file($model);
|
||||
$id = static::id($model);
|
||||
|
||||
// return from cache if file was already loaded
|
||||
if (isset($this->data[$file]) === true) {
|
||||
return $this->data[$file][$id] ?? [];
|
||||
}
|
||||
// return from cache if file was already loaded
|
||||
if (isset($this->data[$file]) === true) {
|
||||
return $this->data[$file][$id] ?? [];
|
||||
}
|
||||
|
||||
// first get a handle to ensure a file system lock
|
||||
$handle = $this->handle($file);
|
||||
// first get a handle to ensure a file system lock
|
||||
$handle = $this->handle($file);
|
||||
|
||||
if (is_resource($handle) === true) {
|
||||
// read data from file
|
||||
clearstatcache();
|
||||
$filesize = filesize($file);
|
||||
if (is_resource($handle) === true) {
|
||||
// read data from file
|
||||
clearstatcache();
|
||||
$filesize = filesize($file);
|
||||
|
||||
if ($filesize > 0) {
|
||||
// always read the whole file
|
||||
rewind($handle);
|
||||
$string = fread($handle, $filesize);
|
||||
$data = Data::decode($string, 'yaml');
|
||||
}
|
||||
}
|
||||
if ($filesize > 0) {
|
||||
// always read the whole file
|
||||
rewind($handle);
|
||||
$string = fread($handle, $filesize);
|
||||
$data = Data::decode($string, 'yaml');
|
||||
}
|
||||
}
|
||||
|
||||
$this->data[$file] = $data ?? [];
|
||||
$this->data[$file] = $data ?? [];
|
||||
|
||||
return $this->data[$file][$id] ?? [];
|
||||
}
|
||||
return $this->data[$file][$id] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file handle to a `.lock` file
|
||||
*
|
||||
* @param string $file
|
||||
* @param bool $create Whether to create the file if it does not exist
|
||||
* @return resource|null File handle
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
protected function handle(string $file, bool $create = false)
|
||||
{
|
||||
// check for an already open handle
|
||||
if (isset($this->handles[$file]) === true) {
|
||||
return $this->handles[$file];
|
||||
}
|
||||
/**
|
||||
* Returns the file handle to a `.lock` file
|
||||
*
|
||||
* @param string $file
|
||||
* @param bool $create Whether to create the file if it does not exist
|
||||
* @return resource|null File handle
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
protected function handle(string $file, bool $create = false)
|
||||
{
|
||||
// check for an already open handle
|
||||
if (isset($this->handles[$file]) === true) {
|
||||
return $this->handles[$file];
|
||||
}
|
||||
|
||||
// don't create a file if not requested
|
||||
if (is_file($file) !== true && $create !== true) {
|
||||
return null;
|
||||
}
|
||||
// don't create a file if not requested
|
||||
if (is_file($file) !== true && $create !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$handle = @fopen($file, 'c+b');
|
||||
if (is_resource($handle) === false) {
|
||||
throw new Exception('Lock file ' . $file . ' could not be opened.'); // @codeCoverageIgnore
|
||||
}
|
||||
$handle = @fopen($file, 'c+b');
|
||||
if (is_resource($handle) === false) {
|
||||
throw new Exception('Lock file ' . $file . ' could not be opened.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// lock the lock file exclusively to prevent changes by other threads
|
||||
$result = flock($handle, LOCK_EX);
|
||||
if ($result !== true) {
|
||||
throw new Exception('Unexpected file system error.'); // @codeCoverageIgnore
|
||||
}
|
||||
// lock the lock file exclusively to prevent changes by other threads
|
||||
$result = flock($handle, LOCK_EX);
|
||||
if ($result !== true) {
|
||||
throw new Exception('Unexpected file system error.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return $this->handles[$file] = $handle;
|
||||
}
|
||||
return $this->handles[$file] = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns model ID used as the key for the data array;
|
||||
* prepended with a slash because the $site otherwise won't have an ID
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return string
|
||||
*/
|
||||
public static function id(ModelWithContent $model): string
|
||||
{
|
||||
return '/' . $model->id();
|
||||
}
|
||||
/**
|
||||
* Returns model ID used as the key for the data array;
|
||||
* prepended with a slash because the $site otherwise won't have an ID
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @return string
|
||||
*/
|
||||
public static function id(ModelWithContent $model): string
|
||||
{
|
||||
return '/' . $model->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets and writes the lock/unlock data for the specified model
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
public function set(ModelWithContent $model, array $data): bool
|
||||
{
|
||||
$file = static::file($model);
|
||||
$id = static::id($model);
|
||||
$handle = $this->handle($file, true);
|
||||
/**
|
||||
* Sets and writes the lock/unlock data for the specified model
|
||||
*
|
||||
* @param \Kirby\Cms\ModelWithContent $model
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\Exception
|
||||
*/
|
||||
public function set(ModelWithContent $model, array $data): bool
|
||||
{
|
||||
$file = static::file($model);
|
||||
$id = static::id($model);
|
||||
$handle = $this->handle($file, true);
|
||||
|
||||
$this->data[$file][$id] = $data;
|
||||
$this->data[$file][$id] = $data;
|
||||
|
||||
// make sure to unset model id entries,
|
||||
// if no lock data for the model exists
|
||||
foreach ($this->data[$file] as $id => $data) {
|
||||
// there is no data for that model whatsoever
|
||||
if (
|
||||
isset($data['lock']) === false &&
|
||||
(isset($data['unlock']) === false ||
|
||||
count($data['unlock']) === 0)
|
||||
) {
|
||||
unset($this->data[$file][$id]);
|
||||
// make sure to unset model id entries,
|
||||
// if no lock data for the model exists
|
||||
foreach ($this->data[$file] as $id => $data) {
|
||||
// there is no data for that model whatsoever
|
||||
if (
|
||||
isset($data['lock']) === false &&
|
||||
(isset($data['unlock']) === false ||
|
||||
count($data['unlock']) === 0)
|
||||
) {
|
||||
unset($this->data[$file][$id]);
|
||||
|
||||
// there is empty unlock data, but still lock data
|
||||
} elseif (
|
||||
isset($data['unlock']) === true &&
|
||||
count($data['unlock']) === 0
|
||||
) {
|
||||
unset($this->data[$file][$id]['unlock']);
|
||||
}
|
||||
}
|
||||
// there is empty unlock data, but still lock data
|
||||
} elseif (
|
||||
isset($data['unlock']) === true &&
|
||||
count($data['unlock']) === 0
|
||||
) {
|
||||
unset($this->data[$file][$id]['unlock']);
|
||||
}
|
||||
}
|
||||
|
||||
// there is no data left in the file whatsoever, delete the file
|
||||
if (count($this->data[$file]) === 0) {
|
||||
unset($this->data[$file]);
|
||||
// there is no data left in the file whatsoever, delete the file
|
||||
if (count($this->data[$file]) === 0) {
|
||||
unset($this->data[$file]);
|
||||
|
||||
// close the file handle, otherwise we can't delete it on Windows
|
||||
$this->closeHandle($file);
|
||||
// close the file handle, otherwise we can't delete it on Windows
|
||||
$this->closeHandle($file);
|
||||
|
||||
return F::remove($file);
|
||||
}
|
||||
return F::remove($file);
|
||||
}
|
||||
|
||||
$yaml = Data::encode($this->data[$file], 'yaml');
|
||||
$yaml = Data::encode($this->data[$file], 'yaml');
|
||||
|
||||
// delete all file contents first
|
||||
if (rewind($handle) !== true || ftruncate($handle, 0) !== true) {
|
||||
throw new Exception('Could not write lock file ' . $file . '.'); // @codeCoverageIgnore
|
||||
}
|
||||
// delete all file contents first
|
||||
if (rewind($handle) !== true || ftruncate($handle, 0) !== true) {
|
||||
throw new Exception('Could not write lock file ' . $file . '.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// write the new contents
|
||||
$result = fwrite($handle, $yaml);
|
||||
if (is_int($result) === false || $result === 0) {
|
||||
throw new Exception('Could not write lock file ' . $file . '.'); // @codeCoverageIgnore
|
||||
}
|
||||
// write the new contents
|
||||
$result = fwrite($handle, $yaml);
|
||||
if (is_int($result) === false || $result === 0) {
|
||||
throw new Exception('Could not write lock file ' . $file . '.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,232 +17,232 @@ use Kirby\Toolkit\Properties;
|
||||
*/
|
||||
class ContentTranslation
|
||||
{
|
||||
use Properties;
|
||||
use Properties;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $code;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $content;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $contentFile;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $contentFile;
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $slug;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $slug;
|
||||
|
||||
/**
|
||||
* Creates a new translation object
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setRequiredProperties($props, ['parent', 'code']);
|
||||
$this->setOptionalProperties($props, ['slug', 'content']);
|
||||
}
|
||||
/**
|
||||
* Creates a new translation object
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setRequiredProperties($props, ['parent', 'code']);
|
||||
$this->setOptionalProperties($props, ['slug', 'content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improve `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language code of the
|
||||
* translation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
/**
|
||||
* Returns the language code of the
|
||||
* translation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation content
|
||||
* as plain array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function content(): array
|
||||
{
|
||||
$parent = $this->parent();
|
||||
/**
|
||||
* Returns the translation content
|
||||
* as plain array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function content(): array
|
||||
{
|
||||
$parent = $this->parent();
|
||||
|
||||
if ($this->content === null) {
|
||||
$this->content = $parent->readContent($this->code());
|
||||
}
|
||||
if ($this->content === null) {
|
||||
$this->content = $parent->readContent($this->code());
|
||||
}
|
||||
|
||||
$content = $this->content;
|
||||
$content = $this->content;
|
||||
|
||||
// merge with the default content
|
||||
if ($this->isDefault() === false && $defaultLanguage = $parent->kirby()->defaultLanguage()) {
|
||||
$default = [];
|
||||
// merge with the default content
|
||||
if ($this->isDefault() === false && $defaultLanguage = $parent->kirby()->defaultLanguage()) {
|
||||
$default = [];
|
||||
|
||||
if ($defaultTranslation = $parent->translation($defaultLanguage->code())) {
|
||||
$default = $defaultTranslation->content();
|
||||
}
|
||||
if ($defaultTranslation = $parent->translation($defaultLanguage->code())) {
|
||||
$default = $defaultTranslation->content();
|
||||
}
|
||||
|
||||
$content = array_merge($default, $content);
|
||||
}
|
||||
$content = array_merge($default, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path to the translation content file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function contentFile(): string
|
||||
{
|
||||
return $this->contentFile = $this->parent->contentFile($this->code, true);
|
||||
}
|
||||
/**
|
||||
* Absolute path to the translation content file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function contentFile(): string
|
||||
{
|
||||
return $this->contentFile = $this->parent->contentFile($this->code, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation file exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return file_exists($this->contentFile()) === true;
|
||||
}
|
||||
/**
|
||||
* Checks if the translation file exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return file_exists($this->contentFile()) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation code as id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->code();
|
||||
}
|
||||
/**
|
||||
* Returns the translation code as id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->code();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the this is the default translation
|
||||
* of the model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefault(): bool
|
||||
{
|
||||
if ($defaultLanguage = $this->parent->kirby()->defaultLanguage()) {
|
||||
return $this->code() === $defaultLanguage->code();
|
||||
}
|
||||
/**
|
||||
* Checks if the this is the default translation
|
||||
* of the model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefault(): bool
|
||||
{
|
||||
if ($defaultLanguage = $this->parent->kirby()->defaultLanguage()) {
|
||||
return $this->code() === $defaultLanguage->code();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent page, file or site object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent page, file or site object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
* @return $this
|
||||
*/
|
||||
protected function setCode(string $code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $code
|
||||
* @return $this
|
||||
*/
|
||||
protected function setCode(string $code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $content
|
||||
* @return $this
|
||||
*/
|
||||
protected function setContent(array $content = null)
|
||||
{
|
||||
if ($content !== null) {
|
||||
$this->content = array_change_key_case($content);
|
||||
} else {
|
||||
$this->content = null;
|
||||
}
|
||||
/**
|
||||
* @param array|null $content
|
||||
* @return $this
|
||||
*/
|
||||
protected function setContent(array $content = null)
|
||||
{
|
||||
if ($content !== null) {
|
||||
$this->content = array_change_key_case($content);
|
||||
} else {
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return $this
|
||||
*/
|
||||
protected function setParent(Model $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return $this
|
||||
*/
|
||||
protected function setParent(Model $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $slug
|
||||
* @return $this
|
||||
*/
|
||||
protected function setSlug(string $slug = null)
|
||||
{
|
||||
$this->slug = $slug;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string|null $slug
|
||||
* @return $this
|
||||
*/
|
||||
protected function setSlug(string $slug = null)
|
||||
{
|
||||
$this->slug = $slug;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom translation slug
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function slug(): ?string
|
||||
{
|
||||
return $this->slug ??= ($this->content()['slug'] ?? null);
|
||||
}
|
||||
/**
|
||||
* Returns the custom translation slug
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function slug(): ?string
|
||||
{
|
||||
return $this->slug ??= ($this->content()['slug'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the old and new data
|
||||
*
|
||||
* @param array|null $data
|
||||
* @param bool $overwrite
|
||||
* @return $this
|
||||
*/
|
||||
public function update(array $data = null, bool $overwrite = false)
|
||||
{
|
||||
$data = array_change_key_case((array)$data);
|
||||
$this->content = $overwrite === true ? $data : array_merge($this->content(), $data);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Merge the old and new data
|
||||
*
|
||||
* @param array|null $data
|
||||
* @param bool $overwrite
|
||||
* @return $this
|
||||
*/
|
||||
public function update(array $data = null, bool $overwrite = false)
|
||||
{
|
||||
$data = array_change_key_case((array)$data);
|
||||
$this->content = $overwrite === true ? $data : array_merge($this->content(), $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the most important translation
|
||||
* props to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->code(),
|
||||
'content' => $this->content(),
|
||||
'exists' => $this->exists(),
|
||||
'slug' => $this->slug(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the most important translation
|
||||
* props to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->code(),
|
||||
'content' => $this->content(),
|
||||
'exists' => $this->exists(),
|
||||
'slug' => $this->slug(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,457 +21,457 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Core
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
*/
|
||||
public function __construct(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
$this->root = dirname(__DIR__, 2) . '/config';
|
||||
}
|
||||
/**
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
*/
|
||||
public function __construct(App $kirby)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
$this->root = dirname(__DIR__, 2) . '/config';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the definition array of a particular area.
|
||||
*
|
||||
* This is a shortcut for `$kirby->core()->load()->area()`
|
||||
* to give faster access to original area code in plugins.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function area(string $name): ?array
|
||||
{
|
||||
return $this->load()->area($name);
|
||||
}
|
||||
/**
|
||||
* Fetches the definition array of a particular area.
|
||||
*
|
||||
* This is a shortcut for `$kirby->core()->load()->area()`
|
||||
* to give faster access to original area code in plugins.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function area(string $name): ?array
|
||||
{
|
||||
return $this->load()->area($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all paths to area definition files
|
||||
*
|
||||
* They are located in `/kirby/config/areas`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function areas(): array
|
||||
{
|
||||
return [
|
||||
'account' => $this->root . '/areas/account.php',
|
||||
'installation' => $this->root . '/areas/installation.php',
|
||||
'languages' => $this->root . '/areas/languages.php',
|
||||
'login' => $this->root . '/areas/login.php',
|
||||
'site' => $this->root . '/areas/site.php',
|
||||
'system' => $this->root . '/areas/system.php',
|
||||
'users' => $this->root . '/areas/users.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all paths to area definition files
|
||||
*
|
||||
* They are located in `/kirby/config/areas`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function areas(): array
|
||||
{
|
||||
return [
|
||||
'account' => $this->root . '/areas/account.php',
|
||||
'installation' => $this->root . '/areas/installation.php',
|
||||
'languages' => $this->root . '/areas/languages.php',
|
||||
'login' => $this->root . '/areas/login.php',
|
||||
'site' => $this->root . '/areas/site.php',
|
||||
'system' => $this->root . '/areas/system.php',
|
||||
'users' => $this->root . '/areas/users.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all default auth challenge classes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authChallenges(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'Kirby\Cms\Auth\EmailChallenge'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all default auth challenge classes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authChallenges(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'Kirby\Cms\Auth\EmailChallenge'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all paths to blueprint presets
|
||||
*
|
||||
* They are located in `/kirby/config/presets`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function blueprintPresets(): array
|
||||
{
|
||||
return [
|
||||
'pages' => $this->root . '/presets/pages.php',
|
||||
'page' => $this->root . '/presets/page.php',
|
||||
'files' => $this->root . '/presets/files.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all paths to blueprint presets
|
||||
*
|
||||
* They are located in `/kirby/config/presets`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function blueprintPresets(): array
|
||||
{
|
||||
return [
|
||||
'pages' => $this->root . '/presets/pages.php',
|
||||
'page' => $this->root . '/presets/page.php',
|
||||
'files' => $this->root . '/presets/files.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all paths to core blueprints
|
||||
*
|
||||
* They are located in `/kirby/config/blueprints`.
|
||||
* Block blueprints are located in `/kirby/config/blocks`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function blueprints(): array
|
||||
{
|
||||
return [
|
||||
// blocks
|
||||
'blocks/code' => $this->root . '/blocks/code/code.yml',
|
||||
'blocks/gallery' => $this->root . '/blocks/gallery/gallery.yml',
|
||||
'blocks/heading' => $this->root . '/blocks/heading/heading.yml',
|
||||
'blocks/image' => $this->root . '/blocks/image/image.yml',
|
||||
'blocks/line' => $this->root . '/blocks/line/line.yml',
|
||||
'blocks/list' => $this->root . '/blocks/list/list.yml',
|
||||
'blocks/markdown' => $this->root . '/blocks/markdown/markdown.yml',
|
||||
'blocks/quote' => $this->root . '/blocks/quote/quote.yml',
|
||||
'blocks/table' => $this->root . '/blocks/table/table.yml',
|
||||
'blocks/text' => $this->root . '/blocks/text/text.yml',
|
||||
'blocks/video' => $this->root . '/blocks/video/video.yml',
|
||||
/**
|
||||
* Returns a list of all paths to core blueprints
|
||||
*
|
||||
* They are located in `/kirby/config/blueprints`.
|
||||
* Block blueprints are located in `/kirby/config/blocks`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function blueprints(): array
|
||||
{
|
||||
return [
|
||||
// blocks
|
||||
'blocks/code' => $this->root . '/blocks/code/code.yml',
|
||||
'blocks/gallery' => $this->root . '/blocks/gallery/gallery.yml',
|
||||
'blocks/heading' => $this->root . '/blocks/heading/heading.yml',
|
||||
'blocks/image' => $this->root . '/blocks/image/image.yml',
|
||||
'blocks/line' => $this->root . '/blocks/line/line.yml',
|
||||
'blocks/list' => $this->root . '/blocks/list/list.yml',
|
||||
'blocks/markdown' => $this->root . '/blocks/markdown/markdown.yml',
|
||||
'blocks/quote' => $this->root . '/blocks/quote/quote.yml',
|
||||
'blocks/table' => $this->root . '/blocks/table/table.yml',
|
||||
'blocks/text' => $this->root . '/blocks/text/text.yml',
|
||||
'blocks/video' => $this->root . '/blocks/video/video.yml',
|
||||
|
||||
// file blueprints
|
||||
'files/default' => $this->root . '/blueprints/files/default.yml',
|
||||
// file blueprints
|
||||
'files/default' => $this->root . '/blueprints/files/default.yml',
|
||||
|
||||
// page blueprints
|
||||
'pages/default' => $this->root . '/blueprints/pages/default.yml',
|
||||
// page blueprints
|
||||
'pages/default' => $this->root . '/blueprints/pages/default.yml',
|
||||
|
||||
// site blueprints
|
||||
'site' => $this->root . '/blueprints/site.yml'
|
||||
];
|
||||
}
|
||||
// site blueprints
|
||||
'site' => $this->root . '/blueprints/site.yml'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all cache driver classes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function cacheTypes(): array
|
||||
{
|
||||
return [
|
||||
'apcu' => 'Kirby\Cache\ApcuCache',
|
||||
'file' => 'Kirby\Cache\FileCache',
|
||||
'memcached' => 'Kirby\Cache\MemCached',
|
||||
'memory' => 'Kirby\Cache\MemoryCache',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all cache driver classes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function cacheTypes(): array
|
||||
{
|
||||
return [
|
||||
'apcu' => 'Kirby\Cache\ApcuCache',
|
||||
'file' => 'Kirby\Cache\FileCache',
|
||||
'memcached' => 'Kirby\Cache\MemCached',
|
||||
'memory' => 'Kirby\Cache\MemoryCache',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all core component functions
|
||||
*
|
||||
* The component functions can be found in
|
||||
* `/kirby/config/components.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function components(): array
|
||||
{
|
||||
return $this->cache['components'] ??= include $this->root . '/components.php';
|
||||
}
|
||||
/**
|
||||
* Returns an array of all core component functions
|
||||
*
|
||||
* The component functions can be found in
|
||||
* `/kirby/config/components.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function components(): array
|
||||
{
|
||||
return $this->cache['components'] ??= include $this->root . '/components.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all field method aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMethodAliases(): array
|
||||
{
|
||||
return [
|
||||
'bool' => 'toBool',
|
||||
'esc' => 'escape',
|
||||
'excerpt' => 'toExcerpt',
|
||||
'float' => 'toFloat',
|
||||
'h' => 'html',
|
||||
'int' => 'toInt',
|
||||
'kt' => 'kirbytext',
|
||||
'kti' => 'kirbytextinline',
|
||||
'link' => 'toLink',
|
||||
'md' => 'markdown',
|
||||
'sp' => 'smartypants',
|
||||
'v' => 'isValid',
|
||||
'x' => 'xml'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a map of all field method aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMethodAliases(): array
|
||||
{
|
||||
return [
|
||||
'bool' => 'toBool',
|
||||
'esc' => 'escape',
|
||||
'excerpt' => 'toExcerpt',
|
||||
'float' => 'toFloat',
|
||||
'h' => 'html',
|
||||
'int' => 'toInt',
|
||||
'kt' => 'kirbytext',
|
||||
'kti' => 'kirbytextinline',
|
||||
'link' => 'toLink',
|
||||
'md' => 'markdown',
|
||||
'sp' => 'smartypants',
|
||||
'v' => 'isValid',
|
||||
'x' => 'xml'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all field method functions
|
||||
*
|
||||
* Field methods are stored in `/kirby/config/methods.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMethods(): array
|
||||
{
|
||||
return $this->cache['fieldMethods'] ??= (include $this->root . '/methods.php')($this->kirby);
|
||||
}
|
||||
/**
|
||||
* Returns an array of all field method functions
|
||||
*
|
||||
* Field methods are stored in `/kirby/config/methods.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMethods(): array
|
||||
{
|
||||
return $this->cache['fieldMethods'] ??= (include $this->root . '/methods.php')($this->kirby);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of paths for field mixins
|
||||
*
|
||||
* They are located in `/kirby/config/fields/mixins`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMixins(): array
|
||||
{
|
||||
return [
|
||||
'datetime' => $this->root . '/fields/mixins/datetime.php',
|
||||
'filepicker' => $this->root . '/fields/mixins/filepicker.php',
|
||||
'layout' => $this->root . '/fields/mixins/layout.php',
|
||||
'min' => $this->root . '/fields/mixins/min.php',
|
||||
'options' => $this->root . '/fields/mixins/options.php',
|
||||
'pagepicker' => $this->root . '/fields/mixins/pagepicker.php',
|
||||
'picker' => $this->root . '/fields/mixins/picker.php',
|
||||
'upload' => $this->root . '/fields/mixins/upload.php',
|
||||
'userpicker' => $this->root . '/fields/mixins/userpicker.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns an array of paths for field mixins
|
||||
*
|
||||
* They are located in `/kirby/config/fields/mixins`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldMixins(): array
|
||||
{
|
||||
return [
|
||||
'datetime' => $this->root . '/fields/mixins/datetime.php',
|
||||
'filepicker' => $this->root . '/fields/mixins/filepicker.php',
|
||||
'layout' => $this->root . '/fields/mixins/layout.php',
|
||||
'min' => $this->root . '/fields/mixins/min.php',
|
||||
'options' => $this->root . '/fields/mixins/options.php',
|
||||
'pagepicker' => $this->root . '/fields/mixins/pagepicker.php',
|
||||
'picker' => $this->root . '/fields/mixins/picker.php',
|
||||
'upload' => $this->root . '/fields/mixins/upload.php',
|
||||
'userpicker' => $this->root . '/fields/mixins/userpicker.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all paths and class names of panel fields
|
||||
*
|
||||
* Traditional panel fields are located in `/kirby/config/fields`
|
||||
*
|
||||
* The more complex field classes can be found in
|
||||
* `/kirby/src/Form/Fields`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
'blocks' => 'Kirby\Form\Field\BlocksField',
|
||||
'checkboxes' => $this->root . '/fields/checkboxes.php',
|
||||
'date' => $this->root . '/fields/date.php',
|
||||
'email' => $this->root . '/fields/email.php',
|
||||
'files' => $this->root . '/fields/files.php',
|
||||
'gap' => $this->root . '/fields/gap.php',
|
||||
'headline' => $this->root . '/fields/headline.php',
|
||||
'hidden' => $this->root . '/fields/hidden.php',
|
||||
'info' => $this->root . '/fields/info.php',
|
||||
'layout' => 'Kirby\Form\Field\LayoutField',
|
||||
'line' => $this->root . '/fields/line.php',
|
||||
'list' => $this->root . '/fields/list.php',
|
||||
'multiselect' => $this->root . '/fields/multiselect.php',
|
||||
'number' => $this->root . '/fields/number.php',
|
||||
'pages' => $this->root . '/fields/pages.php',
|
||||
'radio' => $this->root . '/fields/radio.php',
|
||||
'range' => $this->root . '/fields/range.php',
|
||||
'select' => $this->root . '/fields/select.php',
|
||||
'slug' => $this->root . '/fields/slug.php',
|
||||
'structure' => $this->root . '/fields/structure.php',
|
||||
'tags' => $this->root . '/fields/tags.php',
|
||||
'tel' => $this->root . '/fields/tel.php',
|
||||
'text' => $this->root . '/fields/text.php',
|
||||
'textarea' => $this->root . '/fields/textarea.php',
|
||||
'time' => $this->root . '/fields/time.php',
|
||||
'toggle' => $this->root . '/fields/toggle.php',
|
||||
'toggles' => $this->root . '/fields/toggles.php',
|
||||
'url' => $this->root . '/fields/url.php',
|
||||
'users' => $this->root . '/fields/users.php',
|
||||
'writer' => $this->root . '/fields/writer.php'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns an array of all paths and class names of panel fields
|
||||
*
|
||||
* Traditional panel fields are located in `/kirby/config/fields`
|
||||
*
|
||||
* The more complex field classes can be found in
|
||||
* `/kirby/src/Form/Fields`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
'blocks' => 'Kirby\Form\Field\BlocksField',
|
||||
'checkboxes' => $this->root . '/fields/checkboxes.php',
|
||||
'date' => $this->root . '/fields/date.php',
|
||||
'email' => $this->root . '/fields/email.php',
|
||||
'files' => $this->root . '/fields/files.php',
|
||||
'gap' => $this->root . '/fields/gap.php',
|
||||
'headline' => $this->root . '/fields/headline.php',
|
||||
'hidden' => $this->root . '/fields/hidden.php',
|
||||
'info' => $this->root . '/fields/info.php',
|
||||
'layout' => 'Kirby\Form\Field\LayoutField',
|
||||
'line' => $this->root . '/fields/line.php',
|
||||
'list' => $this->root . '/fields/list.php',
|
||||
'multiselect' => $this->root . '/fields/multiselect.php',
|
||||
'number' => $this->root . '/fields/number.php',
|
||||
'pages' => $this->root . '/fields/pages.php',
|
||||
'radio' => $this->root . '/fields/radio.php',
|
||||
'range' => $this->root . '/fields/range.php',
|
||||
'select' => $this->root . '/fields/select.php',
|
||||
'slug' => $this->root . '/fields/slug.php',
|
||||
'structure' => $this->root . '/fields/structure.php',
|
||||
'tags' => $this->root . '/fields/tags.php',
|
||||
'tel' => $this->root . '/fields/tel.php',
|
||||
'text' => $this->root . '/fields/text.php',
|
||||
'textarea' => $this->root . '/fields/textarea.php',
|
||||
'time' => $this->root . '/fields/time.php',
|
||||
'toggle' => $this->root . '/fields/toggle.php',
|
||||
'toggles' => $this->root . '/fields/toggles.php',
|
||||
'url' => $this->root . '/fields/url.php',
|
||||
'users' => $this->root . '/fields/users.php',
|
||||
'writer' => $this->root . '/fields/writer.php'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all kirbytag aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function kirbyTagAliases(): array
|
||||
{
|
||||
return [
|
||||
'youtube' => 'video',
|
||||
'vimeo' => 'video'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a map of all kirbytag aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function kirbyTagAliases(): array
|
||||
{
|
||||
return [
|
||||
'youtube' => 'video',
|
||||
'vimeo' => 'video'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all kirbytag definitions
|
||||
*
|
||||
* They are located in `/kirby/config/tags.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function kirbyTags(): array
|
||||
{
|
||||
return $this->cache['kirbytags'] ??= include $this->root . '/tags.php';
|
||||
}
|
||||
/**
|
||||
* Returns an array of all kirbytag definitions
|
||||
*
|
||||
* They are located in `/kirby/config/tags.php`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function kirbyTags(): array
|
||||
{
|
||||
return $this->cache['kirbytags'] ??= include $this->root . '/tags.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a core part of Kirby
|
||||
*
|
||||
* The loader is set to not include plugins.
|
||||
* This way, you can access original Kirby core code
|
||||
* through this load method.
|
||||
*
|
||||
* @return \Kirby\Cms\Loader
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
return new Loader($this->kirby, false);
|
||||
}
|
||||
/**
|
||||
* Loads a core part of Kirby
|
||||
*
|
||||
* The loader is set to not include plugins.
|
||||
* This way, you can access original Kirby core code
|
||||
* through this load method.
|
||||
*
|
||||
* @return \Kirby\Cms\Loader
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
return new Loader($this->kirby, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all absolute paths to important directories
|
||||
*
|
||||
* Roots are resolved and baked in `\Kirby\Cms\App::bakeRoots()`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function roots(): array
|
||||
{
|
||||
return $this->cache['roots'] ??= [
|
||||
'kirby' => fn (array $roots) => dirname(__DIR__, 2),
|
||||
'i18n' => fn (array $roots) => $roots['kirby'] . '/i18n',
|
||||
'i18n:translations' => fn (array $roots) => $roots['i18n'] . '/translations',
|
||||
'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules',
|
||||
/**
|
||||
* Returns all absolute paths to important directories
|
||||
*
|
||||
* Roots are resolved and baked in `\Kirby\Cms\App::bakeRoots()`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function roots(): array
|
||||
{
|
||||
return $this->cache['roots'] ??= [
|
||||
'kirby' => fn (array $roots) => dirname(__DIR__, 2),
|
||||
'i18n' => fn (array $roots) => $roots['kirby'] . '/i18n',
|
||||
'i18n:translations' => fn (array $roots) => $roots['i18n'] . '/translations',
|
||||
'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules',
|
||||
|
||||
'index' => fn (array $roots) => dirname(__DIR__, 3),
|
||||
'assets' => fn (array $roots) => $roots['index'] . '/assets',
|
||||
'content' => fn (array $roots) => $roots['index'] . '/content',
|
||||
'media' => fn (array $roots) => $roots['index'] . '/media',
|
||||
'panel' => fn (array $roots) => $roots['kirby'] . '/panel',
|
||||
'site' => fn (array $roots) => $roots['index'] . '/site',
|
||||
'accounts' => fn (array $roots) => $roots['site'] . '/accounts',
|
||||
'blueprints' => fn (array $roots) => $roots['site'] . '/blueprints',
|
||||
'cache' => fn (array $roots) => $roots['site'] . '/cache',
|
||||
'collections' => fn (array $roots) => $roots['site'] . '/collections',
|
||||
'config' => fn (array $roots) => $roots['site'] . '/config',
|
||||
'controllers' => fn (array $roots) => $roots['site'] . '/controllers',
|
||||
'languages' => fn (array $roots) => $roots['site'] . '/languages',
|
||||
'license' => fn (array $roots) => $roots['config'] . '/.license',
|
||||
'logs' => fn (array $roots) => $roots['site'] . '/logs',
|
||||
'models' => fn (array $roots) => $roots['site'] . '/models',
|
||||
'plugins' => fn (array $roots) => $roots['site'] . '/plugins',
|
||||
'sessions' => fn (array $roots) => $roots['site'] . '/sessions',
|
||||
'snippets' => fn (array $roots) => $roots['site'] . '/snippets',
|
||||
'templates' => fn (array $roots) => $roots['site'] . '/templates',
|
||||
'roles' => fn (array $roots) => $roots['blueprints'] . '/users',
|
||||
];
|
||||
}
|
||||
'index' => fn (array $roots) => dirname(__DIR__, 3),
|
||||
'assets' => fn (array $roots) => $roots['index'] . '/assets',
|
||||
'content' => fn (array $roots) => $roots['index'] . '/content',
|
||||
'media' => fn (array $roots) => $roots['index'] . '/media',
|
||||
'panel' => fn (array $roots) => $roots['kirby'] . '/panel',
|
||||
'site' => fn (array $roots) => $roots['index'] . '/site',
|
||||
'accounts' => fn (array $roots) => $roots['site'] . '/accounts',
|
||||
'blueprints' => fn (array $roots) => $roots['site'] . '/blueprints',
|
||||
'cache' => fn (array $roots) => $roots['site'] . '/cache',
|
||||
'collections' => fn (array $roots) => $roots['site'] . '/collections',
|
||||
'config' => fn (array $roots) => $roots['site'] . '/config',
|
||||
'controllers' => fn (array $roots) => $roots['site'] . '/controllers',
|
||||
'languages' => fn (array $roots) => $roots['site'] . '/languages',
|
||||
'license' => fn (array $roots) => $roots['config'] . '/.license',
|
||||
'logs' => fn (array $roots) => $roots['site'] . '/logs',
|
||||
'models' => fn (array $roots) => $roots['site'] . '/models',
|
||||
'plugins' => fn (array $roots) => $roots['site'] . '/plugins',
|
||||
'sessions' => fn (array $roots) => $roots['site'] . '/sessions',
|
||||
'snippets' => fn (array $roots) => $roots['site'] . '/snippets',
|
||||
'templates' => fn (array $roots) => $roots['site'] . '/templates',
|
||||
'roles' => fn (array $roots) => $roots['blueprints'] . '/users',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all routes for Kirby’s router
|
||||
*
|
||||
* Routes are split into `before` and `after` routes.
|
||||
*
|
||||
* Plugin routes will be injected inbetween.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function routes(): array
|
||||
{
|
||||
return $this->cache['routes'] ??= (include $this->root . '/routes.php')($this->kirby);
|
||||
}
|
||||
/**
|
||||
* Returns an array of all routes for Kirby’s router
|
||||
*
|
||||
* Routes are split into `before` and `after` routes.
|
||||
*
|
||||
* Plugin routes will be injected inbetween.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function routes(): array
|
||||
{
|
||||
return $this->cache['routes'] ??= (include $this->root . '/routes.php')($this->kirby);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all paths to core block snippets
|
||||
*
|
||||
* They are located in `/kirby/config/blocks`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function snippets(): array
|
||||
{
|
||||
return [
|
||||
'blocks/code' => $this->root . '/blocks/code/code.php',
|
||||
'blocks/gallery' => $this->root . '/blocks/gallery/gallery.php',
|
||||
'blocks/heading' => $this->root . '/blocks/heading/heading.php',
|
||||
'blocks/image' => $this->root . '/blocks/image/image.php',
|
||||
'blocks/line' => $this->root . '/blocks/line/line.php',
|
||||
'blocks/list' => $this->root . '/blocks/list/list.php',
|
||||
'blocks/markdown' => $this->root . '/blocks/markdown/markdown.php',
|
||||
'blocks/quote' => $this->root . '/blocks/quote/quote.php',
|
||||
'blocks/table' => $this->root . '/blocks/table/table.php',
|
||||
'blocks/text' => $this->root . '/blocks/text/text.php',
|
||||
'blocks/video' => $this->root . '/blocks/video/video.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all paths to core block snippets
|
||||
*
|
||||
* They are located in `/kirby/config/blocks`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function snippets(): array
|
||||
{
|
||||
return [
|
||||
'blocks/code' => $this->root . '/blocks/code/code.php',
|
||||
'blocks/gallery' => $this->root . '/blocks/gallery/gallery.php',
|
||||
'blocks/heading' => $this->root . '/blocks/heading/heading.php',
|
||||
'blocks/image' => $this->root . '/blocks/image/image.php',
|
||||
'blocks/line' => $this->root . '/blocks/line/line.php',
|
||||
'blocks/list' => $this->root . '/blocks/list/list.php',
|
||||
'blocks/markdown' => $this->root . '/blocks/markdown/markdown.php',
|
||||
'blocks/quote' => $this->root . '/blocks/quote/quote.php',
|
||||
'blocks/table' => $this->root . '/blocks/table/table.php',
|
||||
'blocks/text' => $this->root . '/blocks/text/text.php',
|
||||
'blocks/video' => $this->root . '/blocks/video/video.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paths to section mixins
|
||||
*
|
||||
* They are located in `/kirby/config/sections/mixins`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sectionMixins(): array
|
||||
{
|
||||
return [
|
||||
'details' => $this->root . '/sections/mixins/details.php',
|
||||
'empty' => $this->root . '/sections/mixins/empty.php',
|
||||
'headline' => $this->root . '/sections/mixins/headline.php',
|
||||
'help' => $this->root . '/sections/mixins/help.php',
|
||||
'layout' => $this->root . '/sections/mixins/layout.php',
|
||||
'max' => $this->root . '/sections/mixins/max.php',
|
||||
'min' => $this->root . '/sections/mixins/min.php',
|
||||
'pagination' => $this->root . '/sections/mixins/pagination.php',
|
||||
'parent' => $this->root . '/sections/mixins/parent.php',
|
||||
'search' => $this->root . '/sections/mixins/search.php',
|
||||
'sort' => $this->root . '/sections/mixins/sort.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of paths to section mixins
|
||||
*
|
||||
* They are located in `/kirby/config/sections/mixins`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sectionMixins(): array
|
||||
{
|
||||
return [
|
||||
'details' => $this->root . '/sections/mixins/details.php',
|
||||
'empty' => $this->root . '/sections/mixins/empty.php',
|
||||
'headline' => $this->root . '/sections/mixins/headline.php',
|
||||
'help' => $this->root . '/sections/mixins/help.php',
|
||||
'layout' => $this->root . '/sections/mixins/layout.php',
|
||||
'max' => $this->root . '/sections/mixins/max.php',
|
||||
'min' => $this->root . '/sections/mixins/min.php',
|
||||
'pagination' => $this->root . '/sections/mixins/pagination.php',
|
||||
'parent' => $this->root . '/sections/mixins/parent.php',
|
||||
'search' => $this->root . '/sections/mixins/search.php',
|
||||
'sort' => $this->root . '/sections/mixins/sort.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all section definitions
|
||||
*
|
||||
* They are located in `/kirby/config/sections`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sections(): array
|
||||
{
|
||||
return [
|
||||
'fields' => $this->root . '/sections/fields.php',
|
||||
'files' => $this->root . '/sections/files.php',
|
||||
'info' => $this->root . '/sections/info.php',
|
||||
'pages' => $this->root . '/sections/pages.php',
|
||||
'stats' => $this->root . '/sections/stats.php',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of all section definitions
|
||||
*
|
||||
* They are located in `/kirby/config/sections`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sections(): array
|
||||
{
|
||||
return [
|
||||
'fields' => $this->root . '/sections/fields.php',
|
||||
'files' => $this->root . '/sections/files.php',
|
||||
'info' => $this->root . '/sections/info.php',
|
||||
'pages' => $this->root . '/sections/pages.php',
|
||||
'stats' => $this->root . '/sections/stats.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paths to all system templates
|
||||
*
|
||||
* They are located in `/kirby/config/templates`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templates(): array
|
||||
{
|
||||
return [
|
||||
'emails/auth/login' => $this->root . '/templates/emails/auth/login.php',
|
||||
'emails/auth/password-reset' => $this->root . '/templates/emails/auth/password-reset.php'
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns a list of paths to all system templates
|
||||
*
|
||||
* They are located in `/kirby/config/templates`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templates(): array
|
||||
{
|
||||
return [
|
||||
'emails/auth/login' => $this->root . '/templates/emails/auth/login.php',
|
||||
'emails/auth/password-reset' => $this->root . '/templates/emails/auth/password-reset.php'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all system URLs
|
||||
*
|
||||
* URLs are resolved and baked in `\Kirby\Cms\App::bakeUrls()`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function urls(): array
|
||||
{
|
||||
return $this->cache['urls'] ??= [
|
||||
'index' => fn () => $this->kirby->environment()->baseUrl(),
|
||||
'base' => fn (array $urls) => rtrim($urls['index'], '/'),
|
||||
'current' => function (array $urls) {
|
||||
$path = trim($this->kirby->path(), '/');
|
||||
/**
|
||||
* Returns an array with all system URLs
|
||||
*
|
||||
* URLs are resolved and baked in `\Kirby\Cms\App::bakeUrls()`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function urls(): array
|
||||
{
|
||||
return $this->cache['urls'] ??= [
|
||||
'index' => fn () => $this->kirby->environment()->baseUrl(),
|
||||
'base' => fn (array $urls) => rtrim($urls['index'], '/'),
|
||||
'current' => function (array $urls) {
|
||||
$path = trim($this->kirby->path(), '/');
|
||||
|
||||
if (empty($path) === true) {
|
||||
return $urls['index'];
|
||||
} else {
|
||||
return $urls['base'] . '/' . $path;
|
||||
}
|
||||
},
|
||||
'assets' => fn (array $urls) => $urls['base'] . '/assets',
|
||||
'api' => fn (array $urls) => $urls['base'] . '/' . $this->kirby->option('api.slug', 'api'),
|
||||
'media' => fn (array $urls) => $urls['base'] . '/media',
|
||||
'panel' => fn (array $urls) => $urls['base'] . '/' . $this->kirby->option('panel.slug', 'panel')
|
||||
];
|
||||
}
|
||||
if (empty($path) === true) {
|
||||
return $urls['index'];
|
||||
} else {
|
||||
return $urls['base'] . '/' . $path;
|
||||
}
|
||||
},
|
||||
'assets' => fn (array $urls) => $urls['base'] . '/assets',
|
||||
'api' => fn (array $urls) => $urls['base'] . '/' . $this->kirby->option('api.slug', 'api'),
|
||||
'media' => fn (array $urls) => $urls['base'] . '/media',
|
||||
'panel' => fn (array $urls) => $urls['base'] . '/' . $this->kirby->option('panel.slug', 'panel')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,237 +19,237 @@ use Kirby\Exception\NotFoundException;
|
||||
*/
|
||||
class Email
|
||||
{
|
||||
/**
|
||||
* Options configured through the `email` CMS option
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
/**
|
||||
* Options configured through the `email` CMS option
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Props for the email object; will be passed to the
|
||||
* Kirby\Email\Email class
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $props;
|
||||
/**
|
||||
* Props for the email object; will be passed to the
|
||||
* Kirby\Email\Email class
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $props;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string|array $preset Preset name from the config or a simple props array
|
||||
* @param array $props Props array to override the $preset
|
||||
*/
|
||||
public function __construct($preset = [], array $props = [])
|
||||
{
|
||||
$this->options = App::instance()->option('email');
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string|array $preset Preset name from the config or a simple props array
|
||||
* @param array $props Props array to override the $preset
|
||||
*/
|
||||
public function __construct($preset = [], array $props = [])
|
||||
{
|
||||
$this->options = App::instance()->option('email');
|
||||
|
||||
// build a prop array based on preset and props
|
||||
$preset = $this->preset($preset);
|
||||
$this->props = array_merge($preset, $props);
|
||||
// build a prop array based on preset and props
|
||||
$preset = $this->preset($preset);
|
||||
$this->props = array_merge($preset, $props);
|
||||
|
||||
// add transport settings
|
||||
if (isset($this->props['transport']) === false) {
|
||||
$this->props['transport'] = $this->options['transport'] ?? [];
|
||||
}
|
||||
// add transport settings
|
||||
if (isset($this->props['transport']) === false) {
|
||||
$this->props['transport'] = $this->options['transport'] ?? [];
|
||||
}
|
||||
|
||||
// add predefined beforeSend option
|
||||
if (isset($this->props['beforeSend']) === false) {
|
||||
$this->props['beforeSend'] = $this->options['beforeSend'] ?? null;
|
||||
}
|
||||
// add predefined beforeSend option
|
||||
if (isset($this->props['beforeSend']) === false) {
|
||||
$this->props['beforeSend'] = $this->options['beforeSend'] ?? null;
|
||||
}
|
||||
|
||||
// transform model objects to values
|
||||
$this->transformUserSingle('from', 'fromName');
|
||||
$this->transformUserSingle('replyTo', 'replyToName');
|
||||
$this->transformUserMultiple('to');
|
||||
$this->transformUserMultiple('cc');
|
||||
$this->transformUserMultiple('bcc');
|
||||
$this->transformFile('attachments');
|
||||
// transform model objects to values
|
||||
$this->transformUserSingle('from', 'fromName');
|
||||
$this->transformUserSingle('replyTo', 'replyToName');
|
||||
$this->transformUserMultiple('to');
|
||||
$this->transformUserMultiple('cc');
|
||||
$this->transformUserMultiple('bcc');
|
||||
$this->transformFile('attachments');
|
||||
|
||||
// load template for body text
|
||||
$this->template();
|
||||
}
|
||||
// load template for body text
|
||||
$this->template();
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs a preset from the options; supports fixed
|
||||
* prop arrays in case a preset is not needed
|
||||
*
|
||||
* @param string|array $preset Preset name or simple prop array
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
protected function preset($preset): array
|
||||
{
|
||||
// only passed props, not preset name
|
||||
if (is_array($preset) === true) {
|
||||
return $preset;
|
||||
}
|
||||
/**
|
||||
* Grabs a preset from the options; supports fixed
|
||||
* prop arrays in case a preset is not needed
|
||||
*
|
||||
* @param string|array $preset Preset name or simple prop array
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
protected function preset($preset): array
|
||||
{
|
||||
// only passed props, not preset name
|
||||
if (is_array($preset) === true) {
|
||||
return $preset;
|
||||
}
|
||||
|
||||
// preset does not exist
|
||||
if (isset($this->options['presets'][$preset]) !== true) {
|
||||
throw new NotFoundException([
|
||||
'key' => 'email.preset.notFound',
|
||||
'data' => ['name' => $preset]
|
||||
]);
|
||||
}
|
||||
// preset does not exist
|
||||
if (isset($this->options['presets'][$preset]) !== true) {
|
||||
throw new NotFoundException([
|
||||
'key' => 'email.preset.notFound',
|
||||
'data' => ['name' => $preset]
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->options['presets'][$preset];
|
||||
}
|
||||
return $this->options['presets'][$preset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the email template(s) and sets the body props
|
||||
* to the result
|
||||
*
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
protected function template(): void
|
||||
{
|
||||
if (isset($this->props['template']) === true) {
|
||||
/**
|
||||
* Renders the email template(s) and sets the body props
|
||||
* to the result
|
||||
*
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
protected function template(): void
|
||||
{
|
||||
if (isset($this->props['template']) === true) {
|
||||
|
||||
// prepare data to be passed to template
|
||||
$data = $this->props['data'] ?? [];
|
||||
// prepare data to be passed to template
|
||||
$data = $this->props['data'] ?? [];
|
||||
|
||||
// check if html/text templates exist
|
||||
$html = $this->getTemplate($this->props['template'], 'html');
|
||||
$text = $this->getTemplate($this->props['template'], 'text');
|
||||
// check if html/text templates exist
|
||||
$html = $this->getTemplate($this->props['template'], 'html');
|
||||
$text = $this->getTemplate($this->props['template'], 'text');
|
||||
|
||||
if ($html->exists()) {
|
||||
$this->props['body'] = [
|
||||
'html' => $html->render($data)
|
||||
];
|
||||
if ($html->exists()) {
|
||||
$this->props['body'] = [
|
||||
'html' => $html->render($data)
|
||||
];
|
||||
|
||||
if ($text->exists()) {
|
||||
$this->props['body']['text'] = $text->render($data);
|
||||
}
|
||||
if ($text->exists()) {
|
||||
$this->props['body']['text'] = $text->render($data);
|
||||
}
|
||||
|
||||
// fallback to single email text template
|
||||
} elseif ($text->exists()) {
|
||||
$this->props['body'] = $text->render($data);
|
||||
} else {
|
||||
throw new NotFoundException('The email template "' . $this->props['template'] . '" cannot be found');
|
||||
}
|
||||
}
|
||||
}
|
||||
// fallback to single email text template
|
||||
} elseif ($text->exists()) {
|
||||
$this->props['body'] = $text->render($data);
|
||||
} else {
|
||||
throw new NotFoundException('The email template "' . $this->props['template'] . '" cannot be found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an email template by name and type
|
||||
*
|
||||
* @param string $name Template name
|
||||
* @param string|null $type `html` or `text`
|
||||
* @return \Kirby\Cms\Template
|
||||
*/
|
||||
protected function getTemplate(string $name, string $type = null)
|
||||
{
|
||||
return App::instance()->template('emails/' . $name, $type, 'text');
|
||||
}
|
||||
/**
|
||||
* Returns an email template by name and type
|
||||
*
|
||||
* @param string $name Template name
|
||||
* @param string|null $type `html` or `text`
|
||||
* @return \Kirby\Cms\Template
|
||||
*/
|
||||
protected function getTemplate(string $name, string $type = null)
|
||||
{
|
||||
return App::instance()->template('emails/' . $name, $type, 'text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the prop array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->props;
|
||||
}
|
||||
/**
|
||||
* Returns the prop array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms file object(s) to an array of file roots;
|
||||
* supports simple strings, file objects or collections/arrays of either
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @return void
|
||||
*/
|
||||
protected function transformFile(string $prop): void
|
||||
{
|
||||
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\File', 'root');
|
||||
}
|
||||
/**
|
||||
* Transforms file object(s) to an array of file roots;
|
||||
* supports simple strings, file objects or collections/arrays of either
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @return void
|
||||
*/
|
||||
protected function transformFile(string $prop): void
|
||||
{
|
||||
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\File', 'root');
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms Kirby models to a simplified collection
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @param string $class Fully qualified class name of the supported model
|
||||
* @param string $contentValue Model method that returns the array value
|
||||
* @param string|null $contentKey Optional model method that returns the array key;
|
||||
* returns a simple value-only array if not given
|
||||
* @return array Simple key-value or just value array with the transformed prop data
|
||||
*/
|
||||
protected function transformModel(string $prop, string $class, string $contentValue, string $contentKey = null): array
|
||||
{
|
||||
$value = $this->props[$prop] ?? [];
|
||||
/**
|
||||
* Transforms Kirby models to a simplified collection
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @param string $class Fully qualified class name of the supported model
|
||||
* @param string $contentValue Model method that returns the array value
|
||||
* @param string|null $contentKey Optional model method that returns the array key;
|
||||
* returns a simple value-only array if not given
|
||||
* @return array Simple key-value or just value array with the transformed prop data
|
||||
*/
|
||||
protected function transformModel(string $prop, string $class, string $contentValue, string $contentKey = null): array
|
||||
{
|
||||
$value = $this->props[$prop] ?? [];
|
||||
|
||||
// ensure consistent input by making everything an iterable value
|
||||
if (is_iterable($value) !== true) {
|
||||
$value = [$value];
|
||||
}
|
||||
// ensure consistent input by making everything an iterable value
|
||||
if (is_iterable($value) !== true) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($value as $key => $item) {
|
||||
if (is_string($item) === true) {
|
||||
// value is already a string
|
||||
if ($contentKey !== null && is_string($key) === true) {
|
||||
$result[$key] = $item;
|
||||
} else {
|
||||
$result[] = $item;
|
||||
}
|
||||
} elseif (is_a($item, $class) === true) {
|
||||
// value is a model object, get value through content method(s)
|
||||
if ($contentKey !== null) {
|
||||
$result[(string)$item->$contentKey()] = (string)$item->$contentValue();
|
||||
} else {
|
||||
$result[] = (string)$item->$contentValue();
|
||||
}
|
||||
} else {
|
||||
// invalid input
|
||||
throw new InvalidArgumentException('Invalid input for prop "' . $prop . '", expected string or "' . $class . '" object or collection');
|
||||
}
|
||||
}
|
||||
$result = [];
|
||||
foreach ($value as $key => $item) {
|
||||
if (is_string($item) === true) {
|
||||
// value is already a string
|
||||
if ($contentKey !== null && is_string($key) === true) {
|
||||
$result[$key] = $item;
|
||||
} else {
|
||||
$result[] = $item;
|
||||
}
|
||||
} elseif (is_a($item, $class) === true) {
|
||||
// value is a model object, get value through content method(s)
|
||||
if ($contentKey !== null) {
|
||||
$result[(string)$item->$contentKey()] = (string)$item->$contentValue();
|
||||
} else {
|
||||
$result[] = (string)$item->$contentValue();
|
||||
}
|
||||
} else {
|
||||
// invalid input
|
||||
throw new InvalidArgumentException('Invalid input for prop "' . $prop . '", expected string or "' . $class . '" object or collection');
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an user object to the email address and name;
|
||||
* supports simple strings, user objects or collections/arrays of either
|
||||
* (note: only the first item in a collection/array will be used)
|
||||
*
|
||||
* @param string $addressProp Prop with the email address
|
||||
* @param string $nameProp Prop with the name corresponding to the $addressProp
|
||||
* @return void
|
||||
*/
|
||||
protected function transformUserSingle(string $addressProp, string $nameProp): void
|
||||
{
|
||||
$result = $this->transformModel($addressProp, 'Kirby\Cms\User', 'name', 'email');
|
||||
/**
|
||||
* Transforms an user object to the email address and name;
|
||||
* supports simple strings, user objects or collections/arrays of either
|
||||
* (note: only the first item in a collection/array will be used)
|
||||
*
|
||||
* @param string $addressProp Prop with the email address
|
||||
* @param string $nameProp Prop with the name corresponding to the $addressProp
|
||||
* @return void
|
||||
*/
|
||||
protected function transformUserSingle(string $addressProp, string $nameProp): void
|
||||
{
|
||||
$result = $this->transformModel($addressProp, 'Kirby\Cms\User', 'name', 'email');
|
||||
|
||||
$address = array_keys($result)[0] ?? null;
|
||||
$name = $result[$address] ?? null;
|
||||
$address = array_keys($result)[0] ?? null;
|
||||
$name = $result[$address] ?? null;
|
||||
|
||||
// if the array is non-associative, the value is the address
|
||||
if (is_int($address) === true) {
|
||||
$address = $name;
|
||||
$name = null;
|
||||
}
|
||||
// if the array is non-associative, the value is the address
|
||||
if (is_int($address) === true) {
|
||||
$address = $name;
|
||||
$name = null;
|
||||
}
|
||||
|
||||
// always use the address as we have transformed that prop above
|
||||
$this->props[$addressProp] = $address;
|
||||
// always use the address as we have transformed that prop above
|
||||
$this->props[$addressProp] = $address;
|
||||
|
||||
// only use the name from the user if no custom name was set
|
||||
if (isset($this->props[$nameProp]) === false || $this->props[$nameProp] === null) {
|
||||
$this->props[$nameProp] = $name;
|
||||
}
|
||||
}
|
||||
// only use the name from the user if no custom name was set
|
||||
if (isset($this->props[$nameProp]) === false || $this->props[$nameProp] === null) {
|
||||
$this->props[$nameProp] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms user object(s) to the email address(es) and name(s);
|
||||
* supports simple strings, user objects or collections/arrays of either
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @return void
|
||||
*/
|
||||
protected function transformUserMultiple(string $prop): void
|
||||
{
|
||||
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\User', 'name', 'email');
|
||||
}
|
||||
/**
|
||||
* Transforms user object(s) to the email address(es) and name(s);
|
||||
* supports simple strings, user objects or collections/arrays of either
|
||||
*
|
||||
* @param string $prop Prop to transform
|
||||
* @return void
|
||||
*/
|
||||
protected function transformUserMultiple(string $prop): void
|
||||
{
|
||||
$this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\User', 'name', 'email');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,270 +21,270 @@ use Kirby\Toolkit\Controller;
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/**
|
||||
* The full event name
|
||||
* (e.g. `page.create:after`)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
/**
|
||||
* The full event name
|
||||
* (e.g. `page.create:after`)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The event type
|
||||
* (e.g. `page` in `page.create:after`)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* The event type
|
||||
* (e.g. `page` in `page.create:after`)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The event action
|
||||
* (e.g. `create` in `page.create:after`)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* The event action
|
||||
* (e.g. `create` in `page.create:after`)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* The event state
|
||||
* (e.g. `after` in `page.create:after`)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $state;
|
||||
/**
|
||||
* The event state
|
||||
* (e.g. `after` in `page.create:after`)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The event arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
/**
|
||||
* The event arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name Full event name
|
||||
* @param array $arguments Associative array of named event arguments
|
||||
*/
|
||||
public function __construct(string $name, array $arguments = [])
|
||||
{
|
||||
// split the event name into `$type.$action:$state`
|
||||
// $action and $state are optional;
|
||||
// if there is more than one dot, $type will be greedy
|
||||
$regex = '/^(?<type>.+?)(?:\.(?<action>[^.]*?))?(?:\:(?<state>.*))?$/';
|
||||
preg_match($regex, $name, $matches, PREG_UNMATCHED_AS_NULL);
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name Full event name
|
||||
* @param array $arguments Associative array of named event arguments
|
||||
*/
|
||||
public function __construct(string $name, array $arguments = [])
|
||||
{
|
||||
// split the event name into `$type.$action:$state`
|
||||
// $action and $state are optional;
|
||||
// if there is more than one dot, $type will be greedy
|
||||
$regex = '/^(?<type>.+?)(?:\.(?<action>[^.]*?))?(?:\:(?<state>.*))?$/';
|
||||
preg_match($regex, $name, $matches, PREG_UNMATCHED_AS_NULL);
|
||||
|
||||
$this->name = $name;
|
||||
$this->type = $matches['type'];
|
||||
$this->action = $matches['action'] ?? null;
|
||||
$this->state = $matches['state'] ?? null;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->type = $matches['type'];
|
||||
$this->action = $matches['action'] ?? null;
|
||||
$this->state = $matches['state'] ?? null;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic caller for event arguments
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
return $this->argument($method);
|
||||
}
|
||||
/**
|
||||
* Magic caller for event arguments
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
return $this->argument($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it possible to simply echo
|
||||
* or stringify the entire object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
/**
|
||||
* Makes it possible to simply echo
|
||||
* or stringify the entire object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action of the event (e.g. `create`)
|
||||
* or `null` if the event name does not include an action
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function action(): ?string
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
/**
|
||||
* Returns the action of the event (e.g. `create`)
|
||||
* or `null` if the event name does not include an action
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function action(): ?string
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific event argument
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function argument(string $name)
|
||||
{
|
||||
if (isset($this->arguments[$name]) === true) {
|
||||
return $this->arguments[$name];
|
||||
}
|
||||
/**
|
||||
* Returns a specific event argument
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function argument(string $name)
|
||||
{
|
||||
if (isset($this->arguments[$name]) === true) {
|
||||
return $this->arguments[$name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments of the event
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
/**
|
||||
* Returns the arguments of the event
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a hook with the event data and returns
|
||||
* the hook's return value
|
||||
*
|
||||
* @param object|null $bind Optional object to bind to the hook function
|
||||
* @param \Closure $hook
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(?object $bind, Closure $hook)
|
||||
{
|
||||
// collect the list of possible hook arguments
|
||||
$data = $this->arguments();
|
||||
$data['event'] = $this;
|
||||
/**
|
||||
* Calls a hook with the event data and returns
|
||||
* the hook's return value
|
||||
*
|
||||
* @param object|null $bind Optional object to bind to the hook function
|
||||
* @param \Closure $hook
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(?object $bind, Closure $hook)
|
||||
{
|
||||
// collect the list of possible hook arguments
|
||||
$data = $this->arguments();
|
||||
$data['event'] = $this;
|
||||
|
||||
// magically call the hook with the arguments it requested
|
||||
$hook = new Controller($hook);
|
||||
return $hook->call($bind, $data);
|
||||
}
|
||||
// magically call the hook with the arguments it requested
|
||||
$hook = new Controller($hook);
|
||||
return $hook->call($bind, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full name of the event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* Returns the full name of the event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of possible wildcard
|
||||
* event names based on the current event name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function nameWildcards(): array
|
||||
{
|
||||
// if the event is already a wildcard event, no further variation is possible
|
||||
if ($this->type === '*' || $this->action === '*' || $this->state === '*') {
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Returns the full list of possible wildcard
|
||||
* event names based on the current event name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function nameWildcards(): array
|
||||
{
|
||||
// if the event is already a wildcard event, no further variation is possible
|
||||
if ($this->type === '*' || $this->action === '*' || $this->state === '*') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->action !== null && $this->state !== null) {
|
||||
// full $type.$action:$state event
|
||||
if ($this->action !== null && $this->state !== null) {
|
||||
// full $type.$action:$state event
|
||||
|
||||
return [
|
||||
$this->type . '.*:' . $this->state,
|
||||
$this->type . '.' . $this->action . ':*',
|
||||
$this->type . '.*:*',
|
||||
'*.' . $this->action . ':' . $this->state,
|
||||
'*.' . $this->action . ':*',
|
||||
'*:' . $this->state,
|
||||
'*'
|
||||
];
|
||||
} elseif ($this->state !== null) {
|
||||
// event without action: $type:$state
|
||||
return [
|
||||
$this->type . '.*:' . $this->state,
|
||||
$this->type . '.' . $this->action . ':*',
|
||||
$this->type . '.*:*',
|
||||
'*.' . $this->action . ':' . $this->state,
|
||||
'*.' . $this->action . ':*',
|
||||
'*:' . $this->state,
|
||||
'*'
|
||||
];
|
||||
} elseif ($this->state !== null) {
|
||||
// event without action: $type:$state
|
||||
|
||||
return [
|
||||
$this->type . ':*',
|
||||
'*:' . $this->state,
|
||||
'*'
|
||||
];
|
||||
} elseif ($this->action !== null) {
|
||||
// event without state: $type.$action
|
||||
return [
|
||||
$this->type . ':*',
|
||||
'*:' . $this->state,
|
||||
'*'
|
||||
];
|
||||
} elseif ($this->action !== null) {
|
||||
// event without state: $type.$action
|
||||
|
||||
return [
|
||||
$this->type . '.*',
|
||||
'*.' . $this->action,
|
||||
'*'
|
||||
];
|
||||
} else {
|
||||
// event with a simple name
|
||||
return [
|
||||
$this->type . '.*',
|
||||
'*.' . $this->action,
|
||||
'*'
|
||||
];
|
||||
} else {
|
||||
// event with a simple name
|
||||
|
||||
return ['*'];
|
||||
}
|
||||
}
|
||||
return ['*'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the event (e.g. `after`)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function state(): ?string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
/**
|
||||
* Returns the state of the event (e.g. `after`)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function state(): ?string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event data as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'arguments' => $this->arguments
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns the event data as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'arguments' => $this->arguments
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event name as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* Returns the event name as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the event (e.g. `page`)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
/**
|
||||
* Returns the type of the event (e.g. `page`)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a given argument with a new value
|
||||
*
|
||||
* @internal
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function updateArgument(string $name, $value): void
|
||||
{
|
||||
if (array_key_exists($name, $this->arguments) !== true) {
|
||||
throw new InvalidArgumentException('The argument ' . $name . ' does not exist');
|
||||
}
|
||||
/**
|
||||
* Updates a given argument with a new value
|
||||
*
|
||||
* @internal
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function updateArgument(string $name, $value): void
|
||||
{
|
||||
if (array_key_exists($name, $this->arguments) !== true) {
|
||||
throw new InvalidArgumentException('The argument ' . $name . ' does not exist');
|
||||
}
|
||||
|
||||
$this->arguments[$name] = $value;
|
||||
}
|
||||
$this->arguments[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,232 +26,232 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class Field
|
||||
{
|
||||
/**
|
||||
* Field method aliases
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $aliases = [];
|
||||
/**
|
||||
* Field method aliases
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $aliases = [];
|
||||
|
||||
/**
|
||||
* The field name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
/**
|
||||
* The field name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* Registered field methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
/**
|
||||
* Registered field methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
|
||||
/**
|
||||
* The parent object if available.
|
||||
* This will be the page, site, user or file
|
||||
* to which the content belongs
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* The parent object if available.
|
||||
* This will be the page, site, user or file
|
||||
* to which the content belongs
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* The value of the field
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $value;
|
||||
/**
|
||||
* The value of the field
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Magic caller for field methods
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
$method = strtolower($method);
|
||||
/**
|
||||
* Magic caller for field methods
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
$method = strtolower($method);
|
||||
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return (static::$methods[$method])(clone $this, ...$arguments);
|
||||
}
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return (static::$methods[$method])(clone $this, ...$arguments);
|
||||
}
|
||||
|
||||
if (isset(static::$aliases[$method]) === true) {
|
||||
$method = strtolower(static::$aliases[$method]);
|
||||
if (isset(static::$aliases[$method]) === true) {
|
||||
$method = strtolower(static::$aliases[$method]);
|
||||
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return (static::$methods[$method])(clone $this, ...$arguments);
|
||||
}
|
||||
}
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return (static::$methods[$method])(clone $this, ...$arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new field object
|
||||
*
|
||||
* @param object|null $parent
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(?object $parent, string $key, $value)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
/**
|
||||
* Creates a new field object
|
||||
*
|
||||
* @param object|null $parent
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(?object $parent, string $key, $value)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplifies the var_dump result
|
||||
*
|
||||
* @see Field::toArray
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Simplifies the var_dump result
|
||||
*
|
||||
* @see Field::toArray
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it possible to simply echo
|
||||
* or stringify the entire object
|
||||
*
|
||||
* @see Field::toString
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
/**
|
||||
* Makes it possible to simply echo
|
||||
* or stringify the entire object
|
||||
*
|
||||
* @see Field::toString
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the field exists in the content data array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return $this->parent->content()->has($this->key);
|
||||
}
|
||||
/**
|
||||
* Checks if the field exists in the content data array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return $this->parent->content()->has($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the field content is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->value) === true && in_array($this->value, [0, '0', false], true) === false;
|
||||
}
|
||||
/**
|
||||
* Checks if the field content is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->value) === true && in_array($this->value, [0, '0', false], true) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the field content is not empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
/**
|
||||
* Checks if the field content is not empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
/**
|
||||
* Returns the name of the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Field::parent()
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* @see Field::parent()
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a fallback if the field value is empty
|
||||
*
|
||||
* @param mixed $fallback
|
||||
* @return $this|static
|
||||
*/
|
||||
public function or($fallback = null)
|
||||
{
|
||||
if ($this->isNotEmpty()) {
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Provides a fallback if the field value is empty
|
||||
*
|
||||
* @param mixed $fallback
|
||||
* @return $this|static
|
||||
*/
|
||||
public function or($fallback = null)
|
||||
{
|
||||
if ($this->isNotEmpty()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_a($fallback, 'Kirby\Cms\Field') === true) {
|
||||
return $fallback;
|
||||
}
|
||||
if (is_a($fallback, 'Kirby\Cms\Field') === true) {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
$field = clone $this;
|
||||
$field->value = $fallback;
|
||||
return $field;
|
||||
}
|
||||
$field = clone $this;
|
||||
$field->value = $fallback;
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent object of the field
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent object of the field
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Field object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [$this->key => $this->value];
|
||||
}
|
||||
/**
|
||||
* Converts the Field object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [$this->key => $this->value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field value as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return (string)$this->value;
|
||||
}
|
||||
/**
|
||||
* Returns the field value as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return (string)$this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field content. If a new value is passed,
|
||||
* the modified field will be returned. Otherwise it
|
||||
* will return the field value.
|
||||
*
|
||||
* @param string|\Closure $value
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function value($value = null)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->value;
|
||||
}
|
||||
/**
|
||||
* Returns the field content. If a new value is passed,
|
||||
* the modified field will be returned. Otherwise it
|
||||
* will return the field value.
|
||||
*
|
||||
* @param string|\Closure $value
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function value($value = null)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
if (is_scalar($value)) {
|
||||
$value = (string)$value;
|
||||
} elseif (is_callable($value)) {
|
||||
$value = (string)$value->call($this, $this->value);
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid field value type: ' . gettype($value));
|
||||
}
|
||||
if (is_scalar($value)) {
|
||||
$value = (string)$value;
|
||||
} elseif (is_callable($value)) {
|
||||
$value = (string)$value->call($this, $this->value);
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid field value type: ' . gettype($value));
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->value = $value;
|
||||
$clone = clone $this;
|
||||
$clone->value = $value;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,276 +19,276 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Fieldset extends Item
|
||||
{
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Fieldsets';
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Fieldsets';
|
||||
|
||||
protected $disabled;
|
||||
protected $editable;
|
||||
protected $fields = [];
|
||||
protected $icon;
|
||||
protected $label;
|
||||
protected $model;
|
||||
protected $name;
|
||||
protected $preview;
|
||||
protected $tabs;
|
||||
protected $translate;
|
||||
protected $type;
|
||||
protected $unset;
|
||||
protected $wysiwyg;
|
||||
protected $disabled;
|
||||
protected $editable;
|
||||
protected $fields = [];
|
||||
protected $icon;
|
||||
protected $label;
|
||||
protected $model;
|
||||
protected $name;
|
||||
protected $preview;
|
||||
protected $tabs;
|
||||
protected $translate;
|
||||
protected $type;
|
||||
protected $unset;
|
||||
protected $wysiwyg;
|
||||
|
||||
/**
|
||||
* Creates a new Fieldset object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
if (empty($params['type']) === true) {
|
||||
throw new InvalidArgumentException('The fieldset type is missing');
|
||||
}
|
||||
/**
|
||||
* Creates a new Fieldset object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
if (empty($params['type']) === true) {
|
||||
throw new InvalidArgumentException('The fieldset type is missing');
|
||||
}
|
||||
|
||||
$this->type = $params['id'] = $params['type'];
|
||||
$this->type = $params['id'] = $params['type'];
|
||||
|
||||
parent::__construct($params);
|
||||
parent::__construct($params);
|
||||
|
||||
$this->disabled = $params['disabled'] ?? false;
|
||||
$this->editable = $params['editable'] ?? true;
|
||||
$this->icon = $params['icon'] ?? null;
|
||||
$this->model = $this->parent;
|
||||
$this->name = $this->createName($params['title'] ?? $params['name'] ?? Str::ucfirst($this->type));
|
||||
$this->label = $this->createLabel($params['label'] ?? null);
|
||||
$this->preview = $params['preview'] ?? null;
|
||||
$this->tabs = $this->createTabs($params);
|
||||
$this->translate = $params['translate'] ?? true;
|
||||
$this->unset = $params['unset'] ?? false;
|
||||
$this->wysiwyg = $params['wysiwyg'] ?? false;
|
||||
$this->disabled = $params['disabled'] ?? false;
|
||||
$this->editable = $params['editable'] ?? true;
|
||||
$this->icon = $params['icon'] ?? null;
|
||||
$this->model = $this->parent;
|
||||
$this->name = $this->createName($params['title'] ?? $params['name'] ?? Str::ucfirst($this->type));
|
||||
$this->label = $this->createLabel($params['label'] ?? null);
|
||||
$this->preview = $params['preview'] ?? null;
|
||||
$this->tabs = $this->createTabs($params);
|
||||
$this->translate = $params['translate'] ?? true;
|
||||
$this->unset = $params['unset'] ?? false;
|
||||
$this->wysiwyg = $params['wysiwyg'] ?? false;
|
||||
|
||||
if (
|
||||
$this->translate === false &&
|
||||
$this->kirby()->multilang() === true &&
|
||||
$this->kirby()->language()->isDefault() === false
|
||||
) {
|
||||
// disable and unset the fieldset if it's not translatable
|
||||
$this->unset = true;
|
||||
$this->disabled = true;
|
||||
}
|
||||
}
|
||||
if (
|
||||
$this->translate === false &&
|
||||
$this->kirby()->multilang() === true &&
|
||||
$this->kirby()->language()->isDefault() === false
|
||||
) {
|
||||
// disable and unset the fieldset if it's not translatable
|
||||
$this->unset = true;
|
||||
$this->disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
* @return array
|
||||
*/
|
||||
protected function createFields(array $fields = []): array
|
||||
{
|
||||
$fields = Blueprint::fieldsProps($fields);
|
||||
$fields = $this->form($fields)->fields()->toArray();
|
||||
/**
|
||||
* @param array $fields
|
||||
* @return array
|
||||
*/
|
||||
protected function createFields(array $fields = []): array
|
||||
{
|
||||
$fields = Blueprint::fieldsProps($fields);
|
||||
$fields = $this->form($fields)->fields()->toArray();
|
||||
|
||||
// collect all fields
|
||||
$this->fields = array_merge($this->fields, $fields);
|
||||
// collect all fields
|
||||
$this->fields = array_merge($this->fields, $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $name
|
||||
* @return string|null
|
||||
*/
|
||||
protected function createName($name): ?string
|
||||
{
|
||||
return I18n::translate($name, $name);
|
||||
}
|
||||
/**
|
||||
* @param array|string $name
|
||||
* @return string|null
|
||||
*/
|
||||
protected function createName($name): ?string
|
||||
{
|
||||
return I18n::translate($name, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $label
|
||||
* @return string|null
|
||||
*/
|
||||
protected function createLabel($label = null): ?string
|
||||
{
|
||||
return I18n::translate($label, $label);
|
||||
}
|
||||
/**
|
||||
* @param array|string $label
|
||||
* @return string|null
|
||||
*/
|
||||
protected function createLabel($label = null): ?string
|
||||
{
|
||||
return I18n::translate($label, $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
protected function createTabs(array $params = []): array
|
||||
{
|
||||
$tabs = $params['tabs'] ?? [];
|
||||
/**
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
protected function createTabs(array $params = []): array
|
||||
{
|
||||
$tabs = $params['tabs'] ?? [];
|
||||
|
||||
// return a single tab if there are only fields
|
||||
if (empty($tabs) === true) {
|
||||
return [
|
||||
'content' => [
|
||||
'fields' => $this->createFields($params['fields'] ?? []),
|
||||
]
|
||||
];
|
||||
}
|
||||
// return a single tab if there are only fields
|
||||
if (empty($tabs) === true) {
|
||||
return [
|
||||
'content' => [
|
||||
'fields' => $this->createFields($params['fields'] ?? []),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// normalize tabs props
|
||||
foreach ($tabs as $name => $tab) {
|
||||
// unset/remove tab if its property is false
|
||||
if ($tab === false) {
|
||||
unset($tabs[$name]);
|
||||
continue;
|
||||
}
|
||||
// normalize tabs props
|
||||
foreach ($tabs as $name => $tab) {
|
||||
// unset/remove tab if its property is false
|
||||
if ($tab === false) {
|
||||
unset($tabs[$name]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$tab = Blueprint::extend($tab);
|
||||
$tab = Blueprint::extend($tab);
|
||||
|
||||
$tab['fields'] = $this->createFields($tab['fields'] ?? []);
|
||||
$tab['label'] = $this->createLabel($tab['label'] ?? Str::ucfirst($name));
|
||||
$tab['name'] = $name;
|
||||
$tab['fields'] = $this->createFields($tab['fields'] ?? []);
|
||||
$tab['label'] = $this->createLabel($tab['label'] ?? Str::ucfirst($name));
|
||||
$tab['name'] = $name;
|
||||
|
||||
$tabs[$name] = $tab;
|
||||
}
|
||||
$tabs[$name] = $tab;
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function disabled(): bool
|
||||
{
|
||||
return $this->disabled;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function disabled(): bool
|
||||
{
|
||||
return $this->disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function editable(): bool
|
||||
{
|
||||
if ($this->editable === false) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function editable(): bool
|
||||
{
|
||||
if ($this->editable === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($this->fields) === 0) {
|
||||
return false;
|
||||
}
|
||||
if (count($this->fields) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form for the given fields
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $input
|
||||
* @return \Kirby\Form\Form
|
||||
*/
|
||||
public function form(array $fields, array $input = [])
|
||||
{
|
||||
return new Form([
|
||||
'fields' => $fields,
|
||||
'model' => $this->model,
|
||||
'strict' => true,
|
||||
'values' => $input,
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Creates a form for the given fields
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $input
|
||||
* @return \Kirby\Form\Form
|
||||
*/
|
||||
public function form(array $fields, array $input = [])
|
||||
{
|
||||
return new Form([
|
||||
'fields' => $fields,
|
||||
'model' => $this->model,
|
||||
'strict' => true,
|
||||
'values' => $input,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function icon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function icon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function label(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function label(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Kirby\Cms\ModelWithContent
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Cms\ModelWithContent
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
return $this->preview;
|
||||
}
|
||||
/**
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
return $this->preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function tabs(): array
|
||||
{
|
||||
return $this->tabs;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function tabs(): array
|
||||
{
|
||||
return $this->tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function translate(): bool
|
||||
{
|
||||
return $this->translate;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function translate(): bool
|
||||
{
|
||||
return $this->translate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'disabled' => $this->disabled(),
|
||||
'editable' => $this->editable(),
|
||||
'icon' => $this->icon(),
|
||||
'label' => $this->label(),
|
||||
'name' => $this->name(),
|
||||
'preview' => $this->preview(),
|
||||
'tabs' => $this->tabs(),
|
||||
'translate' => $this->translate(),
|
||||
'type' => $this->type(),
|
||||
'unset' => $this->unset(),
|
||||
'wysiwyg' => $this->wysiwyg(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'disabled' => $this->disabled(),
|
||||
'editable' => $this->editable(),
|
||||
'icon' => $this->icon(),
|
||||
'label' => $this->label(),
|
||||
'name' => $this->name(),
|
||||
'preview' => $this->preview(),
|
||||
'tabs' => $this->tabs(),
|
||||
'translate' => $this->translate(),
|
||||
'type' => $this->type(),
|
||||
'unset' => $this->unset(),
|
||||
'wysiwyg' => $this->wysiwyg(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function unset(): bool
|
||||
{
|
||||
return $this->unset;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function unset(): bool
|
||||
{
|
||||
return $this->unset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function wysiwyg(): bool
|
||||
{
|
||||
return $this->wysiwyg;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function wysiwyg(): bool
|
||||
{
|
||||
return $this->wysiwyg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,85 +19,85 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Fieldsets extends Items
|
||||
{
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Fieldset';
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Fieldset';
|
||||
|
||||
protected static function createFieldsets($params)
|
||||
{
|
||||
$fieldsets = [];
|
||||
$groups = [];
|
||||
protected static function createFieldsets($params)
|
||||
{
|
||||
$fieldsets = [];
|
||||
$groups = [];
|
||||
|
||||
foreach ($params as $type => $fieldset) {
|
||||
if (is_int($type) === true && is_string($fieldset)) {
|
||||
$type = $fieldset;
|
||||
$fieldset = 'blocks/' . $type;
|
||||
}
|
||||
foreach ($params as $type => $fieldset) {
|
||||
if (is_int($type) === true && is_string($fieldset)) {
|
||||
$type = $fieldset;
|
||||
$fieldset = 'blocks/' . $type;
|
||||
}
|
||||
|
||||
if ($fieldset === false) {
|
||||
continue;
|
||||
}
|
||||
if ($fieldset === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($fieldset === true) {
|
||||
$fieldset = 'blocks/' . $type;
|
||||
}
|
||||
if ($fieldset === true) {
|
||||
$fieldset = 'blocks/' . $type;
|
||||
}
|
||||
|
||||
$fieldset = Blueprint::extend($fieldset);
|
||||
$fieldset = Blueprint::extend($fieldset);
|
||||
|
||||
// make sure the type is always set
|
||||
$fieldset['type'] ??= $type;
|
||||
// make sure the type is always set
|
||||
$fieldset['type'] ??= $type;
|
||||
|
||||
// extract groups
|
||||
if ($fieldset['type'] === 'group') {
|
||||
$result = static::createFieldsets($fieldset['fieldsets'] ?? []);
|
||||
$fieldsets = array_merge($fieldsets, $result['fieldsets']);
|
||||
$label = $fieldset['label'] ?? Str::ucfirst($type);
|
||||
// extract groups
|
||||
if ($fieldset['type'] === 'group') {
|
||||
$result = static::createFieldsets($fieldset['fieldsets'] ?? []);
|
||||
$fieldsets = array_merge($fieldsets, $result['fieldsets']);
|
||||
$label = $fieldset['label'] ?? Str::ucfirst($type);
|
||||
|
||||
$groups[$type] = [
|
||||
'label' => I18n::translate($label, $label),
|
||||
'name' => $type,
|
||||
'open' => $fieldset['open'] ?? true,
|
||||
'sets' => array_column($result['fieldsets'], 'type'),
|
||||
];
|
||||
} else {
|
||||
$fieldsets[$fieldset['type']] = $fieldset;
|
||||
}
|
||||
}
|
||||
$groups[$type] = [
|
||||
'label' => I18n::translate($label, $label),
|
||||
'name' => $type,
|
||||
'open' => $fieldset['open'] ?? true,
|
||||
'sets' => array_column($result['fieldsets'], 'type'),
|
||||
];
|
||||
} else {
|
||||
$fieldsets[$fieldset['type']] = $fieldset;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'fieldsets' => $fieldsets,
|
||||
'groups' => $groups
|
||||
];
|
||||
}
|
||||
return [
|
||||
'fieldsets' => $fieldsets,
|
||||
'groups' => $groups
|
||||
];
|
||||
}
|
||||
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$items ??= App::instance()->option('blocks.fieldsets', [
|
||||
'code' => 'blocks/code',
|
||||
'gallery' => 'blocks/gallery',
|
||||
'heading' => 'blocks/heading',
|
||||
'image' => 'blocks/image',
|
||||
'line' => 'blocks/line',
|
||||
'list' => 'blocks/list',
|
||||
'markdown' => 'blocks/markdown',
|
||||
'quote' => 'blocks/quote',
|
||||
'text' => 'blocks/text',
|
||||
'video' => 'blocks/video',
|
||||
]);
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$items ??= App::instance()->option('blocks.fieldsets', [
|
||||
'code' => 'blocks/code',
|
||||
'gallery' => 'blocks/gallery',
|
||||
'heading' => 'blocks/heading',
|
||||
'image' => 'blocks/image',
|
||||
'line' => 'blocks/line',
|
||||
'list' => 'blocks/list',
|
||||
'markdown' => 'blocks/markdown',
|
||||
'quote' => 'blocks/quote',
|
||||
'text' => 'blocks/text',
|
||||
'video' => 'blocks/video',
|
||||
]);
|
||||
|
||||
$result = static::createFieldsets($items);
|
||||
$result = static::createFieldsets($items);
|
||||
|
||||
return parent::factory($result['fieldsets'], ['groups' => $result['groups']] + $params);
|
||||
}
|
||||
return parent::factory($result['fieldsets'], ['groups' => $result['groups']] + $params);
|
||||
}
|
||||
|
||||
public function groups(): array
|
||||
{
|
||||
return $this->options['groups'] ?? [];
|
||||
}
|
||||
public function groups(): array
|
||||
{
|
||||
return $this->options['groups'] ?? [];
|
||||
}
|
||||
|
||||
public function toArray(?Closure $map = null): array
|
||||
{
|
||||
return A::map(
|
||||
$this->data,
|
||||
$map ?? fn ($fieldset) => $fieldset->toArray()
|
||||
);
|
||||
}
|
||||
public function toArray(?Closure $map = null): array
|
||||
{
|
||||
return A::map(
|
||||
$this->data,
|
||||
$map ?? fn ($fieldset) => $fieldset->toArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,298 +19,298 @@ use Kirby\Form\Form;
|
||||
*/
|
||||
trait FileActions
|
||||
{
|
||||
/**
|
||||
* Renames the file without touching the extension
|
||||
* The store is used to actually execute this.
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $sanitize
|
||||
* @return $this|static
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function changeName(string $name, bool $sanitize = true)
|
||||
{
|
||||
if ($sanitize === true) {
|
||||
$name = F::safeName($name);
|
||||
}
|
||||
/**
|
||||
* Renames the file without touching the extension
|
||||
* The store is used to actually execute this.
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $sanitize
|
||||
* @return $this|static
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function changeName(string $name, bool $sanitize = true)
|
||||
{
|
||||
if ($sanitize === true) {
|
||||
$name = F::safeName($name);
|
||||
}
|
||||
|
||||
// don't rename if not necessary
|
||||
if ($name === $this->name()) {
|
||||
return $this;
|
||||
}
|
||||
// don't rename if not necessary
|
||||
if ($name === $this->name()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->commit('changeName', ['file' => $this, 'name' => $name], function ($oldFile, $name) {
|
||||
$newFile = $oldFile->clone([
|
||||
'filename' => $name . '.' . $oldFile->extension(),
|
||||
]);
|
||||
return $this->commit('changeName', ['file' => $this, 'name' => $name], function ($oldFile, $name) {
|
||||
$newFile = $oldFile->clone([
|
||||
'filename' => $name . '.' . $oldFile->extension(),
|
||||
]);
|
||||
|
||||
if ($oldFile->exists() === false) {
|
||||
return $newFile;
|
||||
}
|
||||
if ($oldFile->exists() === false) {
|
||||
return $newFile;
|
||||
}
|
||||
|
||||
if ($newFile->exists() === true) {
|
||||
throw new LogicException('The new file exists and cannot be overwritten');
|
||||
}
|
||||
if ($newFile->exists() === true) {
|
||||
throw new LogicException('The new file exists and cannot be overwritten');
|
||||
}
|
||||
|
||||
// remove the lock of the old file
|
||||
if ($lock = $oldFile->lock()) {
|
||||
$lock->remove();
|
||||
}
|
||||
// remove the lock of the old file
|
||||
if ($lock = $oldFile->lock()) {
|
||||
$lock->remove();
|
||||
}
|
||||
|
||||
// remove all public versions
|
||||
$oldFile->unpublish();
|
||||
// remove all public versions
|
||||
$oldFile->unpublish();
|
||||
|
||||
// rename the main file
|
||||
F::move($oldFile->root(), $newFile->root());
|
||||
// rename the main file
|
||||
F::move($oldFile->root(), $newFile->root());
|
||||
|
||||
if ($newFile->kirby()->multilang() === true) {
|
||||
foreach ($newFile->translations() as $translation) {
|
||||
$translationCode = $translation->code();
|
||||
if ($newFile->kirby()->multilang() === true) {
|
||||
foreach ($newFile->translations() as $translation) {
|
||||
$translationCode = $translation->code();
|
||||
|
||||
// rename the content file
|
||||
F::move($oldFile->contentFile($translationCode), $newFile->contentFile($translationCode));
|
||||
}
|
||||
} else {
|
||||
// rename the content file
|
||||
F::move($oldFile->contentFile(), $newFile->contentFile());
|
||||
}
|
||||
// rename the content file
|
||||
F::move($oldFile->contentFile($translationCode), $newFile->contentFile($translationCode));
|
||||
}
|
||||
} else {
|
||||
// rename the content file
|
||||
F::move($oldFile->contentFile(), $newFile->contentFile());
|
||||
}
|
||||
|
||||
$newFile->parent()->files()->remove($oldFile->id());
|
||||
$newFile->parent()->files()->set($newFile->id(), $newFile);
|
||||
$newFile->parent()->files()->remove($oldFile->id());
|
||||
$newFile->parent()->files()->set($newFile->id(), $newFile);
|
||||
|
||||
return $newFile;
|
||||
});
|
||||
}
|
||||
return $newFile;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the file's sorting number in the meta file
|
||||
*
|
||||
* @param int $sort
|
||||
* @return static
|
||||
*/
|
||||
public function changeSort(int $sort)
|
||||
{
|
||||
return $this->commit(
|
||||
'changeSort',
|
||||
['file' => $this, 'position' => $sort],
|
||||
fn ($file, $sort) => $file->save(['sort' => $sort])
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Changes the file's sorting number in the meta file
|
||||
*
|
||||
* @param int $sort
|
||||
* @return static
|
||||
*/
|
||||
public function changeSort(int $sort)
|
||||
{
|
||||
return $this->commit(
|
||||
'changeSort',
|
||||
['file' => $this, 'position' => $sort],
|
||||
fn ($file, $sort) => $file->save(['sort' => $sort])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a file action, by following these steps
|
||||
*
|
||||
* 1. checks the action rules
|
||||
* 2. sends the before hook
|
||||
* 3. commits the store action
|
||||
* 4. sends the after hook
|
||||
* 5. returns the result
|
||||
*
|
||||
* @param string $action
|
||||
* @param array $arguments
|
||||
* @param Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
protected function commit(string $action, array $arguments, Closure $callback)
|
||||
{
|
||||
$old = $this->hardcopy();
|
||||
$kirby = $this->kirby();
|
||||
$argumentValues = array_values($arguments);
|
||||
/**
|
||||
* Commits a file action, by following these steps
|
||||
*
|
||||
* 1. checks the action rules
|
||||
* 2. sends the before hook
|
||||
* 3. commits the store action
|
||||
* 4. sends the after hook
|
||||
* 5. returns the result
|
||||
*
|
||||
* @param string $action
|
||||
* @param array $arguments
|
||||
* @param Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
protected function commit(string $action, array $arguments, Closure $callback)
|
||||
{
|
||||
$old = $this->hardcopy();
|
||||
$kirby = $this->kirby();
|
||||
$argumentValues = array_values($arguments);
|
||||
|
||||
$this->rules()->$action(...$argumentValues);
|
||||
$kirby->trigger('file.' . $action . ':before', $arguments);
|
||||
$this->rules()->$action(...$argumentValues);
|
||||
$kirby->trigger('file.' . $action . ':before', $arguments);
|
||||
|
||||
$result = $callback(...$argumentValues);
|
||||
$result = $callback(...$argumentValues);
|
||||
|
||||
if ($action === 'create') {
|
||||
$argumentsAfter = ['file' => $result];
|
||||
} elseif ($action === 'delete') {
|
||||
$argumentsAfter = ['status' => $result, 'file' => $old];
|
||||
} else {
|
||||
$argumentsAfter = ['newFile' => $result, 'oldFile' => $old];
|
||||
}
|
||||
$kirby->trigger('file.' . $action . ':after', $argumentsAfter);
|
||||
if ($action === 'create') {
|
||||
$argumentsAfter = ['file' => $result];
|
||||
} elseif ($action === 'delete') {
|
||||
$argumentsAfter = ['status' => $result, 'file' => $old];
|
||||
} else {
|
||||
$argumentsAfter = ['newFile' => $result, 'oldFile' => $old];
|
||||
}
|
||||
$kirby->trigger('file.' . $action . ':after', $argumentsAfter);
|
||||
|
||||
$kirby->cache('pages')->flush();
|
||||
return $result;
|
||||
}
|
||||
$kirby->cache('pages')->flush();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the file to the given page
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return \Kirby\Cms\File
|
||||
*/
|
||||
public function copy(Page $page)
|
||||
{
|
||||
F::copy($this->root(), $page->root() . '/' . $this->filename());
|
||||
/**
|
||||
* Copy the file to the given page
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return \Kirby\Cms\File
|
||||
*/
|
||||
public function copy(Page $page)
|
||||
{
|
||||
F::copy($this->root(), $page->root() . '/' . $this->filename());
|
||||
|
||||
if ($this->kirby()->multilang() === true) {
|
||||
foreach ($this->kirby()->languages() as $language) {
|
||||
$contentFile = $this->contentFile($language->code());
|
||||
F::copy($contentFile, $page->root() . '/' . basename($contentFile));
|
||||
}
|
||||
} else {
|
||||
$contentFile = $this->contentFile();
|
||||
F::copy($contentFile, $page->root() . '/' . basename($contentFile));
|
||||
}
|
||||
if ($this->kirby()->multilang() === true) {
|
||||
foreach ($this->kirby()->languages() as $language) {
|
||||
$contentFile = $this->contentFile($language->code());
|
||||
F::copy($contentFile, $page->root() . '/' . basename($contentFile));
|
||||
}
|
||||
} else {
|
||||
$contentFile = $this->contentFile();
|
||||
F::copy($contentFile, $page->root() . '/' . basename($contentFile));
|
||||
}
|
||||
|
||||
return $page->clone()->file($this->filename());
|
||||
}
|
||||
return $page->clone()->file($this->filename());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file on disk and returns the
|
||||
* File object. The store is used to handle file
|
||||
* writing, so it can be replaced by any other
|
||||
* way of generating files.
|
||||
*
|
||||
* @param array $props
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public static function create(array $props)
|
||||
{
|
||||
if (isset($props['source'], $props['parent']) === false) {
|
||||
throw new InvalidArgumentException('Please provide the "source" and "parent" props for the File');
|
||||
}
|
||||
/**
|
||||
* Creates a new file on disk and returns the
|
||||
* File object. The store is used to handle file
|
||||
* writing, so it can be replaced by any other
|
||||
* way of generating files.
|
||||
*
|
||||
* @param array $props
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public static function create(array $props)
|
||||
{
|
||||
if (isset($props['source'], $props['parent']) === false) {
|
||||
throw new InvalidArgumentException('Please provide the "source" and "parent" props for the File');
|
||||
}
|
||||
|
||||
// prefer the filename from the props
|
||||
$props['filename'] = F::safeName($props['filename'] ?? basename($props['source']));
|
||||
// prefer the filename from the props
|
||||
$props['filename'] = F::safeName($props['filename'] ?? basename($props['source']));
|
||||
|
||||
$props['model'] = strtolower($props['template'] ?? 'default');
|
||||
$props['model'] = strtolower($props['template'] ?? 'default');
|
||||
|
||||
// create the basic file and a test upload object
|
||||
$file = static::factory($props);
|
||||
$upload = $file->asset($props['source']);
|
||||
// create the basic file and a test upload object
|
||||
$file = static::factory($props);
|
||||
$upload = $file->asset($props['source']);
|
||||
|
||||
// create a form for the file
|
||||
$form = Form::for($file, [
|
||||
'values' => $props['content'] ?? []
|
||||
]);
|
||||
// create a form for the file
|
||||
$form = Form::for($file, [
|
||||
'values' => $props['content'] ?? []
|
||||
]);
|
||||
|
||||
// inject the content
|
||||
$file = $file->clone(['content' => $form->strings(true)]);
|
||||
// inject the content
|
||||
$file = $file->clone(['content' => $form->strings(true)]);
|
||||
|
||||
// run the hook
|
||||
return $file->commit('create', compact('file', 'upload'), function ($file, $upload) {
|
||||
// run the hook
|
||||
return $file->commit('create', compact('file', 'upload'), function ($file, $upload) {
|
||||
|
||||
// delete all public versions
|
||||
$file->unpublish();
|
||||
// delete all public versions
|
||||
$file->unpublish();
|
||||
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
|
||||
// always create pages in the default language
|
||||
if ($file->kirby()->multilang() === true) {
|
||||
$languageCode = $file->kirby()->defaultLanguage()->code();
|
||||
} else {
|
||||
$languageCode = null;
|
||||
}
|
||||
// always create pages in the default language
|
||||
if ($file->kirby()->multilang() === true) {
|
||||
$languageCode = $file->kirby()->defaultLanguage()->code();
|
||||
} else {
|
||||
$languageCode = null;
|
||||
}
|
||||
|
||||
// store the content if necessary
|
||||
$file->save($file->content()->toArray(), $languageCode);
|
||||
// store the content if necessary
|
||||
$file->save($file->content()->toArray(), $languageCode);
|
||||
|
||||
// add the file to the list of siblings
|
||||
$file->siblings()->append($file->id(), $file);
|
||||
// add the file to the list of siblings
|
||||
$file->siblings()->append($file->id(), $file);
|
||||
|
||||
// return a fresh clone
|
||||
return $file->clone();
|
||||
});
|
||||
}
|
||||
// return a fresh clone
|
||||
return $file->clone();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the file. The store is used to
|
||||
* manipulate the filesystem or whatever you prefer.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(): bool
|
||||
{
|
||||
return $this->commit('delete', ['file' => $this], function ($file) {
|
||||
/**
|
||||
* Deletes the file. The store is used to
|
||||
* manipulate the filesystem or whatever you prefer.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(): bool
|
||||
{
|
||||
return $this->commit('delete', ['file' => $this], function ($file) {
|
||||
|
||||
// remove all versions in the media folder
|
||||
$file->unpublish();
|
||||
// remove all versions in the media folder
|
||||
$file->unpublish();
|
||||
|
||||
// remove the lock of the old file
|
||||
if ($lock = $file->lock()) {
|
||||
$lock->remove();
|
||||
}
|
||||
// remove the lock of the old file
|
||||
if ($lock = $file->lock()) {
|
||||
$lock->remove();
|
||||
}
|
||||
|
||||
if ($file->kirby()->multilang() === true) {
|
||||
foreach ($file->translations() as $translation) {
|
||||
F::remove($file->contentFile($translation->code()));
|
||||
}
|
||||
} else {
|
||||
F::remove($file->contentFile());
|
||||
}
|
||||
if ($file->kirby()->multilang() === true) {
|
||||
foreach ($file->translations() as $translation) {
|
||||
F::remove($file->contentFile($translation->code()));
|
||||
}
|
||||
} else {
|
||||
F::remove($file->contentFile());
|
||||
}
|
||||
|
||||
F::remove($file->root());
|
||||
F::remove($file->root());
|
||||
|
||||
// remove the file from the sibling collection
|
||||
$file->parent()->files()->remove($file);
|
||||
// remove the file from the sibling collection
|
||||
$file->parent()->files()->remove($file);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the file to the public media folder
|
||||
* if it's not already there.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function publish()
|
||||
{
|
||||
Media::publish($this, $this->mediaRoot());
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Move the file to the public media folder
|
||||
* if it's not already there.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function publish()
|
||||
{
|
||||
Media::publish($this, $this->mediaRoot());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the file. The source must
|
||||
* be an absolute path to a file or a Url.
|
||||
* The store handles the replacement so it
|
||||
* finally decides what it will support as
|
||||
* source.
|
||||
*
|
||||
* @param string $source
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function replace(string $source)
|
||||
{
|
||||
$file = $this->clone();
|
||||
/**
|
||||
* Replaces the file. The source must
|
||||
* be an absolute path to a file or a Url.
|
||||
* The store handles the replacement so it
|
||||
* finally decides what it will support as
|
||||
* source.
|
||||
*
|
||||
* @param string $source
|
||||
* @return static
|
||||
* @throws \Kirby\Exception\LogicException
|
||||
*/
|
||||
public function replace(string $source)
|
||||
{
|
||||
$file = $this->clone();
|
||||
|
||||
$arguments = [
|
||||
'file' => $file,
|
||||
'upload' => $file->asset($source)
|
||||
];
|
||||
$arguments = [
|
||||
'file' => $file,
|
||||
'upload' => $file->asset($source)
|
||||
];
|
||||
|
||||
return $this->commit('replace', $arguments, function ($file, $upload) {
|
||||
return $this->commit('replace', $arguments, function ($file, $upload) {
|
||||
|
||||
// delete all public versions
|
||||
$file->unpublish();
|
||||
// delete all public versions
|
||||
$file->unpublish();
|
||||
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
// overwrite the original
|
||||
if (F::copy($upload->root(), $file->root(), true) !== true) {
|
||||
throw new LogicException('The file could not be created');
|
||||
}
|
||||
|
||||
// return a fresh clone
|
||||
return $file->clone();
|
||||
});
|
||||
}
|
||||
// return a fresh clone
|
||||
return $file->clone();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all public versions of this file
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unpublish()
|
||||
{
|
||||
Media::unpublish($this->parent()->mediaRoot(), $this);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Remove all public versions of this file
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unpublish()
|
||||
{
|
||||
Media::unpublish($this->parent()->mediaRoot(), $this);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,172 +17,172 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class FileBlueprint extends Blueprint
|
||||
{
|
||||
/**
|
||||
* `true` if the default accepted
|
||||
* types are being used
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defaultTypes = false;
|
||||
/**
|
||||
* `true` if the default accepted
|
||||
* types are being used
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defaultTypes = false;
|
||||
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeName' => null,
|
||||
'create' => null,
|
||||
'delete' => null,
|
||||
'read' => null,
|
||||
'replace' => null,
|
||||
'update' => null,
|
||||
]
|
||||
);
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeName' => null,
|
||||
'create' => null,
|
||||
'delete' => null,
|
||||
'read' => null,
|
||||
'replace' => null,
|
||||
'update' => null,
|
||||
]
|
||||
);
|
||||
|
||||
// normalize the accept settings
|
||||
$this->props['accept'] = $this->normalizeAccept($this->props['accept'] ?? []);
|
||||
}
|
||||
// normalize the accept settings
|
||||
$this->props['accept'] = $this->normalizeAccept($this->props['accept'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function accept(): array
|
||||
{
|
||||
return $this->props['accept'];
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function accept(): array
|
||||
{
|
||||
return $this->props['accept'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all accepted MIME types for
|
||||
* file upload or `*` if all MIME types are allowed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acceptMime(): string
|
||||
{
|
||||
// don't disclose the specific default types
|
||||
if ($this->defaultTypes === true) {
|
||||
return '*';
|
||||
}
|
||||
/**
|
||||
* Returns the list of all accepted MIME types for
|
||||
* file upload or `*` if all MIME types are allowed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acceptMime(): string
|
||||
{
|
||||
// don't disclose the specific default types
|
||||
if ($this->defaultTypes === true) {
|
||||
return '*';
|
||||
}
|
||||
|
||||
$accept = $this->accept();
|
||||
$restrictions = [];
|
||||
$accept = $this->accept();
|
||||
$restrictions = [];
|
||||
|
||||
if (is_array($accept['mime']) === true) {
|
||||
$restrictions[] = $accept['mime'];
|
||||
} else {
|
||||
// only fall back to the extension or type if
|
||||
// no explicit MIME types were defined
|
||||
// (allows to set custom MIME types for the frontend
|
||||
// check but still restrict the extension and/or type)
|
||||
if (is_array($accept['mime']) === true) {
|
||||
$restrictions[] = $accept['mime'];
|
||||
} else {
|
||||
// only fall back to the extension or type if
|
||||
// no explicit MIME types were defined
|
||||
// (allows to set custom MIME types for the frontend
|
||||
// check but still restrict the extension and/or type)
|
||||
|
||||
if (is_array($accept['extension']) === true) {
|
||||
// determine the main MIME type for each extension
|
||||
$restrictions[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $accept['extension']);
|
||||
}
|
||||
if (is_array($accept['extension']) === true) {
|
||||
// determine the main MIME type for each extension
|
||||
$restrictions[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $accept['extension']);
|
||||
}
|
||||
|
||||
if (is_array($accept['type']) === true) {
|
||||
// determine the MIME types of each file type
|
||||
$mimes = [];
|
||||
foreach ($accept['type'] as $type) {
|
||||
if ($extensions = F::typeToExtensions($type)) {
|
||||
$mimes[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $extensions);
|
||||
}
|
||||
}
|
||||
if (is_array($accept['type']) === true) {
|
||||
// determine the MIME types of each file type
|
||||
$mimes = [];
|
||||
foreach ($accept['type'] as $type) {
|
||||
if ($extensions = F::typeToExtensions($type)) {
|
||||
$mimes[] = array_map(['Kirby\Filesystem\Mime', 'fromExtension'], $extensions);
|
||||
}
|
||||
}
|
||||
|
||||
$restrictions[] = array_merge(...$mimes);
|
||||
}
|
||||
}
|
||||
$restrictions[] = array_merge(...$mimes);
|
||||
}
|
||||
}
|
||||
|
||||
if ($restrictions !== []) {
|
||||
if (count($restrictions) > 1) {
|
||||
// only return the MIME types that are allowed by all restrictions
|
||||
$mimes = array_intersect(...$restrictions);
|
||||
} else {
|
||||
$mimes = $restrictions[0];
|
||||
}
|
||||
if ($restrictions !== []) {
|
||||
if (count($restrictions) > 1) {
|
||||
// only return the MIME types that are allowed by all restrictions
|
||||
$mimes = array_intersect(...$restrictions);
|
||||
} else {
|
||||
$mimes = $restrictions[0];
|
||||
}
|
||||
|
||||
// filter out empty MIME types and duplicates
|
||||
return implode(', ', array_filter(array_unique($mimes)));
|
||||
}
|
||||
// filter out empty MIME types and duplicates
|
||||
return implode(', ', array_filter(array_unique($mimes)));
|
||||
}
|
||||
|
||||
// no restrictions, accept everything
|
||||
return '*';
|
||||
}
|
||||
// no restrictions, accept everything
|
||||
return '*';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $accept
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeAccept($accept = null): array
|
||||
{
|
||||
if (is_string($accept) === true) {
|
||||
$accept = [
|
||||
'mime' => $accept
|
||||
];
|
||||
} elseif ($accept === true) {
|
||||
// explicitly no restrictions at all
|
||||
$accept = [
|
||||
'mime' => null
|
||||
];
|
||||
} elseif (empty($accept) === true) {
|
||||
// no custom restrictions
|
||||
$accept = [];
|
||||
}
|
||||
/**
|
||||
* @param mixed $accept
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeAccept($accept = null): array
|
||||
{
|
||||
if (is_string($accept) === true) {
|
||||
$accept = [
|
||||
'mime' => $accept
|
||||
];
|
||||
} elseif ($accept === true) {
|
||||
// explicitly no restrictions at all
|
||||
$accept = [
|
||||
'mime' => null
|
||||
];
|
||||
} elseif (empty($accept) === true) {
|
||||
// no custom restrictions
|
||||
$accept = [];
|
||||
}
|
||||
|
||||
$accept = array_change_key_case($accept);
|
||||
$accept = array_change_key_case($accept);
|
||||
|
||||
$defaults = [
|
||||
'extension' => null,
|
||||
'mime' => null,
|
||||
'maxheight' => null,
|
||||
'maxsize' => null,
|
||||
'maxwidth' => null,
|
||||
'minheight' => null,
|
||||
'minsize' => null,
|
||||
'minwidth' => null,
|
||||
'orientation' => null,
|
||||
'type' => null
|
||||
];
|
||||
$defaults = [
|
||||
'extension' => null,
|
||||
'mime' => null,
|
||||
'maxheight' => null,
|
||||
'maxsize' => null,
|
||||
'maxwidth' => null,
|
||||
'minheight' => null,
|
||||
'minsize' => null,
|
||||
'minwidth' => null,
|
||||
'orientation' => null,
|
||||
'type' => null
|
||||
];
|
||||
|
||||
// default type restriction if none are configured;
|
||||
// this ensures that no unexpected files are uploaded
|
||||
if (
|
||||
array_key_exists('mime', $accept) === false &&
|
||||
array_key_exists('extension', $accept) === false &&
|
||||
array_key_exists('type', $accept) === false
|
||||
) {
|
||||
$defaults['type'] = ['image', 'document', 'archive', 'audio', 'video'];
|
||||
$this->defaultTypes = true;
|
||||
}
|
||||
// default type restriction if none are configured;
|
||||
// this ensures that no unexpected files are uploaded
|
||||
if (
|
||||
array_key_exists('mime', $accept) === false &&
|
||||
array_key_exists('extension', $accept) === false &&
|
||||
array_key_exists('type', $accept) === false
|
||||
) {
|
||||
$defaults['type'] = ['image', 'document', 'archive', 'audio', 'video'];
|
||||
$this->defaultTypes = true;
|
||||
}
|
||||
|
||||
$accept = array_merge($defaults, $accept);
|
||||
$accept = array_merge($defaults, $accept);
|
||||
|
||||
// normalize the MIME, extension and type from strings into arrays
|
||||
if (is_string($accept['mime']) === true) {
|
||||
$accept['mime'] = array_map(
|
||||
fn ($mime) => $mime['value'],
|
||||
Str::accepted($accept['mime'])
|
||||
);
|
||||
}
|
||||
// normalize the MIME, extension and type from strings into arrays
|
||||
if (is_string($accept['mime']) === true) {
|
||||
$accept['mime'] = array_map(
|
||||
fn ($mime) => $mime['value'],
|
||||
Str::accepted($accept['mime'])
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($accept['extension']) === true) {
|
||||
$accept['extension'] = array_map(
|
||||
'trim',
|
||||
explode(',', $accept['extension'])
|
||||
);
|
||||
}
|
||||
if (is_string($accept['extension']) === true) {
|
||||
$accept['extension'] = array_map(
|
||||
'trim',
|
||||
explode(',', $accept['extension'])
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($accept['type']) === true) {
|
||||
$accept['type'] = array_map(
|
||||
'trim',
|
||||
explode(',', $accept['type'])
|
||||
);
|
||||
}
|
||||
if (is_string($accept['type']) === true) {
|
||||
$accept['type'] = array_map(
|
||||
'trim',
|
||||
explode(',', $accept['type'])
|
||||
);
|
||||
}
|
||||
|
||||
return $accept;
|
||||
}
|
||||
return $accept;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,200 +15,200 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
trait FileModifications
|
||||
{
|
||||
/**
|
||||
* Blurs the image by the given amount of pixels
|
||||
*
|
||||
* @param bool $pixels
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function blur($pixels = true)
|
||||
{
|
||||
return $this->thumb(['blur' => $pixels]);
|
||||
}
|
||||
/**
|
||||
* Blurs the image by the given amount of pixels
|
||||
*
|
||||
* @param bool $pixels
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function blur($pixels = true)
|
||||
{
|
||||
return $this->thumb(['blur' => $pixels]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the image to black and white
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function bw()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
/**
|
||||
* Converts the image to black and white
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function bw()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops the image by the given width and height
|
||||
*
|
||||
* @param int $width
|
||||
* @param int|null $height
|
||||
* @param string|array $options
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function crop(int $width, int $height = null, $options = null)
|
||||
{
|
||||
$quality = null;
|
||||
$crop = 'center';
|
||||
/**
|
||||
* Crops the image by the given width and height
|
||||
*
|
||||
* @param int $width
|
||||
* @param int|null $height
|
||||
* @param string|array $options
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function crop(int $width, int $height = null, $options = null)
|
||||
{
|
||||
$quality = null;
|
||||
$crop = 'center';
|
||||
|
||||
if (is_int($options) === true) {
|
||||
$quality = $options;
|
||||
} elseif (is_string($options)) {
|
||||
$crop = $options;
|
||||
} elseif (is_a($options, 'Kirby\Cms\Field') === true) {
|
||||
$crop = $options->value();
|
||||
} elseif (is_array($options)) {
|
||||
$quality = $options['quality'] ?? $quality;
|
||||
$crop = $options['crop'] ?? $crop;
|
||||
}
|
||||
if (is_int($options) === true) {
|
||||
$quality = $options;
|
||||
} elseif (is_string($options)) {
|
||||
$crop = $options;
|
||||
} elseif (is_a($options, 'Kirby\Cms\Field') === true) {
|
||||
$crop = $options->value();
|
||||
} elseif (is_array($options)) {
|
||||
$quality = $options['quality'] ?? $quality;
|
||||
$crop = $options['crop'] ?? $crop;
|
||||
}
|
||||
|
||||
return $this->thumb([
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'quality' => $quality,
|
||||
'crop' => $crop
|
||||
]);
|
||||
}
|
||||
return $this->thumb([
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'quality' => $quality,
|
||||
'crop' => $crop
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for File::bw()
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function grayscale()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
/**
|
||||
* Alias for File::bw()
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function grayscale()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for File::bw()
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function greyscale()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
/**
|
||||
* Alias for File::bw()
|
||||
*
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function greyscale()
|
||||
{
|
||||
return $this->thumb(['grayscale' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the JPEG compression quality
|
||||
*
|
||||
* @param int $quality
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function quality(int $quality)
|
||||
{
|
||||
return $this->thumb(['quality' => $quality]);
|
||||
}
|
||||
/**
|
||||
* Sets the JPEG compression quality
|
||||
*
|
||||
* @param int $quality
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
*/
|
||||
public function quality(int $quality)
|
||||
{
|
||||
return $this->thumb(['quality' => $quality]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the file with the given width and height
|
||||
* while keeping the aspect ratio.
|
||||
*
|
||||
* @param int|null $width
|
||||
* @param int|null $height
|
||||
* @param int|null $quality
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function resize(int $width = null, int $height = null, int $quality = null)
|
||||
{
|
||||
return $this->thumb([
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'quality' => $quality
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Resizes the file with the given width and height
|
||||
* while keeping the aspect ratio.
|
||||
*
|
||||
* @param int|null $width
|
||||
* @param int|null $height
|
||||
* @param int|null $quality
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function resize(int $width = null, int $height = null, int $quality = null)
|
||||
{
|
||||
return $this->thumb([
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'quality' => $quality
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a srcset definition for the given sizes
|
||||
* Sizes can be defined as a simple array. They can
|
||||
* also be set up in the config with the thumbs.srcsets option.
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param array|string|null $sizes
|
||||
* @return string|null
|
||||
*/
|
||||
public function srcset($sizes = null): ?string
|
||||
{
|
||||
if (empty($sizes) === true) {
|
||||
$sizes = $this->kirby()->option('thumbs.srcsets.default', []);
|
||||
}
|
||||
/**
|
||||
* Create a srcset definition for the given sizes
|
||||
* Sizes can be defined as a simple array. They can
|
||||
* also be set up in the config with the thumbs.srcsets option.
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param array|string|null $sizes
|
||||
* @return string|null
|
||||
*/
|
||||
public function srcset($sizes = null): ?string
|
||||
{
|
||||
if (empty($sizes) === true) {
|
||||
$sizes = $this->kirby()->option('thumbs.srcsets.default', []);
|
||||
}
|
||||
|
||||
if (is_string($sizes) === true) {
|
||||
$sizes = $this->kirby()->option('thumbs.srcsets.' . $sizes, []);
|
||||
}
|
||||
if (is_string($sizes) === true) {
|
||||
$sizes = $this->kirby()->option('thumbs.srcsets.' . $sizes, []);
|
||||
}
|
||||
|
||||
if (is_array($sizes) === false || empty($sizes) === true) {
|
||||
return null;
|
||||
}
|
||||
if (is_array($sizes) === false || empty($sizes) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$set = [];
|
||||
$set = [];
|
||||
|
||||
foreach ($sizes as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$options = $value;
|
||||
$condition = $key;
|
||||
} elseif (is_string($value) === true) {
|
||||
$options = [
|
||||
'width' => $key
|
||||
];
|
||||
$condition = $value;
|
||||
} else {
|
||||
$options = [
|
||||
'width' => $value
|
||||
];
|
||||
$condition = $value . 'w';
|
||||
}
|
||||
foreach ($sizes as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$options = $value;
|
||||
$condition = $key;
|
||||
} elseif (is_string($value) === true) {
|
||||
$options = [
|
||||
'width' => $key
|
||||
];
|
||||
$condition = $value;
|
||||
} else {
|
||||
$options = [
|
||||
'width' => $value
|
||||
];
|
||||
$condition = $value . 'w';
|
||||
}
|
||||
|
||||
$set[] = $this->thumb($options)->url() . ' ' . $condition;
|
||||
}
|
||||
$set[] = $this->thumb($options)->url() . ' ' . $condition;
|
||||
}
|
||||
|
||||
return implode(', ', $set);
|
||||
}
|
||||
return implode(', ', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a modified version of images
|
||||
* The media manager takes care of generating
|
||||
* those modified versions and putting them
|
||||
* in the right place. This is normally the
|
||||
* `/media` folder of your installation, but
|
||||
* could potentially also be a CDN or any other
|
||||
* place.
|
||||
*
|
||||
* @param array|null|string $options
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function thumb($options = null)
|
||||
{
|
||||
// thumb presets
|
||||
if (empty($options) === true) {
|
||||
$options = $this->kirby()->option('thumbs.presets.default');
|
||||
} elseif (is_string($options) === true) {
|
||||
$options = $this->kirby()->option('thumbs.presets.' . $options);
|
||||
}
|
||||
/**
|
||||
* Creates a modified version of images
|
||||
* The media manager takes care of generating
|
||||
* those modified versions and putting them
|
||||
* in the right place. This is normally the
|
||||
* `/media` folder of your installation, but
|
||||
* could potentially also be a CDN or any other
|
||||
* place.
|
||||
*
|
||||
* @param array|null|string $options
|
||||
* @return \Kirby\Cms\FileVersion|\Kirby\Cms\File
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function thumb($options = null)
|
||||
{
|
||||
// thumb presets
|
||||
if (empty($options) === true) {
|
||||
$options = $this->kirby()->option('thumbs.presets.default');
|
||||
} elseif (is_string($options) === true) {
|
||||
$options = $this->kirby()->option('thumbs.presets.' . $options);
|
||||
}
|
||||
|
||||
if (empty($options) === true || is_array($options) === false) {
|
||||
return $this;
|
||||
}
|
||||
if (empty($options) === true || is_array($options) === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// fallback to global config options
|
||||
if (isset($options['format']) === false) {
|
||||
if ($format = $this->kirby()->option('thumbs.format')) {
|
||||
$options['format'] = $format;
|
||||
}
|
||||
}
|
||||
// fallback to global config options
|
||||
if (isset($options['format']) === false) {
|
||||
if ($format = $this->kirby()->option('thumbs.format')) {
|
||||
$options['format'] = $format;
|
||||
}
|
||||
}
|
||||
|
||||
$component = $this->kirby()->component('file::version');
|
||||
$result = $component($this->kirby(), $this, $options);
|
||||
$component = $this->kirby()->component('file::version');
|
||||
$result = $component($this->kirby(), $this, $options);
|
||||
|
||||
if (
|
||||
is_a($result, 'Kirby\Cms\FileVersion') === false &&
|
||||
is_a($result, 'Kirby\Cms\File') === false &&
|
||||
is_a($result, 'Kirby\Filesystem\Asset') === false
|
||||
) {
|
||||
throw new InvalidArgumentException('The file::version component must return a File, FileVersion or Asset object');
|
||||
}
|
||||
if (
|
||||
is_a($result, 'Kirby\Cms\FileVersion') === false &&
|
||||
is_a($result, 'Kirby\Cms\File') === false &&
|
||||
is_a($result, 'Kirby\Filesystem\Asset') === false
|
||||
) {
|
||||
throw new InvalidArgumentException('The file::version component must return a File, FileVersion or Asset object');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class FilePermissions extends ModelPermissions
|
||||
{
|
||||
protected $category = 'files';
|
||||
protected $category = 'files';
|
||||
}
|
||||
|
||||
@@ -17,58 +17,58 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class FilePicker extends Picker
|
||||
{
|
||||
/**
|
||||
* Extends the basic defaults
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
$defaults = parent::defaults();
|
||||
$defaults['text'] = '{{ file.filename }}';
|
||||
/**
|
||||
* Extends the basic defaults
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
$defaults = parent::defaults();
|
||||
$defaults['text'] = '{{ file.filename }}';
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all files for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Files|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
$model = $this->options['model'];
|
||||
/**
|
||||
* Search all files for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Files|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
$model = $this->options['model'];
|
||||
|
||||
// find the right default query
|
||||
if (empty($this->options['query']) === false) {
|
||||
$query = $this->options['query'];
|
||||
} elseif (is_a($model, 'Kirby\Cms\File') === true) {
|
||||
$query = 'file.siblings';
|
||||
} else {
|
||||
$query = $model::CLASS_ALIAS . '.files';
|
||||
}
|
||||
// find the right default query
|
||||
if (empty($this->options['query']) === false) {
|
||||
$query = $this->options['query'];
|
||||
} elseif (is_a($model, 'Kirby\Cms\File') === true) {
|
||||
$query = 'file.siblings';
|
||||
} else {
|
||||
$query = $model::CLASS_ALIAS . '.files';
|
||||
}
|
||||
|
||||
// fetch all files for the picker
|
||||
$files = $model->query($query);
|
||||
// fetch all files for the picker
|
||||
$files = $model->query($query);
|
||||
|
||||
// help mitigate some typical query usage issues
|
||||
// by converting site and page objects to proper
|
||||
// pages by returning their children
|
||||
if (is_a($files, 'Kirby\Cms\Site') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\Page') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\User') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\Files') === false) {
|
||||
throw new InvalidArgumentException('Your query must return a set of files');
|
||||
}
|
||||
// help mitigate some typical query usage issues
|
||||
// by converting site and page objects to proper
|
||||
// pages by returning their children
|
||||
if (is_a($files, 'Kirby\Cms\Site') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\Page') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\User') === true) {
|
||||
$files = $files->files();
|
||||
} elseif (is_a($files, 'Kirby\Cms\Files') === false) {
|
||||
throw new InvalidArgumentException('Your query must return a set of files');
|
||||
}
|
||||
|
||||
// search
|
||||
$files = $this->search($files);
|
||||
// search
|
||||
$files = $this->search($files);
|
||||
|
||||
// paginate
|
||||
return $this->paginate($files);
|
||||
}
|
||||
// paginate
|
||||
return $this->paginate($files);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,315 +20,315 @@ use Kirby\Toolkit\V;
|
||||
*/
|
||||
class FileRules
|
||||
{
|
||||
/**
|
||||
* Validates if the filename can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $name
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a file with this name exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to rename the file
|
||||
*/
|
||||
public static function changeName(File $file, string $name): bool
|
||||
{
|
||||
if ($file->permissions()->changeName() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'file.changeName.permission',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the filename can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $name
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a file with this name exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to rename the file
|
||||
*/
|
||||
public static function changeName(File $file, string $name): bool
|
||||
{
|
||||
if ($file->permissions()->changeName() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'file.changeName.permission',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
|
||||
if (Str::length($name) === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.changeName.empty'
|
||||
]);
|
||||
}
|
||||
if (Str::length($name) === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.changeName.empty'
|
||||
]);
|
||||
}
|
||||
|
||||
$parent = $file->parent();
|
||||
$duplicate = $parent->files()->not($file)->findBy('filename', $name . '.' . $file->extension());
|
||||
$parent = $file->parent();
|
||||
$duplicate = $parent->files()->not($file)->findBy('filename', $name . '.' . $file->extension());
|
||||
|
||||
if ($duplicate) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'file.duplicate',
|
||||
'data' => ['filename' => $duplicate->filename()]
|
||||
]);
|
||||
}
|
||||
if ($duplicate) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'file.duplicate',
|
||||
'data' => ['filename' => $duplicate->filename()]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the file can be sorted
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param int $sort
|
||||
* @return bool
|
||||
*/
|
||||
public static function changeSort(File $file, int $sort): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Validates if the file can be sorted
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param int $sort
|
||||
* @return bool
|
||||
*/
|
||||
public static function changeSort(File $file, int $sort): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the file can be created
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param \Kirby\Filesystem\File $upload
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a file with the same name exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to create the file
|
||||
*/
|
||||
public static function create(File $file, BaseFile $upload): bool
|
||||
{
|
||||
// We want to ensure that we are not creating duplicate files.
|
||||
// If a file with the same name already exists
|
||||
if ($file->exists() === true) {
|
||||
// $file will be based on the props of the new file,
|
||||
// to compare templates, we need to get the props of
|
||||
// the already existing file from meta content file
|
||||
$existing = $file->parent()->file($file->filename());
|
||||
/**
|
||||
* Validates if the file can be created
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param \Kirby\Filesystem\File $upload
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a file with the same name exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to create the file
|
||||
*/
|
||||
public static function create(File $file, BaseFile $upload): bool
|
||||
{
|
||||
// We want to ensure that we are not creating duplicate files.
|
||||
// If a file with the same name already exists
|
||||
if ($file->exists() === true) {
|
||||
// $file will be based on the props of the new file,
|
||||
// to compare templates, we need to get the props of
|
||||
// the already existing file from meta content file
|
||||
$existing = $file->parent()->file($file->filename());
|
||||
|
||||
// if the new upload is the exact same file
|
||||
// and uses the same template, we can continue
|
||||
if (
|
||||
$file->sha1() === $upload->sha1() &&
|
||||
$file->template() === $existing->template()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// if the new upload is the exact same file
|
||||
// and uses the same template, we can continue
|
||||
if (
|
||||
$file->sha1() === $upload->sha1() &&
|
||||
$file->template() === $existing->template()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise throw an error for duplicate file
|
||||
throw new DuplicateException([
|
||||
'key' => 'file.duplicate',
|
||||
'data' => [
|
||||
'filename' => $file->filename()
|
||||
]
|
||||
]);
|
||||
}
|
||||
// otherwise throw an error for duplicate file
|
||||
throw new DuplicateException([
|
||||
'key' => 'file.duplicate',
|
||||
'data' => [
|
||||
'filename' => $file->filename()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if ($file->permissions()->create() !== true) {
|
||||
throw new PermissionException('The file cannot be created');
|
||||
}
|
||||
if ($file->permissions()->create() !== true) {
|
||||
throw new PermissionException('The file cannot be created');
|
||||
}
|
||||
|
||||
static::validFile($file, $upload->mime());
|
||||
static::validFile($file, $upload->mime());
|
||||
|
||||
$upload->match($file->blueprint()->accept());
|
||||
$upload->validateContents(true);
|
||||
$upload->match($file->blueprint()->accept());
|
||||
$upload->validateContents(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the file can be deleted
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the file
|
||||
*/
|
||||
public static function delete(File $file): bool
|
||||
{
|
||||
if ($file->permissions()->delete() !== true) {
|
||||
throw new PermissionException('The file cannot be deleted');
|
||||
}
|
||||
/**
|
||||
* Validates if the file can be deleted
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the file
|
||||
*/
|
||||
public static function delete(File $file): bool
|
||||
{
|
||||
if ($file->permissions()->delete() !== true) {
|
||||
throw new PermissionException('The file cannot be deleted');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the file can be replaced
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param \Kirby\Filesystem\File $upload
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to replace the file
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the file type of the new file is different
|
||||
*/
|
||||
public static function replace(File $file, BaseFile $upload): bool
|
||||
{
|
||||
if ($file->permissions()->replace() !== true) {
|
||||
throw new PermissionException('The file cannot be replaced');
|
||||
}
|
||||
/**
|
||||
* Validates if the file can be replaced
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param \Kirby\Filesystem\File $upload
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to replace the file
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the file type of the new file is different
|
||||
*/
|
||||
public static function replace(File $file, BaseFile $upload): bool
|
||||
{
|
||||
if ($file->permissions()->replace() !== true) {
|
||||
throw new PermissionException('The file cannot be replaced');
|
||||
}
|
||||
|
||||
static::validMime($file, $upload->mime());
|
||||
static::validMime($file, $upload->mime());
|
||||
|
||||
if (
|
||||
(string)$upload->mime() !== (string)$file->mime() &&
|
||||
(string)$upload->extension() !== (string)$file->extension()
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.differs',
|
||||
'data' => ['mime' => $file->mime()]
|
||||
]);
|
||||
}
|
||||
if (
|
||||
(string)$upload->mime() !== (string)$file->mime() &&
|
||||
(string)$upload->extension() !== (string)$file->extension()
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.differs',
|
||||
'data' => ['mime' => $file->mime()]
|
||||
]);
|
||||
}
|
||||
|
||||
$upload->match($file->blueprint()->accept());
|
||||
$upload->validateContents(true);
|
||||
$upload->match($file->blueprint()->accept());
|
||||
$upload->validateContents(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the file can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the file
|
||||
*/
|
||||
public static function update(File $file, array $content = []): bool
|
||||
{
|
||||
if ($file->permissions()->update() !== true) {
|
||||
throw new PermissionException('The file cannot be updated');
|
||||
}
|
||||
/**
|
||||
* Validates if the file can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the file
|
||||
*/
|
||||
public static function update(File $file, array $content = []): bool
|
||||
{
|
||||
if ($file->permissions()->update() !== true) {
|
||||
throw new PermissionException('The file cannot be updated');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the file extension
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the extension is missing or forbidden
|
||||
*/
|
||||
public static function validExtension(File $file, string $extension): bool
|
||||
{
|
||||
// make it easier to compare the extension
|
||||
$extension = strtolower($extension);
|
||||
/**
|
||||
* Validates the file extension
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the extension is missing or forbidden
|
||||
*/
|
||||
public static function validExtension(File $file, string $extension): bool
|
||||
{
|
||||
// make it easier to compare the extension
|
||||
$extension = strtolower($extension);
|
||||
|
||||
if (empty($extension) === true) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.extension.missing',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
if (empty($extension) === true) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.extension.missing',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
|
||||
if (
|
||||
Str::contains($extension, 'php') !== false ||
|
||||
Str::contains($extension, 'phar') !== false ||
|
||||
Str::contains($extension, 'phtml') !== false
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'PHP']
|
||||
]);
|
||||
}
|
||||
if (
|
||||
Str::contains($extension, 'php') !== false ||
|
||||
Str::contains($extension, 'phar') !== false ||
|
||||
Str::contains($extension, 'phtml') !== false
|
||||
) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'PHP']
|
||||
]);
|
||||
}
|
||||
|
||||
if (Str::contains($extension, 'htm') !== false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'HTML']
|
||||
]);
|
||||
}
|
||||
if (Str::contains($extension, 'htm') !== false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'HTML']
|
||||
]);
|
||||
}
|
||||
|
||||
if (V::in($extension, ['exe', App::instance()->contentExtension()]) !== false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.extension.forbidden',
|
||||
'data' => ['extension' => $extension]
|
||||
]);
|
||||
}
|
||||
if (V::in($extension, ['exe', App::instance()->contentExtension()]) !== false) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.extension.forbidden',
|
||||
'data' => ['extension' => $extension]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the extension, MIME type and filename
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null|false $mime If not passed, the MIME type is detected from the file,
|
||||
* if `false`, the MIME type is not validated for performance reasons
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the extension, MIME type or filename is missing or forbidden
|
||||
*/
|
||||
public static function validFile(File $file, $mime = null): bool
|
||||
{
|
||||
if ($mime === false) {
|
||||
// request to skip the MIME check for performance reasons
|
||||
$validMime = true;
|
||||
} else {
|
||||
$validMime = static::validMime($file, $mime ?? $file->mime());
|
||||
}
|
||||
/**
|
||||
* Validates the extension, MIME type and filename
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null|false $mime If not passed, the MIME type is detected from the file,
|
||||
* if `false`, the MIME type is not validated for performance reasons
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the extension, MIME type or filename is missing or forbidden
|
||||
*/
|
||||
public static function validFile(File $file, $mime = null): bool
|
||||
{
|
||||
if ($mime === false) {
|
||||
// request to skip the MIME check for performance reasons
|
||||
$validMime = true;
|
||||
} else {
|
||||
$validMime = static::validMime($file, $mime ?? $file->mime());
|
||||
}
|
||||
|
||||
return
|
||||
$validMime &&
|
||||
static::validExtension($file, $file->extension()) &&
|
||||
static::validFilename($file, $file->filename());
|
||||
}
|
||||
return
|
||||
$validMime &&
|
||||
static::validExtension($file, $file->extension()) &&
|
||||
static::validFilename($file, $file->filename());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the filename
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the filename is missing or forbidden
|
||||
*/
|
||||
public static function validFilename(File $file, string $filename): bool
|
||||
{
|
||||
// make it easier to compare the filename
|
||||
$filename = strtolower($filename);
|
||||
/**
|
||||
* Validates the filename
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the filename is missing or forbidden
|
||||
*/
|
||||
public static function validFilename(File $file, string $filename): bool
|
||||
{
|
||||
// make it easier to compare the filename
|
||||
$filename = strtolower($filename);
|
||||
|
||||
// check for missing filenames
|
||||
if (empty($filename)) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.name.missing'
|
||||
]);
|
||||
}
|
||||
// check for missing filenames
|
||||
if (empty($filename)) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.name.missing'
|
||||
]);
|
||||
}
|
||||
|
||||
// Block htaccess files
|
||||
if (Str::startsWith($filename, '.ht')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'Apache config']
|
||||
]);
|
||||
}
|
||||
// Block htaccess files
|
||||
if (Str::startsWith($filename, '.ht')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'Apache config']
|
||||
]);
|
||||
}
|
||||
|
||||
// Block invisible files
|
||||
if (Str::startsWith($filename, '.')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'invisible']
|
||||
]);
|
||||
}
|
||||
// Block invisible files
|
||||
if (Str::startsWith($filename, '.')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'invisible']
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the MIME type
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null $mime
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the MIME type is missing or forbidden
|
||||
*/
|
||||
public static function validMime(File $file, string $mime = null): bool
|
||||
{
|
||||
// make it easier to compare the mime
|
||||
$mime = strtolower($mime);
|
||||
/**
|
||||
* Validates the MIME type
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null $mime
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the MIME type is missing or forbidden
|
||||
*/
|
||||
public static function validMime(File $file, string $mime = null): bool
|
||||
{
|
||||
// make it easier to compare the mime
|
||||
$mime = strtolower($mime);
|
||||
|
||||
if (empty($mime)) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.missing',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
if (empty($mime)) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.missing',
|
||||
'data' => ['filename' => $file->filename()]
|
||||
]);
|
||||
}
|
||||
|
||||
if (Str::contains($mime, 'php')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'PHP']
|
||||
]);
|
||||
}
|
||||
if (Str::contains($mime, 'php')) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.type.forbidden',
|
||||
'data' => ['type' => 'PHP']
|
||||
]);
|
||||
}
|
||||
|
||||
if (V::in($mime, ['text/html', 'application/x-msdownload'])) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.forbidden',
|
||||
'data' => ['mime' => $mime]
|
||||
]);
|
||||
}
|
||||
if (V::in($mime, ['text/html', 'application/x-msdownload'])) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'file.mime.forbidden',
|
||||
'data' => ['mime' => $mime]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,130 +15,130 @@ use Kirby\Filesystem\IsFile;
|
||||
*/
|
||||
class FileVersion
|
||||
{
|
||||
use IsFile;
|
||||
use IsFile;
|
||||
|
||||
protected $modifications;
|
||||
protected $original;
|
||||
protected $modifications;
|
||||
protected $original;
|
||||
|
||||
/**
|
||||
* Proxy for public properties, asset methods
|
||||
* and content field getters
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
// public property access
|
||||
if (isset($this->$method) === true) {
|
||||
return $this->$method;
|
||||
}
|
||||
/**
|
||||
* Proxy for public properties, asset methods
|
||||
* and content field getters
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
// public property access
|
||||
if (isset($this->$method) === true) {
|
||||
return $this->$method;
|
||||
}
|
||||
|
||||
// asset method proxy
|
||||
if (method_exists($this->asset(), $method)) {
|
||||
if ($this->exists() === false) {
|
||||
$this->save();
|
||||
}
|
||||
// asset method proxy
|
||||
if (method_exists($this->asset(), $method)) {
|
||||
if ($this->exists() === false) {
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $this->asset()->$method(...$arguments);
|
||||
}
|
||||
return $this->asset()->$method(...$arguments);
|
||||
}
|
||||
|
||||
// content fields
|
||||
if (is_a($this->original(), 'Kirby\Cms\File') === true) {
|
||||
return $this->original()->content()->get($method, $arguments);
|
||||
}
|
||||
}
|
||||
// content fields
|
||||
if (is_a($this->original(), 'Kirby\Cms\File') === true) {
|
||||
return $this->original()->content()->get($method, $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return dirname($this->original()->id()) . '/' . $this->filename();
|
||||
}
|
||||
/**
|
||||
* Returns the unique ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return dirname($this->original()->id()) . '/' . $this->filename();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent Kirby App instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->original()->kirby();
|
||||
}
|
||||
/**
|
||||
* Returns the parent Kirby App instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->original()->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all applied modifications
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function modifications(): array
|
||||
{
|
||||
return $this->modifications ?? [];
|
||||
}
|
||||
/**
|
||||
* Returns an array with all applied modifications
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function modifications(): array
|
||||
{
|
||||
return $this->modifications ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the original File object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function original()
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
/**
|
||||
* Returns the instance of the original File object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function original()
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the stored modifications and
|
||||
* saves the file on disk
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->kirby()->thumb(
|
||||
$this->original()->root(),
|
||||
$this->root(),
|
||||
$this->modifications()
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Applies the stored modifications and
|
||||
* saves the file on disk
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->kirby()->thumb(
|
||||
$this->original()->root(),
|
||||
$this->root(),
|
||||
$this->modifications()
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for modifications
|
||||
*
|
||||
* @param array|null $modifications
|
||||
*/
|
||||
protected function setModifications(array $modifications = null)
|
||||
{
|
||||
$this->modifications = $modifications;
|
||||
}
|
||||
/**
|
||||
* Setter for modifications
|
||||
*
|
||||
* @param array|null $modifications
|
||||
*/
|
||||
protected function setModifications(array $modifications = null)
|
||||
{
|
||||
$this->modifications = $modifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the original File object
|
||||
*
|
||||
* @param $original
|
||||
*/
|
||||
protected function setOriginal($original)
|
||||
{
|
||||
$this->original = $original;
|
||||
}
|
||||
/**
|
||||
* Setter for the original File object
|
||||
*
|
||||
* @param $original
|
||||
*/
|
||||
protected function setOriginal($original)
|
||||
{
|
||||
$this->original = $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = array_merge($this->asset()->toArray(), [
|
||||
'modifications' => $this->modifications(),
|
||||
]);
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = array_merge($this->asset()->toArray(), [
|
||||
'modifications' => $this->modifications(),
|
||||
]);
|
||||
|
||||
ksort($array);
|
||||
ksort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,177 +21,177 @@ use Kirby\Filesystem\F;
|
||||
*/
|
||||
class Files extends Collection
|
||||
{
|
||||
/**
|
||||
* All registered files methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
/**
|
||||
* All registered files methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
|
||||
/**
|
||||
* Adds a single file or
|
||||
* an entire second collection to the
|
||||
* current collection
|
||||
*
|
||||
* @param \Kirby\Cms\Files|\Kirby\Cms\File|string $object
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException When no `File` or `Files` object or an ID of an existing file is passed
|
||||
*/
|
||||
public function add($object)
|
||||
{
|
||||
// add a files collection
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
/**
|
||||
* Adds a single file or
|
||||
* an entire second collection to the
|
||||
* current collection
|
||||
*
|
||||
* @param \Kirby\Cms\Files|\Kirby\Cms\File|string $object
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException When no `File` or `Files` object or an ID of an existing file is passed
|
||||
*/
|
||||
public function add($object)
|
||||
{
|
||||
// add a files collection
|
||||
if (is_a($object, self::class) === true) {
|
||||
$this->data = array_merge($this->data, $object->data);
|
||||
|
||||
// add a file by id
|
||||
} elseif (is_string($object) === true && $file = App::instance()->file($object)) {
|
||||
$this->__set($file->id(), $file);
|
||||
// add a file by id
|
||||
} elseif (is_string($object) === true && $file = App::instance()->file($object)) {
|
||||
$this->__set($file->id(), $file);
|
||||
|
||||
// add a file object
|
||||
} elseif (is_a($object, 'Kirby\Cms\File') === true) {
|
||||
$this->__set($object->id(), $object);
|
||||
// add a file object
|
||||
} elseif (is_a($object, 'Kirby\Cms\File') === true) {
|
||||
$this->__set($object->id(), $object);
|
||||
|
||||
// give a useful error message on invalid input;
|
||||
// silently ignore "empty" values for compatibility with existing setups
|
||||
} elseif (in_array($object, [null, false, true], true) !== true) {
|
||||
throw new InvalidArgumentException('You must pass a Files or File object or an ID of an existing file to the Files collection');
|
||||
}
|
||||
// give a useful error message on invalid input;
|
||||
// silently ignore "empty" values for compatibility with existing setups
|
||||
} elseif (in_array($object, [null, false, true], true) !== true) {
|
||||
throw new InvalidArgumentException('You must pass a Files or File object or an ID of an existing file to the Files collection');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort all given files by the
|
||||
* order in the array
|
||||
*
|
||||
* @param array $files List of file ids
|
||||
* @param int $offset Sorting offset
|
||||
* @return $this
|
||||
*/
|
||||
public function changeSort(array $files, int $offset = 0)
|
||||
{
|
||||
foreach ($files as $filename) {
|
||||
if ($file = $this->get($filename)) {
|
||||
$offset++;
|
||||
$file->changeSort($offset);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sort all given files by the
|
||||
* order in the array
|
||||
*
|
||||
* @param array $files List of file ids
|
||||
* @param int $offset Sorting offset
|
||||
* @return $this
|
||||
*/
|
||||
public function changeSort(array $files, int $offset = 0)
|
||||
{
|
||||
foreach ($files as $filename) {
|
||||
if ($file = $this->get($filename)) {
|
||||
$offset++;
|
||||
$file->changeSort($offset);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a files collection from an array of props
|
||||
*
|
||||
* @param array $files
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $files, Model $parent)
|
||||
{
|
||||
$collection = new static([], $parent);
|
||||
$kirby = $parent->kirby();
|
||||
/**
|
||||
* Creates a files collection from an array of props
|
||||
*
|
||||
* @param array $files
|
||||
* @param \Kirby\Cms\Model $parent
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $files, Model $parent)
|
||||
{
|
||||
$collection = new static([], $parent);
|
||||
$kirby = $parent->kirby();
|
||||
|
||||
foreach ($files as $props) {
|
||||
$props['collection'] = $collection;
|
||||
$props['kirby'] = $kirby;
|
||||
$props['parent'] = $parent;
|
||||
foreach ($files as $props) {
|
||||
$props['collection'] = $collection;
|
||||
$props['kirby'] = $kirby;
|
||||
$props['parent'] = $parent;
|
||||
|
||||
$file = File::factory($props);
|
||||
$file = File::factory($props);
|
||||
|
||||
$collection->data[$file->id()] = $file;
|
||||
}
|
||||
$collection->data[$file->id()] = $file;
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a file by id/filename
|
||||
* @deprecated 3.7.0 Use `$files->find()` instead
|
||||
* @todo 3.8.0 Remove method
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $id
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function findById(string $id)
|
||||
{
|
||||
Helpers::deprecated('Cms\Files::findById() has been deprecated and will be removed in Kirby 3.8.0. Use $files->find() instead.');
|
||||
/**
|
||||
* Tries to find a file by id/filename
|
||||
* @deprecated 3.7.0 Use `$files->find()` instead
|
||||
* @todo 3.8.0 Remove method
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $id
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function findById(string $id)
|
||||
{
|
||||
Helpers::deprecated('Cms\Files::findById() has been deprecated and will be removed in Kirby 3.8.0. Use $files->find() instead.');
|
||||
|
||||
return $this->findByKey($id);
|
||||
}
|
||||
return $this->findByKey($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a file by its filename
|
||||
* @internal Use `$files->find()` instead
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function findByKey(string $key)
|
||||
{
|
||||
return $this->get(ltrim($this->parent->id() . '/' . $key, '/'));
|
||||
}
|
||||
/**
|
||||
* Finds a file by its filename
|
||||
* @internal Use `$files->find()` instead
|
||||
*
|
||||
* @param string $key
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function findByKey(string $key)
|
||||
{
|
||||
return $this->get(ltrim($this->parent->id() . '/' . $key, '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file size for all
|
||||
* files in the collection in a
|
||||
* human-readable format
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string|null|false $locale Locale for number formatting,
|
||||
* `null` for the current locale,
|
||||
* `false` to disable number formatting
|
||||
* @return string
|
||||
*/
|
||||
public function niceSize($locale = null): string
|
||||
{
|
||||
return F::niceSize($this->size(), $locale);
|
||||
}
|
||||
/**
|
||||
* Returns the file size for all
|
||||
* files in the collection in a
|
||||
* human-readable format
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string|null|false $locale Locale for number formatting,
|
||||
* `null` for the current locale,
|
||||
* `false` to disable number formatting
|
||||
* @return string
|
||||
*/
|
||||
public function niceSize($locale = null): string
|
||||
{
|
||||
return F::niceSize($this->size(), $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw size for all
|
||||
* files in the collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function size(): int
|
||||
{
|
||||
return F::size($this->values(fn ($file) => $file->root()));
|
||||
}
|
||||
/**
|
||||
* Returns the raw size for all
|
||||
* files in the collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function size(): int
|
||||
{
|
||||
return F::size($this->values(fn ($file) => $file->root()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection sorted by
|
||||
* the sort number and the filename
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function sorted()
|
||||
{
|
||||
return $this->sort('sort', 'asc', 'filename', 'asc');
|
||||
}
|
||||
/**
|
||||
* Returns the collection sorted by
|
||||
* the sort number and the filename
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function sorted()
|
||||
{
|
||||
return $this->sort('sort', 'asc', 'filename', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all files by the given template
|
||||
*
|
||||
* @param null|string|array $template
|
||||
* @return $this|static
|
||||
*/
|
||||
public function template($template)
|
||||
{
|
||||
if (empty($template) === true) {
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Filter all files by the given template
|
||||
*
|
||||
* @param null|string|array $template
|
||||
* @return $this|static
|
||||
*/
|
||||
public function template($template)
|
||||
{
|
||||
if (empty($template) === true) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($template === 'default') {
|
||||
$template = ['default', ''];
|
||||
}
|
||||
if ($template === 'default') {
|
||||
$template = ['default', ''];
|
||||
}
|
||||
|
||||
return $this->filter(
|
||||
'template',
|
||||
is_array($template) ? 'in' : '==',
|
||||
$template
|
||||
);
|
||||
}
|
||||
return $this->filter(
|
||||
'template',
|
||||
is_array($template) ? 'in' : '==',
|
||||
$template
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,173 +19,173 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Find
|
||||
{
|
||||
/**
|
||||
* Returns the file object for the given
|
||||
* parent path and filename
|
||||
*
|
||||
* @param string|null $path Path to file's parent model
|
||||
* @param string $filename Filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the file cannot be found
|
||||
*/
|
||||
public static function file(string $path = null, string $filename)
|
||||
{
|
||||
$filename = urldecode($filename);
|
||||
$file = static::parent($path)->file($filename);
|
||||
/**
|
||||
* Returns the file object for the given
|
||||
* parent path and filename
|
||||
*
|
||||
* @param string|null $path Path to file's parent model
|
||||
* @param string $filename Filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the file cannot be found
|
||||
*/
|
||||
public static function file(string $path = null, string $filename)
|
||||
{
|
||||
$filename = urldecode($filename);
|
||||
$file = static::parent($path)->file($filename);
|
||||
|
||||
if ($file && $file->isReadable() === true) {
|
||||
return $file;
|
||||
}
|
||||
if ($file && $file->isReadable() === true) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'file.notFound',
|
||||
'data' => [
|
||||
'filename' => $filename
|
||||
]
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => 'file.notFound',
|
||||
'data' => [
|
||||
'filename' => $filename
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language object for the given code
|
||||
*
|
||||
* @param string $code Language code
|
||||
* @return \Kirby\Cms\Language|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the language cannot be found
|
||||
*/
|
||||
public static function language(string $code)
|
||||
{
|
||||
if ($language = App::instance()->language($code)) {
|
||||
return $language;
|
||||
}
|
||||
/**
|
||||
* Returns the language object for the given code
|
||||
*
|
||||
* @param string $code Language code
|
||||
* @return \Kirby\Cms\Language|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the language cannot be found
|
||||
*/
|
||||
public static function language(string $code)
|
||||
{
|
||||
if ($language = App::instance()->language($code)) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'language.notFound',
|
||||
'data' => [
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => 'language.notFound',
|
||||
'data' => [
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object for the given id
|
||||
*
|
||||
* @param string $id Page's id
|
||||
* @return \Kirby\Cms\Page|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the page cannot be found
|
||||
*/
|
||||
public static function page(string $id)
|
||||
{
|
||||
$id = str_replace(['+', ' '], '/', $id);
|
||||
$page = App::instance()->page($id);
|
||||
/**
|
||||
* Returns the page object for the given id
|
||||
*
|
||||
* @param string $id Page's id
|
||||
* @return \Kirby\Cms\Page|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the page cannot be found
|
||||
*/
|
||||
public static function page(string $id)
|
||||
{
|
||||
$id = str_replace(['+', ' '], '/', $id);
|
||||
$page = App::instance()->page($id);
|
||||
|
||||
if ($page && $page->isReadable() === true) {
|
||||
return $page;
|
||||
}
|
||||
if ($page && $page->isReadable() === true) {
|
||||
return $page;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'page.notFound',
|
||||
'data' => [
|
||||
'slug' => $id
|
||||
]
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => 'page.notFound',
|
||||
'data' => [
|
||||
'slug' => $id
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model's object for the given path
|
||||
*
|
||||
* @param string $path Path to parent model
|
||||
* @return \Kirby\Cms\Model|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid
|
||||
* @throws \Kirby\Exception\NotFoundException if the model cannot be found
|
||||
*/
|
||||
public static function parent(string $path)
|
||||
{
|
||||
$path = trim($path, '/');
|
||||
$modelType = in_array($path, ['site', 'account']) ? $path : trim(dirname($path), '/');
|
||||
$modelTypes = [
|
||||
'site' => 'site',
|
||||
'users' => 'user',
|
||||
'pages' => 'page',
|
||||
'account' => 'account'
|
||||
];
|
||||
/**
|
||||
* Returns the model's object for the given path
|
||||
*
|
||||
* @param string $path Path to parent model
|
||||
* @return \Kirby\Cms\Model|null
|
||||
* @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid
|
||||
* @throws \Kirby\Exception\NotFoundException if the model cannot be found
|
||||
*/
|
||||
public static function parent(string $path)
|
||||
{
|
||||
$path = trim($path, '/');
|
||||
$modelType = in_array($path, ['site', 'account']) ? $path : trim(dirname($path), '/');
|
||||
$modelTypes = [
|
||||
'site' => 'site',
|
||||
'users' => 'user',
|
||||
'pages' => 'page',
|
||||
'account' => 'account'
|
||||
];
|
||||
|
||||
$modelName = $modelTypes[$modelType] ?? null;
|
||||
$modelName = $modelTypes[$modelType] ?? null;
|
||||
|
||||
if (Str::endsWith($modelType, '/files') === true) {
|
||||
$modelName = 'file';
|
||||
}
|
||||
if (Str::endsWith($modelType, '/files') === true) {
|
||||
$modelName = 'file';
|
||||
}
|
||||
|
||||
$kirby = App::instance();
|
||||
$kirby = App::instance();
|
||||
|
||||
switch ($modelName) {
|
||||
case 'site':
|
||||
$model = $kirby->site();
|
||||
break;
|
||||
case 'account':
|
||||
$model = static::user();
|
||||
break;
|
||||
case 'page':
|
||||
$model = static::page(basename($path));
|
||||
break;
|
||||
case 'file':
|
||||
$model = static::file(...explode('/files/', $path));
|
||||
break;
|
||||
case 'user':
|
||||
$model = $kirby->user(basename($path));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid model type: ' . $modelType);
|
||||
}
|
||||
switch ($modelName) {
|
||||
case 'site':
|
||||
$model = $kirby->site();
|
||||
break;
|
||||
case 'account':
|
||||
$model = static::user();
|
||||
break;
|
||||
case 'page':
|
||||
$model = static::page(basename($path));
|
||||
break;
|
||||
case 'file':
|
||||
$model = static::file(...explode('/files/', $path));
|
||||
break;
|
||||
case 'user':
|
||||
$model = $kirby->user(basename($path));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid model type: ' . $modelType);
|
||||
}
|
||||
|
||||
if ($model) {
|
||||
return $model;
|
||||
}
|
||||
if ($model) {
|
||||
return $model;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => $modelName . '.undefined'
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => $modelName . '.undefined'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user object for the given id or
|
||||
* returns the current authenticated user if no
|
||||
* id is passed
|
||||
*
|
||||
* @param string|null $id User's id
|
||||
* @return \Kirby\Cms\User|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found
|
||||
*/
|
||||
public static function user(string $id = null)
|
||||
{
|
||||
// account is a reserved word to find the current
|
||||
// user. It's used in various API and area routes.
|
||||
if ($id === 'account') {
|
||||
$id = null;
|
||||
}
|
||||
/**
|
||||
* Returns the user object for the given id or
|
||||
* returns the current authenticated user if no
|
||||
* id is passed
|
||||
*
|
||||
* @param string|null $id User's id
|
||||
* @return \Kirby\Cms\User|null
|
||||
* @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found
|
||||
*/
|
||||
public static function user(string $id = null)
|
||||
{
|
||||
// account is a reserved word to find the current
|
||||
// user. It's used in various API and area routes.
|
||||
if ($id === 'account') {
|
||||
$id = null;
|
||||
}
|
||||
|
||||
$kirby = App::instance();
|
||||
$kirby = App::instance();
|
||||
|
||||
// get the authenticated user
|
||||
if ($id === null) {
|
||||
if ($user = $kirby->user(null, $kirby->option('api.allowImpersonation', false))) {
|
||||
return $user;
|
||||
}
|
||||
// get the authenticated user
|
||||
if ($id === null) {
|
||||
if ($user = $kirby->user(null, $kirby->option('api.allowImpersonation', false))) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.undefined'
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.undefined'
|
||||
]);
|
||||
}
|
||||
|
||||
// get a specific user by id
|
||||
if ($user = $kirby->user($id)) {
|
||||
return $user;
|
||||
}
|
||||
// get a specific user by id
|
||||
if ($user = $kirby->user($id)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.notFound',
|
||||
'data' => [
|
||||
'name' => $id
|
||||
]
|
||||
]);
|
||||
}
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.notFound',
|
||||
'data' => [
|
||||
'name' => $id
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,227 +16,227 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
trait HasChildren
|
||||
{
|
||||
/**
|
||||
* The list of available published children
|
||||
*
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
public $children;
|
||||
/**
|
||||
* The list of available published children
|
||||
*
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
public $children;
|
||||
|
||||
/**
|
||||
* The list of available draft children
|
||||
*
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
public $drafts;
|
||||
/**
|
||||
* The list of available draft children
|
||||
*
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
public $drafts;
|
||||
|
||||
/**
|
||||
* Returns all published children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
if (is_a($this->children, 'Kirby\Cms\Pages') === true) {
|
||||
return $this->children;
|
||||
}
|
||||
/**
|
||||
* Returns all published children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
if (is_a($this->children, 'Kirby\Cms\Pages') === true) {
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
return $this->children = Pages::factory($this->inventory()['children'], $this);
|
||||
}
|
||||
return $this->children = Pages::factory($this->inventory()['children'], $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all published and draft children at the same time
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function childrenAndDrafts()
|
||||
{
|
||||
return $this->children()->merge($this->drafts());
|
||||
}
|
||||
/**
|
||||
* Returns all published and draft children at the same time
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function childrenAndDrafts()
|
||||
{
|
||||
return $this->children()->merge($this->drafts());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of IDs for the model's
|
||||
* `toArray` method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function convertChildrenToArray(): array
|
||||
{
|
||||
return $this->children()->keys();
|
||||
}
|
||||
/**
|
||||
* Returns a list of IDs for the model's
|
||||
* `toArray` method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function convertChildrenToArray(): array
|
||||
{
|
||||
return $this->children()->keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a draft child by ID
|
||||
*
|
||||
* @param string $path
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function draft(string $path)
|
||||
{
|
||||
$path = str_replace('_drafts/', '', $path);
|
||||
/**
|
||||
* Searches for a draft child by ID
|
||||
*
|
||||
* @param string $path
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function draft(string $path)
|
||||
{
|
||||
$path = str_replace('_drafts/', '', $path);
|
||||
|
||||
if (Str::contains($path, '/') === false) {
|
||||
return $this->drafts()->find($path);
|
||||
}
|
||||
if (Str::contains($path, '/') === false) {
|
||||
return $this->drafts()->find($path);
|
||||
}
|
||||
|
||||
$parts = explode('/', $path);
|
||||
$parent = $this;
|
||||
$parts = explode('/', $path);
|
||||
$parent = $this;
|
||||
|
||||
foreach ($parts as $slug) {
|
||||
if ($page = $parent->find($slug)) {
|
||||
$parent = $page;
|
||||
continue;
|
||||
}
|
||||
foreach ($parts as $slug) {
|
||||
if ($page = $parent->find($slug)) {
|
||||
$parent = $page;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($draft = $parent->drafts()->find($slug)) {
|
||||
$parent = $draft;
|
||||
continue;
|
||||
}
|
||||
if ($draft = $parent->drafts()->find($slug)) {
|
||||
$parent = $draft;
|
||||
continue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all draft children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function drafts()
|
||||
{
|
||||
if (is_a($this->drafts, 'Kirby\Cms\Pages') === true) {
|
||||
return $this->drafts;
|
||||
}
|
||||
/**
|
||||
* Returns all draft children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function drafts()
|
||||
{
|
||||
if (is_a($this->drafts, 'Kirby\Cms\Pages') === true) {
|
||||
return $this->drafts;
|
||||
}
|
||||
|
||||
$kirby = $this->kirby();
|
||||
$kirby = $this->kirby();
|
||||
|
||||
// create the inventory for all drafts
|
||||
$inventory = Dir::inventory(
|
||||
$this->root() . '/_drafts',
|
||||
$kirby->contentExtension(),
|
||||
$kirby->contentIgnore(),
|
||||
$kirby->multilang()
|
||||
);
|
||||
// create the inventory for all drafts
|
||||
$inventory = Dir::inventory(
|
||||
$this->root() . '/_drafts',
|
||||
$kirby->contentExtension(),
|
||||
$kirby->contentIgnore(),
|
||||
$kirby->multilang()
|
||||
);
|
||||
|
||||
return $this->drafts = Pages::factory($inventory['children'], $this, true);
|
||||
}
|
||||
return $this->drafts = Pages::factory($inventory['children'], $this, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds one or multiple published children by ID
|
||||
*
|
||||
* @param string ...$arguments
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Pages|null
|
||||
*/
|
||||
public function find(...$arguments)
|
||||
{
|
||||
return $this->children()->find(...$arguments);
|
||||
}
|
||||
/**
|
||||
* Finds one or multiple published children by ID
|
||||
*
|
||||
* @param string ...$arguments
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Pages|null
|
||||
*/
|
||||
public function find(...$arguments)
|
||||
{
|
||||
return $this->children()->find(...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single published or draft child
|
||||
*
|
||||
* @param string $path
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function findPageOrDraft(string $path)
|
||||
{
|
||||
return $this->children()->find($path) ?? $this->drafts()->find($path);
|
||||
}
|
||||
/**
|
||||
* Finds a single published or draft child
|
||||
*
|
||||
* @param string $path
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function findPageOrDraft(string $path)
|
||||
{
|
||||
return $this->children()->find($path) ?? $this->drafts()->find($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all published children of published children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function grandChildren()
|
||||
{
|
||||
return $this->children()->children();
|
||||
}
|
||||
/**
|
||||
* Returns a collection of all published children of published children
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function grandChildren()
|
||||
{
|
||||
return $this->children()->children();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the model has any published children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren(): bool
|
||||
{
|
||||
return $this->children()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the model has any published children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren(): bool
|
||||
{
|
||||
return $this->children()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the model has any draft children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDrafts(): bool
|
||||
{
|
||||
return $this->drafts()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the model has any draft children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDrafts(): bool
|
||||
{
|
||||
return $this->drafts()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the page has any listed children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasListedChildren(): bool
|
||||
{
|
||||
return $this->children()->listed()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the page has any listed children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasListedChildren(): bool
|
||||
{
|
||||
return $this->children()->listed()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the page has any unlisted children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUnlistedChildren(): bool
|
||||
{
|
||||
return $this->children()->unlisted()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the page has any unlisted children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUnlistedChildren(): bool
|
||||
{
|
||||
return $this->children()->unlisted()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a flat child index
|
||||
*
|
||||
* @param bool $drafts If set to `true`, draft children are included
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function index(bool $drafts = false)
|
||||
{
|
||||
if ($drafts === true) {
|
||||
return $this->childrenAndDrafts()->index($drafts);
|
||||
} else {
|
||||
return $this->children()->index();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a flat child index
|
||||
*
|
||||
* @param bool $drafts If set to `true`, draft children are included
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function index(bool $drafts = false)
|
||||
{
|
||||
if ($drafts === true) {
|
||||
return $this->childrenAndDrafts()->index($drafts);
|
||||
} else {
|
||||
return $this->children()->index();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the published children collection
|
||||
*
|
||||
* @param array|null $children
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChildren(array $children = null)
|
||||
{
|
||||
if ($children !== null) {
|
||||
$this->children = Pages::factory($children, $this);
|
||||
}
|
||||
/**
|
||||
* Sets the published children collection
|
||||
*
|
||||
* @param array|null $children
|
||||
* @return $this
|
||||
*/
|
||||
protected function setChildren(array $children = null)
|
||||
{
|
||||
if ($children !== null) {
|
||||
$this->children = Pages::factory($children, $this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the draft children collection
|
||||
*
|
||||
* @param array|null $drafts
|
||||
* @return $this
|
||||
*/
|
||||
protected function setDrafts(array $drafts = null)
|
||||
{
|
||||
if ($drafts !== null) {
|
||||
$this->drafts = Pages::factory($drafts, $this, true);
|
||||
}
|
||||
/**
|
||||
* Sets the draft children collection
|
||||
*
|
||||
* @param array|null $drafts
|
||||
* @return $this
|
||||
*/
|
||||
protected function setDrafts(array $drafts = null)
|
||||
{
|
||||
if ($drafts !== null) {
|
||||
$this->drafts = Pages::factory($drafts, $this, true);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,214 +13,214 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
trait HasFiles
|
||||
{
|
||||
/**
|
||||
* The Files collection
|
||||
*
|
||||
* @var \Kirby\Cms\Files
|
||||
*/
|
||||
protected $files;
|
||||
/**
|
||||
* The Files collection
|
||||
*
|
||||
* @var \Kirby\Cms\Files
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Filters the Files collection by type audio
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function audio()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'audio');
|
||||
}
|
||||
/**
|
||||
* Filters the Files collection by type audio
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function audio()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'audio');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Files collection by type code
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function code()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'code');
|
||||
}
|
||||
/**
|
||||
* Filters the Files collection by type code
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function code()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'code');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of file ids
|
||||
* for the toArray method of the model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function convertFilesToArray(): array
|
||||
{
|
||||
return $this->files()->keys();
|
||||
}
|
||||
/**
|
||||
* Returns a list of file ids
|
||||
* for the toArray method of the model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function convertFilesToArray(): array
|
||||
{
|
||||
return $this->files()->keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\File
|
||||
*/
|
||||
public function createFile(array $props)
|
||||
{
|
||||
$props = array_merge($props, [
|
||||
'parent' => $this,
|
||||
'url' => null
|
||||
]);
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\File
|
||||
*/
|
||||
public function createFile(array $props)
|
||||
{
|
||||
$props = array_merge($props, [
|
||||
'parent' => $this,
|
||||
'url' => null
|
||||
]);
|
||||
|
||||
return File::create($props);
|
||||
}
|
||||
return File::create($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Files collection by type documents
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function documents()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'document');
|
||||
}
|
||||
/**
|
||||
* Filters the Files collection by type documents
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function documents()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'document');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific file by filename or the first one
|
||||
*
|
||||
* @param string|null $filename
|
||||
* @param string $in
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function file(string $filename = null, string $in = 'files')
|
||||
{
|
||||
if ($filename === null) {
|
||||
return $this->$in()->first();
|
||||
}
|
||||
/**
|
||||
* Returns a specific file by filename or the first one
|
||||
*
|
||||
* @param string|null $filename
|
||||
* @param string $in
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function file(string $filename = null, string $in = 'files')
|
||||
{
|
||||
if ($filename === null) {
|
||||
return $this->$in()->first();
|
||||
}
|
||||
|
||||
if (strpos($filename, '/') !== false) {
|
||||
$path = dirname($filename);
|
||||
$filename = basename($filename);
|
||||
if (strpos($filename, '/') !== false) {
|
||||
$path = dirname($filename);
|
||||
$filename = basename($filename);
|
||||
|
||||
if ($page = $this->find($path)) {
|
||||
return $page->$in()->find($filename);
|
||||
}
|
||||
if ($page = $this->find($path)) {
|
||||
return $page->$in()->find($filename);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->$in()->find($filename);
|
||||
}
|
||||
return $this->$in()->find($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Files collection
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
if (is_a($this->files, 'Kirby\Cms\Files') === true) {
|
||||
return $this->files;
|
||||
}
|
||||
/**
|
||||
* Returns the Files collection
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
if (is_a($this->files, 'Kirby\Cms\Files') === true) {
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
return $this->files = Files::factory($this->inventory()['files'], $this);
|
||||
}
|
||||
return $this->files = Files::factory($this->inventory()['files'], $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any audio files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAudio(): bool
|
||||
{
|
||||
return $this->audio()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any audio files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAudio(): bool
|
||||
{
|
||||
return $this->audio()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any code files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCode(): bool
|
||||
{
|
||||
return $this->code()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any code files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCode(): bool
|
||||
{
|
||||
return $this->code()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any document files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDocuments(): bool
|
||||
{
|
||||
return $this->documents()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any document files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDocuments(): bool
|
||||
{
|
||||
return $this->documents()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFiles(): bool
|
||||
{
|
||||
return $this->files()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFiles(): bool
|
||||
{
|
||||
return $this->files()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any images
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasImages(): bool
|
||||
{
|
||||
return $this->images()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any images
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasImages(): bool
|
||||
{
|
||||
return $this->images()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Files collection has any videos
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVideos(): bool
|
||||
{
|
||||
return $this->videos()->count() > 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the Files collection has any videos
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVideos(): bool
|
||||
{
|
||||
return $this->videos()->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific image by filename or the first one
|
||||
*
|
||||
* @param string|null $filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function image(string $filename = null)
|
||||
{
|
||||
return $this->file($filename, 'images');
|
||||
}
|
||||
/**
|
||||
* Returns a specific image by filename or the first one
|
||||
*
|
||||
* @param string|null $filename
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function image(string $filename = null)
|
||||
{
|
||||
return $this->file($filename, 'images');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Files collection by type image
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'image');
|
||||
}
|
||||
/**
|
||||
* Filters the Files collection by type image
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'image');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Files collection
|
||||
*
|
||||
* @param \Kirby\Cms\Files|null $files
|
||||
* @return $this
|
||||
*/
|
||||
protected function setFiles(array $files = null)
|
||||
{
|
||||
if ($files !== null) {
|
||||
$this->files = Files::factory($files, $this);
|
||||
}
|
||||
/**
|
||||
* Sets the Files collection
|
||||
*
|
||||
* @param \Kirby\Cms\Files|null $files
|
||||
* @return $this
|
||||
*/
|
||||
protected function setFiles(array $files = null)
|
||||
{
|
||||
if ($files !== null) {
|
||||
$this->files = Files::factory($files, $this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Files collection by type videos
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'video');
|
||||
}
|
||||
/**
|
||||
* Filters the Files collection by type videos
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
return $this->files()->filter('type', '==', 'video');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,66 +15,66 @@ use Kirby\Exception\BadMethodCallException;
|
||||
*/
|
||||
trait HasMethods
|
||||
{
|
||||
/**
|
||||
* All registered methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
/**
|
||||
* All registered methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $methods = [];
|
||||
|
||||
/**
|
||||
* Calls a registered method class with the
|
||||
* passed arguments
|
||||
*
|
||||
* @internal
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\BadMethodCallException
|
||||
*/
|
||||
public function callMethod(string $method, array $args = [])
|
||||
{
|
||||
$closure = $this->getMethod($method);
|
||||
/**
|
||||
* Calls a registered method class with the
|
||||
* passed arguments
|
||||
*
|
||||
* @internal
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
* @throws \Kirby\Exception\BadMethodCallException
|
||||
*/
|
||||
public function callMethod(string $method, array $args = [])
|
||||
{
|
||||
$closure = $this->getMethod($method);
|
||||
|
||||
if ($closure === null) {
|
||||
throw new BadMethodCallException('The method ' . $method . ' does not exist');
|
||||
}
|
||||
if ($closure === null) {
|
||||
throw new BadMethodCallException('The method ' . $method . ' does not exist');
|
||||
}
|
||||
|
||||
return $closure->call($this, ...$args);
|
||||
}
|
||||
return $closure->call($this, ...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object has a registered method
|
||||
*
|
||||
* @internal
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMethod(string $method): bool
|
||||
{
|
||||
return $this->getMethod($method) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if the object has a registered method
|
||||
*
|
||||
* @internal
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMethod(string $method): bool
|
||||
{
|
||||
return $this->getMethod($method) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a registered method by name, either from
|
||||
* the current class or from a parent class ordered by
|
||||
* inheritance order (top to bottom)
|
||||
*
|
||||
* @param string $method
|
||||
* @return \Closure|null
|
||||
*/
|
||||
protected function getMethod(string $method)
|
||||
{
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return static::$methods[$method];
|
||||
}
|
||||
/**
|
||||
* Returns a registered method by name, either from
|
||||
* the current class or from a parent class ordered by
|
||||
* inheritance order (top to bottom)
|
||||
*
|
||||
* @param string $method
|
||||
* @return \Closure|null
|
||||
*/
|
||||
protected function getMethod(string $method)
|
||||
{
|
||||
if (isset(static::$methods[$method]) === true) {
|
||||
return static::$methods[$method];
|
||||
}
|
||||
|
||||
foreach (class_parents($this) as $parent) {
|
||||
if (isset($parent::$methods[$method]) === true) {
|
||||
return $parent::$methods[$method];
|
||||
}
|
||||
}
|
||||
foreach (class_parents($this) as $parent) {
|
||||
if (isset($parent::$methods[$method]) === true) {
|
||||
return $parent::$methods[$method];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,169 +14,169 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
trait HasSiblings
|
||||
{
|
||||
/**
|
||||
* Returns the position / index in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf($collection = null): int
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Returns the position / index in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf($collection = null): int
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->indexOf($this);
|
||||
}
|
||||
return $collection->indexOf($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next item in the collection if available
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function next($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Returns the next item in the collection if available
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function next($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->nth($this->indexOf($collection) + 1);
|
||||
}
|
||||
return $collection->nth($this->indexOf($collection) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of the collection starting after the current item
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function nextAll($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Returns the end of the collection starting after the current item
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function nextAll($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->slice($this->indexOf($collection) + 1);
|
||||
}
|
||||
return $collection->slice($this->indexOf($collection) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous item in the collection if available
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function prev($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Returns the previous item in the collection if available
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function prev($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->nth($this->indexOf($collection) - 1);
|
||||
}
|
||||
return $collection->nth($this->indexOf($collection) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the beginning of the collection before the current item
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function prevAll($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Returns the beginning of the collection before the current item
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function prevAll($collection = null)
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->slice(0, $this->indexOf($collection));
|
||||
}
|
||||
return $collection->slice(0, $this->indexOf($collection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all sibling elements
|
||||
*
|
||||
* @param bool $self
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function siblings(bool $self = true)
|
||||
{
|
||||
$siblings = $this->siblingsCollection();
|
||||
/**
|
||||
* Returns all sibling elements
|
||||
*
|
||||
* @param bool $self
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function siblings(bool $self = true)
|
||||
{
|
||||
$siblings = $this->siblingsCollection();
|
||||
|
||||
if ($self === false) {
|
||||
return $siblings->not($this);
|
||||
}
|
||||
if ($self === false) {
|
||||
return $siblings->not($this);
|
||||
}
|
||||
|
||||
return $siblings;
|
||||
}
|
||||
return $siblings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a next item in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNext($collection = null): bool
|
||||
{
|
||||
return $this->next($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a next item in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNext($collection = null): bool
|
||||
{
|
||||
return $this->next($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a previous item in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrev($collection = null): bool
|
||||
{
|
||||
return $this->prev($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a previous item in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrev($collection = null): bool
|
||||
{
|
||||
return $this->prev($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is the first in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirst($collection = null): bool
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Checks if the item is the first in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirst($collection = null): bool
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->first()->is($this);
|
||||
}
|
||||
return $collection->first()->is($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is the last in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLast($collection = null): bool
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
/**
|
||||
* Checks if the item is the last in the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLast($collection = null): bool
|
||||
{
|
||||
if ($collection === null) {
|
||||
$collection = $this->siblingsCollection();
|
||||
}
|
||||
|
||||
return $collection->last()->is($this);
|
||||
}
|
||||
return $collection->last()->is($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is at a certain position
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
* @param int $n
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNth(int $n, $collection = null): bool
|
||||
{
|
||||
return $this->indexOf($collection) === $n;
|
||||
}
|
||||
/**
|
||||
* Checks if the item is at a certain position
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
* @param int $n
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNth(int $n, $collection = null): bool
|
||||
{
|
||||
return $this->indexOf($collection) === $n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,71 +17,71 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Helpers
|
||||
{
|
||||
/**
|
||||
* Triggers a deprecation warning if debug mode is active
|
||||
*
|
||||
* @param string $message
|
||||
* @return bool Whether the warning was triggered
|
||||
*/
|
||||
public static function deprecated(string $message): bool
|
||||
{
|
||||
if (App::instance()->option('debug') === true) {
|
||||
return trigger_error($message, E_USER_DEPRECATED) === true;
|
||||
}
|
||||
/**
|
||||
* Triggers a deprecation warning if debug mode is active
|
||||
*
|
||||
* @param string $message
|
||||
* @return bool Whether the warning was triggered
|
||||
*/
|
||||
public static function deprecated(string $message): bool
|
||||
{
|
||||
if (App::instance()->option('debug') === true) {
|
||||
return trigger_error($message, E_USER_DEPRECATED) === true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple object and variable dumper
|
||||
* to help with debugging.
|
||||
*
|
||||
* @param mixed $variable
|
||||
* @param bool $echo
|
||||
* @return string
|
||||
*/
|
||||
public static function dump($variable, bool $echo = true): string
|
||||
{
|
||||
$kirby = App::instance();
|
||||
return ($kirby->component('dump'))($kirby, $variable, $echo);
|
||||
}
|
||||
/**
|
||||
* Simple object and variable dumper
|
||||
* to help with debugging.
|
||||
*
|
||||
* @param mixed $variable
|
||||
* @param bool $echo
|
||||
* @return string
|
||||
*/
|
||||
public static function dump($variable, bool $echo = true): string
|
||||
{
|
||||
$kirby = App::instance();
|
||||
return ($kirby->component('dump'))($kirby, $variable, $echo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a helper was overridden by the user
|
||||
* by setting the `KIRBY_HELPER_*` constant
|
||||
* @internal
|
||||
*
|
||||
* @param string $name Name of the helper
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasOverride(string $name): bool
|
||||
{
|
||||
$name = 'KIRBY_HELPER_' . strtoupper($name);
|
||||
return defined($name) === true && constant($name) === false;
|
||||
}
|
||||
/**
|
||||
* Checks if a helper was overridden by the user
|
||||
* by setting the `KIRBY_HELPER_*` constant
|
||||
* @internal
|
||||
*
|
||||
* @param string $name Name of the helper
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasOverride(string $name): bool
|
||||
{
|
||||
$name = 'KIRBY_HELPER_' . strtoupper($name);
|
||||
return defined($name) === true && constant($name) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the size/length of numbers,
|
||||
* strings, arrays and countable objects
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public static function size($value): int
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
return (int)$value;
|
||||
}
|
||||
/**
|
||||
* Determines the size/length of numbers,
|
||||
* strings, arrays and countable objects
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public static function size($value): int
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return Str::length(trim($value));
|
||||
}
|
||||
if (is_string($value)) {
|
||||
return Str::length(trim($value));
|
||||
}
|
||||
|
||||
if (is_countable($value)) {
|
||||
return count($value);
|
||||
}
|
||||
if (is_countable($value)) {
|
||||
return count($value);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Could not determine the size of the given value');
|
||||
}
|
||||
throw new InvalidArgumentException('Could not determine the size of the given value');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,132 +18,132 @@ use Kirby\Toolkit\A;
|
||||
*/
|
||||
class Html extends \Kirby\Toolkit\Html
|
||||
{
|
||||
/**
|
||||
* Creates one or multiple CSS link tags
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|array $url Relative or absolute URLs, an array of URLs or `@auto` for automatic template css loading
|
||||
* @param string|array $options Pass an array of attributes for the link tag or a media attribute string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function css($url, $options = null): ?string
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$links = A::map($url, fn ($url) => static::css($url, $options));
|
||||
return implode(PHP_EOL, $links);
|
||||
}
|
||||
/**
|
||||
* Creates one or multiple CSS link tags
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|array $url Relative or absolute URLs, an array of URLs or `@auto` for automatic template css loading
|
||||
* @param string|array $options Pass an array of attributes for the link tag or a media attribute string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function css($url, $options = null): ?string
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$links = A::map($url, fn ($url) => static::css($url, $options));
|
||||
return implode(PHP_EOL, $links);
|
||||
}
|
||||
|
||||
if (is_string($options) === true) {
|
||||
$options = ['media' => $options];
|
||||
}
|
||||
if (is_string($options) === true) {
|
||||
$options = ['media' => $options];
|
||||
}
|
||||
|
||||
$kirby = App::instance();
|
||||
$kirby = App::instance();
|
||||
|
||||
if ($url === '@auto') {
|
||||
if (!$url = Url::toTemplateAsset('css/templates', 'css')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($url === '@auto') {
|
||||
if (!$url = Url::toTemplateAsset('css/templates', 'css')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// only valid value for 'rel' is 'alternate stylesheet', if 'title' is given as well
|
||||
if (
|
||||
($options['rel'] ?? '') !== 'alternate stylesheet' ||
|
||||
($options['title'] ?? '') === ''
|
||||
) {
|
||||
$options['rel'] = 'stylesheet';
|
||||
}
|
||||
// only valid value for 'rel' is 'alternate stylesheet', if 'title' is given as well
|
||||
if (
|
||||
($options['rel'] ?? '') !== 'alternate stylesheet' ||
|
||||
($options['title'] ?? '') === ''
|
||||
) {
|
||||
$options['rel'] = 'stylesheet';
|
||||
}
|
||||
|
||||
$url = ($kirby->component('css'))($kirby, $url, $options);
|
||||
$url = Url::to($url);
|
||||
$attr = array_merge((array)$options, [
|
||||
'href' => $url
|
||||
]);
|
||||
$url = ($kirby->component('css'))($kirby, $url, $options);
|
||||
$url = Url::to($url);
|
||||
$attr = array_merge((array)$options, [
|
||||
'href' => $url
|
||||
]);
|
||||
|
||||
return '<link ' . static::attr($attr) . '>';
|
||||
}
|
||||
return '<link ' . static::attr($attr) . '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an `a` tag with an absolute Url
|
||||
*
|
||||
* @param string|null $href Relative or absolute Url
|
||||
* @param string|array|null $text If `null`, the link will be used as link text. If an array is passed, each element will be added unencoded
|
||||
* @param array $attr Additional attributes for the a tag.
|
||||
* @return string
|
||||
*/
|
||||
public static function link(string $href = null, $text = null, array $attr = []): string
|
||||
{
|
||||
return parent::link(Url::to($href), $text, $attr);
|
||||
}
|
||||
/**
|
||||
* Generates an `a` tag with an absolute Url
|
||||
*
|
||||
* @param string|null $href Relative or absolute Url
|
||||
* @param string|array|null $text If `null`, the link will be used as link text. If an array is passed, each element will be added unencoded
|
||||
* @param array $attr Additional attributes for the a tag.
|
||||
* @return string
|
||||
*/
|
||||
public static function link(string $href = null, $text = null, array $attr = []): string
|
||||
{
|
||||
return parent::link(Url::to($href), $text, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a script tag to load a javascript file
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|array $url
|
||||
* @param string|array $options
|
||||
* @return string|null
|
||||
*/
|
||||
public static function js($url, $options = null): ?string
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$scripts = A::map($url, fn ($url) => static::js($url, $options));
|
||||
return implode(PHP_EOL, $scripts);
|
||||
}
|
||||
/**
|
||||
* Creates a script tag to load a javascript file
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|array $url
|
||||
* @param string|array $options
|
||||
* @return string|null
|
||||
*/
|
||||
public static function js($url, $options = null): ?string
|
||||
{
|
||||
if (is_array($url) === true) {
|
||||
$scripts = A::map($url, fn ($url) => static::js($url, $options));
|
||||
return implode(PHP_EOL, $scripts);
|
||||
}
|
||||
|
||||
if (is_bool($options) === true) {
|
||||
$options = ['async' => $options];
|
||||
}
|
||||
if (is_bool($options) === true) {
|
||||
$options = ['async' => $options];
|
||||
}
|
||||
|
||||
$kirby = App::instance();
|
||||
$kirby = App::instance();
|
||||
|
||||
if ($url === '@auto') {
|
||||
if (!$url = Url::toTemplateAsset('js/templates', 'js')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($url === '@auto') {
|
||||
if (!$url = Url::toTemplateAsset('js/templates', 'js')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$url = ($kirby->component('js'))($kirby, $url, $options);
|
||||
$url = Url::to($url);
|
||||
$attr = array_merge((array)$options, ['src' => $url]);
|
||||
$url = ($kirby->component('js'))($kirby, $url, $options);
|
||||
$url = Url::to($url);
|
||||
$attr = array_merge((array)$options, ['src' => $url]);
|
||||
|
||||
return '<script ' . static::attr($attr) . '></script>';
|
||||
}
|
||||
return '<script ' . static::attr($attr) . '></script>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes an SVG file by absolute or
|
||||
* relative file path.
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|\Kirby\Cms\File $file
|
||||
* @return string|false
|
||||
*/
|
||||
public static function svg($file)
|
||||
{
|
||||
// support for Kirby's file objects
|
||||
if (
|
||||
is_a($file, 'Kirby\Cms\File') === true &&
|
||||
$file->extension() === 'svg'
|
||||
) {
|
||||
return $file->read();
|
||||
}
|
||||
/**
|
||||
* Includes an SVG file by absolute or
|
||||
* relative file path.
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|\Kirby\Cms\File $file
|
||||
* @return string|false
|
||||
*/
|
||||
public static function svg($file)
|
||||
{
|
||||
// support for Kirby's file objects
|
||||
if (
|
||||
is_a($file, 'Kirby\Cms\File') === true &&
|
||||
$file->extension() === 'svg'
|
||||
) {
|
||||
return $file->read();
|
||||
}
|
||||
|
||||
if (is_string($file) === false) {
|
||||
return false;
|
||||
}
|
||||
if (is_string($file) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$extension = F::extension($file);
|
||||
$extension = F::extension($file);
|
||||
|
||||
// check for valid svg files
|
||||
if ($extension !== 'svg') {
|
||||
return false;
|
||||
}
|
||||
// check for valid svg files
|
||||
if ($extension !== 'svg') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to convert relative paths to absolute
|
||||
if (file_exists($file) === false) {
|
||||
$root = App::instance()->root();
|
||||
$file = realpath($root . '/' . $file);
|
||||
}
|
||||
// try to convert relative paths to absolute
|
||||
if (file_exists($file) === false) {
|
||||
$root = App::instance()->root();
|
||||
$file = realpath($root . '/' . $file);
|
||||
}
|
||||
|
||||
return F::read($file);
|
||||
}
|
||||
return F::read($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,80 +16,80 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Ingredients
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $ingredients = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $ingredients = [];
|
||||
|
||||
/**
|
||||
* Creates a new ingredient collection
|
||||
*
|
||||
* @param array $ingredients
|
||||
*/
|
||||
public function __construct(array $ingredients)
|
||||
{
|
||||
$this->ingredients = $ingredients;
|
||||
}
|
||||
/**
|
||||
* Creates a new ingredient collection
|
||||
*
|
||||
* @param array $ingredients
|
||||
*/
|
||||
public function __construct(array $ingredients)
|
||||
{
|
||||
$this->ingredients = $ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter for single ingredients
|
||||
*
|
||||
* @param string $method
|
||||
* @param array|null $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $args = null)
|
||||
{
|
||||
return $this->ingredients[$method] ?? null;
|
||||
}
|
||||
/**
|
||||
* Magic getter for single ingredients
|
||||
*
|
||||
* @param string $method
|
||||
* @param array|null $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $args = null)
|
||||
{
|
||||
return $this->ingredients[$method] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->ingredients;
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single ingredient by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $key)
|
||||
{
|
||||
return $this->ingredients[$key] ?? null;
|
||||
}
|
||||
/**
|
||||
* Get a single ingredient by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $key)
|
||||
{
|
||||
return $this->ingredients[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all ingredient callbacks
|
||||
* and creates a plain array
|
||||
*
|
||||
* @internal
|
||||
* @param array $ingredients
|
||||
* @return static
|
||||
*/
|
||||
public static function bake(array $ingredients)
|
||||
{
|
||||
foreach ($ingredients as $name => $ingredient) {
|
||||
if (is_a($ingredient, 'Closure') === true) {
|
||||
$ingredients[$name] = $ingredient($ingredients);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Resolves all ingredient callbacks
|
||||
* and creates a plain array
|
||||
*
|
||||
* @internal
|
||||
* @param array $ingredients
|
||||
* @return static
|
||||
*/
|
||||
public static function bake(array $ingredients)
|
||||
{
|
||||
foreach ($ingredients as $name => $ingredient) {
|
||||
if (is_a($ingredient, 'Closure') === true) {
|
||||
$ingredients[$name] = $ingredient($ingredients);
|
||||
}
|
||||
}
|
||||
|
||||
return new static($ingredients);
|
||||
}
|
||||
return new static($ingredients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all ingredients as plain array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->ingredients;
|
||||
}
|
||||
/**
|
||||
* Returns all ingredients as plain array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->ingredients;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,118 +22,118 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Item
|
||||
{
|
||||
use HasSiblings;
|
||||
use HasSiblings;
|
||||
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Items';
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Items';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @var \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Items
|
||||
*/
|
||||
protected $siblings;
|
||||
/**
|
||||
* @var \Kirby\Cms\Items
|
||||
*/
|
||||
protected $siblings;
|
||||
|
||||
/**
|
||||
* Creates a new item
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$siblingsClass = static::ITEMS_CLASS;
|
||||
/**
|
||||
* Creates a new item
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$siblingsClass = static::ITEMS_CLASS;
|
||||
|
||||
$this->id = $params['id'] ?? Str::uuid();
|
||||
$this->params = $params;
|
||||
$this->parent = $params['parent'] ?? App::instance()->site();
|
||||
$this->siblings = $params['siblings'] ?? new $siblingsClass();
|
||||
}
|
||||
$this->id = $params['id'] ?? Str::uuid();
|
||||
$this->params = $params;
|
||||
$this->parent = $params['parent'] ?? App::instance()->site();
|
||||
$this->siblings = $params['siblings'] ?? new $siblingsClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static Item factory
|
||||
*
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Item
|
||||
*/
|
||||
public static function factory(array $params)
|
||||
{
|
||||
return new static($params);
|
||||
}
|
||||
/**
|
||||
* Static Item factory
|
||||
*
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Item
|
||||
*/
|
||||
public static function factory(array $params)
|
||||
{
|
||||
return new static($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique item id (UUID v4)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
/**
|
||||
* Returns the unique item id (UUID v4)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the item to another one
|
||||
*
|
||||
* @param \Kirby\Cms\Item $item
|
||||
* @return bool
|
||||
*/
|
||||
public function is(Item $item): bool
|
||||
{
|
||||
return $this->id() === $item->id();
|
||||
}
|
||||
/**
|
||||
* Compares the item to another one
|
||||
*
|
||||
* @param \Kirby\Cms\Item $item
|
||||
* @return bool
|
||||
*/
|
||||
public function is(Item $item): bool
|
||||
{
|
||||
return $this->id() === $item->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->parent()->kirby();
|
||||
}
|
||||
/**
|
||||
* Returns the Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->parent()->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sibling collection
|
||||
* This is required by the HasSiblings trait
|
||||
*
|
||||
* @return \Kirby\Cms\Items
|
||||
* @psalm-return self::ITEMS_CLASS
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
return $this->siblings;
|
||||
}
|
||||
/**
|
||||
* Returns the sibling collection
|
||||
* This is required by the HasSiblings trait
|
||||
*
|
||||
* @return \Kirby\Cms\Items
|
||||
* @psalm-return self::ITEMS_CLASS
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
return $this->siblings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the item to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the item to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,81 +17,81 @@ use Exception;
|
||||
*/
|
||||
class Items extends Collection
|
||||
{
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Item';
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Item';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\ModelWithContent
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @var \Kirby\Cms\ModelWithContent
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $objects
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($objects = [], array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->parent = $options['parent'] ?? App::instance()->site();
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $objects
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($objects = [], array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->parent = $options['parent'] ?? App::instance()->site();
|
||||
|
||||
parent::__construct($objects, $this->parent);
|
||||
}
|
||||
parent::__construct($objects, $this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new item collection from a
|
||||
* an array of item props
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Items
|
||||
*/
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'options' => [],
|
||||
'parent' => App::instance()->site(),
|
||||
], $params);
|
||||
/**
|
||||
* Creates a new item collection from a
|
||||
* an array of item props
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Items
|
||||
*/
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'options' => [],
|
||||
'parent' => App::instance()->site(),
|
||||
], $params);
|
||||
|
||||
if (empty($items) === true || is_array($items) === false) {
|
||||
return new static();
|
||||
}
|
||||
if (empty($items) === true || is_array($items) === false) {
|
||||
return new static();
|
||||
}
|
||||
|
||||
if (is_array($options) === false) {
|
||||
throw new Exception('Invalid item options');
|
||||
}
|
||||
if (is_array($options) === false) {
|
||||
throw new Exception('Invalid item options');
|
||||
}
|
||||
|
||||
// create a new collection of blocks
|
||||
$collection = new static([], $options);
|
||||
// create a new collection of blocks
|
||||
$collection = new static([], $options);
|
||||
|
||||
foreach ($items as $params) {
|
||||
if (is_array($params) === false) {
|
||||
continue;
|
||||
}
|
||||
foreach ($items as $params) {
|
||||
if (is_array($params) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$params['options'] = $options['options'];
|
||||
$params['parent'] = $options['parent'];
|
||||
$params['siblings'] = $collection;
|
||||
$class = static::ITEM_CLASS;
|
||||
$item = $class::factory($params);
|
||||
$collection->append($item->id(), $item);
|
||||
}
|
||||
$params['options'] = $options['options'];
|
||||
$params['parent'] = $options['parent'];
|
||||
$params['siblings'] = $collection;
|
||||
$class = static::ITEM_CLASS;
|
||||
$item = $class::factory($params);
|
||||
$collection->append($item->id(), $item);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the items to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return array_values(parent::toArray($map));
|
||||
}
|
||||
/**
|
||||
* Convert the items to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return array_values(parent::toArray($map));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,117 +20,117 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class LanguageRouter
|
||||
{
|
||||
/**
|
||||
* The parent language
|
||||
*
|
||||
* @var Language
|
||||
*/
|
||||
protected $language;
|
||||
/**
|
||||
* The parent language
|
||||
*
|
||||
* @var Language
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* The router instance
|
||||
*
|
||||
* @var Router
|
||||
*/
|
||||
protected $router;
|
||||
/**
|
||||
* The router instance
|
||||
*
|
||||
* @var Router
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* Creates a new language router instance
|
||||
* for the given language
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
*/
|
||||
public function __construct(Language $language)
|
||||
{
|
||||
$this->language = $language;
|
||||
}
|
||||
/**
|
||||
* Creates a new language router instance
|
||||
* for the given language
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
*/
|
||||
public function __construct(Language $language)
|
||||
{
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all scoped routes for the
|
||||
* current language from the Kirby instance
|
||||
*
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
public function routes(): array
|
||||
{
|
||||
$language = $this->language;
|
||||
$kirby = $language->kirby();
|
||||
$routes = $kirby->routes();
|
||||
/**
|
||||
* Fetches all scoped routes for the
|
||||
* current language from the Kirby instance
|
||||
*
|
||||
* @return array
|
||||
* @throws \Kirby\Exception\NotFoundException
|
||||
*/
|
||||
public function routes(): array
|
||||
{
|
||||
$language = $this->language;
|
||||
$kirby = $language->kirby();
|
||||
$routes = $kirby->routes();
|
||||
|
||||
// only keep the scoped language routes
|
||||
$routes = array_values(array_filter($routes, function ($route) use ($language) {
|
||||
// only keep the scoped language routes
|
||||
$routes = array_values(array_filter($routes, function ($route) use ($language) {
|
||||
|
||||
// no language scope
|
||||
if (empty($route['language']) === true) {
|
||||
return false;
|
||||
}
|
||||
// no language scope
|
||||
if (empty($route['language']) === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// wildcard
|
||||
if ($route['language'] === '*') {
|
||||
return true;
|
||||
}
|
||||
// wildcard
|
||||
if ($route['language'] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get all applicable languages
|
||||
$languages = Str::split(strtolower($route['language']), '|');
|
||||
// get all applicable languages
|
||||
$languages = Str::split(strtolower($route['language']), '|');
|
||||
|
||||
// validate the language
|
||||
return in_array($language->code(), $languages) === true;
|
||||
}));
|
||||
// validate the language
|
||||
return in_array($language->code(), $languages) === true;
|
||||
}));
|
||||
|
||||
// add the page-scope if necessary
|
||||
foreach ($routes as $index => $route) {
|
||||
if ($pageId = ($route['page'] ?? null)) {
|
||||
if ($page = $kirby->page($pageId)) {
|
||||
// add the page-scope if necessary
|
||||
foreach ($routes as $index => $route) {
|
||||
if ($pageId = ($route['page'] ?? null)) {
|
||||
if ($page = $kirby->page($pageId)) {
|
||||
|
||||
// convert string patterns to arrays
|
||||
$patterns = A::wrap($route['pattern']);
|
||||
// convert string patterns to arrays
|
||||
$patterns = A::wrap($route['pattern']);
|
||||
|
||||
// prefix all patterns with the page slug
|
||||
$patterns = A::map(
|
||||
$patterns,
|
||||
fn ($pattern) => $page->uri($language) . '/' . $pattern
|
||||
);
|
||||
// prefix all patterns with the page slug
|
||||
$patterns = A::map(
|
||||
$patterns,
|
||||
fn ($pattern) => $page->uri($language) . '/' . $pattern
|
||||
);
|
||||
|
||||
// re-inject the pattern and the full page object
|
||||
$routes[$index]['pattern'] = $patterns;
|
||||
$routes[$index]['page'] = $page;
|
||||
} else {
|
||||
throw new NotFoundException('The page "' . $pageId . '" does not exist');
|
||||
}
|
||||
}
|
||||
}
|
||||
// re-inject the pattern and the full page object
|
||||
$routes[$index]['pattern'] = $patterns;
|
||||
$routes[$index]['page'] = $page;
|
||||
} else {
|
||||
throw new NotFoundException('The page "' . $pageId . '" does not exist');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
return $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around the Router::call method
|
||||
* that injects the Language instance and
|
||||
* if needed also the Page as arguments.
|
||||
*
|
||||
* @param string|null $path
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null)
|
||||
{
|
||||
$language = $this->language;
|
||||
$kirby = $language->kirby();
|
||||
$router = new Router($this->routes());
|
||||
/**
|
||||
* Wrapper around the Router::call method
|
||||
* that injects the Language instance and
|
||||
* if needed also the Page as arguments.
|
||||
*
|
||||
* @param string|null $path
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null)
|
||||
{
|
||||
$language = $this->language;
|
||||
$kirby = $language->kirby();
|
||||
$router = new Router($this->routes());
|
||||
|
||||
try {
|
||||
return $router->call($path, $kirby->request()->method(), function ($route) use ($kirby, $language) {
|
||||
$kirby->setCurrentTranslation($language);
|
||||
$kirby->setCurrentLanguage($language);
|
||||
try {
|
||||
return $router->call($path, $kirby->request()->method(), function ($route) use ($kirby, $language) {
|
||||
$kirby->setCurrentTranslation($language);
|
||||
$kirby->setCurrentLanguage($language);
|
||||
|
||||
if ($page = $route->page()) {
|
||||
return $route->action()->call($route, $language, $page, ...$route->arguments());
|
||||
} else {
|
||||
return $route->action()->call($route, $language, ...$route->arguments());
|
||||
}
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
return $kirby->resolve($path, $language->code());
|
||||
}
|
||||
}
|
||||
if ($page = $route->page()) {
|
||||
return $route->action()->call($route, $language, $page, ...$route->arguments());
|
||||
} else {
|
||||
return $route->action()->call($route, $language, ...$route->arguments());
|
||||
}
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
return $kirby->resolve($path, $language->code());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,150 +6,150 @@ use Kirby\Filesystem\F;
|
||||
|
||||
class LanguageRoutes
|
||||
{
|
||||
/**
|
||||
* Creates all multi-language routes
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function create(App $kirby): array
|
||||
{
|
||||
$routes = [];
|
||||
/**
|
||||
* Creates all multi-language routes
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function create(App $kirby): array
|
||||
{
|
||||
$routes = [];
|
||||
|
||||
// add the route for the home page
|
||||
$routes[] = static::home($kirby);
|
||||
// add the route for the home page
|
||||
$routes[] = static::home($kirby);
|
||||
|
||||
// Kirby's base url
|
||||
$baseurl = $kirby->url();
|
||||
// Kirby's base url
|
||||
$baseurl = $kirby->url();
|
||||
|
||||
foreach ($kirby->languages() as $language) {
|
||||
foreach ($kirby->languages() as $language) {
|
||||
|
||||
// ignore languages with a different base url
|
||||
if ($language->baseurl() !== $baseurl) {
|
||||
continue;
|
||||
}
|
||||
// ignore languages with a different base url
|
||||
if ($language->baseurl() !== $baseurl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes[] = [
|
||||
'pattern' => $language->pattern(),
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function ($path = null) use ($language) {
|
||||
if ($result = $language->router()->call($path)) {
|
||||
return $result;
|
||||
}
|
||||
$routes[] = [
|
||||
'pattern' => $language->pattern(),
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function ($path = null) use ($language) {
|
||||
if ($result = $language->router()->call($path)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// jump through to the fallback if nothing
|
||||
// can be found for this language
|
||||
/** @var \Kirby\Http\Route $this */
|
||||
$this->next();
|
||||
}
|
||||
];
|
||||
}
|
||||
// jump through to the fallback if nothing
|
||||
// can be found for this language
|
||||
/** @var \Kirby\Http\Route $this */
|
||||
$this->next();
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
$routes[] = static::fallback($kirby);
|
||||
$routes[] = static::fallback($kirby);
|
||||
|
||||
return $routes;
|
||||
}
|
||||
return $routes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the fallback route
|
||||
* for unprefixed default language URLs.
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function fallback(App $kirby): array
|
||||
{
|
||||
return [
|
||||
'pattern' => '(:all)',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function (string $path) use ($kirby) {
|
||||
/**
|
||||
* Create the fallback route
|
||||
* for unprefixed default language URLs.
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function fallback(App $kirby): array
|
||||
{
|
||||
return [
|
||||
'pattern' => '(:all)',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function (string $path) use ($kirby) {
|
||||
|
||||
// check for content representations or files
|
||||
$extension = F::extension($path);
|
||||
// check for content representations or files
|
||||
$extension = F::extension($path);
|
||||
|
||||
// try to redirect prefixed pages
|
||||
if (empty($extension) === true && $page = $kirby->page($path)) {
|
||||
$url = $kirby->request()->url([
|
||||
'query' => null,
|
||||
'params' => null,
|
||||
'fragment' => null
|
||||
]);
|
||||
// try to redirect prefixed pages
|
||||
if (empty($extension) === true && $page = $kirby->page($path)) {
|
||||
$url = $kirby->request()->url([
|
||||
'query' => null,
|
||||
'params' => null,
|
||||
'fragment' => null
|
||||
]);
|
||||
|
||||
if ($url->toString() !== $page->url()) {
|
||||
// redirect to translated page directly
|
||||
// if translation is exists and languages detect is enabled
|
||||
if (
|
||||
$kirby->option('languages.detect') === true &&
|
||||
$page->translation($kirby->detectedLanguage()->code())->exists() === true
|
||||
) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($page->url($kirby->detectedLanguage()->code()));
|
||||
}
|
||||
if ($url->toString() !== $page->url()) {
|
||||
// redirect to translated page directly
|
||||
// if translation is exists and languages detect is enabled
|
||||
if (
|
||||
$kirby->option('languages.detect') === true &&
|
||||
$page->translation($kirby->detectedLanguage()->code())->exists() === true
|
||||
) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($page->url($kirby->detectedLanguage()->code()));
|
||||
}
|
||||
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($page->url());
|
||||
}
|
||||
}
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($page->url());
|
||||
}
|
||||
}
|
||||
|
||||
return $kirby->language()->router()->call($path);
|
||||
}
|
||||
];
|
||||
}
|
||||
return $kirby->language()->router()->call($path);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the multi-language home page route
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function home(App $kirby): array
|
||||
{
|
||||
// Multi-language home
|
||||
return [
|
||||
'pattern' => '',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function () use ($kirby) {
|
||||
/**
|
||||
* Create the multi-language home page route
|
||||
*
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @return array
|
||||
*/
|
||||
public static function home(App $kirby): array
|
||||
{
|
||||
// Multi-language home
|
||||
return [
|
||||
'pattern' => '',
|
||||
'method' => 'ALL',
|
||||
'env' => 'site',
|
||||
'action' => function () use ($kirby) {
|
||||
|
||||
// find all languages with the same base url as the current installation
|
||||
$languages = $kirby->languages()->filter('baseurl', $kirby->url());
|
||||
// find all languages with the same base url as the current installation
|
||||
$languages = $kirby->languages()->filter('baseurl', $kirby->url());
|
||||
|
||||
// if there's no language with a matching base url,
|
||||
// redirect to the default language
|
||||
if ($languages->count() === 0) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->defaultLanguage()->url());
|
||||
}
|
||||
// if there's no language with a matching base url,
|
||||
// redirect to the default language
|
||||
if ($languages->count() === 0) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->defaultLanguage()->url());
|
||||
}
|
||||
|
||||
// if there's just one language, we take that to render the home page
|
||||
if ($languages->count() === 1) {
|
||||
$currentLanguage = $languages->first();
|
||||
} else {
|
||||
$currentLanguage = $kirby->defaultLanguage();
|
||||
}
|
||||
// if there's just one language, we take that to render the home page
|
||||
if ($languages->count() === 1) {
|
||||
$currentLanguage = $languages->first();
|
||||
} else {
|
||||
$currentLanguage = $kirby->defaultLanguage();
|
||||
}
|
||||
|
||||
// language detection on the home page with / as URL
|
||||
if ($kirby->url() !== $currentLanguage->url()) {
|
||||
if ($kirby->option('languages.detect') === true) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->detectedLanguage()->url());
|
||||
}
|
||||
// language detection on the home page with / as URL
|
||||
if ($kirby->url() !== $currentLanguage->url()) {
|
||||
if ($kirby->option('languages.detect') === true) {
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($kirby->detectedLanguage()->url());
|
||||
}
|
||||
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($currentLanguage->url());
|
||||
}
|
||||
return $kirby
|
||||
->response()
|
||||
->redirect($currentLanguage->url());
|
||||
}
|
||||
|
||||
// render the home page of the current language
|
||||
return $currentLanguage->router()->call();
|
||||
}
|
||||
];
|
||||
}
|
||||
// render the home page of the current language
|
||||
return $currentLanguage->router()->call();
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,82 +17,82 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class LanguageRules
|
||||
{
|
||||
/**
|
||||
* Validates if the language can be created
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If the language already exists
|
||||
*/
|
||||
public static function create(Language $language): bool
|
||||
{
|
||||
static::validLanguageCode($language);
|
||||
static::validLanguageName($language);
|
||||
/**
|
||||
* Validates if the language can be created
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If the language already exists
|
||||
*/
|
||||
public static function create(Language $language): bool
|
||||
{
|
||||
static::validLanguageCode($language);
|
||||
static::validLanguageName($language);
|
||||
|
||||
if ($language->exists() === true) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'language.duplicate',
|
||||
'data' => [
|
||||
'code' => $language->code()
|
||||
]
|
||||
]);
|
||||
}
|
||||
if ($language->exists() === true) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'language.duplicate',
|
||||
'data' => [
|
||||
'code' => $language->code()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the language can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
*/
|
||||
public static function update(Language $language)
|
||||
{
|
||||
static::validLanguageCode($language);
|
||||
static::validLanguageName($language);
|
||||
}
|
||||
/**
|
||||
* Validates if the language can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
*/
|
||||
public static function update(Language $language)
|
||||
{
|
||||
static::validLanguageCode($language);
|
||||
static::validLanguageName($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the language code is formatted correctly
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the language code is not valid
|
||||
*/
|
||||
public static function validLanguageCode(Language $language): bool
|
||||
{
|
||||
if (Str::length($language->code()) < 2) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'language.code',
|
||||
'data' => [
|
||||
'code' => $language->code(),
|
||||
'name' => $language->name()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the language code is formatted correctly
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the language code is not valid
|
||||
*/
|
||||
public static function validLanguageCode(Language $language): bool
|
||||
{
|
||||
if (Str::length($language->code()) < 2) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'language.code',
|
||||
'data' => [
|
||||
'code' => $language->code(),
|
||||
'name' => $language->name()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the language name is formatted correctly
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the language name is invalid
|
||||
*/
|
||||
public static function validLanguageName(Language $language): bool
|
||||
{
|
||||
if (Str::length($language->name()) < 1) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'language.name',
|
||||
'data' => [
|
||||
'code' => $language->code(),
|
||||
'name' => $language->name()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the language name is formatted correctly
|
||||
*
|
||||
* @param \Kirby\Cms\Language $language
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the language name is invalid
|
||||
*/
|
||||
public static function validLanguageName(Language $language): bool
|
||||
{
|
||||
if (Str::length($language->name()) < 1) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'language.name',
|
||||
'data' => [
|
||||
'code' => $language->code(),
|
||||
'name' => $language->name()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,86 +16,86 @@ use Kirby\Filesystem\F;
|
||||
*/
|
||||
class Languages extends Collection
|
||||
{
|
||||
/**
|
||||
* Creates a new collection with the given language objects
|
||||
*
|
||||
* @param array $objects `Kirby\Cms\Language` objects
|
||||
* @param null $parent
|
||||
* @throws \Kirby\Exception\DuplicateException
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$defaults = array_filter(
|
||||
$objects,
|
||||
fn ($language) => $language->isDefault() === true
|
||||
);
|
||||
/**
|
||||
* Creates a new collection with the given language objects
|
||||
*
|
||||
* @param array $objects `Kirby\Cms\Language` objects
|
||||
* @param null $parent
|
||||
* @throws \Kirby\Exception\DuplicateException
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$defaults = array_filter(
|
||||
$objects,
|
||||
fn ($language) => $language->isDefault() === true
|
||||
);
|
||||
|
||||
if (count($defaults) > 1) {
|
||||
throw new DuplicateException('You cannot have multiple default languages. Please check your language config files.');
|
||||
}
|
||||
if (count($defaults) > 1) {
|
||||
throw new DuplicateException('You cannot have multiple default languages. Please check your language config files.');
|
||||
}
|
||||
|
||||
parent::__construct($objects, $parent);
|
||||
}
|
||||
parent::__construct($objects, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all language codes as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function codes(): array
|
||||
{
|
||||
return $this->keys();
|
||||
}
|
||||
/**
|
||||
* Returns all language codes as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function codes(): array
|
||||
{
|
||||
return $this->keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new language with the given props
|
||||
*
|
||||
* @internal
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\Language
|
||||
*/
|
||||
public function create(array $props)
|
||||
{
|
||||
return Language::create($props);
|
||||
}
|
||||
/**
|
||||
* Creates a new language with the given props
|
||||
*
|
||||
* @internal
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\Language
|
||||
*/
|
||||
public function create(array $props)
|
||||
{
|
||||
return Language::create($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default language
|
||||
*
|
||||
* @return \Kirby\Cms\Language|null
|
||||
*/
|
||||
public function default()
|
||||
{
|
||||
if ($language = $this->findBy('isDefault', true)) {
|
||||
return $language;
|
||||
} else {
|
||||
return $this->first();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the default language
|
||||
*
|
||||
* @return \Kirby\Cms\Language|null
|
||||
*/
|
||||
public function default()
|
||||
{
|
||||
if ($language = $this->findBy('isDefault', true)) {
|
||||
return $language;
|
||||
} else {
|
||||
return $this->first();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all defined languages to a collection
|
||||
*
|
||||
* @internal
|
||||
* @return static
|
||||
*/
|
||||
public static function load()
|
||||
{
|
||||
$languages = [];
|
||||
$files = glob(App::instance()->root('languages') . '/*.php');
|
||||
/**
|
||||
* Convert all defined languages to a collection
|
||||
*
|
||||
* @internal
|
||||
* @return static
|
||||
*/
|
||||
public static function load()
|
||||
{
|
||||
$languages = [];
|
||||
$files = glob(App::instance()->root('languages') . '/*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
$props = F::load($file);
|
||||
foreach ($files as $file) {
|
||||
$props = F::load($file);
|
||||
|
||||
if (is_array($props) === true) {
|
||||
// inject the language code from the filename
|
||||
// if it does not exist
|
||||
$props['code'] ??= F::name($file);
|
||||
if (is_array($props) === true) {
|
||||
// inject the language code from the filename
|
||||
// if it does not exist
|
||||
$props['code'] ??= F::name($file);
|
||||
|
||||
$languages[] = new Language($props);
|
||||
}
|
||||
}
|
||||
$languages[] = new Language($props);
|
||||
}
|
||||
}
|
||||
|
||||
return new static($languages);
|
||||
}
|
||||
return new static($languages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,113 +15,113 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Layout extends Item
|
||||
{
|
||||
use HasMethods;
|
||||
use HasMethods;
|
||||
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Layouts';
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\Layouts';
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Content
|
||||
*/
|
||||
protected $attrs;
|
||||
/**
|
||||
* @var \Kirby\Cms\Content
|
||||
*/
|
||||
protected $attrs;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\LayoutColumns
|
||||
*/
|
||||
protected $columns;
|
||||
/**
|
||||
* @var \Kirby\Cms\LayoutColumns
|
||||
*/
|
||||
protected $columns;
|
||||
|
||||
/**
|
||||
* Proxy for attrs
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $method, array $args = [])
|
||||
{
|
||||
// layout methods
|
||||
if ($this->hasMethod($method) === true) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
/**
|
||||
* Proxy for attrs
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return \Kirby\Cms\Field
|
||||
*/
|
||||
public function __call(string $method, array $args = [])
|
||||
{
|
||||
// layout methods
|
||||
if ($this->hasMethod($method) === true) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
|
||||
return $this->attrs()->get($method);
|
||||
}
|
||||
return $this->attrs()->get($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Layout object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
parent::__construct($params);
|
||||
/**
|
||||
* Creates a new Layout object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
parent::__construct($params);
|
||||
|
||||
$this->columns = LayoutColumns::factory($params['columns'] ?? [], [
|
||||
'parent' => $this->parent
|
||||
]);
|
||||
$this->columns = LayoutColumns::factory($params['columns'] ?? [], [
|
||||
'parent' => $this->parent
|
||||
]);
|
||||
|
||||
// create the attrs object
|
||||
$this->attrs = new Content($params['attrs'] ?? [], $this->parent);
|
||||
}
|
||||
// create the attrs object
|
||||
$this->attrs = new Content($params['attrs'] ?? [], $this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attrs object
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function attrs()
|
||||
{
|
||||
return $this->attrs;
|
||||
}
|
||||
/**
|
||||
* Returns the attrs object
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function attrs()
|
||||
{
|
||||
return $this->attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns in this layout
|
||||
*
|
||||
* @return \Kirby\Cms\LayoutColumns
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
/**
|
||||
* Returns the columns in this layout
|
||||
*
|
||||
* @return \Kirby\Cms\LayoutColumns
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the layout is empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this
|
||||
->columns()
|
||||
->filter(function ($column) {
|
||||
return $column->isNotEmpty();
|
||||
})
|
||||
->count() === 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the layout is empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this
|
||||
->columns()
|
||||
->filter(function ($column) {
|
||||
return $column->isNotEmpty();
|
||||
})
|
||||
->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the layout is not empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
/**
|
||||
* Checks if the layout is not empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'attrs' => $this->attrs()->toArray(),
|
||||
'columns' => $this->columns()->toArray(),
|
||||
'id' => $this->id(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'attrs' => $this->attrs()->toArray(),
|
||||
'columns' => $this->columns()->toArray(),
|
||||
'id' => $this->id(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,128 +17,128 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class LayoutColumn extends Item
|
||||
{
|
||||
use HasMethods;
|
||||
use HasMethods;
|
||||
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\LayoutColumns';
|
||||
public const ITEMS_CLASS = '\Kirby\Cms\LayoutColumns';
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Blocks
|
||||
*/
|
||||
protected $blocks;
|
||||
/**
|
||||
* @var \Kirby\Cms\Blocks
|
||||
*/
|
||||
protected $blocks;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $width;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $width;
|
||||
|
||||
/**
|
||||
* Creates a new LayoutColumn object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
parent::__construct($params);
|
||||
/**
|
||||
* Creates a new LayoutColumn object
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
parent::__construct($params);
|
||||
|
||||
$this->blocks = Blocks::factory($params['blocks'] ?? [], [
|
||||
'parent' => $this->parent
|
||||
]);
|
||||
$this->blocks = Blocks::factory($params['blocks'] ?? [], [
|
||||
'parent' => $this->parent
|
||||
]);
|
||||
|
||||
$this->width = $params['width'] ?? '1/1';
|
||||
}
|
||||
$this->width = $params['width'] ?? '1/1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter function
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, $args)
|
||||
{
|
||||
// layout column methods
|
||||
if ($this->hasMethod($method) === true) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Magic getter function
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, $args)
|
||||
{
|
||||
// layout column methods
|
||||
if ($this->hasMethod($method) === true) {
|
||||
return $this->callMethod($method, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blocks collection
|
||||
*
|
||||
* @param bool $includeHidden Sets whether to include hidden blocks
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public function blocks(bool $includeHidden = false)
|
||||
{
|
||||
if ($includeHidden === false) {
|
||||
return $this->blocks->filter('isHidden', false);
|
||||
}
|
||||
/**
|
||||
* Returns the blocks collection
|
||||
*
|
||||
* @param bool $includeHidden Sets whether to include hidden blocks
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public function blocks(bool $includeHidden = false)
|
||||
{
|
||||
if ($includeHidden === false) {
|
||||
return $this->blocks->filter('isHidden', false);
|
||||
}
|
||||
|
||||
return $this->blocks;
|
||||
}
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the column is empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this
|
||||
->blocks()
|
||||
->filter('isHidden', false)
|
||||
->count() === 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the column is empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this
|
||||
->blocks()
|
||||
->filter('isHidden', false)
|
||||
->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the column is not empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
/**
|
||||
* Checks if the column is not empty
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return $this->isEmpty() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns this column spans
|
||||
*
|
||||
* @param int $columns
|
||||
* @return int
|
||||
*/
|
||||
public function span(int $columns = 12): int
|
||||
{
|
||||
$fraction = Str::split($this->width, '/');
|
||||
$a = $fraction[0] ?? 1;
|
||||
$b = $fraction[1] ?? 1;
|
||||
/**
|
||||
* Returns the number of columns this column spans
|
||||
*
|
||||
* @param int $columns
|
||||
* @return int
|
||||
*/
|
||||
public function span(int $columns = 12): int
|
||||
{
|
||||
$fraction = Str::split($this->width, '/');
|
||||
$a = $fraction[0] ?? 1;
|
||||
$b = $fraction[1] ?? 1;
|
||||
|
||||
return $columns * $a / $b;
|
||||
}
|
||||
return $columns * $a / $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'blocks' => $this->blocks(true)->toArray(),
|
||||
'id' => $this->id(),
|
||||
'width' => $this->width(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* The result is being sent to the editor
|
||||
* via the API in the panel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'blocks' => $this->blocks(true)->toArray(),
|
||||
'id' => $this->id(),
|
||||
'width' => $this->width(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the column
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function width(): string
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
/**
|
||||
* Returns the width of the column
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function width(): string
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class LayoutColumns extends Items
|
||||
{
|
||||
public const ITEM_CLASS = '\Kirby\Cms\LayoutColumn';
|
||||
public const ITEM_CLASS = '\Kirby\Cms\LayoutColumn';
|
||||
}
|
||||
|
||||
@@ -18,86 +18,86 @@ use Throwable;
|
||||
*/
|
||||
class Layouts extends Items
|
||||
{
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Layout';
|
||||
public const ITEM_CLASS = '\Kirby\Cms\Layout';
|
||||
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$first = $items[0] ?? [];
|
||||
public static function factory(array $items = null, array $params = [])
|
||||
{
|
||||
$first = $items[0] ?? [];
|
||||
|
||||
// if there are no wrapping layouts for blocks yet …
|
||||
if (array_key_exists('content', $first) === true || array_key_exists('type', $first) === true) {
|
||||
$items = [
|
||||
[
|
||||
'id' => Str::uuid(),
|
||||
'columns' => [
|
||||
[
|
||||
'width' => '1/1',
|
||||
'blocks' => $items
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
// if there are no wrapping layouts for blocks yet …
|
||||
if (array_key_exists('content', $first) === true || array_key_exists('type', $first) === true) {
|
||||
$items = [
|
||||
[
|
||||
'id' => Str::uuid(),
|
||||
'columns' => [
|
||||
[
|
||||
'width' => '1/1',
|
||||
'blocks' => $items
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
return parent::factory($items, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given block type exists in the layouts collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBlockType(string $type): bool
|
||||
{
|
||||
return $this->toBlocks()->hasType($type);
|
||||
}
|
||||
/**
|
||||
* Checks if a given block type exists in the layouts collection
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBlockType(string $type): bool
|
||||
{
|
||||
return $this->toBlocks()->hasType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse layouts data
|
||||
*
|
||||
* @param array|string $input
|
||||
* @return array
|
||||
*/
|
||||
public static function parse($input): array
|
||||
{
|
||||
if (empty($input) === false && is_array($input) === false) {
|
||||
try {
|
||||
$input = Data::decode($input, 'json');
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse layouts data
|
||||
*
|
||||
* @param array|string $input
|
||||
* @return array
|
||||
*/
|
||||
public static function parse($input): array
|
||||
{
|
||||
if (empty($input) === false && is_array($input) === false) {
|
||||
try {
|
||||
$input = Data::decode($input, 'json');
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
if (empty($input) === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts layouts to blocks
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param bool $includeHidden Sets whether to include hidden blocks
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public function toBlocks(bool $includeHidden = false)
|
||||
{
|
||||
$blocks = [];
|
||||
/**
|
||||
* Converts layouts to blocks
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param bool $includeHidden Sets whether to include hidden blocks
|
||||
* @return \Kirby\Cms\Blocks
|
||||
*/
|
||||
public function toBlocks(bool $includeHidden = false)
|
||||
{
|
||||
$blocks = [];
|
||||
|
||||
if ($this->isNotEmpty() === true) {
|
||||
foreach ($this->data() as $layout) {
|
||||
foreach ($layout->columns() as $column) {
|
||||
foreach ($column->blocks($includeHidden) as $block) {
|
||||
$blocks[] = $block->toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->isNotEmpty() === true) {
|
||||
foreach ($this->data() as $layout) {
|
||||
foreach ($layout->columns() as $column) {
|
||||
foreach ($column->blocks($includeHidden) as $block) {
|
||||
$blocks[] = $block->toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Blocks::factory($blocks);
|
||||
}
|
||||
return Blocks::factory($blocks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,223 +28,223 @@ use Kirby\Filesystem\F;
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $withPlugins;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $withPlugins;
|
||||
|
||||
/**
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @param bool $withPlugins
|
||||
*/
|
||||
public function __construct(App $kirby, bool $withPlugins = true)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
$this->withPlugins = $withPlugins;
|
||||
}
|
||||
/**
|
||||
* @param \Kirby\Cms\App $kirby
|
||||
* @param bool $withPlugins
|
||||
*/
|
||||
public function __construct(App $kirby, bool $withPlugins = true)
|
||||
{
|
||||
$this->kirby = $kirby;
|
||||
$this->withPlugins = $withPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the area definition
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function area(string $name): ?array
|
||||
{
|
||||
return $this->areas()[$name] ?? null;
|
||||
}
|
||||
/**
|
||||
* Loads the area definition
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function area(string $name): ?array
|
||||
{
|
||||
return $this->areas()[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all areas and makes sure that plugins
|
||||
* are injected properly
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function areas(): array
|
||||
{
|
||||
$areas = [];
|
||||
$extensions = $this->withPlugins === true ? $this->kirby->extensions('areas') : [];
|
||||
/**
|
||||
* Loads all areas and makes sure that plugins
|
||||
* are injected properly
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function areas(): array
|
||||
{
|
||||
$areas = [];
|
||||
$extensions = $this->withPlugins === true ? $this->kirby->extensions('areas') : [];
|
||||
|
||||
// load core areas and extend them with elements from plugins if they exist
|
||||
foreach ($this->kirby->core()->areas() as $id => $area) {
|
||||
$area = $this->resolveArea($area);
|
||||
// load core areas and extend them with elements from plugins if they exist
|
||||
foreach ($this->kirby->core()->areas() as $id => $area) {
|
||||
$area = $this->resolveArea($area);
|
||||
|
||||
if (isset($extensions[$id]) === true) {
|
||||
foreach ($extensions[$id] as $areaExtension) {
|
||||
$extension = $this->resolveArea($areaExtension);
|
||||
$area = array_replace_recursive($area, $extension);
|
||||
}
|
||||
if (isset($extensions[$id]) === true) {
|
||||
foreach ($extensions[$id] as $areaExtension) {
|
||||
$extension = $this->resolveArea($areaExtension);
|
||||
$area = array_replace_recursive($area, $extension);
|
||||
}
|
||||
|
||||
unset($extensions[$id]);
|
||||
}
|
||||
unset($extensions[$id]);
|
||||
}
|
||||
|
||||
$areas[$id] = $area;
|
||||
}
|
||||
$areas[$id] = $area;
|
||||
}
|
||||
|
||||
// add additional areas from plugins
|
||||
foreach ($extensions as $id => $areaExtensions) {
|
||||
foreach ($areaExtensions as $areaExtension) {
|
||||
$areas[$id] = $this->resolve($areaExtension);
|
||||
}
|
||||
}
|
||||
// add additional areas from plugins
|
||||
foreach ($extensions as $id => $areaExtensions) {
|
||||
foreach ($areaExtensions as $areaExtension) {
|
||||
$areas[$id] = $this->resolve($areaExtension);
|
||||
}
|
||||
}
|
||||
|
||||
return $areas;
|
||||
}
|
||||
return $areas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a core component closure
|
||||
*
|
||||
* @param string $name
|
||||
* @return \Closure|null
|
||||
*/
|
||||
public function component(string $name): ?Closure
|
||||
{
|
||||
return $this->extension('components', $name);
|
||||
}
|
||||
/**
|
||||
* Loads a core component closure
|
||||
*
|
||||
* @param string $name
|
||||
* @return \Closure|null
|
||||
*/
|
||||
public function component(string $name): ?Closure
|
||||
{
|
||||
return $this->extension('components', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all core component closures
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function components(): array
|
||||
{
|
||||
return $this->extensions('components');
|
||||
}
|
||||
/**
|
||||
* Loads all core component closures
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function components(): array
|
||||
{
|
||||
return $this->extensions('components');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a particular extension
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function extension(string $type, string $name)
|
||||
{
|
||||
return $this->extensions($type)[$name] ?? null;
|
||||
}
|
||||
/**
|
||||
* Loads a particular extension
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function extension(string $type, string $name)
|
||||
{
|
||||
return $this->extensions($type)[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all defined extensions
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function extensions(string $type): array
|
||||
{
|
||||
return $this->withPlugins === false ? $this->kirby->core()->$type() : $this->kirby->extensions($type);
|
||||
}
|
||||
/**
|
||||
* Loads all defined extensions
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function extensions(string $type): array
|
||||
{
|
||||
return $this->withPlugins === false ? $this->kirby->core()->$type() : $this->kirby->extensions($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The resolver takes a string, array or closure.
|
||||
*
|
||||
* 1.) a string is supposed to be a path to an existing file.
|
||||
* The file will either be included when it's a PHP file and
|
||||
* the array contents will be read. Or it will be parsed with
|
||||
* the Data class to read yml or json data into an array
|
||||
*
|
||||
* 2.) arrays are untouched and returned
|
||||
*
|
||||
* 3.) closures will be called and the Kirby instance will be
|
||||
* passed as first argument
|
||||
*
|
||||
* @param mixed $item
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolve($item)
|
||||
{
|
||||
if (is_string($item) === true) {
|
||||
if (F::extension($item) !== 'php') {
|
||||
$item = Data::read($item);
|
||||
} else {
|
||||
$item = require $item;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The resolver takes a string, array or closure.
|
||||
*
|
||||
* 1.) a string is supposed to be a path to an existing file.
|
||||
* The file will either be included when it's a PHP file and
|
||||
* the array contents will be read. Or it will be parsed with
|
||||
* the Data class to read yml or json data into an array
|
||||
*
|
||||
* 2.) arrays are untouched and returned
|
||||
*
|
||||
* 3.) closures will be called and the Kirby instance will be
|
||||
* passed as first argument
|
||||
*
|
||||
* @param mixed $item
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolve($item)
|
||||
{
|
||||
if (is_string($item) === true) {
|
||||
if (F::extension($item) !== 'php') {
|
||||
$item = Data::read($item);
|
||||
} else {
|
||||
$item = require $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_callable($item)) {
|
||||
$item = $item($this->kirby);
|
||||
}
|
||||
if (is_callable($item)) {
|
||||
$item = $item($this->kirby);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `static::resolve()` on all items
|
||||
* in the given array
|
||||
*
|
||||
* @param array $items
|
||||
* @return array
|
||||
*/
|
||||
public function resolveAll(array $items): array
|
||||
{
|
||||
$result = [];
|
||||
/**
|
||||
* Calls `static::resolve()` on all items
|
||||
* in the given array
|
||||
*
|
||||
* @param array $items
|
||||
* @return array
|
||||
*/
|
||||
public function resolveAll(array $items): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($items as $key => $value) {
|
||||
$result[$key] = $this->resolve($value);
|
||||
}
|
||||
foreach ($items as $key => $value) {
|
||||
$result[$key] = $this->resolve($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Areas need a bit of special treatment
|
||||
* when they are being loaded
|
||||
*
|
||||
* @param string|array|Closure $area
|
||||
* @return array
|
||||
*/
|
||||
public function resolveArea($area): array
|
||||
{
|
||||
$area = $this->resolve($area);
|
||||
$dropdowns = $area['dropdowns'] ?? [];
|
||||
/**
|
||||
* Areas need a bit of special treatment
|
||||
* when they are being loaded
|
||||
*
|
||||
* @param string|array|Closure $area
|
||||
* @return array
|
||||
*/
|
||||
public function resolveArea($area): array
|
||||
{
|
||||
$area = $this->resolve($area);
|
||||
$dropdowns = $area['dropdowns'] ?? [];
|
||||
|
||||
// convert closure dropdowns to an array definition
|
||||
// otherwise they cannot be merged properly later
|
||||
foreach ($dropdowns as $key => $dropdown) {
|
||||
if (is_a($dropdown, 'Closure') === true) {
|
||||
$area['dropdowns'][$key] = [
|
||||
'options' => $dropdown
|
||||
];
|
||||
}
|
||||
}
|
||||
// convert closure dropdowns to an array definition
|
||||
// otherwise they cannot be merged properly later
|
||||
foreach ($dropdowns as $key => $dropdown) {
|
||||
if (is_a($dropdown, 'Closure') === true) {
|
||||
$area['dropdowns'][$key] = [
|
||||
'options' => $dropdown
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $area;
|
||||
}
|
||||
return $area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a particular section definition
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function section(string $name): ?array
|
||||
{
|
||||
return $this->resolve($this->extension('sections', $name));
|
||||
}
|
||||
/**
|
||||
* Loads a particular section definition
|
||||
*
|
||||
* @param string $name
|
||||
* @return array|null
|
||||
*/
|
||||
public function section(string $name): ?array
|
||||
{
|
||||
return $this->resolve($this->extension('sections', $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all section defintions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sections(): array
|
||||
{
|
||||
return $this->resolveAll($this->extensions('sections'));
|
||||
}
|
||||
/**
|
||||
* Loads all section defintions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sections(): array
|
||||
{
|
||||
return $this->resolveAll($this->extensions('sections'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status flag, which shows
|
||||
* if plugins are loaded as well.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function withPlugins(): bool
|
||||
{
|
||||
return $this->withPlugins;
|
||||
}
|
||||
/**
|
||||
* Returns the status flag, which shows
|
||||
* if plugins are loaded as well.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function withPlugins(): bool
|
||||
{
|
||||
return $this->withPlugins;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,153 +20,153 @@ use Throwable;
|
||||
*/
|
||||
class Media
|
||||
{
|
||||
/**
|
||||
* Tries to find a file by model and filename
|
||||
* and to copy it to the media folder.
|
||||
*
|
||||
* @param \Kirby\Cms\Model|null $model
|
||||
* @param string $hash
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|false
|
||||
*/
|
||||
public static function link(Model $model = null, string $hash, string $filename)
|
||||
{
|
||||
if ($model === null) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Tries to find a file by model and filename
|
||||
* and to copy it to the media folder.
|
||||
*
|
||||
* @param \Kirby\Cms\Model|null $model
|
||||
* @param string $hash
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|false
|
||||
*/
|
||||
public static function link(Model $model = null, string $hash, string $filename)
|
||||
{
|
||||
if ($model === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fix issues with spaces in filenames
|
||||
$filename = urldecode($filename);
|
||||
// fix issues with spaces in filenames
|
||||
$filename = urldecode($filename);
|
||||
|
||||
// try to find a file by model and filename
|
||||
// this should work for all original files
|
||||
if ($file = $model->file($filename)) {
|
||||
// try to find a file by model and filename
|
||||
// this should work for all original files
|
||||
if ($file = $model->file($filename)) {
|
||||
|
||||
// check if the request contained an outdated media hash
|
||||
if ($file->mediaHash() !== $hash) {
|
||||
// if at least the token was correct, redirect
|
||||
if (Str::startsWith($hash, $file->mediaToken() . '-') === true) {
|
||||
return Response::redirect($file->mediaUrl(), 307);
|
||||
} else {
|
||||
// don't leak the correct token, render the error page
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check if the request contained an outdated media hash
|
||||
if ($file->mediaHash() !== $hash) {
|
||||
// if at least the token was correct, redirect
|
||||
if (Str::startsWith($hash, $file->mediaToken() . '-') === true) {
|
||||
return Response::redirect($file->mediaUrl(), 307);
|
||||
} else {
|
||||
// don't leak the correct token, render the error page
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// send the file to the browser
|
||||
return Response::file($file->publish()->mediaRoot());
|
||||
}
|
||||
// send the file to the browser
|
||||
return Response::file($file->publish()->mediaRoot());
|
||||
}
|
||||
|
||||
// try to generate a thumb for the file
|
||||
return static::thumb($model, $hash, $filename);
|
||||
}
|
||||
// try to generate a thumb for the file
|
||||
return static::thumb($model, $hash, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the file to the final media folder location
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $dest
|
||||
* @return bool
|
||||
*/
|
||||
public static function publish(File $file, string $dest): bool
|
||||
{
|
||||
// never publish risky files (e.g. HTML, PHP or Apache config files)
|
||||
FileRules::validFile($file, false);
|
||||
/**
|
||||
* Copy the file to the final media folder location
|
||||
*
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string $dest
|
||||
* @return bool
|
||||
*/
|
||||
public static function publish(File $file, string $dest): bool
|
||||
{
|
||||
// never publish risky files (e.g. HTML, PHP or Apache config files)
|
||||
FileRules::validFile($file, false);
|
||||
|
||||
$src = $file->root();
|
||||
$version = dirname($dest);
|
||||
$directory = dirname($version);
|
||||
$src = $file->root();
|
||||
$version = dirname($dest);
|
||||
$directory = dirname($version);
|
||||
|
||||
// unpublish all files except stuff in the version folder
|
||||
Media::unpublish($directory, $file, $version);
|
||||
// unpublish all files except stuff in the version folder
|
||||
Media::unpublish($directory, $file, $version);
|
||||
|
||||
// copy/overwrite the file to the dest folder
|
||||
return F::copy($src, $dest, true);
|
||||
}
|
||||
// copy/overwrite the file to the dest folder
|
||||
return F::copy($src, $dest, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a job file for the
|
||||
* given filename and then calls the thumb
|
||||
* component to create a thumbnail accordingly
|
||||
*
|
||||
* @param \Kirby\Cms\Model|string $model
|
||||
* @param string $hash
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|false
|
||||
*/
|
||||
public static function thumb($model, string $hash, string $filename)
|
||||
{
|
||||
$kirby = App::instance();
|
||||
/**
|
||||
* Tries to find a job file for the
|
||||
* given filename and then calls the thumb
|
||||
* component to create a thumbnail accordingly
|
||||
*
|
||||
* @param \Kirby\Cms\Model|string $model
|
||||
* @param string $hash
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|false
|
||||
*/
|
||||
public static function thumb($model, string $hash, string $filename)
|
||||
{
|
||||
$kirby = App::instance();
|
||||
|
||||
// assets
|
||||
if (is_string($model) === true) {
|
||||
$root = $kirby->root('media') . '/assets/' . $model . '/' . $hash;
|
||||
// parent files for file model that already included hash
|
||||
} elseif (is_a($model, '\Kirby\Cms\File')) {
|
||||
$root = dirname($model->mediaRoot());
|
||||
// model files
|
||||
} else {
|
||||
$root = $model->mediaRoot() . '/' . $hash;
|
||||
}
|
||||
// assets
|
||||
if (is_string($model) === true) {
|
||||
$root = $kirby->root('media') . '/assets/' . $model . '/' . $hash;
|
||||
// parent files for file model that already included hash
|
||||
} elseif (is_a($model, '\Kirby\Cms\File')) {
|
||||
$root = dirname($model->mediaRoot());
|
||||
// model files
|
||||
} else {
|
||||
$root = $model->mediaRoot() . '/' . $hash;
|
||||
}
|
||||
|
||||
try {
|
||||
$thumb = $root . '/' . $filename;
|
||||
$job = $root . '/.jobs/' . $filename . '.json';
|
||||
$options = Data::read($job);
|
||||
try {
|
||||
$thumb = $root . '/' . $filename;
|
||||
$job = $root . '/.jobs/' . $filename . '.json';
|
||||
$options = Data::read($job);
|
||||
|
||||
if (empty($options) === true) {
|
||||
return false;
|
||||
}
|
||||
if (empty($options) === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string($model) === true) {
|
||||
$source = $kirby->root('index') . '/' . $model . '/' . $options['filename'];
|
||||
} else {
|
||||
$source = $model->file($options['filename'])->root();
|
||||
}
|
||||
if (is_string($model) === true) {
|
||||
$source = $kirby->root('index') . '/' . $model . '/' . $options['filename'];
|
||||
} else {
|
||||
$source = $model->file($options['filename'])->root();
|
||||
}
|
||||
|
||||
try {
|
||||
$kirby->thumb($source, $thumb, $options);
|
||||
F::remove($job);
|
||||
return Response::file($thumb);
|
||||
} catch (Throwable $e) {
|
||||
F::remove($thumb);
|
||||
return Response::file($source);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$kirby->thumb($source, $thumb, $options);
|
||||
F::remove($job);
|
||||
return Response::file($thumb);
|
||||
} catch (Throwable $e) {
|
||||
F::remove($thumb);
|
||||
return Response::file($source);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all versions of the given file
|
||||
* within the parent directory
|
||||
*
|
||||
* @param string $directory
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null $ignore
|
||||
* @return bool
|
||||
*/
|
||||
public static function unpublish(string $directory, File $file, string $ignore = null): bool
|
||||
{
|
||||
if (is_dir($directory) === false) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Deletes all versions of the given file
|
||||
* within the parent directory
|
||||
*
|
||||
* @param string $directory
|
||||
* @param \Kirby\Cms\File $file
|
||||
* @param string|null $ignore
|
||||
* @return bool
|
||||
*/
|
||||
public static function unpublish(string $directory, File $file, string $ignore = null): bool
|
||||
{
|
||||
if (is_dir($directory) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get both old and new versions (pre and post Kirby 3.4.0)
|
||||
$versions = array_merge(
|
||||
glob($directory . '/' . crc32($file->filename()) . '-*', GLOB_ONLYDIR),
|
||||
glob($directory . '/' . $file->mediaToken() . '-*', GLOB_ONLYDIR)
|
||||
);
|
||||
// get both old and new versions (pre and post Kirby 3.4.0)
|
||||
$versions = array_merge(
|
||||
glob($directory . '/' . crc32($file->filename()) . '-*', GLOB_ONLYDIR),
|
||||
glob($directory . '/' . $file->mediaToken() . '-*', GLOB_ONLYDIR)
|
||||
);
|
||||
|
||||
// delete all versions of the file
|
||||
foreach ($versions as $version) {
|
||||
if ($version === $ignore) {
|
||||
continue;
|
||||
}
|
||||
// delete all versions of the file
|
||||
foreach ($versions as $version) {
|
||||
if ($version === $ignore) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Dir::remove($version);
|
||||
}
|
||||
Dir::remove($version);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,103 +15,103 @@ use Kirby\Toolkit\Properties;
|
||||
*/
|
||||
abstract class Model
|
||||
{
|
||||
use Properties;
|
||||
use Properties;
|
||||
|
||||
/**
|
||||
* Each model must define a CLASS_ALIAS
|
||||
* which will be used in template queries.
|
||||
* The CLASS_ALIAS is a short human-readable
|
||||
* version of the class name. I.e. page.
|
||||
*/
|
||||
public const CLASS_ALIAS = null;
|
||||
/**
|
||||
* Each model must define a CLASS_ALIAS
|
||||
* which will be used in template queries.
|
||||
* The CLASS_ALIAS is a short human-readable
|
||||
* version of the class name. I.e. page.
|
||||
*/
|
||||
public const CLASS_ALIAS = null;
|
||||
|
||||
/**
|
||||
* The parent Kirby instance
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
public static $kirby;
|
||||
/**
|
||||
* The parent Kirby instance
|
||||
*
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
public static $kirby;
|
||||
|
||||
/**
|
||||
* The parent site instance
|
||||
*
|
||||
* @var \Kirby\Cms\Site
|
||||
*/
|
||||
protected $site;
|
||||
/**
|
||||
* The parent site instance
|
||||
*
|
||||
* @var \Kirby\Cms\Site
|
||||
*/
|
||||
protected $site;
|
||||
|
||||
/**
|
||||
* Makes it possible to convert the entire model
|
||||
* to a string. Mostly useful for debugging
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->id();
|
||||
}
|
||||
/**
|
||||
* Makes it possible to convert the entire model
|
||||
* to a string. Mostly useful for debugging
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Each model must return a unique id
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function id()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Each model must return a unique id
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function id()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return static::$kirby ??= App::instance();
|
||||
}
|
||||
/**
|
||||
* Returns the parent Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return static::$kirby ??= App::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent Site instance
|
||||
*
|
||||
* @return \Kirby\Cms\Site
|
||||
*/
|
||||
public function site()
|
||||
{
|
||||
return $this->site ??= $this->kirby()->site();
|
||||
}
|
||||
/**
|
||||
* Returns the parent Site instance
|
||||
*
|
||||
* @return \Kirby\Cms\Site
|
||||
*/
|
||||
public function site()
|
||||
{
|
||||
return $this->site ??= $this->kirby()->site();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent Kirby object
|
||||
*
|
||||
* @param \Kirby\Cms\App|null $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby = null)
|
||||
{
|
||||
static::$kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Setter for the parent Kirby object
|
||||
*
|
||||
* @param \Kirby\Cms\App|null $kirby
|
||||
* @return $this
|
||||
*/
|
||||
protected function setKirby(App $kirby = null)
|
||||
{
|
||||
static::$kirby = $kirby;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent site object
|
||||
*
|
||||
* @internal
|
||||
* @param \Kirby\Cms\Site|null $site
|
||||
* @return $this
|
||||
*/
|
||||
public function setSite(Site $site = null)
|
||||
{
|
||||
$this->site = $site;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Setter for the parent site object
|
||||
*
|
||||
* @internal
|
||||
* @param \Kirby\Cms\Site|null $site
|
||||
* @return $this
|
||||
*/
|
||||
public function setSite(Site $site = null)
|
||||
{
|
||||
$this->site = $site;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the model to a simple array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->propertiesToArray();
|
||||
}
|
||||
/**
|
||||
* Convert the model to a simple array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->propertiesToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,102 +15,102 @@ use Kirby\Toolkit\A;
|
||||
*/
|
||||
abstract class ModelPermissions
|
||||
{
|
||||
protected $category;
|
||||
protected $model;
|
||||
protected $options;
|
||||
protected $permissions;
|
||||
protected $user;
|
||||
protected $category;
|
||||
protected $model;
|
||||
protected $options;
|
||||
protected $permissions;
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function __call(string $method, array $arguments = []): bool
|
||||
{
|
||||
return $this->can($method);
|
||||
}
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function __call(string $method, array $arguments = []): bool
|
||||
{
|
||||
return $this->can($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* ModelPermissions constructor
|
||||
*
|
||||
* @param \Kirby\Cms\Model $model
|
||||
*/
|
||||
public function __construct(Model $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->options = $model->blueprint()->options();
|
||||
$this->user = $model->kirby()->user() ?? User::nobody();
|
||||
$this->permissions = $this->user->role()->permissions();
|
||||
}
|
||||
/**
|
||||
* ModelPermissions constructor
|
||||
*
|
||||
* @param \Kirby\Cms\Model $model
|
||||
*/
|
||||
public function __construct(Model $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->options = $model->blueprint()->options();
|
||||
$this->user = $model->kirby()->user() ?? User::nobody();
|
||||
$this->permissions = $this->user->role()->permissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function can(string $action): bool
|
||||
{
|
||||
$role = $this->user->role()->id();
|
||||
/**
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function can(string $action): bool
|
||||
{
|
||||
$role = $this->user->role()->id();
|
||||
|
||||
if ($role === 'nobody') {
|
||||
return false;
|
||||
}
|
||||
if ($role === 'nobody') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for a custom overall can method
|
||||
if (method_exists($this, 'can' . $action) === true && $this->{'can' . $action}() === false) {
|
||||
return false;
|
||||
}
|
||||
// check for a custom overall can method
|
||||
if (method_exists($this, 'can' . $action) === true && $this->{'can' . $action}() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// evaluate the blueprint options block
|
||||
if (isset($this->options[$action]) === true) {
|
||||
$options = $this->options[$action];
|
||||
// evaluate the blueprint options block
|
||||
if (isset($this->options[$action]) === true) {
|
||||
$options = $this->options[$action];
|
||||
|
||||
if ($options === false) {
|
||||
return false;
|
||||
}
|
||||
if ($options === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($options === true) {
|
||||
return true;
|
||||
}
|
||||
if ($options === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($options) === true && A::isAssociative($options) === true) {
|
||||
return $options[$role] ?? $options['*'] ?? false;
|
||||
}
|
||||
}
|
||||
if (is_array($options) === true && A::isAssociative($options) === true) {
|
||||
return $options[$role] ?? $options['*'] ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->permissions->for($this->category, $action);
|
||||
}
|
||||
return $this->permissions->for($this->category, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function cannot(string $action): bool
|
||||
{
|
||||
return $this->can($action) === false;
|
||||
}
|
||||
/**
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function cannot(string $action): bool
|
||||
{
|
||||
return $this->can($action) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = [];
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($this->options as $key => $value) {
|
||||
$array[$key] = $this->can($key);
|
||||
}
|
||||
foreach ($this->options as $key => $value) {
|
||||
$array[$key] = $this->can($key);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,31 +18,31 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Nest
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
* @param null $parent
|
||||
* @return mixed
|
||||
*/
|
||||
public static function create($data, $parent = null)
|
||||
{
|
||||
if (is_scalar($data) === true) {
|
||||
return new Field($parent, $data, $data);
|
||||
}
|
||||
/**
|
||||
* @param $data
|
||||
* @param null $parent
|
||||
* @return mixed
|
||||
*/
|
||||
public static function create($data, $parent = null)
|
||||
{
|
||||
if (is_scalar($data) === true) {
|
||||
return new Field($parent, $data, $data);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$result = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value) === true) {
|
||||
$result[$key] = static::create($value, $parent);
|
||||
} elseif (is_scalar($value) === true) {
|
||||
$result[$key] = new Field($parent, $key, $value);
|
||||
}
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value) === true) {
|
||||
$result[$key] = static::create($value, $parent);
|
||||
} elseif (is_scalar($value) === true) {
|
||||
$result[$key] = new Field($parent, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_int(key($data))) {
|
||||
return new NestCollection($result);
|
||||
} else {
|
||||
return new NestObject($result);
|
||||
}
|
||||
}
|
||||
if (is_int(key($data))) {
|
||||
return new NestCollection($result);
|
||||
} else {
|
||||
return new NestObject($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,16 @@ use Kirby\Toolkit\Collection as BaseCollection;
|
||||
*/
|
||||
class NestCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* Converts all objects in the collection
|
||||
* to an array. This can also take a callback
|
||||
* function to further modify the array result.
|
||||
*
|
||||
* @param \Closure|null $map
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return parent::toArray($map ?? fn ($object) => $object->toArray());
|
||||
}
|
||||
/**
|
||||
* Converts all objects in the collection
|
||||
* to an array. This can also take a callback
|
||||
* function to further modify the array result.
|
||||
*
|
||||
* @param \Closure|null $map
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(Closure $map = null): array
|
||||
{
|
||||
return parent::toArray($map ?? fn ($object) => $object->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,29 +15,29 @@ use Kirby\Toolkit\Obj;
|
||||
*/
|
||||
class NestObject extends Obj
|
||||
{
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ((array)$this as $key => $value) {
|
||||
if (is_a($value, 'Kirby\Cms\Field') === true) {
|
||||
$result[$key] = $value->value();
|
||||
continue;
|
||||
}
|
||||
foreach ((array)$this as $key => $value) {
|
||||
if (is_a($value, 'Kirby\Cms\Field') === true) {
|
||||
$result[$key] = $value->value();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_object($value) === true && method_exists($value, 'toArray')) {
|
||||
$result[$key] = $value->toArray();
|
||||
continue;
|
||||
}
|
||||
if (is_object($value) === true && method_exists($value, 'toArray')) {
|
||||
$result[$key] = $value->toArray();
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$key] = $value;
|
||||
}
|
||||
$result[$key] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,197 +13,197 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class PageBlueprint extends Blueprint
|
||||
{
|
||||
/**
|
||||
* Creates a new page blueprint object
|
||||
* with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
/**
|
||||
* Creates a new page blueprint object
|
||||
* with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeSlug' => null,
|
||||
'changeStatus' => null,
|
||||
'changeTemplate' => null,
|
||||
'changeTitle' => null,
|
||||
'create' => null,
|
||||
'delete' => null,
|
||||
'duplicate' => null,
|
||||
'read' => null,
|
||||
'preview' => null,
|
||||
'sort' => null,
|
||||
'update' => null,
|
||||
],
|
||||
// aliases (from v2)
|
||||
[
|
||||
'status' => 'changeStatus',
|
||||
'template' => 'changeTemplate',
|
||||
'title' => 'changeTitle',
|
||||
'url' => 'changeSlug',
|
||||
]
|
||||
);
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeSlug' => null,
|
||||
'changeStatus' => null,
|
||||
'changeTemplate' => null,
|
||||
'changeTitle' => null,
|
||||
'create' => null,
|
||||
'delete' => null,
|
||||
'duplicate' => null,
|
||||
'read' => null,
|
||||
'preview' => null,
|
||||
'sort' => null,
|
||||
'update' => null,
|
||||
],
|
||||
// aliases (from v2)
|
||||
[
|
||||
'status' => 'changeStatus',
|
||||
'template' => 'changeTemplate',
|
||||
'title' => 'changeTitle',
|
||||
'url' => 'changeSlug',
|
||||
]
|
||||
);
|
||||
|
||||
// normalize the ordering number
|
||||
$this->props['num'] = $this->normalizeNum($this->props['num'] ?? 'default');
|
||||
// normalize the ordering number
|
||||
$this->props['num'] = $this->normalizeNum($this->props['num'] ?? 'default');
|
||||
|
||||
// normalize the available status array
|
||||
$this->props['status'] = $this->normalizeStatus($this->props['status'] ?? null);
|
||||
}
|
||||
// normalize the available status array
|
||||
$this->props['status'] = $this->normalizeStatus($this->props['status'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page numbering mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function num(): string
|
||||
{
|
||||
return $this->props['num'];
|
||||
}
|
||||
/**
|
||||
* Returns the page numbering mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function num(): string
|
||||
{
|
||||
return $this->props['num'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the ordering number
|
||||
*
|
||||
* @param mixed $num
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeNum($num): string
|
||||
{
|
||||
$aliases = [
|
||||
'0' => 'zero',
|
||||
'sort' => 'default',
|
||||
];
|
||||
/**
|
||||
* Normalizes the ordering number
|
||||
*
|
||||
* @param mixed $num
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeNum($num): string
|
||||
{
|
||||
$aliases = [
|
||||
'0' => 'zero',
|
||||
'sort' => 'default',
|
||||
];
|
||||
|
||||
if (isset($aliases[$num]) === true) {
|
||||
return $aliases[$num];
|
||||
}
|
||||
if (isset($aliases[$num]) === true) {
|
||||
return $aliases[$num];
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
return $num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the available status options for the page
|
||||
*
|
||||
* @param mixed $status
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeStatus($status): array
|
||||
{
|
||||
$defaults = [
|
||||
'draft' => [
|
||||
'label' => $this->i18n('page.status.draft'),
|
||||
'text' => $this->i18n('page.status.draft.description'),
|
||||
],
|
||||
'unlisted' => [
|
||||
'label' => $this->i18n('page.status.unlisted'),
|
||||
'text' => $this->i18n('page.status.unlisted.description'),
|
||||
],
|
||||
'listed' => [
|
||||
'label' => $this->i18n('page.status.listed'),
|
||||
'text' => $this->i18n('page.status.listed.description'),
|
||||
]
|
||||
];
|
||||
/**
|
||||
* Normalizes the available status options for the page
|
||||
*
|
||||
* @param mixed $status
|
||||
* @return array
|
||||
*/
|
||||
protected function normalizeStatus($status): array
|
||||
{
|
||||
$defaults = [
|
||||
'draft' => [
|
||||
'label' => $this->i18n('page.status.draft'),
|
||||
'text' => $this->i18n('page.status.draft.description'),
|
||||
],
|
||||
'unlisted' => [
|
||||
'label' => $this->i18n('page.status.unlisted'),
|
||||
'text' => $this->i18n('page.status.unlisted.description'),
|
||||
],
|
||||
'listed' => [
|
||||
'label' => $this->i18n('page.status.listed'),
|
||||
'text' => $this->i18n('page.status.listed.description'),
|
||||
]
|
||||
];
|
||||
|
||||
// use the defaults, when the status is not defined
|
||||
if (empty($status) === true) {
|
||||
$status = $defaults;
|
||||
}
|
||||
// use the defaults, when the status is not defined
|
||||
if (empty($status) === true) {
|
||||
$status = $defaults;
|
||||
}
|
||||
|
||||
// extend the status definition
|
||||
$status = $this->extend($status);
|
||||
// extend the status definition
|
||||
$status = $this->extend($status);
|
||||
|
||||
// clean up and translate each status
|
||||
foreach ($status as $key => $options) {
|
||||
// clean up and translate each status
|
||||
foreach ($status as $key => $options) {
|
||||
|
||||
// skip invalid status definitions
|
||||
if (in_array($key, ['draft', 'listed', 'unlisted']) === false || $options === false) {
|
||||
unset($status[$key]);
|
||||
continue;
|
||||
}
|
||||
// skip invalid status definitions
|
||||
if (in_array($key, ['draft', 'listed', 'unlisted']) === false || $options === false) {
|
||||
unset($status[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($options === true) {
|
||||
$status[$key] = $defaults[$key];
|
||||
continue;
|
||||
}
|
||||
if ($options === true) {
|
||||
$status[$key] = $defaults[$key];
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert everything to a simple array
|
||||
if (is_array($options) === false) {
|
||||
$status[$key] = [
|
||||
'label' => $options,
|
||||
'text' => null
|
||||
];
|
||||
}
|
||||
// convert everything to a simple array
|
||||
if (is_array($options) === false) {
|
||||
$status[$key] = [
|
||||
'label' => $options,
|
||||
'text' => null
|
||||
];
|
||||
}
|
||||
|
||||
// always make sure to have a proper label
|
||||
if (empty($status[$key]['label']) === true) {
|
||||
$status[$key]['label'] = $defaults[$key]['label'];
|
||||
}
|
||||
// always make sure to have a proper label
|
||||
if (empty($status[$key]['label']) === true) {
|
||||
$status[$key]['label'] = $defaults[$key]['label'];
|
||||
}
|
||||
|
||||
// also make sure to have the text field set
|
||||
if (isset($status[$key]['text']) === false) {
|
||||
$status[$key]['text'] = null;
|
||||
}
|
||||
// also make sure to have the text field set
|
||||
if (isset($status[$key]['text']) === false) {
|
||||
$status[$key]['text'] = null;
|
||||
}
|
||||
|
||||
// translate text and label if necessary
|
||||
$status[$key]['label'] = $this->i18n($status[$key]['label'], $status[$key]['label']);
|
||||
$status[$key]['text'] = $this->i18n($status[$key]['text'], $status[$key]['text']);
|
||||
}
|
||||
// translate text and label if necessary
|
||||
$status[$key]['label'] = $this->i18n($status[$key]['label'], $status[$key]['label']);
|
||||
$status[$key]['text'] = $this->i18n($status[$key]['text'], $status[$key]['text']);
|
||||
}
|
||||
|
||||
// the draft status is required
|
||||
if (isset($status['draft']) === false) {
|
||||
$status = ['draft' => $defaults['draft']] + $status;
|
||||
}
|
||||
// the draft status is required
|
||||
if (isset($status['draft']) === false) {
|
||||
$status = ['draft' => $defaults['draft']] + $status;
|
||||
}
|
||||
|
||||
// remove the draft status for the home and error pages
|
||||
if ($this->model->isHomeOrErrorPage() === true) {
|
||||
unset($status['draft']);
|
||||
}
|
||||
// remove the draft status for the home and error pages
|
||||
if ($this->model->isHomeOrErrorPage() === true) {
|
||||
unset($status['draft']);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options object
|
||||
* that handles page options and permissions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->props['options'];
|
||||
}
|
||||
/**
|
||||
* Returns the options object
|
||||
* that handles page options and permissions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->props['options'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preview settings
|
||||
* The preview setting controls the "Open"
|
||||
* button in the panel and redirects it to a
|
||||
* different URL if necessary.
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$preview = $this->props['options']['preview'] ?? true;
|
||||
/**
|
||||
* Returns the preview settings
|
||||
* The preview setting controls the "Open"
|
||||
* button in the panel and redirects it to a
|
||||
* different URL if necessary.
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$preview = $this->props['options']['preview'] ?? true;
|
||||
|
||||
if (is_string($preview) === true) {
|
||||
return $this->model->toString($preview);
|
||||
}
|
||||
if (is_string($preview) === true) {
|
||||
return $this->model->toString($preview);
|
||||
}
|
||||
|
||||
return $preview;
|
||||
}
|
||||
return $preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function status(): array
|
||||
{
|
||||
return $this->props['status'];
|
||||
}
|
||||
/**
|
||||
* Returns the status array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function status(): array
|
||||
{
|
||||
return $this->props['status'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,68 +13,68 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class PagePermissions extends ModelPermissions
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $category = 'pages';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $category = 'pages';
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeSlug(): bool
|
||||
{
|
||||
return $this->model->isHomeOrErrorPage() !== true;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeSlug(): bool
|
||||
{
|
||||
return $this->model->isHomeOrErrorPage() !== true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeStatus(): bool
|
||||
{
|
||||
return $this->model->isErrorPage() !== true;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeStatus(): bool
|
||||
{
|
||||
return $this->model->isErrorPage() !== true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeTemplate(): bool
|
||||
{
|
||||
if ($this->model->isHomeOrErrorPage() === true) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canChangeTemplate(): bool
|
||||
{
|
||||
if ($this->model->isHomeOrErrorPage() === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($this->model->blueprints()) <= 1) {
|
||||
return false;
|
||||
}
|
||||
if (count($this->model->blueprints()) <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canDelete(): bool
|
||||
{
|
||||
return $this->model->isHomeOrErrorPage() !== true;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canDelete(): bool
|
||||
{
|
||||
return $this->model->isHomeOrErrorPage() !== true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canSort(): bool
|
||||
{
|
||||
if ($this->model->isErrorPage() === true) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canSort(): bool
|
||||
{
|
||||
if ($this->model->isErrorPage() === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->model->isListed() !== true) {
|
||||
return false;
|
||||
}
|
||||
if ($this->model->isListed() !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->model->blueprint()->num() !== 'default') {
|
||||
return false;
|
||||
}
|
||||
if ($this->model->blueprint()->num() !== 'default') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,248 +18,248 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class PagePicker extends Picker
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
protected $items;
|
||||
/**
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
protected $itemsForQuery;
|
||||
/**
|
||||
* @var \Kirby\Cms\Pages
|
||||
*/
|
||||
protected $itemsForQuery;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @var \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Extends the basic defaults
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
return array_merge(parent::defaults(), [
|
||||
// Page ID of the selected parent. Used to navigate
|
||||
'parent' => null,
|
||||
// enable/disable subpage navigation
|
||||
'subpages' => true,
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Extends the basic defaults
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
return array_merge(parent::defaults(), [
|
||||
// Page ID of the selected parent. Used to navigate
|
||||
'parent' => null,
|
||||
// enable/disable subpage navigation
|
||||
'subpages' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model object that
|
||||
* is currently selected in the page picker.
|
||||
* It normally starts at the site, but can
|
||||
* also be any subpage. When a query is given
|
||||
* and subpage navigation is deactivated,
|
||||
* there will be no model available at all.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
// no subpages navigation = no model
|
||||
if ($this->options['subpages'] === false) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the parent model object that
|
||||
* is currently selected in the page picker.
|
||||
* It normally starts at the site, but can
|
||||
* also be any subpage. When a query is given
|
||||
* and subpage navigation is deactivated,
|
||||
* there will be no model available at all.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
// no subpages navigation = no model
|
||||
if ($this->options['subpages'] === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the model for queries is a bit more tricky to find
|
||||
if (empty($this->options['query']) === false) {
|
||||
return $this->modelForQuery();
|
||||
}
|
||||
// the model for queries is a bit more tricky to find
|
||||
if (empty($this->options['query']) === false) {
|
||||
return $this->modelForQuery();
|
||||
}
|
||||
|
||||
return $this->parent();
|
||||
}
|
||||
return $this->parent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a model object for the given
|
||||
* query, depending on the parent and subpages
|
||||
* options.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
public function modelForQuery()
|
||||
{
|
||||
if ($this->options['subpages'] === true && empty($this->options['parent']) === false) {
|
||||
return $this->parent();
|
||||
}
|
||||
/**
|
||||
* Returns a model object for the given
|
||||
* query, depending on the parent and subpages
|
||||
* options.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site|null
|
||||
*/
|
||||
public function modelForQuery()
|
||||
{
|
||||
if ($this->options['subpages'] === true && empty($this->options['parent']) === false) {
|
||||
return $this->parent();
|
||||
}
|
||||
|
||||
if ($items = $this->items()) {
|
||||
return $items->parent();
|
||||
}
|
||||
if ($items = $this->items()) {
|
||||
return $items->parent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns basic information about the
|
||||
* parent model that is currently selected
|
||||
* in the page picker.
|
||||
*
|
||||
* @param \Kirby\Cms\Site|\Kirby\Cms\Page|null
|
||||
* @return array|null
|
||||
*/
|
||||
public function modelToArray($model = null): ?array
|
||||
{
|
||||
if ($model === null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns basic information about the
|
||||
* parent model that is currently selected
|
||||
* in the page picker.
|
||||
*
|
||||
* @param \Kirby\Cms\Site|\Kirby\Cms\Page|null
|
||||
* @return array|null
|
||||
*/
|
||||
public function modelToArray($model = null): ?array
|
||||
{
|
||||
if ($model === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the selected model is the site. there's nothing above
|
||||
if (is_a($model, 'Kirby\Cms\Site') === true) {
|
||||
return [
|
||||
'id' => null,
|
||||
'parent' => null,
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
// the selected model is the site. there's nothing above
|
||||
if (is_a($model, 'Kirby\Cms\Site') === true) {
|
||||
return [
|
||||
'id' => null,
|
||||
'parent' => null,
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
|
||||
// the top-most page has been reached
|
||||
// the missing id indicates that there's nothing above
|
||||
if ($model->id() === $this->start()->id()) {
|
||||
return [
|
||||
'id' => null,
|
||||
'parent' => null,
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
// the top-most page has been reached
|
||||
// the missing id indicates that there's nothing above
|
||||
if ($model->id() === $this->start()->id()) {
|
||||
return [
|
||||
'id' => null,
|
||||
'parent' => null,
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
|
||||
// the model is a regular page
|
||||
return [
|
||||
'id' => $model->id(),
|
||||
'parent' => $model->parentModel()->id(),
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
// the model is a regular page
|
||||
return [
|
||||
'id' => $model->id(),
|
||||
'parent' => $model->parentModel()->id(),
|
||||
'title' => $model->title()->value()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all pages for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Pages|null
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
// cache
|
||||
if ($this->items !== null) {
|
||||
return $this->items;
|
||||
}
|
||||
/**
|
||||
* Search all pages for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Pages|null
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
// cache
|
||||
if ($this->items !== null) {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
// no query? simple parent-based search for pages
|
||||
if (empty($this->options['query']) === true) {
|
||||
$items = $this->itemsForParent();
|
||||
// no query? simple parent-based search for pages
|
||||
if (empty($this->options['query']) === true) {
|
||||
$items = $this->itemsForParent();
|
||||
|
||||
// when subpage navigation is enabled, a parent
|
||||
// might be passed in addition to the query.
|
||||
// The parent then takes priority.
|
||||
} elseif ($this->options['subpages'] === true && empty($this->options['parent']) === false) {
|
||||
$items = $this->itemsForParent();
|
||||
// when subpage navigation is enabled, a parent
|
||||
// might be passed in addition to the query.
|
||||
// The parent then takes priority.
|
||||
} elseif ($this->options['subpages'] === true && empty($this->options['parent']) === false) {
|
||||
$items = $this->itemsForParent();
|
||||
|
||||
// search by query
|
||||
} else {
|
||||
$items = $this->itemsForQuery();
|
||||
}
|
||||
// search by query
|
||||
} else {
|
||||
$items = $this->itemsForQuery();
|
||||
}
|
||||
|
||||
// filter protected pages
|
||||
$items = $items->filter('isReadable', true);
|
||||
// filter protected pages
|
||||
$items = $items->filter('isReadable', true);
|
||||
|
||||
// search
|
||||
$items = $this->search($items);
|
||||
// search
|
||||
$items = $this->search($items);
|
||||
|
||||
// paginate the result
|
||||
return $this->items = $this->paginate($items);
|
||||
}
|
||||
// paginate the result
|
||||
return $this->items = $this->paginate($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for pages by parent
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function itemsForParent()
|
||||
{
|
||||
return $this->parent()->children();
|
||||
}
|
||||
/**
|
||||
* Search for pages by parent
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function itemsForParent()
|
||||
{
|
||||
return $this->parent()->children();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for pages by query string
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function itemsForQuery()
|
||||
{
|
||||
// cache
|
||||
if ($this->itemsForQuery !== null) {
|
||||
return $this->itemsForQuery;
|
||||
}
|
||||
/**
|
||||
* Search for pages by query string
|
||||
*
|
||||
* @return \Kirby\Cms\Pages
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function itemsForQuery()
|
||||
{
|
||||
// cache
|
||||
if ($this->itemsForQuery !== null) {
|
||||
return $this->itemsForQuery;
|
||||
}
|
||||
|
||||
$model = $this->options['model'];
|
||||
$items = $model->query($this->options['query']);
|
||||
$model = $this->options['model'];
|
||||
$items = $model->query($this->options['query']);
|
||||
|
||||
// help mitigate some typical query usage issues
|
||||
// by converting site and page objects to proper
|
||||
// pages by returning their children
|
||||
if (is_a($items, 'Kirby\Cms\Site') === true) {
|
||||
$items = $items->children();
|
||||
} elseif (is_a($items, 'Kirby\Cms\Page') === true) {
|
||||
$items = $items->children();
|
||||
} elseif (is_a($items, 'Kirby\Cms\Pages') === false) {
|
||||
throw new InvalidArgumentException('Your query must return a set of pages');
|
||||
}
|
||||
// help mitigate some typical query usage issues
|
||||
// by converting site and page objects to proper
|
||||
// pages by returning their children
|
||||
if (is_a($items, 'Kirby\Cms\Site') === true) {
|
||||
$items = $items->children();
|
||||
} elseif (is_a($items, 'Kirby\Cms\Page') === true) {
|
||||
$items = $items->children();
|
||||
} elseif (is_a($items, 'Kirby\Cms\Pages') === false) {
|
||||
throw new InvalidArgumentException('Your query must return a set of pages');
|
||||
}
|
||||
|
||||
return $this->itemsForQuery = $items;
|
||||
}
|
||||
return $this->itemsForQuery = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model.
|
||||
* The model will be used to fetch
|
||||
* subpages unless there's a specific
|
||||
* query to find pages instead.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
if ($this->parent !== null) {
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent model.
|
||||
* The model will be used to fetch
|
||||
* subpages unless there's a specific
|
||||
* query to find pages instead.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
if ($this->parent !== null) {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
return $this->parent = $this->kirby->page($this->options['parent']) ?? $this->site;
|
||||
}
|
||||
return $this->parent = $this->kirby->page($this->options['parent']) ?? $this->site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the top-most model (page or site)
|
||||
* that can be accessed when navigating
|
||||
* through pages.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if (empty($this->options['query']) === false) {
|
||||
if ($items = $this->itemsForQuery()) {
|
||||
return $items->parent();
|
||||
}
|
||||
/**
|
||||
* Calculates the top-most model (page or site)
|
||||
* that can be accessed when navigating
|
||||
* through pages.
|
||||
*
|
||||
* @return \Kirby\Cms\Page|\Kirby\Cms\Site
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if (empty($this->options['query']) === false) {
|
||||
if ($items = $this->itemsForQuery()) {
|
||||
return $items->parent();
|
||||
}
|
||||
|
||||
return $this->site;
|
||||
}
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
return $this->site;
|
||||
}
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array
|
||||
* with all information for the picker.
|
||||
* This will be passed directly to the API.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = parent::toArray();
|
||||
$array['model'] = $this->modelToArray($this->model());
|
||||
/**
|
||||
* Returns an associative array
|
||||
* with all information for the picker.
|
||||
* This will be passed directly to the API.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = parent::toArray();
|
||||
$array['model'] = $this->modelToArray($this->model());
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,421 +19,421 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class PageRules
|
||||
{
|
||||
/**
|
||||
* Validates if the sorting number of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param int|null $num
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given number is invalid
|
||||
*/
|
||||
public static function changeNum(Page $page, int $num = null): bool
|
||||
{
|
||||
if ($num !== null && $num < 0) {
|
||||
throw new InvalidArgumentException(['key' => 'page.num.invalid']);
|
||||
}
|
||||
/**
|
||||
* Validates if the sorting number of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param int|null $num
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given number is invalid
|
||||
*/
|
||||
public static function changeNum(Page $page, int $num = null): bool
|
||||
{
|
||||
if ($num !== null && $num < 0) {
|
||||
throw new InvalidArgumentException(['key' => 'page.num.invalid']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the slug for the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $slug
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a page with this slug already exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the slug
|
||||
*/
|
||||
public static function changeSlug(Page $page, string $slug): bool
|
||||
{
|
||||
if ($page->permissions()->changeSlug() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeSlug.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the slug for the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $slug
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If a page with this slug already exists
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the slug
|
||||
*/
|
||||
public static function changeSlug(Page $page, string $slug): bool
|
||||
{
|
||||
if ($page->permissions()->changeSlug() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeSlug.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
self::validateSlugLength($slug);
|
||||
self::validateSlugLength($slug);
|
||||
|
||||
$siblings = $page->parentModel()->children();
|
||||
$drafts = $page->parentModel()->drafts();
|
||||
$siblings = $page->parentModel()->children();
|
||||
$drafts = $page->parentModel()->drafts();
|
||||
|
||||
if ($duplicate = $siblings->find($slug)) {
|
||||
if ($duplicate->is($page) === false) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.duplicate',
|
||||
'data' => [
|
||||
'slug' => $slug
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($duplicate = $siblings->find($slug)) {
|
||||
if ($duplicate->is($page) === false) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.duplicate',
|
||||
'data' => [
|
||||
'slug' => $slug
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($duplicate = $drafts->find($slug)) {
|
||||
if ($duplicate->is($page) === false) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => [
|
||||
'slug' => $slug
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($duplicate = $drafts->find($slug)) {
|
||||
if ($duplicate->is($page) === false) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => [
|
||||
'slug' => $slug
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the status for the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $status
|
||||
* @param int|null $position
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given status is invalid
|
||||
*/
|
||||
public static function changeStatus(Page $page, string $status, int $position = null): bool
|
||||
{
|
||||
if (isset($page->blueprint()->status()[$status]) === false) {
|
||||
throw new InvalidArgumentException(['key' => 'page.status.invalid']);
|
||||
}
|
||||
/**
|
||||
* Validates if the status for the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $status
|
||||
* @param int|null $position
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given status is invalid
|
||||
*/
|
||||
public static function changeStatus(Page $page, string $status, int $position = null): bool
|
||||
{
|
||||
if (isset($page->blueprint()->status()[$status]) === false) {
|
||||
throw new InvalidArgumentException(['key' => 'page.status.invalid']);
|
||||
}
|
||||
|
||||
switch ($status) {
|
||||
case 'draft':
|
||||
return static::changeStatusToDraft($page);
|
||||
case 'listed':
|
||||
return static::changeStatusToListed($page, $position);
|
||||
case 'unlisted':
|
||||
return static::changeStatusToUnlisted($page);
|
||||
default:
|
||||
throw new InvalidArgumentException(['key' => 'page.status.invalid']);
|
||||
}
|
||||
}
|
||||
switch ($status) {
|
||||
case 'draft':
|
||||
return static::changeStatusToDraft($page);
|
||||
case 'listed':
|
||||
return static::changeStatusToListed($page, $position);
|
||||
case 'unlisted':
|
||||
return static::changeStatusToUnlisted($page);
|
||||
default:
|
||||
throw new InvalidArgumentException(['key' => 'page.status.invalid']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a page can be converted to a draft
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the page cannot be converted to a draft
|
||||
*/
|
||||
public static function changeStatusToDraft(Page $page)
|
||||
{
|
||||
if ($page->permissions()->changeStatus() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if a page can be converted to a draft
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the page cannot be converted to a draft
|
||||
*/
|
||||
public static function changeStatusToDraft(Page $page)
|
||||
{
|
||||
if ($page->permissions()->changeStatus() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if ($page->isHomeOrErrorPage() === true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.toDraft.invalid',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
if ($page->isHomeOrErrorPage() === true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.toDraft.invalid',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the status of a page can be changed to listed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param int $position
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given position is invalid
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the status for the page cannot be changed by any user
|
||||
*/
|
||||
public static function changeStatusToListed(Page $page, int $position)
|
||||
{
|
||||
// no need to check for status changing permissions,
|
||||
// instead we need to check for sorting permissions
|
||||
if ($page->isListed() === true) {
|
||||
if ($page->isSortable() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.sort.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the status of a page can be changed to listed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param int $position
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the given position is invalid
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the status for the page cannot be changed by any user
|
||||
*/
|
||||
public static function changeStatusToListed(Page $page, int $position)
|
||||
{
|
||||
// no need to check for status changing permissions,
|
||||
// instead we need to check for sorting permissions
|
||||
if ($page->isListed() === true) {
|
||||
if ($page->isSortable() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.sort.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static::publish($page);
|
||||
static::publish($page);
|
||||
|
||||
if ($position !== null && $position < 0) {
|
||||
throw new InvalidArgumentException(['key' => 'page.num.invalid']);
|
||||
}
|
||||
if ($position !== null && $position < 0) {
|
||||
throw new InvalidArgumentException(['key' => 'page.num.invalid']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the status of a page can be changed to unlisted
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status
|
||||
*/
|
||||
public static function changeStatusToUnlisted(Page $page)
|
||||
{
|
||||
static::publish($page);
|
||||
/**
|
||||
* Validates if the status of a page can be changed to unlisted
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status
|
||||
*/
|
||||
public static function changeStatusToUnlisted(Page $page)
|
||||
{
|
||||
static::publish($page);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the template of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $template
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException If the template of the page cannot be changed at all
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the template
|
||||
*/
|
||||
public static function changeTemplate(Page $page, string $template): bool
|
||||
{
|
||||
if ($page->permissions()->changeTemplate() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeTemplate.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the template of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $template
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException If the template of the page cannot be changed at all
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the template
|
||||
*/
|
||||
public static function changeTemplate(Page $page, string $template): bool
|
||||
{
|
||||
if ($page->permissions()->changeTemplate() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeTemplate.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if (count($page->blueprints()) <= 1) {
|
||||
throw new LogicException([
|
||||
'key' => 'page.changeTemplate.invalid',
|
||||
'data' => ['slug' => $page->slug()]
|
||||
]);
|
||||
}
|
||||
if (count($page->blueprints()) <= 1) {
|
||||
throw new LogicException([
|
||||
'key' => 'page.changeTemplate.invalid',
|
||||
'data' => ['slug' => $page->slug()]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the title of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $title
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the new title is empty
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title
|
||||
*/
|
||||
public static function changeTitle(Page $page, string $title): bool
|
||||
{
|
||||
if ($page->permissions()->changeTitle() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeTitle.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the title of the page can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $title
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the new title is empty
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title
|
||||
*/
|
||||
public static function changeTitle(Page $page, string $title): bool
|
||||
{
|
||||
if ($page->permissions()->changeTitle() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeTitle.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if (Str::length($title) === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.changeTitle.empty',
|
||||
]);
|
||||
}
|
||||
if (Str::length($title) === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.changeTitle.empty',
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the page can be created
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If the same page or a draft already exists
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the slug is invalid
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to create this page
|
||||
*/
|
||||
public static function create(Page $page): bool
|
||||
{
|
||||
if ($page->permissions()->create() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.create.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the page can be created
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\DuplicateException If the same page or a draft already exists
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the slug is invalid
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to create this page
|
||||
*/
|
||||
public static function create(Page $page): bool
|
||||
{
|
||||
if ($page->permissions()->create() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.create.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
self::validateSlugLength($page->slug());
|
||||
self::validateSlugLength($page->slug());
|
||||
|
||||
if ($page->exists() === true) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
if ($page->exists() === true) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
$siblings = $page->parentModel()->children();
|
||||
$drafts = $page->parentModel()->drafts();
|
||||
$slug = $page->slug();
|
||||
$siblings = $page->parentModel()->children();
|
||||
$drafts = $page->parentModel()->drafts();
|
||||
$slug = $page->slug();
|
||||
|
||||
if ($siblings->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
]);
|
||||
}
|
||||
if ($siblings->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
]);
|
||||
}
|
||||
|
||||
if ($drafts->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
]);
|
||||
}
|
||||
if ($drafts->find($slug)) {
|
||||
throw new DuplicateException([
|
||||
'key' => 'page.draft.duplicate',
|
||||
'data' => ['slug' => $slug]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the page can be deleted
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param bool $force
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException If the page has children and should not be force-deleted
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the page
|
||||
*/
|
||||
public static function delete(Page $page, bool $force = false): bool
|
||||
{
|
||||
if ($page->permissions()->delete() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.delete.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the page can be deleted
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param bool $force
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\LogicException If the page has children and should not be force-deleted
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the page
|
||||
*/
|
||||
public static function delete(Page $page, bool $force = false): bool
|
||||
{
|
||||
if ($page->permissions()->delete() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.delete.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if (($page->hasChildren() === true || $page->hasDrafts() === true) && $force === false) {
|
||||
throw new LogicException(['key' => 'page.delete.hasChildren']);
|
||||
}
|
||||
if (($page->hasChildren() === true || $page->hasDrafts() === true) && $force === false) {
|
||||
throw new LogicException(['key' => 'page.delete.hasChildren']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the page can be duplicated
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $slug
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to duplicate the page
|
||||
*/
|
||||
public static function duplicate(Page $page, string $slug, array $options = []): bool
|
||||
{
|
||||
if ($page->permissions()->duplicate() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.duplicate.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the page can be duplicated
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param string $slug
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to duplicate the page
|
||||
*/
|
||||
public static function duplicate(Page $page, string $slug, array $options = []): bool
|
||||
{
|
||||
if ($page->permissions()->duplicate() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.duplicate.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
self::validateSlugLength($slug);
|
||||
self::validateSlugLength($slug);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page can be published
|
||||
* (status change from draft to listed or unlisted)
|
||||
*
|
||||
* @param Page $page
|
||||
* @return bool
|
||||
*/
|
||||
public static function publish(Page $page): bool
|
||||
{
|
||||
if ($page->permissions()->changeStatus() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Check if the page can be published
|
||||
* (status change from draft to listed or unlisted)
|
||||
*
|
||||
* @param Page $page
|
||||
* @return bool
|
||||
*/
|
||||
public static function publish(Page $page): bool
|
||||
{
|
||||
if ($page->permissions()->changeStatus() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
if ($page->isDraft() === true && empty($page->errors()) === false) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.incomplete',
|
||||
'details' => $page->errors()
|
||||
]);
|
||||
}
|
||||
if ($page->isDraft() === true && empty($page->errors()) === false) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.changeStatus.incomplete',
|
||||
'details' => $page->errors()
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the page can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the page
|
||||
*/
|
||||
public static function update(Page $page, array $content = []): bool
|
||||
{
|
||||
if ($page->permissions()->update() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.update.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Validates if the page can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Page $page
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the page
|
||||
*/
|
||||
public static function update(Page $page, array $content = []): bool
|
||||
{
|
||||
if ($page->permissions()->update() !== true) {
|
||||
throw new PermissionException([
|
||||
'key' => 'page.update.permission',
|
||||
'data' => [
|
||||
'slug' => $page->slug()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the slug is not empty and doesn't exceed the maximum length
|
||||
* to make sure that the directory name will be accepted by the filesystem
|
||||
*
|
||||
* @param string $slug New slug to check
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the slug is empty or too long
|
||||
*/
|
||||
protected static function validateSlugLength(string $slug): void
|
||||
{
|
||||
$slugLength = Str::length($slug);
|
||||
/**
|
||||
* Ensures that the slug is not empty and doesn't exceed the maximum length
|
||||
* to make sure that the directory name will be accepted by the filesystem
|
||||
*
|
||||
* @param string $slug New slug to check
|
||||
* @return void
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the slug is empty or too long
|
||||
*/
|
||||
protected static function validateSlugLength(string $slug): void
|
||||
{
|
||||
$slugLength = Str::length($slug);
|
||||
|
||||
if ($slugLength === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.slug.invalid',
|
||||
]);
|
||||
}
|
||||
if ($slugLength === 0) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.slug.invalid',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($slugsMaxlength = App::instance()->option('slugs.maxlength', 255)) {
|
||||
$maxlength = (int)$slugsMaxlength;
|
||||
if ($slugsMaxlength = App::instance()->option('slugs.maxlength', 255)) {
|
||||
$maxlength = (int)$slugsMaxlength;
|
||||
|
||||
if ($slugLength > $maxlength) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.slug.maxlength',
|
||||
'data' => [
|
||||
'length' => $maxlength
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($slugLength > $maxlength) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'page.slug.maxlength',
|
||||
'data' => [
|
||||
'length' => $maxlength
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,128 +13,128 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
trait PageSiblings
|
||||
{
|
||||
/**
|
||||
* Checks if there's a next listed
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextListed($collection = null): bool
|
||||
{
|
||||
return $this->nextListed($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a next listed
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextListed($collection = null): bool
|
||||
{
|
||||
return $this->nextListed($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a next unlisted
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextUnlisted($collection = null): bool
|
||||
{
|
||||
return $this->nextUnlisted($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a next unlisted
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextUnlisted($collection = null): bool
|
||||
{
|
||||
return $this->nextUnlisted($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a previous listed
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevListed($collection = null): bool
|
||||
{
|
||||
return $this->prevListed($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a previous listed
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevListed($collection = null): bool
|
||||
{
|
||||
return $this->prevListed($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a previous unlisted
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevUnlisted($collection = null): bool
|
||||
{
|
||||
return $this->prevUnlisted($collection) !== null;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a previous unlisted
|
||||
* page in the siblings collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevUnlisted($collection = null): bool
|
||||
{
|
||||
return $this->prevUnlisted($collection) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next listed page if it exists
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function nextListed($collection = null)
|
||||
{
|
||||
return $this->nextAll($collection)->listed()->first();
|
||||
}
|
||||
/**
|
||||
* Returns the next listed page if it exists
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function nextListed($collection = null)
|
||||
{
|
||||
return $this->nextAll($collection)->listed()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next unlisted page if it exists
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function nextUnlisted($collection = null)
|
||||
{
|
||||
return $this->nextAll($collection)->unlisted()->first();
|
||||
}
|
||||
/**
|
||||
* Returns the next unlisted page if it exists
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function nextUnlisted($collection = null)
|
||||
{
|
||||
return $this->nextAll($collection)->unlisted()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous listed page
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function prevListed($collection = null)
|
||||
{
|
||||
return $this->prevAll($collection)->listed()->last();
|
||||
}
|
||||
/**
|
||||
* Returns the previous listed page
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function prevListed($collection = null)
|
||||
{
|
||||
return $this->prevAll($collection)->listed()->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous unlisted page
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function prevUnlisted($collection = null)
|
||||
{
|
||||
return $this->prevAll($collection)->unlisted()->last();
|
||||
}
|
||||
/**
|
||||
* Returns the previous unlisted page
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $collection
|
||||
*
|
||||
* @return \Kirby\Cms\Page|null
|
||||
*/
|
||||
public function prevUnlisted($collection = null)
|
||||
{
|
||||
return $this->prevAll($collection)->unlisted()->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private siblings collector
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
if ($this->isDraft() === true) {
|
||||
return $this->parentModel()->drafts();
|
||||
} else {
|
||||
return $this->parentModel()->children();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Private siblings collector
|
||||
*
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
if ($this->isDraft() === true) {
|
||||
return $this->parentModel()->drafts();
|
||||
} else {
|
||||
return $this->parentModel()->children();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns siblings with the same template
|
||||
*
|
||||
* @param bool $self
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function templateSiblings(bool $self = true)
|
||||
{
|
||||
return $this->siblings($self)->filter('intendedTemplate', $this->intendedTemplate()->name());
|
||||
}
|
||||
/**
|
||||
* Returns siblings with the same template
|
||||
*
|
||||
* @param bool $self
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public function templateSiblings(bool $self = true)
|
||||
{
|
||||
return $this->siblings($self)->filter('intendedTemplate', $this->intendedTemplate()->name());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,158 +22,158 @@ use Kirby\Toolkit\Pagination as BasePagination;
|
||||
*/
|
||||
class Pagination extends BasePagination
|
||||
{
|
||||
/**
|
||||
* Pagination method (param, query, none)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
/**
|
||||
* Pagination method (param, query, none)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* The base URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
/**
|
||||
* The base URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Variable name for query strings
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $variable;
|
||||
/**
|
||||
* Variable name for query strings
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $variable;
|
||||
|
||||
/**
|
||||
* Creates the pagination object. As a new
|
||||
* property you can now pass the base Url.
|
||||
* That Url must be the Url of the first
|
||||
* page of the collection without additional
|
||||
* pagination information/query parameters in it.
|
||||
*
|
||||
* ```php
|
||||
* $pagination = new Pagination([
|
||||
* 'page' => 1,
|
||||
* 'limit' => 10,
|
||||
* 'total' => 120,
|
||||
* 'method' => 'query',
|
||||
* 'variable' => 'p',
|
||||
* 'url' => new Uri('https://getkirby.com/blog')
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
$config = $kirby->option('pagination', []);
|
||||
$request = $kirby->request();
|
||||
/**
|
||||
* Creates the pagination object. As a new
|
||||
* property you can now pass the base Url.
|
||||
* That Url must be the Url of the first
|
||||
* page of the collection without additional
|
||||
* pagination information/query parameters in it.
|
||||
*
|
||||
* ```php
|
||||
* $pagination = new Pagination([
|
||||
* 'page' => 1,
|
||||
* 'limit' => 10,
|
||||
* 'total' => 120,
|
||||
* 'method' => 'query',
|
||||
* 'variable' => 'p',
|
||||
* 'url' => new Uri('https://getkirby.com/blog')
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
$config = $kirby->option('pagination', []);
|
||||
$request = $kirby->request();
|
||||
|
||||
$params['limit'] ??= $config['limit'] ?? 20;
|
||||
$params['method'] ??= $config['method'] ?? 'param';
|
||||
$params['variable'] ??= $config['variable'] ?? 'page';
|
||||
$params['limit'] ??= $config['limit'] ?? 20;
|
||||
$params['method'] ??= $config['method'] ?? 'param';
|
||||
$params['variable'] ??= $config['variable'] ?? 'page';
|
||||
|
||||
if (empty($params['url']) === true) {
|
||||
$params['url'] = new Uri($kirby->url('current'), [
|
||||
'params' => $request->params(),
|
||||
'query' => $request->query()->toArray(),
|
||||
]);
|
||||
}
|
||||
if (empty($params['url']) === true) {
|
||||
$params['url'] = new Uri($kirby->url('current'), [
|
||||
'params' => $request->params(),
|
||||
'query' => $request->query()->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($params['method'] === 'query') {
|
||||
$params['page'] ??= $params['url']->query()->get($params['variable']);
|
||||
} elseif ($params['method'] === 'param') {
|
||||
$params['page'] ??= $params['url']->params()->get($params['variable']);
|
||||
}
|
||||
if ($params['method'] === 'query') {
|
||||
$params['page'] ??= $params['url']->query()->get($params['variable']);
|
||||
} elseif ($params['method'] === 'param') {
|
||||
$params['page'] ??= $params['url']->params()->get($params['variable']);
|
||||
}
|
||||
|
||||
parent::__construct($params);
|
||||
parent::__construct($params);
|
||||
|
||||
$this->method = $params['method'];
|
||||
$this->url = $params['url'];
|
||||
$this->variable = $params['variable'];
|
||||
}
|
||||
$this->method = $params['method'];
|
||||
$this->url = $params['url'];
|
||||
$this->variable = $params['variable'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Url for the first page
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function firstPageUrl(): ?string
|
||||
{
|
||||
return $this->pageUrl(1);
|
||||
}
|
||||
/**
|
||||
* Returns the Url for the first page
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function firstPageUrl(): ?string
|
||||
{
|
||||
return $this->pageUrl(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Url for the last page
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function lastPageUrl(): ?string
|
||||
{
|
||||
return $this->pageUrl($this->lastPage());
|
||||
}
|
||||
/**
|
||||
* Returns the Url for the last page
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function lastPageUrl(): ?string
|
||||
{
|
||||
return $this->pageUrl($this->lastPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Url for the next page.
|
||||
* Returns null if there's no next page.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function nextPageUrl(): ?string
|
||||
{
|
||||
if ($page = $this->nextPage()) {
|
||||
return $this->pageUrl($page);
|
||||
}
|
||||
/**
|
||||
* Returns the Url for the next page.
|
||||
* Returns null if there's no next page.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function nextPageUrl(): ?string
|
||||
{
|
||||
if ($page = $this->nextPage()) {
|
||||
return $this->pageUrl($page);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the current page.
|
||||
* If the `$page` variable is set, the URL
|
||||
* for that page will be returned.
|
||||
*
|
||||
* @param int|null $page
|
||||
* @return string|null
|
||||
*/
|
||||
public function pageUrl(int $page = null): ?string
|
||||
{
|
||||
if ($page === null) {
|
||||
return $this->pageUrl($this->page());
|
||||
}
|
||||
/**
|
||||
* Returns the URL of the current page.
|
||||
* If the `$page` variable is set, the URL
|
||||
* for that page will be returned.
|
||||
*
|
||||
* @param int|null $page
|
||||
* @return string|null
|
||||
*/
|
||||
public function pageUrl(int $page = null): ?string
|
||||
{
|
||||
if ($page === null) {
|
||||
return $this->pageUrl($this->page());
|
||||
}
|
||||
|
||||
$url = clone $this->url;
|
||||
$variable = $this->variable;
|
||||
$url = clone $this->url;
|
||||
$variable = $this->variable;
|
||||
|
||||
if ($this->hasPage($page) === false) {
|
||||
return null;
|
||||
}
|
||||
if ($this->hasPage($page) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pageValue = $page === 1 ? null : $page;
|
||||
$pageValue = $page === 1 ? null : $page;
|
||||
|
||||
if ($this->method === 'query') {
|
||||
$url->query->$variable = $pageValue;
|
||||
} elseif ($this->method === 'param') {
|
||||
$url->params->$variable = $pageValue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if ($this->method === 'query') {
|
||||
$url->query->$variable = $pageValue;
|
||||
} elseif ($this->method === 'param') {
|
||||
$url->params->$variable = $pageValue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $url->toString();
|
||||
}
|
||||
return $url->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Url for the previous page.
|
||||
* Returns null if there's no previous page.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function prevPageUrl(): ?string
|
||||
{
|
||||
if ($page = $this->prevPage()) {
|
||||
return $this->pageUrl($page);
|
||||
}
|
||||
/**
|
||||
* Returns the Url for the previous page.
|
||||
* Returns null if there's no previous page.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function prevPageUrl(): ?string
|
||||
{
|
||||
if ($page = $this->prevPage()) {
|
||||
return $this->pageUrl($page);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,222 +17,222 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class Permissions
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $extendedActions = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $extendedActions = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $actions = [
|
||||
'access' => [
|
||||
'account' => true,
|
||||
'languages' => true,
|
||||
'panel' => true,
|
||||
'site' => true,
|
||||
'system' => true,
|
||||
'users' => true,
|
||||
],
|
||||
'files' => [
|
||||
'changeName' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'read' => true,
|
||||
'replace' => true,
|
||||
'update' => true
|
||||
],
|
||||
'languages' => [
|
||||
'create' => true,
|
||||
'delete' => true
|
||||
],
|
||||
'pages' => [
|
||||
'changeSlug' => true,
|
||||
'changeStatus' => true,
|
||||
'changeTemplate' => true,
|
||||
'changeTitle' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'duplicate' => true,
|
||||
'preview' => true,
|
||||
'read' => true,
|
||||
'sort' => true,
|
||||
'update' => true
|
||||
],
|
||||
'site' => [
|
||||
'changeTitle' => true,
|
||||
'update' => true
|
||||
],
|
||||
'users' => [
|
||||
'changeEmail' => true,
|
||||
'changeLanguage' => true,
|
||||
'changeName' => true,
|
||||
'changePassword' => true,
|
||||
'changeRole' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'update' => true
|
||||
],
|
||||
'user' => [
|
||||
'changeEmail' => true,
|
||||
'changeLanguage' => true,
|
||||
'changeName' => true,
|
||||
'changePassword' => true,
|
||||
'changeRole' => true,
|
||||
'delete' => true,
|
||||
'update' => true
|
||||
]
|
||||
];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $actions = [
|
||||
'access' => [
|
||||
'account' => true,
|
||||
'languages' => true,
|
||||
'panel' => true,
|
||||
'site' => true,
|
||||
'system' => true,
|
||||
'users' => true,
|
||||
],
|
||||
'files' => [
|
||||
'changeName' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'read' => true,
|
||||
'replace' => true,
|
||||
'update' => true
|
||||
],
|
||||
'languages' => [
|
||||
'create' => true,
|
||||
'delete' => true
|
||||
],
|
||||
'pages' => [
|
||||
'changeSlug' => true,
|
||||
'changeStatus' => true,
|
||||
'changeTemplate' => true,
|
||||
'changeTitle' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'duplicate' => true,
|
||||
'preview' => true,
|
||||
'read' => true,
|
||||
'sort' => true,
|
||||
'update' => true
|
||||
],
|
||||
'site' => [
|
||||
'changeTitle' => true,
|
||||
'update' => true
|
||||
],
|
||||
'users' => [
|
||||
'changeEmail' => true,
|
||||
'changeLanguage' => true,
|
||||
'changeName' => true,
|
||||
'changePassword' => true,
|
||||
'changeRole' => true,
|
||||
'create' => true,
|
||||
'delete' => true,
|
||||
'update' => true
|
||||
],
|
||||
'user' => [
|
||||
'changeEmail' => true,
|
||||
'changeLanguage' => true,
|
||||
'changeName' => true,
|
||||
'changePassword' => true,
|
||||
'changeRole' => true,
|
||||
'delete' => true,
|
||||
'update' => true
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Permissions constructor
|
||||
*
|
||||
* @param array $settings
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
// dynamically register the extended actions
|
||||
foreach (static::$extendedActions as $key => $actions) {
|
||||
if (isset($this->actions[$key]) === true) {
|
||||
throw new InvalidArgumentException('The action ' . $key . ' is already a core action');
|
||||
}
|
||||
/**
|
||||
* Permissions constructor
|
||||
*
|
||||
* @param array $settings
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
// dynamically register the extended actions
|
||||
foreach (static::$extendedActions as $key => $actions) {
|
||||
if (isset($this->actions[$key]) === true) {
|
||||
throw new InvalidArgumentException('The action ' . $key . ' is already a core action');
|
||||
}
|
||||
|
||||
$this->actions[$key] = $actions;
|
||||
}
|
||||
$this->actions[$key] = $actions;
|
||||
}
|
||||
|
||||
if (is_array($settings) === true) {
|
||||
return $this->setCategories($settings);
|
||||
}
|
||||
if (is_array($settings) === true) {
|
||||
return $this->setCategories($settings);
|
||||
}
|
||||
|
||||
if (is_bool($settings) === true) {
|
||||
return $this->setAll($settings);
|
||||
}
|
||||
}
|
||||
if (is_bool($settings) === true) {
|
||||
return $this->setAll($settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $category
|
||||
* @param string|null $action
|
||||
* @return bool
|
||||
*/
|
||||
public function for(string $category = null, string $action = null): bool
|
||||
{
|
||||
if ($action === null) {
|
||||
if ($this->hasCategory($category) === false) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @param string|null $category
|
||||
* @param string|null $action
|
||||
* @return bool
|
||||
*/
|
||||
public function for(string $category = null, string $action = null): bool
|
||||
{
|
||||
if ($action === null) {
|
||||
if ($this->hasCategory($category) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->actions[$category];
|
||||
}
|
||||
return $this->actions[$category];
|
||||
}
|
||||
|
||||
if ($this->hasAction($category, $action) === false) {
|
||||
return false;
|
||||
}
|
||||
if ($this->hasAction($category, $action) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->actions[$category][$action];
|
||||
}
|
||||
return $this->actions[$category][$action];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasAction(string $category, string $action): bool
|
||||
{
|
||||
return $this->hasCategory($category) === true && array_key_exists($action, $this->actions[$category]) === true;
|
||||
}
|
||||
/**
|
||||
* @param string $category
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasAction(string $category, string $action): bool
|
||||
{
|
||||
return $this->hasCategory($category) === true && array_key_exists($action, $this->actions[$category]) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasCategory(string $category): bool
|
||||
{
|
||||
return array_key_exists($category, $this->actions) === true;
|
||||
}
|
||||
/**
|
||||
* @param string $category
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasCategory(string $category): bool
|
||||
{
|
||||
return array_key_exists($category, $this->actions) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
* @param string $action
|
||||
* @param $setting
|
||||
* @return $this
|
||||
*/
|
||||
protected function setAction(string $category, string $action, $setting)
|
||||
{
|
||||
// deprecated fallback for the settings/system view
|
||||
// TODO: remove in 3.8.0
|
||||
if ($category === 'access' && $action === 'settings') {
|
||||
$action = 'system';
|
||||
}
|
||||
/**
|
||||
* @param string $category
|
||||
* @param string $action
|
||||
* @param $setting
|
||||
* @return $this
|
||||
*/
|
||||
protected function setAction(string $category, string $action, $setting)
|
||||
{
|
||||
// deprecated fallback for the settings/system view
|
||||
// TODO: remove in 3.8.0
|
||||
if ($category === 'access' && $action === 'settings') {
|
||||
$action = 'system';
|
||||
}
|
||||
|
||||
// wildcard to overwrite the entire category
|
||||
if ($action === '*') {
|
||||
return $this->setCategory($category, $setting);
|
||||
}
|
||||
// wildcard to overwrite the entire category
|
||||
if ($action === '*') {
|
||||
return $this->setCategory($category, $setting);
|
||||
}
|
||||
|
||||
$this->actions[$category][$action] = $setting;
|
||||
$this->actions[$category][$action] = $setting;
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $setting
|
||||
* @return $this
|
||||
*/
|
||||
protected function setAll(bool $setting)
|
||||
{
|
||||
foreach ($this->actions as $categoryName => $actions) {
|
||||
$this->setCategory($categoryName, $setting);
|
||||
}
|
||||
/**
|
||||
* @param bool $setting
|
||||
* @return $this
|
||||
*/
|
||||
protected function setAll(bool $setting)
|
||||
{
|
||||
foreach ($this->actions as $categoryName => $actions) {
|
||||
$this->setCategory($categoryName, $setting);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
* @return $this
|
||||
*/
|
||||
protected function setCategories(array $settings)
|
||||
{
|
||||
foreach ($settings as $categoryName => $categoryActions) {
|
||||
if (is_bool($categoryActions) === true) {
|
||||
$this->setCategory($categoryName, $categoryActions);
|
||||
}
|
||||
/**
|
||||
* @param array $settings
|
||||
* @return $this
|
||||
*/
|
||||
protected function setCategories(array $settings)
|
||||
{
|
||||
foreach ($settings as $categoryName => $categoryActions) {
|
||||
if (is_bool($categoryActions) === true) {
|
||||
$this->setCategory($categoryName, $categoryActions);
|
||||
}
|
||||
|
||||
if (is_array($categoryActions) === true) {
|
||||
foreach ($categoryActions as $actionName => $actionSetting) {
|
||||
$this->setAction($categoryName, $actionName, $actionSetting);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_array($categoryActions) === true) {
|
||||
foreach ($categoryActions as $actionName => $actionSetting) {
|
||||
$this->setAction($categoryName, $actionName, $actionSetting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
* @param bool $setting
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function setCategory(string $category, bool $setting)
|
||||
{
|
||||
if ($this->hasCategory($category) === false) {
|
||||
throw new InvalidArgumentException('Invalid permissions category');
|
||||
}
|
||||
/**
|
||||
* @param string $category
|
||||
* @param bool $setting
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function setCategory(string $category, bool $setting)
|
||||
{
|
||||
if ($this->hasCategory($category) === false) {
|
||||
throw new InvalidArgumentException('Invalid permissions category');
|
||||
}
|
||||
|
||||
foreach ($this->actions[$category] as $actionName => $actionSetting) {
|
||||
$this->actions[$category][$actionName] = $setting;
|
||||
}
|
||||
foreach ($this->actions[$category] as $actionName => $actionSetting) {
|
||||
$this->actions[$category][$actionName] = $setting;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,166 +14,166 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
abstract class Picker
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
/**
|
||||
* @var \Kirby\Cms\App
|
||||
*/
|
||||
protected $kirby;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Site
|
||||
*/
|
||||
protected $site;
|
||||
/**
|
||||
* @var \Kirby\Cms\Site
|
||||
*/
|
||||
protected $site;
|
||||
|
||||
/**
|
||||
* Creates a new Picker instance
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$this->options = array_merge($this->defaults(), $params);
|
||||
$this->kirby = $this->options['model']->kirby();
|
||||
$this->site = $this->kirby->site();
|
||||
}
|
||||
/**
|
||||
* Creates a new Picker instance
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function __construct(array $params = [])
|
||||
{
|
||||
$this->options = array_merge($this->defaults(), $params);
|
||||
$this->kirby = $this->options['model']->kirby();
|
||||
$this->site = $this->kirby->site();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of default values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
// default params
|
||||
return [
|
||||
// image settings (ratio, cover, etc.)
|
||||
'image' => [],
|
||||
// query template for the info field
|
||||
'info' => false,
|
||||
// listing style: list, cards, cardlets
|
||||
'layout' =>'list',
|
||||
// number of users displayed per pagination page
|
||||
'limit' => 20,
|
||||
// optional mapping function for the result array
|
||||
'map' => null,
|
||||
// the reference model
|
||||
'model' => App::instance()->site(),
|
||||
// current page when paginating
|
||||
'page' => 1,
|
||||
// a query string to fetch specific items
|
||||
'query' => null,
|
||||
// search query
|
||||
'search' => null,
|
||||
// query template for the text field
|
||||
'text' => null
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Return the array of default values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
// default params
|
||||
return [
|
||||
// image settings (ratio, cover, etc.)
|
||||
'image' => [],
|
||||
// query template for the info field
|
||||
'info' => false,
|
||||
// listing style: list, cards, cardlets
|
||||
'layout' =>'list',
|
||||
// number of users displayed per pagination page
|
||||
'limit' => 20,
|
||||
// optional mapping function for the result array
|
||||
'map' => null,
|
||||
// the reference model
|
||||
'model' => App::instance()->site(),
|
||||
// current page when paginating
|
||||
'page' => 1,
|
||||
// a query string to fetch specific items
|
||||
'query' => null,
|
||||
// search query
|
||||
'search' => null,
|
||||
// query template for the text field
|
||||
'text' => null
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all items for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
abstract public function items();
|
||||
/**
|
||||
* Fetches all items for the picker
|
||||
*
|
||||
* @return \Kirby\Cms\Collection|null
|
||||
*/
|
||||
abstract public function items();
|
||||
|
||||
/**
|
||||
* Converts all given items to an associative
|
||||
* array that is already optimized for the
|
||||
* panel picker component.
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $items
|
||||
* @return array
|
||||
*/
|
||||
public function itemsToArray($items = null): array
|
||||
{
|
||||
if ($items === null) {
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Converts all given items to an associative
|
||||
* array that is already optimized for the
|
||||
* panel picker component.
|
||||
*
|
||||
* @param \Kirby\Cms\Collection|null $items
|
||||
* @return array
|
||||
*/
|
||||
public function itemsToArray($items = null): array
|
||||
{
|
||||
if ($items === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$result = [];
|
||||
|
||||
foreach ($items as $index => $item) {
|
||||
if (empty($this->options['map']) === false) {
|
||||
$result[] = $this->options['map']($item);
|
||||
} else {
|
||||
$result[] = $item->panel()->pickerData([
|
||||
'image' => $this->options['image'],
|
||||
'info' => $this->options['info'],
|
||||
'layout' => $this->options['layout'],
|
||||
'model' => $this->options['model'],
|
||||
'text' => $this->options['text'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
foreach ($items as $index => $item) {
|
||||
if (empty($this->options['map']) === false) {
|
||||
$result[] = $this->options['map']($item);
|
||||
} else {
|
||||
$result[] = $item->panel()->pickerData([
|
||||
'image' => $this->options['image'],
|
||||
'info' => $this->options['info'],
|
||||
'layout' => $this->options['layout'],
|
||||
'model' => $this->options['model'],
|
||||
'text' => $this->options['text'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply pagination to the collection
|
||||
* of items according to the options.
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $items
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function paginate(Collection $items)
|
||||
{
|
||||
return $items->paginate([
|
||||
'limit' => $this->options['limit'],
|
||||
'page' => $this->options['page']
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Apply pagination to the collection
|
||||
* of items according to the options.
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $items
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function paginate(Collection $items)
|
||||
{
|
||||
return $items->paginate([
|
||||
'limit' => $this->options['limit'],
|
||||
'page' => $this->options['page']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the most relevant pagination
|
||||
* info as array
|
||||
*
|
||||
* @param \Kirby\Cms\Pagination $pagination
|
||||
* @return array
|
||||
*/
|
||||
public function paginationToArray(Pagination $pagination): array
|
||||
{
|
||||
return [
|
||||
'limit' => $pagination->limit(),
|
||||
'page' => $pagination->page(),
|
||||
'total' => $pagination->total()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Return the most relevant pagination
|
||||
* info as array
|
||||
*
|
||||
* @param \Kirby\Cms\Pagination $pagination
|
||||
* @return array
|
||||
*/
|
||||
public function paginationToArray(Pagination $pagination): array
|
||||
{
|
||||
return [
|
||||
'limit' => $pagination->limit(),
|
||||
'page' => $pagination->page(),
|
||||
'total' => $pagination->total()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through the collection of items
|
||||
* if not deactivate in the options
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $items
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function search(Collection $items)
|
||||
{
|
||||
if (empty($this->options['search']) === false) {
|
||||
return $items->search($this->options['search']);
|
||||
}
|
||||
/**
|
||||
* Search through the collection of items
|
||||
* if not deactivate in the options
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $items
|
||||
* @return \Kirby\Cms\Collection
|
||||
*/
|
||||
public function search(Collection $items)
|
||||
{
|
||||
if (empty($this->options['search']) === false) {
|
||||
return $items->search($this->options['search']);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array
|
||||
* with all information for the picker.
|
||||
* This will be passed directly to the API.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$items = $this->items();
|
||||
/**
|
||||
* Returns an associative array
|
||||
* with all information for the picker.
|
||||
* This will be passed directly to the API.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$items = $this->items();
|
||||
|
||||
return [
|
||||
'data' => $this->itemsToArray($items),
|
||||
'pagination' => $this->paginationToArray($items->pagination()),
|
||||
];
|
||||
}
|
||||
return [
|
||||
'data' => $this->itemsToArray($items),
|
||||
'pagination' => $this->paginationToArray($items->pagination()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,203 +20,203 @@ use Kirby\Toolkit\V;
|
||||
*/
|
||||
class Plugin extends Model
|
||||
{
|
||||
protected $extends;
|
||||
protected $info;
|
||||
protected $name;
|
||||
protected $root;
|
||||
protected $extends;
|
||||
protected $info;
|
||||
protected $name;
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param array|null $arguments
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __call(string $key, array $arguments = null)
|
||||
{
|
||||
return $this->info()[$key] ?? null;
|
||||
}
|
||||
/**
|
||||
* @param string $key
|
||||
* @param array|null $arguments
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __call(string $key, array $arguments = null)
|
||||
{
|
||||
return $this->info()[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $extends
|
||||
*/
|
||||
public function __construct(string $name, array $extends = [])
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->extends = $extends;
|
||||
$this->root = $extends['root'] ?? dirname(debug_backtrace()[0]['file']);
|
||||
$this->info = empty($extends['info']) === false && is_array($extends['info']) ? $extends['info'] : null;
|
||||
/**
|
||||
* Plugin constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $extends
|
||||
*/
|
||||
public function __construct(string $name, array $extends = [])
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->extends = $extends;
|
||||
$this->root = $extends['root'] ?? dirname(debug_backtrace()[0]['file']);
|
||||
$this->info = empty($extends['info']) === false && is_array($extends['info']) ? $extends['info'] : null;
|
||||
|
||||
unset($this->extends['root'], $this->extends['info']);
|
||||
}
|
||||
unset($this->extends['root'], $this->extends['info']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array with author information
|
||||
* from the composer file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authors(): array
|
||||
{
|
||||
return $this->info()['authors'] ?? [];
|
||||
}
|
||||
/**
|
||||
* Returns the array with author information
|
||||
* from the composer file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authors(): array
|
||||
{
|
||||
return $this->info()['authors'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comma-separated list with all author names
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function authorsNames(): string
|
||||
{
|
||||
$names = [];
|
||||
/**
|
||||
* Returns a comma-separated list with all author names
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function authorsNames(): string
|
||||
{
|
||||
$names = [];
|
||||
|
||||
foreach ($this->authors() as $author) {
|
||||
$names[] = $author['name'] ?? null;
|
||||
}
|
||||
foreach ($this->authors() as $author) {
|
||||
$names[] = $author['name'] ?? null;
|
||||
}
|
||||
|
||||
return implode(', ', array_filter($names));
|
||||
}
|
||||
return implode(', ', array_filter($names));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function extends(): array
|
||||
{
|
||||
return $this->extends;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function extends(): array
|
||||
{
|
||||
return $this->extends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique id for the plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
/**
|
||||
* Returns the unique id for the plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
if (is_array($this->info) === true) {
|
||||
return $this->info;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
if (is_array($this->info) === true) {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
try {
|
||||
$info = Data::read($this->manifest());
|
||||
} catch (Exception $e) {
|
||||
// there is no manifest file or it is invalid
|
||||
$info = [];
|
||||
}
|
||||
try {
|
||||
$info = Data::read($this->manifest());
|
||||
} catch (Exception $e) {
|
||||
// there is no manifest file or it is invalid
|
||||
$info = [];
|
||||
}
|
||||
|
||||
return $this->info = $info;
|
||||
}
|
||||
return $this->info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link to the plugin homepage
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function link(): ?string
|
||||
{
|
||||
$info = $this->info();
|
||||
$homepage = $info['homepage'] ?? null;
|
||||
$docs = $info['support']['docs'] ?? null;
|
||||
$source = $info['support']['source'] ?? null;
|
||||
/**
|
||||
* Returns the link to the plugin homepage
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function link(): ?string
|
||||
{
|
||||
$info = $this->info();
|
||||
$homepage = $info['homepage'] ?? null;
|
||||
$docs = $info['support']['docs'] ?? null;
|
||||
$source = $info['support']['source'] ?? null;
|
||||
|
||||
$link = $homepage ?? $docs ?? $source;
|
||||
$link = $homepage ?? $docs ?? $source;
|
||||
|
||||
return V::url($link) ? $link : null;
|
||||
}
|
||||
return V::url($link) ? $link : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function manifest(): string
|
||||
{
|
||||
return $this->root() . '/composer.json';
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function manifest(): string
|
||||
{
|
||||
return $this->root() . '/composer.json';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function mediaRoot(): string
|
||||
{
|
||||
return App::instance()->root('media') . '/plugins/' . $this->name();
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function mediaRoot(): string
|
||||
{
|
||||
return App::instance()->root('media') . '/plugins/' . $this->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function mediaUrl(): string
|
||||
{
|
||||
return App::instance()->url('media') . '/plugins/' . $this->name();
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function mediaUrl(): string
|
||||
{
|
||||
return App::instance()->url('media') . '/plugins/' . $this->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function option(string $key)
|
||||
{
|
||||
return $this->kirby()->option($this->prefix() . '.' . $key);
|
||||
}
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function option(string $key)
|
||||
{
|
||||
return $this->kirby()->option($this->prefix() . '.' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function prefix(): string
|
||||
{
|
||||
return str_replace('/', '.', $this->name());
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function prefix(): string
|
||||
{
|
||||
return str_replace('/', '.', $this->name());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function root(): string
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function setName(string $name)
|
||||
{
|
||||
if (preg_match('!^[a-z0-9-]+\/[a-z0-9-]+$!i', $name) !== 1) {
|
||||
throw new InvalidArgumentException('The plugin name must follow the format "a-z0-9-/a-z0-9-"');
|
||||
}
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function setName(string $name)
|
||||
{
|
||||
if (preg_match('!^[a-z0-9-]+\/[a-z0-9-]+$!i', $name) !== 1) {
|
||||
throw new InvalidArgumentException('The plugin name must follow the format "a-z0-9-/a-z0-9-"');
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'authors' => $this->authors(),
|
||||
'description' => $this->description(),
|
||||
'name' => $this->name(),
|
||||
'license' => $this->license(),
|
||||
'link' => $this->link(),
|
||||
'root' => $this->root(),
|
||||
'version' => $this->version()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'authors' => $this->authors(),
|
||||
'description' => $this->description(),
|
||||
'name' => $this->name(),
|
||||
'license' => $this->license(),
|
||||
'link' => $this->link(),
|
||||
'root' => $this->root(),
|
||||
'version' => $this->version()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,62 +19,62 @@ use Kirby\Http\Response;
|
||||
*/
|
||||
class PluginAssets
|
||||
{
|
||||
/**
|
||||
* Clean old/deprecated assets on every resolve
|
||||
*
|
||||
* @param string $pluginName
|
||||
* @return void
|
||||
*/
|
||||
public static function clean(string $pluginName): void
|
||||
{
|
||||
if ($plugin = App::instance()->plugin($pluginName)) {
|
||||
$root = $plugin->root() . '/assets';
|
||||
$media = $plugin->mediaRoot();
|
||||
$assets = Dir::index($media, true);
|
||||
/**
|
||||
* Clean old/deprecated assets on every resolve
|
||||
*
|
||||
* @param string $pluginName
|
||||
* @return void
|
||||
*/
|
||||
public static function clean(string $pluginName): void
|
||||
{
|
||||
if ($plugin = App::instance()->plugin($pluginName)) {
|
||||
$root = $plugin->root() . '/assets';
|
||||
$media = $plugin->mediaRoot();
|
||||
$assets = Dir::index($media, true);
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$original = $root . '/' . $asset;
|
||||
foreach ($assets as $asset) {
|
||||
$original = $root . '/' . $asset;
|
||||
|
||||
if (file_exists($original) === false) {
|
||||
$assetRoot = $media . '/' . $asset;
|
||||
if (file_exists($original) === false) {
|
||||
$assetRoot = $media . '/' . $asset;
|
||||
|
||||
if (is_file($assetRoot) === true) {
|
||||
F::remove($assetRoot);
|
||||
} else {
|
||||
Dir::remove($assetRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_file($assetRoot) === true) {
|
||||
F::remove($assetRoot);
|
||||
} else {
|
||||
Dir::remove($assetRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a symlink for a plugin asset and
|
||||
* return the public URL
|
||||
*
|
||||
* @param string $pluginName
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|null
|
||||
*/
|
||||
public static function resolve(string $pluginName, string $filename)
|
||||
{
|
||||
if ($plugin = App::instance()->plugin($pluginName)) {
|
||||
$source = $plugin->root() . '/assets/' . $filename;
|
||||
/**
|
||||
* Create a symlink for a plugin asset and
|
||||
* return the public URL
|
||||
*
|
||||
* @param string $pluginName
|
||||
* @param string $filename
|
||||
* @return \Kirby\Cms\Response|null
|
||||
*/
|
||||
public static function resolve(string $pluginName, string $filename)
|
||||
{
|
||||
if ($plugin = App::instance()->plugin($pluginName)) {
|
||||
$source = $plugin->root() . '/assets/' . $filename;
|
||||
|
||||
if (F::exists($source, $plugin->root()) === true) {
|
||||
// do some spring cleaning for older files
|
||||
static::clean($pluginName);
|
||||
if (F::exists($source, $plugin->root()) === true) {
|
||||
// do some spring cleaning for older files
|
||||
static::clean($pluginName);
|
||||
|
||||
$target = $plugin->mediaRoot() . '/' . $filename;
|
||||
$target = $plugin->mediaRoot() . '/' . $filename;
|
||||
|
||||
// create a symlink if possible
|
||||
F::link($source, $target, 'symlink');
|
||||
// create a symlink if possible
|
||||
F::link($source, $target, 'symlink');
|
||||
|
||||
// return the file response
|
||||
return Response::file($source);
|
||||
}
|
||||
}
|
||||
// return the file response
|
||||
return Response::file($source);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ use Kirby\Toolkit\Facade;
|
||||
*/
|
||||
class R extends Facade
|
||||
{
|
||||
/**
|
||||
* @return \Kirby\Http\Request
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
return App::instance()->request();
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Http\Request
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
return App::instance()->request();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,431 +17,431 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Responder
|
||||
{
|
||||
/**
|
||||
* Timestamp when the response expires
|
||||
* in Kirby's cache
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $expires = null;
|
||||
/**
|
||||
* Timestamp when the response expires
|
||||
* in Kirby's cache
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $expires = null;
|
||||
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $code = null;
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $code = null;
|
||||
|
||||
/**
|
||||
* Response body
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $body = null;
|
||||
/**
|
||||
* Response body
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $body = null;
|
||||
|
||||
/**
|
||||
* Flag that defines whether the current
|
||||
* response can be cached by Kirby's cache
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $cache = true;
|
||||
/**
|
||||
* Flag that defines whether the current
|
||||
* response can be cached by Kirby's cache
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $cache = true;
|
||||
|
||||
/**
|
||||
* HTTP headers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
/**
|
||||
* HTTP headers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Content type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = null;
|
||||
/**
|
||||
* Content type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = null;
|
||||
|
||||
/**
|
||||
* Flag that defines whether the current
|
||||
* response uses the HTTP `Authorization`
|
||||
* request header
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $usesAuth = false;
|
||||
/**
|
||||
* Flag that defines whether the current
|
||||
* response uses the HTTP `Authorization`
|
||||
* request header
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $usesAuth = false;
|
||||
|
||||
/**
|
||||
* List of cookie names the response
|
||||
* relies on
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $usesCookies = [];
|
||||
/**
|
||||
* List of cookie names the response
|
||||
* relies on
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $usesCookies = [];
|
||||
|
||||
/**
|
||||
* Creates and sends the response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->send();
|
||||
}
|
||||
/**
|
||||
* Creates and sends the response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the response body
|
||||
*
|
||||
* @param string|null $body
|
||||
* @return string|$this
|
||||
*/
|
||||
public function body(string $body = null)
|
||||
{
|
||||
if ($body === null) {
|
||||
return $this->body;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the response body
|
||||
*
|
||||
* @param string|null $body
|
||||
* @return string|$this
|
||||
*/
|
||||
public function body(string $body = null)
|
||||
{
|
||||
if ($body === null) {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
$this->body = $body;
|
||||
return $this;
|
||||
}
|
||||
$this->body = $body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the flag that defines
|
||||
* whether the current response can be cached
|
||||
* by Kirby's cache
|
||||
* @since 3.5.5
|
||||
*
|
||||
* @param bool|null $cache
|
||||
* @return bool|$this
|
||||
*/
|
||||
public function cache(?bool $cache = null)
|
||||
{
|
||||
if ($cache === null) {
|
||||
// never ever cache private responses
|
||||
if (static::isPrivate($this->usesAuth(), $this->usesCookies()) === true) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the flag that defines
|
||||
* whether the current response can be cached
|
||||
* by Kirby's cache
|
||||
* @since 3.5.5
|
||||
*
|
||||
* @param bool|null $cache
|
||||
* @return bool|$this
|
||||
*/
|
||||
public function cache(?bool $cache = null)
|
||||
{
|
||||
if ($cache === null) {
|
||||
// never ever cache private responses
|
||||
if (static::isPrivate($this->usesAuth(), $this->usesCookies()) === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
$this->cache = $cache;
|
||||
return $this;
|
||||
}
|
||||
$this->cache = $cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the flag that defines
|
||||
* whether the current response uses the HTTP
|
||||
* `Authorization` request header
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param bool|null $usesAuth
|
||||
* @return bool|$this
|
||||
*/
|
||||
public function usesAuth(?bool $usesAuth = null)
|
||||
{
|
||||
if ($usesAuth === null) {
|
||||
return $this->usesAuth;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the flag that defines
|
||||
* whether the current response uses the HTTP
|
||||
* `Authorization` request header
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param bool|null $usesAuth
|
||||
* @return bool|$this
|
||||
*/
|
||||
public function usesAuth(?bool $usesAuth = null)
|
||||
{
|
||||
if ($usesAuth === null) {
|
||||
return $this->usesAuth;
|
||||
}
|
||||
|
||||
$this->usesAuth = $usesAuth;
|
||||
return $this;
|
||||
}
|
||||
$this->usesAuth = $usesAuth;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for a cookie name that is
|
||||
* used by the response
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function usesCookie(string $name): void
|
||||
{
|
||||
// only add unique names
|
||||
if (in_array($name, $this->usesCookies) === false) {
|
||||
$this->usesCookies[] = $name;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Setter for a cookie name that is
|
||||
* used by the response
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function usesCookie(string $name): void
|
||||
{
|
||||
// only add unique names
|
||||
if (in_array($name, $this->usesCookies) === false) {
|
||||
$this->usesCookies[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the list of cookie
|
||||
* names the response relies on
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param array|null $usesCookies
|
||||
* @return array|$this
|
||||
*/
|
||||
public function usesCookies(?array $usesCookies = null)
|
||||
{
|
||||
if ($usesCookies === null) {
|
||||
return $this->usesCookies;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the list of cookie
|
||||
* names the response relies on
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param array|null $usesCookies
|
||||
* @return array|$this
|
||||
*/
|
||||
public function usesCookies(?array $usesCookies = null)
|
||||
{
|
||||
if ($usesCookies === null) {
|
||||
return $this->usesCookies;
|
||||
}
|
||||
|
||||
$this->usesCookies = $usesCookies;
|
||||
return $this;
|
||||
}
|
||||
$this->usesCookies = $usesCookies;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the cache expiry
|
||||
* timestamp for Kirby's cache
|
||||
* @since 3.5.5
|
||||
*
|
||||
* @param int|string|null $expires Timestamp, number of minutes or time string to parse
|
||||
* @param bool $override If `true`, the already defined timestamp will be overridden
|
||||
* @return int|null|$this
|
||||
*/
|
||||
public function expires($expires = null, bool $override = false)
|
||||
{
|
||||
// getter
|
||||
if ($expires === null && $override === false) {
|
||||
return $this->expires;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the cache expiry
|
||||
* timestamp for Kirby's cache
|
||||
* @since 3.5.5
|
||||
*
|
||||
* @param int|string|null $expires Timestamp, number of minutes or time string to parse
|
||||
* @param bool $override If `true`, the already defined timestamp will be overridden
|
||||
* @return int|null|$this
|
||||
*/
|
||||
public function expires($expires = null, bool $override = false)
|
||||
{
|
||||
// getter
|
||||
if ($expires === null && $override === false) {
|
||||
return $this->expires;
|
||||
}
|
||||
|
||||
// explicit un-setter
|
||||
if ($expires === null) {
|
||||
$this->expires = null;
|
||||
return $this;
|
||||
}
|
||||
// explicit un-setter
|
||||
if ($expires === null) {
|
||||
$this->expires = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// normalize the value to an integer timestamp
|
||||
if (is_int($expires) === true && $expires < 1000000000) {
|
||||
// number of minutes
|
||||
$expires = time() + ($expires * 60);
|
||||
} elseif (is_int($expires) !== true) {
|
||||
// time string
|
||||
$parsedExpires = strtotime($expires);
|
||||
// normalize the value to an integer timestamp
|
||||
if (is_int($expires) === true && $expires < 1000000000) {
|
||||
// number of minutes
|
||||
$expires = time() + ($expires * 60);
|
||||
} elseif (is_int($expires) !== true) {
|
||||
// time string
|
||||
$parsedExpires = strtotime($expires);
|
||||
|
||||
if (is_int($parsedExpires) !== true) {
|
||||
throw new InvalidArgumentException('Invalid time string "' . $expires . '"');
|
||||
}
|
||||
if (is_int($parsedExpires) !== true) {
|
||||
throw new InvalidArgumentException('Invalid time string "' . $expires . '"');
|
||||
}
|
||||
|
||||
$expires = $parsedExpires;
|
||||
}
|
||||
$expires = $parsedExpires;
|
||||
}
|
||||
|
||||
// by default only ever *reduce* the cache expiry time
|
||||
if (
|
||||
$override === true ||
|
||||
$this->expires === null ||
|
||||
$expires < $this->expires
|
||||
) {
|
||||
$this->expires = $expires;
|
||||
}
|
||||
// by default only ever *reduce* the cache expiry time
|
||||
if (
|
||||
$override === true ||
|
||||
$this->expires === null ||
|
||||
$expires < $this->expires
|
||||
) {
|
||||
$this->expires = $expires;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the status code
|
||||
*
|
||||
* @param int|null $code
|
||||
* @return int|$this
|
||||
*/
|
||||
public function code(int $code = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
return $this->code;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the status code
|
||||
*
|
||||
* @param int|null $code
|
||||
* @return int|$this
|
||||
*/
|
||||
public function code(int $code = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct response from an array
|
||||
*
|
||||
* @param array $response
|
||||
*/
|
||||
public function fromArray(array $response): void
|
||||
{
|
||||
$this->body($response['body'] ?? null);
|
||||
$this->cache($response['cache'] ?? null);
|
||||
$this->code($response['code'] ?? null);
|
||||
$this->expires($response['expires'] ?? null);
|
||||
$this->headers($response['headers'] ?? null);
|
||||
$this->type($response['type'] ?? null);
|
||||
$this->usesAuth($response['usesAuth'] ?? null);
|
||||
$this->usesCookies($response['usesCookies'] ?? null);
|
||||
}
|
||||
/**
|
||||
* Construct response from an array
|
||||
*
|
||||
* @param array $response
|
||||
*/
|
||||
public function fromArray(array $response): void
|
||||
{
|
||||
$this->body($response['body'] ?? null);
|
||||
$this->cache($response['cache'] ?? null);
|
||||
$this->code($response['code'] ?? null);
|
||||
$this->expires($response['expires'] ?? null);
|
||||
$this->headers($response['headers'] ?? null);
|
||||
$this->type($response['type'] ?? null);
|
||||
$this->usesAuth($response['usesAuth'] ?? null);
|
||||
$this->usesCookies($response['usesCookies'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for a single header
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|false|null $value
|
||||
* @param bool $lazy If `true`, an existing header value is not overridden
|
||||
* @return string|$this
|
||||
*/
|
||||
public function header(string $key, $value = null, bool $lazy = false)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->headers()[$key] ?? null;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for a single header
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|false|null $value
|
||||
* @param bool $lazy If `true`, an existing header value is not overridden
|
||||
* @return string|$this
|
||||
*/
|
||||
public function header(string $key, $value = null, bool $lazy = false)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->headers()[$key] ?? null;
|
||||
}
|
||||
|
||||
if ($value === false) {
|
||||
unset($this->headers[$key]);
|
||||
return $this;
|
||||
}
|
||||
if ($value === false) {
|
||||
unset($this->headers[$key]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($lazy === true && isset($this->headers[$key]) === true) {
|
||||
return $this;
|
||||
}
|
||||
if ($lazy === true && isset($this->headers[$key]) === true) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->headers[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
$this->headers[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for all headers
|
||||
*
|
||||
* @param array|null $headers
|
||||
* @return array|$this
|
||||
*/
|
||||
public function headers(array $headers = null)
|
||||
{
|
||||
if ($headers === null) {
|
||||
$injectedHeaders = [];
|
||||
/**
|
||||
* Setter and getter for all headers
|
||||
*
|
||||
* @param array|null $headers
|
||||
* @return array|$this
|
||||
*/
|
||||
public function headers(array $headers = null)
|
||||
{
|
||||
if ($headers === null) {
|
||||
$injectedHeaders = [];
|
||||
|
||||
if (static::isPrivate($this->usesAuth(), $this->usesCookies()) === true) {
|
||||
// never ever cache private responses
|
||||
$injectedHeaders['Cache-Control'] = 'no-store, private';
|
||||
} else {
|
||||
// the response is public, but it may
|
||||
// vary based on request headers
|
||||
$vary = [];
|
||||
if (static::isPrivate($this->usesAuth(), $this->usesCookies()) === true) {
|
||||
// never ever cache private responses
|
||||
$injectedHeaders['Cache-Control'] = 'no-store, private';
|
||||
} else {
|
||||
// the response is public, but it may
|
||||
// vary based on request headers
|
||||
$vary = [];
|
||||
|
||||
if ($this->usesAuth() === true) {
|
||||
$vary[] = 'Authorization';
|
||||
}
|
||||
if ($this->usesAuth() === true) {
|
||||
$vary[] = 'Authorization';
|
||||
}
|
||||
|
||||
if ($this->usesCookies() !== []) {
|
||||
$vary[] = 'Cookie';
|
||||
}
|
||||
if ($this->usesCookies() !== []) {
|
||||
$vary[] = 'Cookie';
|
||||
}
|
||||
|
||||
if ($vary !== []) {
|
||||
$injectedHeaders['Vary'] = implode(', ', $vary);
|
||||
}
|
||||
}
|
||||
if ($vary !== []) {
|
||||
$injectedHeaders['Vary'] = implode(', ', $vary);
|
||||
}
|
||||
}
|
||||
|
||||
// lazily inject (never override custom headers)
|
||||
return array_merge($injectedHeaders, $this->headers);
|
||||
}
|
||||
// lazily inject (never override custom headers)
|
||||
return array_merge($injectedHeaders, $this->headers);
|
||||
}
|
||||
|
||||
$this->headers = $headers;
|
||||
return $this;
|
||||
}
|
||||
$this->headers = $headers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to configure a json response
|
||||
*
|
||||
* @param array|null $json
|
||||
* @return string|$this
|
||||
*/
|
||||
public function json(array $json = null)
|
||||
{
|
||||
if ($json !== null) {
|
||||
$this->body(json_encode($json));
|
||||
}
|
||||
/**
|
||||
* Shortcut to configure a json response
|
||||
*
|
||||
* @param array|null $json
|
||||
* @return string|$this
|
||||
*/
|
||||
public function json(array $json = null)
|
||||
{
|
||||
if ($json !== null) {
|
||||
$this->body(json_encode($json));
|
||||
}
|
||||
|
||||
return $this->type('application/json');
|
||||
}
|
||||
return $this->type('application/json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to create a redirect response
|
||||
*
|
||||
* @param string|null $location
|
||||
* @param int|null $code
|
||||
* @return $this
|
||||
*/
|
||||
public function redirect(?string $location = null, ?int $code = null)
|
||||
{
|
||||
$location = Url::to($location ?? '/');
|
||||
$location = Url::unIdn($location);
|
||||
/**
|
||||
* Shortcut to create a redirect response
|
||||
*
|
||||
* @param string|null $location
|
||||
* @param int|null $code
|
||||
* @return $this
|
||||
*/
|
||||
public function redirect(?string $location = null, ?int $code = null)
|
||||
{
|
||||
$location = Url::to($location ?? '/');
|
||||
$location = Url::unIdn($location);
|
||||
|
||||
return $this
|
||||
->header('Location', (string)$location)
|
||||
->code($code ?? 302);
|
||||
}
|
||||
return $this
|
||||
->header('Location', (string)$location)
|
||||
->code($code ?? 302);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the response object from the config
|
||||
*
|
||||
* @param string|null $body
|
||||
* @return \Kirby\Cms\Response
|
||||
*/
|
||||
public function send(string $body = null)
|
||||
{
|
||||
if ($body !== null) {
|
||||
$this->body($body);
|
||||
}
|
||||
/**
|
||||
* Creates and returns the response object from the config
|
||||
*
|
||||
* @param string|null $body
|
||||
* @return \Kirby\Cms\Response
|
||||
*/
|
||||
public function send(string $body = null)
|
||||
{
|
||||
if ($body !== null) {
|
||||
$this->body($body);
|
||||
}
|
||||
|
||||
return new Response($this->toArray());
|
||||
}
|
||||
return new Response($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the response configuration
|
||||
* to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
// the `cache`, `expires`, `usesAuth` and `usesCookies`
|
||||
// values are explicitly *not* serialized as they are
|
||||
// volatile and not to be exported
|
||||
return [
|
||||
'body' => $this->body(),
|
||||
'code' => $this->code(),
|
||||
'headers' => $this->headers(),
|
||||
'type' => $this->type(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the response configuration
|
||||
* to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
// the `cache`, `expires`, `usesAuth` and `usesCookies`
|
||||
// values are explicitly *not* serialized as they are
|
||||
// volatile and not to be exported
|
||||
return [
|
||||
'body' => $this->body(),
|
||||
'code' => $this->code(),
|
||||
'headers' => $this->headers(),
|
||||
'type' => $this->type(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the content type
|
||||
*
|
||||
* @param string|null $type
|
||||
* @return string|$this
|
||||
*/
|
||||
public function type(string $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
return $this->type;
|
||||
}
|
||||
/**
|
||||
* Setter and getter for the content type
|
||||
*
|
||||
* @param string|null $type
|
||||
* @return string|$this
|
||||
*/
|
||||
public function type(string $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
if (Str::contains($type, '/') === false) {
|
||||
$type = Mime::fromExtension($type);
|
||||
}
|
||||
if (Str::contains($type, '/') === false) {
|
||||
$type = Mime::fromExtension($type);
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the response needs to be exempted from
|
||||
* all caches due to using dynamic data based on auth
|
||||
* and/or cookies; the request data only matters if it
|
||||
* is actually used/relied on by the response
|
||||
* @since 3.7.0
|
||||
* @internal
|
||||
*
|
||||
* @param bool $usesAuth
|
||||
* @param array $usesCookies
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPrivate(bool $usesAuth, array $usesCookies): bool
|
||||
{
|
||||
$kirby = App::instance();
|
||||
/**
|
||||
* Checks whether the response needs to be exempted from
|
||||
* all caches due to using dynamic data based on auth
|
||||
* and/or cookies; the request data only matters if it
|
||||
* is actually used/relied on by the response
|
||||
* @since 3.7.0
|
||||
* @internal
|
||||
*
|
||||
* @param bool $usesAuth
|
||||
* @param array $usesCookies
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPrivate(bool $usesAuth, array $usesCookies): bool
|
||||
{
|
||||
$kirby = App::instance();
|
||||
|
||||
if ($usesAuth === true && $kirby->request()->hasAuth() === true) {
|
||||
return true;
|
||||
}
|
||||
if ($usesAuth === true && $kirby->request()->hasAuth() === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($usesCookies as $cookie) {
|
||||
if (isset($_COOKIE[$cookie]) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach ($usesCookies as $cookie) {
|
||||
if (isset($_COOKIE[$cookie]) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Response extends \Kirby\Http\Response
|
||||
{
|
||||
/**
|
||||
* Adjusted redirect creation which
|
||||
* parses locations with the Url::to method
|
||||
* first.
|
||||
*
|
||||
* @param string $location
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public static function redirect(string $location = '/', int $code = 302)
|
||||
{
|
||||
return parent::redirect(Url::to($location), $code);
|
||||
}
|
||||
/**
|
||||
* Adjusted redirect creation which
|
||||
* parses locations with the Url::to method
|
||||
* first.
|
||||
*
|
||||
* @param string $location
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public static function redirect(string $location = '/', int $code = 302)
|
||||
{
|
||||
return parent::redirect(Url::to($location), $code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,214 +19,214 @@ use Kirby\Toolkit\I18n;
|
||||
*/
|
||||
class Role extends Model
|
||||
{
|
||||
protected $description;
|
||||
protected $name;
|
||||
protected $permissions;
|
||||
protected $title;
|
||||
protected $description;
|
||||
protected $name;
|
||||
protected $permissions;
|
||||
protected $title;
|
||||
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function admin(array $inject = [])
|
||||
{
|
||||
try {
|
||||
return static::load('admin');
|
||||
} catch (Exception $e) {
|
||||
return static::factory(static::defaults()['admin'], $inject);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function admin(array $inject = [])
|
||||
{
|
||||
try {
|
||||
return static::load('admin');
|
||||
} catch (Exception $e) {
|
||||
return static::factory(static::defaults()['admin'], $inject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected static function defaults(): array
|
||||
{
|
||||
return [
|
||||
'admin' => [
|
||||
'name' => 'admin',
|
||||
'description' => I18n::translate('role.admin.description'),
|
||||
'title' => I18n::translate('role.admin.title'),
|
||||
'permissions' => true,
|
||||
],
|
||||
'nobody' => [
|
||||
'name' => 'nobody',
|
||||
'description' => I18n::translate('role.nobody.description'),
|
||||
'title' => I18n::translate('role.nobody.title'),
|
||||
'permissions' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected static function defaults(): array
|
||||
{
|
||||
return [
|
||||
'admin' => [
|
||||
'name' => 'admin',
|
||||
'description' => I18n::translate('role.admin.description'),
|
||||
'title' => I18n::translate('role.admin.title'),
|
||||
'permissions' => true,
|
||||
],
|
||||
'nobody' => [
|
||||
'name' => 'nobody',
|
||||
'description' => I18n::translate('role.nobody.description'),
|
||||
'title' => I18n::translate('role.nobody.title'),
|
||||
'permissions' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $props
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $props, array $inject = [])
|
||||
{
|
||||
return new static($props + $inject);
|
||||
}
|
||||
/**
|
||||
* @param array $props
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $props, array $inject = [])
|
||||
{
|
||||
return new static($props + $inject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->name() === 'admin';
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->name() === 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNobody(): bool
|
||||
{
|
||||
return $this->name() === 'nobody';
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNobody(): bool
|
||||
{
|
||||
return $this->name() === 'nobody';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function load(string $file, array $inject = [])
|
||||
{
|
||||
$data = Data::read($file);
|
||||
$data['name'] = F::name($file);
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function load(string $file, array $inject = [])
|
||||
{
|
||||
$data = Data::read($file);
|
||||
$data['name'] = F::name($file);
|
||||
|
||||
return static::factory($data, $inject);
|
||||
}
|
||||
return static::factory($data, $inject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function nobody(array $inject = [])
|
||||
{
|
||||
try {
|
||||
return static::load('nobody');
|
||||
} catch (Exception $e) {
|
||||
return static::factory(static::defaults()['nobody'], $inject);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function nobody(array $inject = [])
|
||||
{
|
||||
try {
|
||||
return static::load('nobody');
|
||||
} catch (Exception $e) {
|
||||
return static::factory(static::defaults()['nobody'], $inject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Kirby\Cms\Permissions
|
||||
*/
|
||||
public function permissions()
|
||||
{
|
||||
return $this->permissions;
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Cms\Permissions
|
||||
*/
|
||||
public function permissions()
|
||||
{
|
||||
return $this->permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $description
|
||||
* @return $this
|
||||
*/
|
||||
protected function setDescription($description = null)
|
||||
{
|
||||
$this->description = I18n::translate($description, $description);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param mixed $description
|
||||
* @return $this
|
||||
*/
|
||||
protected function setDescription($description = null)
|
||||
{
|
||||
$this->description = I18n::translate($description, $description);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
protected function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
protected function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $permissions
|
||||
* @return $this
|
||||
*/
|
||||
protected function setPermissions($permissions = null)
|
||||
{
|
||||
$this->permissions = new Permissions($permissions);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param mixed $permissions
|
||||
* @return $this
|
||||
*/
|
||||
protected function setPermissions($permissions = null)
|
||||
{
|
||||
$this->permissions = new Permissions($permissions);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $title
|
||||
* @return $this
|
||||
*/
|
||||
protected function setTitle($title = null)
|
||||
{
|
||||
$this->title = I18n::translate($title, $title);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param mixed $title
|
||||
* @return $this
|
||||
*/
|
||||
protected function setTitle($title = null)
|
||||
{
|
||||
$this->title = I18n::translate($title, $title);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
return $this->title ??= ucfirst($this->name());
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
return $this->title ??= ucfirst($this->name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the most important role
|
||||
* properties to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'description' => $this->description(),
|
||||
'id' => $this->id(),
|
||||
'name' => $this->name(),
|
||||
'permissions' => $this->permissions()->toArray(),
|
||||
'title' => $this->title(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the most important role
|
||||
* properties to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'description' => $this->description(),
|
||||
'id' => $this->id(),
|
||||
'name' => $this->name(),
|
||||
'permissions' => $this->permissions()->toArray(),
|
||||
'title' => $this->title(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,128 +18,128 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Roles extends Collection
|
||||
{
|
||||
/**
|
||||
* Returns a filtered list of all
|
||||
* roles that can be created by the
|
||||
* current user
|
||||
*
|
||||
* @return $this|static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function canBeChanged()
|
||||
{
|
||||
if (App::instance()->user()) {
|
||||
return $this->filter(function ($role) {
|
||||
$newUser = new User([
|
||||
'email' => 'test@getkirby.com',
|
||||
'role' => $role->id()
|
||||
]);
|
||||
/**
|
||||
* Returns a filtered list of all
|
||||
* roles that can be created by the
|
||||
* current user
|
||||
*
|
||||
* @return $this|static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function canBeChanged()
|
||||
{
|
||||
if (App::instance()->user()) {
|
||||
return $this->filter(function ($role) {
|
||||
$newUser = new User([
|
||||
'email' => 'test@getkirby.com',
|
||||
'role' => $role->id()
|
||||
]);
|
||||
|
||||
return $newUser->permissions()->can('changeRole');
|
||||
});
|
||||
}
|
||||
return $newUser->permissions()->can('changeRole');
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filtered list of all
|
||||
* roles that can be created by the
|
||||
* current user
|
||||
*
|
||||
* @return $this|static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function canBeCreated()
|
||||
{
|
||||
if (App::instance()->user()) {
|
||||
return $this->filter(function ($role) {
|
||||
$newUser = new User([
|
||||
'email' => 'test@getkirby.com',
|
||||
'role' => $role->id()
|
||||
]);
|
||||
/**
|
||||
* Returns a filtered list of all
|
||||
* roles that can be created by the
|
||||
* current user
|
||||
*
|
||||
* @return $this|static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function canBeCreated()
|
||||
{
|
||||
if (App::instance()->user()) {
|
||||
return $this->filter(function ($role) {
|
||||
$newUser = new User([
|
||||
'email' => 'test@getkirby.com',
|
||||
'role' => $role->id()
|
||||
]);
|
||||
|
||||
return $newUser->permissions()->can('create');
|
||||
});
|
||||
}
|
||||
return $newUser->permissions()->can('create');
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $roles
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $roles, array $inject = [])
|
||||
{
|
||||
$collection = new static();
|
||||
/**
|
||||
* @param array $roles
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function factory(array $roles, array $inject = [])
|
||||
{
|
||||
$collection = new static();
|
||||
|
||||
// read all user blueprints
|
||||
foreach ($roles as $props) {
|
||||
$role = Role::factory($props, $inject);
|
||||
$collection->set($role->id(), $role);
|
||||
}
|
||||
// read all user blueprints
|
||||
foreach ($roles as $props) {
|
||||
$role = Role::factory($props, $inject);
|
||||
$collection->set($role->id(), $role);
|
||||
}
|
||||
|
||||
// always include the admin role
|
||||
if ($collection->find('admin') === null) {
|
||||
$collection->set('admin', Role::admin());
|
||||
}
|
||||
// always include the admin role
|
||||
if ($collection->find('admin') === null) {
|
||||
$collection->set('admin', Role::admin());
|
||||
}
|
||||
|
||||
// return the collection sorted by name
|
||||
return $collection->sort('name', 'asc');
|
||||
}
|
||||
// return the collection sorted by name
|
||||
return $collection->sort('name', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $root
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function load(string $root = null, array $inject = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
$roles = new static();
|
||||
/**
|
||||
* @param string|null $root
|
||||
* @param array $inject
|
||||
* @return static
|
||||
*/
|
||||
public static function load(string $root = null, array $inject = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
$roles = new static();
|
||||
|
||||
// load roles from plugins
|
||||
foreach ($kirby->extensions('blueprints') as $blueprintName => $blueprint) {
|
||||
if (substr($blueprintName, 0, 6) !== 'users/') {
|
||||
continue;
|
||||
}
|
||||
// load roles from plugins
|
||||
foreach ($kirby->extensions('blueprints') as $blueprintName => $blueprint) {
|
||||
if (substr($blueprintName, 0, 6) !== 'users/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// callback option can be return array or blueprint file path
|
||||
if (is_callable($blueprint) === true) {
|
||||
$blueprint = $blueprint($kirby);
|
||||
}
|
||||
// callback option can be return array or blueprint file path
|
||||
if (is_callable($blueprint) === true) {
|
||||
$blueprint = $blueprint($kirby);
|
||||
}
|
||||
|
||||
if (is_array($blueprint) === true) {
|
||||
$role = Role::factory($blueprint, $inject);
|
||||
} else {
|
||||
$role = Role::load($blueprint, $inject);
|
||||
}
|
||||
if (is_array($blueprint) === true) {
|
||||
$role = Role::factory($blueprint, $inject);
|
||||
} else {
|
||||
$role = Role::load($blueprint, $inject);
|
||||
}
|
||||
|
||||
$roles->set($role->id(), $role);
|
||||
}
|
||||
$roles->set($role->id(), $role);
|
||||
}
|
||||
|
||||
// load roles from directory
|
||||
if ($root !== null) {
|
||||
foreach (glob($root . '/*.yml') as $file) {
|
||||
$filename = basename($file);
|
||||
// load roles from directory
|
||||
if ($root !== null) {
|
||||
foreach (glob($root . '/*.yml') as $file) {
|
||||
$filename = basename($file);
|
||||
|
||||
if ($filename === 'default.yml') {
|
||||
continue;
|
||||
}
|
||||
if ($filename === 'default.yml') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$role = Role::load($file, $inject);
|
||||
$roles->set($role->id(), $role);
|
||||
}
|
||||
}
|
||||
$role = Role::load($file, $inject);
|
||||
$roles->set($role->id(), $role);
|
||||
}
|
||||
}
|
||||
|
||||
// always include the admin role
|
||||
if ($roles->find('admin') === null) {
|
||||
$roles->set('admin', Role::admin($inject));
|
||||
}
|
||||
// always include the admin role
|
||||
if ($roles->find('admin') === null) {
|
||||
$roles->set('admin', Role::admin($inject));
|
||||
}
|
||||
|
||||
// return the collection sorted by name
|
||||
return $roles->sort('name', 'asc');
|
||||
}
|
||||
// return the collection sorted by name
|
||||
return $roles->sort('name', 'asc');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ use Kirby\Toolkit\Facade;
|
||||
*/
|
||||
class S extends Facade
|
||||
{
|
||||
/**
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
return App::instance()->session();
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
return App::instance()->session();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,47 +16,47 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class Search
|
||||
{
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public static function files(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->site()->index()->files()->search($query, $params);
|
||||
}
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public static function files(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->site()->index()->files()->search($query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Native search method to search for anything within the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $collection
|
||||
* @param string|null $query
|
||||
* @param mixed $params
|
||||
* @return \Kirby\Cms\Collection|bool
|
||||
*/
|
||||
public static function collection(Collection $collection, string $query = null, $params = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
return ($kirby->component('search'))($kirby, $collection, $query, $params);
|
||||
}
|
||||
/**
|
||||
* Native search method to search for anything within the collection
|
||||
*
|
||||
* @param \Kirby\Cms\Collection $collection
|
||||
* @param string|null $query
|
||||
* @param mixed $params
|
||||
* @return \Kirby\Cms\Collection|bool
|
||||
*/
|
||||
public static function collection(Collection $collection, string $query = null, $params = [])
|
||||
{
|
||||
$kirby = App::instance();
|
||||
return ($kirby->component('search'))($kirby, $collection, $query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public static function pages(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->site()->index()->search($query, $params);
|
||||
}
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Pages
|
||||
*/
|
||||
public static function pages(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->site()->index()->search($query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public static function users(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->users()->search($query, $params);
|
||||
}
|
||||
/**
|
||||
* @param string|null $query
|
||||
* @param array $params
|
||||
* @return \Kirby\Cms\Users
|
||||
*/
|
||||
public static function users(string $query = null, $params = [])
|
||||
{
|
||||
return App::instance()->users()->search($query, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,92 +16,92 @@ use Kirby\Toolkit\Component;
|
||||
*/
|
||||
class Section extends Component
|
||||
{
|
||||
/**
|
||||
* Registry for all component mixins
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $mixins = [];
|
||||
/**
|
||||
* Registry for all component mixins
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $mixins = [];
|
||||
|
||||
/**
|
||||
* Registry for all component types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $types = [];
|
||||
/**
|
||||
* Registry for all component types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $types = [];
|
||||
|
||||
|
||||
/**
|
||||
* Section constructor.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $attrs
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(string $type, array $attrs = [])
|
||||
{
|
||||
if (isset($attrs['model']) === false) {
|
||||
throw new InvalidArgumentException('Undefined section model');
|
||||
}
|
||||
/**
|
||||
* Section constructor.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $attrs
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(string $type, array $attrs = [])
|
||||
{
|
||||
if (isset($attrs['model']) === false) {
|
||||
throw new InvalidArgumentException('Undefined section model');
|
||||
}
|
||||
|
||||
if (is_a($attrs['model'], 'Kirby\Cms\Model') === false) {
|
||||
throw new InvalidArgumentException('Invalid section model');
|
||||
}
|
||||
if (is_a($attrs['model'], 'Kirby\Cms\Model') === false) {
|
||||
throw new InvalidArgumentException('Invalid section model');
|
||||
}
|
||||
|
||||
// use the type as fallback for the name
|
||||
$attrs['name'] ??= $type;
|
||||
$attrs['type'] = $type;
|
||||
// use the type as fallback for the name
|
||||
$attrs['name'] ??= $type;
|
||||
$attrs['type'] = $type;
|
||||
|
||||
parent::__construct($type, $attrs);
|
||||
}
|
||||
parent::__construct($type, $attrs);
|
||||
}
|
||||
|
||||
public function errors(): array
|
||||
{
|
||||
if (array_key_exists('errors', $this->methods) === true) {
|
||||
return $this->methods['errors']->call($this);
|
||||
}
|
||||
public function errors(): array
|
||||
{
|
||||
if (array_key_exists('errors', $this->methods) === true) {
|
||||
return $this->methods['errors']->call($this);
|
||||
}
|
||||
|
||||
return $this->errors ?? [];
|
||||
}
|
||||
return $this->errors ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->model()->kirby();
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->model()->kirby();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
/**
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = parent::toArray();
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = parent::toArray();
|
||||
|
||||
unset($array['model']);
|
||||
unset($array['model']);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
return array_merge([
|
||||
'status' => 'ok',
|
||||
'code' => 200,
|
||||
'name' => $this->name,
|
||||
'type' => $this->type
|
||||
], $this->toArray());
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toResponse(): array
|
||||
{
|
||||
return array_merge([
|
||||
'status' => 'ok',
|
||||
'code' => 200,
|
||||
'name' => $this->name,
|
||||
'type' => $this->type
|
||||
], $this->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,87 +15,87 @@ use Closure;
|
||||
*/
|
||||
trait SiteActions
|
||||
{
|
||||
/**
|
||||
* Commits a site action, by following these steps
|
||||
*
|
||||
* 1. checks the action rules
|
||||
* 2. sends the before hook
|
||||
* 3. commits the store action
|
||||
* 4. sends the after hook
|
||||
* 5. returns the result
|
||||
*
|
||||
* @param string $action
|
||||
* @param mixed ...$arguments
|
||||
* @param Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
protected function commit(string $action, array $arguments, Closure $callback)
|
||||
{
|
||||
$old = $this->hardcopy();
|
||||
$kirby = $this->kirby();
|
||||
$argumentValues = array_values($arguments);
|
||||
/**
|
||||
* Commits a site action, by following these steps
|
||||
*
|
||||
* 1. checks the action rules
|
||||
* 2. sends the before hook
|
||||
* 3. commits the store action
|
||||
* 4. sends the after hook
|
||||
* 5. returns the result
|
||||
*
|
||||
* @param string $action
|
||||
* @param mixed ...$arguments
|
||||
* @param Closure $callback
|
||||
* @return mixed
|
||||
*/
|
||||
protected function commit(string $action, array $arguments, Closure $callback)
|
||||
{
|
||||
$old = $this->hardcopy();
|
||||
$kirby = $this->kirby();
|
||||
$argumentValues = array_values($arguments);
|
||||
|
||||
$this->rules()->$action(...$argumentValues);
|
||||
$kirby->trigger('site.' . $action . ':before', $arguments);
|
||||
$this->rules()->$action(...$argumentValues);
|
||||
$kirby->trigger('site.' . $action . ':before', $arguments);
|
||||
|
||||
$result = $callback(...$argumentValues);
|
||||
$result = $callback(...$argumentValues);
|
||||
|
||||
$kirby->trigger('site.' . $action . ':after', ['newSite' => $result, 'oldSite' => $old]);
|
||||
$kirby->trigger('site.' . $action . ':after', ['newSite' => $result, 'oldSite' => $old]);
|
||||
|
||||
$kirby->cache('pages')->flush();
|
||||
return $result;
|
||||
}
|
||||
$kirby->cache('pages')->flush();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the site title
|
||||
*
|
||||
* @param string $title
|
||||
* @param string|null $languageCode
|
||||
* @return static
|
||||
*/
|
||||
public function changeTitle(string $title, string $languageCode = null)
|
||||
{
|
||||
$site = $this;
|
||||
$title = trim($title);
|
||||
$arguments = compact('site', 'title', 'languageCode');
|
||||
/**
|
||||
* Change the site title
|
||||
*
|
||||
* @param string $title
|
||||
* @param string|null $languageCode
|
||||
* @return static
|
||||
*/
|
||||
public function changeTitle(string $title, string $languageCode = null)
|
||||
{
|
||||
$site = $this;
|
||||
$title = trim($title);
|
||||
$arguments = compact('site', 'title', 'languageCode');
|
||||
|
||||
return $this->commit('changeTitle', $arguments, function ($site, $title, $languageCode) {
|
||||
return $site->save(['title' => $title], $languageCode);
|
||||
});
|
||||
}
|
||||
return $this->commit('changeTitle', $arguments, function ($site, $title, $languageCode) {
|
||||
return $site->save(['title' => $title], $languageCode);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a main page
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\Page
|
||||
*/
|
||||
public function createChild(array $props)
|
||||
{
|
||||
$props = array_merge($props, [
|
||||
'url' => null,
|
||||
'num' => null,
|
||||
'parent' => null,
|
||||
'site' => $this,
|
||||
]);
|
||||
/**
|
||||
* Creates a main page
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Cms\Page
|
||||
*/
|
||||
public function createChild(array $props)
|
||||
{
|
||||
$props = array_merge($props, [
|
||||
'url' => null,
|
||||
'num' => null,
|
||||
'parent' => null,
|
||||
'site' => $this,
|
||||
]);
|
||||
|
||||
return Page::create($props);
|
||||
}
|
||||
return Page::create($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean internal caches
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->blueprint = null;
|
||||
$this->children = null;
|
||||
$this->content = null;
|
||||
$this->files = null;
|
||||
$this->inventory = null;
|
||||
$this->translations = null;
|
||||
/**
|
||||
* Clean internal caches
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->blueprint = null;
|
||||
$this->children = null;
|
||||
$this->content = null;
|
||||
$this->files = null;
|
||||
$this->inventory = null;
|
||||
$this->translations = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,47 +14,47 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class SiteBlueprint extends Blueprint
|
||||
{
|
||||
/**
|
||||
* Creates a new page blueprint object
|
||||
* with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
/**
|
||||
* Creates a new page blueprint object
|
||||
* with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
parent::__construct($props);
|
||||
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeTitle' => null,
|
||||
'update' => null,
|
||||
],
|
||||
// aliases
|
||||
[
|
||||
'title' => 'changeTitle',
|
||||
]
|
||||
);
|
||||
}
|
||||
// normalize all available page options
|
||||
$this->props['options'] = $this->normalizeOptions(
|
||||
$this->props['options'] ?? true,
|
||||
// defaults
|
||||
[
|
||||
'changeTitle' => null,
|
||||
'update' => null,
|
||||
],
|
||||
// aliases
|
||||
[
|
||||
'title' => 'changeTitle',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preview settings
|
||||
* The preview setting controls the "Open"
|
||||
* button in the panel and redirects it to a
|
||||
* different URL if necessary.
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$preview = $this->props['options']['preview'] ?? true;
|
||||
/**
|
||||
* Returns the preview settings
|
||||
* The preview setting controls the "Open"
|
||||
* button in the panel and redirects it to a
|
||||
* different URL if necessary.
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$preview = $this->props['options']['preview'] ?? true;
|
||||
|
||||
if (is_string($preview) === true) {
|
||||
return $this->model->toString($preview);
|
||||
}
|
||||
if (is_string($preview) === true) {
|
||||
return $this->model->toString($preview);
|
||||
}
|
||||
|
||||
return $preview;
|
||||
}
|
||||
return $preview;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class SitePermissions extends ModelPermissions
|
||||
{
|
||||
protected $category = 'site';
|
||||
protected $category = 'site';
|
||||
}
|
||||
|
||||
@@ -17,42 +17,42 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class SiteRules
|
||||
{
|
||||
/**
|
||||
* Validates if the site title can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Site $site
|
||||
* @param string $title
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the title is empty
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title
|
||||
*/
|
||||
public static function changeTitle(Site $site, string $title): bool
|
||||
{
|
||||
if ($site->permissions()->changeTitle() !== true) {
|
||||
throw new PermissionException(['key' => 'site.changeTitle.permission']);
|
||||
}
|
||||
/**
|
||||
* Validates if the site title can be changed
|
||||
*
|
||||
* @param \Kirby\Cms\Site $site
|
||||
* @param string $title
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the title is empty
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title
|
||||
*/
|
||||
public static function changeTitle(Site $site, string $title): bool
|
||||
{
|
||||
if ($site->permissions()->changeTitle() !== true) {
|
||||
throw new PermissionException(['key' => 'site.changeTitle.permission']);
|
||||
}
|
||||
|
||||
if (Str::length($title) === 0) {
|
||||
throw new InvalidArgumentException(['key' => 'site.changeTitle.empty']);
|
||||
}
|
||||
if (Str::length($title) === 0) {
|
||||
throw new InvalidArgumentException(['key' => 'site.changeTitle.empty']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the site can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Site $site
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the site
|
||||
*/
|
||||
public static function update(Site $site, array $content = []): bool
|
||||
{
|
||||
if ($site->permissions()->update() !== true) {
|
||||
throw new PermissionException(['key' => 'site.update.permission']);
|
||||
}
|
||||
/**
|
||||
* Validates if the site can be updated
|
||||
*
|
||||
* @param \Kirby\Cms\Site $site
|
||||
* @param array $content
|
||||
* @return bool
|
||||
* @throws \Kirby\Exception\PermissionException If the user is not allowed to update the site
|
||||
*/
|
||||
public static function update(Site $site, array $content = []): bool
|
||||
{
|
||||
if ($site->permissions()->update() !== true) {
|
||||
throw new PermissionException(['key' => 'site.update.permission']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,45 +20,47 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class Structure extends Collection
|
||||
{
|
||||
/**
|
||||
* Creates a new Collection with the given objects
|
||||
*
|
||||
* @param array $objects Kirby\Cms\StructureObject` objects or props arrays
|
||||
* @param object|null $parent
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->set($objects);
|
||||
}
|
||||
/**
|
||||
* Creates a new Collection with the given objects
|
||||
*
|
||||
* @param array $objects Kirby\Cms\StructureObject` objects or props arrays
|
||||
* @param object|null $parent
|
||||
*/
|
||||
public function __construct($objects = [], $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->set($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal setter for collection items.
|
||||
* This makes sure that nothing unexpected ends
|
||||
* up in the collection. You can pass arrays or
|
||||
* StructureObjects
|
||||
*
|
||||
* @param string $id
|
||||
* @param array|StructureObject $props
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __set(string $id, $props)
|
||||
{
|
||||
if (is_a($props, 'Kirby\Cms\StructureObject') === true) {
|
||||
$object = $props;
|
||||
} else {
|
||||
if (is_array($props) === false) {
|
||||
throw new InvalidArgumentException('Invalid structure data');
|
||||
}
|
||||
/**
|
||||
* The internal setter for collection items.
|
||||
* This makes sure that nothing unexpected ends
|
||||
* up in the collection. You can pass arrays or
|
||||
* StructureObjects
|
||||
*
|
||||
* @param string $id
|
||||
* @param array|StructureObject $props
|
||||
* @return void
|
||||
*
|
||||
* @throws \Kirby\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __set(string $id, $props): void
|
||||
{
|
||||
if (is_a($props, 'Kirby\Cms\StructureObject') === true) {
|
||||
$object = $props;
|
||||
} else {
|
||||
if (is_array($props) === false) {
|
||||
throw new InvalidArgumentException('Invalid structure data');
|
||||
}
|
||||
|
||||
$object = new StructureObject([
|
||||
'content' => $props,
|
||||
'id' => $props['id'] ?? $id,
|
||||
'parent' => $this->parent,
|
||||
'structure' => $this
|
||||
]);
|
||||
}
|
||||
$object = new StructureObject([
|
||||
'content' => $props,
|
||||
'id' => $props['id'] ?? $id,
|
||||
'parent' => $this->parent,
|
||||
'structure' => $this
|
||||
]);
|
||||
}
|
||||
|
||||
return parent::__set($object->id(), $object);
|
||||
}
|
||||
parent::__set($object->id(), $object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,190 +20,190 @@ namespace Kirby\Cms;
|
||||
*/
|
||||
class StructureObject extends Model
|
||||
{
|
||||
use HasSiblings;
|
||||
use HasSiblings;
|
||||
|
||||
/**
|
||||
* The content
|
||||
*
|
||||
* @var Content
|
||||
*/
|
||||
protected $content;
|
||||
/**
|
||||
* The content
|
||||
*
|
||||
* @var Content
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @var \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* The parent Structure collection
|
||||
*
|
||||
* @var Structure
|
||||
*/
|
||||
protected $structure;
|
||||
/**
|
||||
* The parent Structure collection
|
||||
*
|
||||
* @var Structure
|
||||
*/
|
||||
protected $structure;
|
||||
|
||||
/**
|
||||
* Modified getter to also return fields
|
||||
* from the object's content
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
// public property access
|
||||
if (isset($this->$method) === true) {
|
||||
return $this->$method;
|
||||
}
|
||||
/**
|
||||
* Modified getter to also return fields
|
||||
* from the object's content
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
// public property access
|
||||
if (isset($this->$method) === true) {
|
||||
return $this->$method;
|
||||
}
|
||||
|
||||
return $this->content()->get($method);
|
||||
}
|
||||
return $this->content()->get($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new StructureObject with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
/**
|
||||
* Creates a new StructureObject with the given props
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props)
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
if (is_a($this->content, 'Kirby\Cms\Content') === true) {
|
||||
return $this->content;
|
||||
}
|
||||
/**
|
||||
* Returns the content
|
||||
*
|
||||
* @return \Kirby\Cms\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
if (is_a($this->content, 'Kirby\Cms\Content') === true) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
if (is_array($this->content) !== true) {
|
||||
$this->content = [];
|
||||
}
|
||||
if (is_array($this->content) !== true) {
|
||||
$this->content = [];
|
||||
}
|
||||
|
||||
return $this->content = new Content($this->content, $this->parent());
|
||||
}
|
||||
return $this->content = new Content($this->content, $this->parent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the required id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
/**
|
||||
* Returns the required id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current object with the given structure object
|
||||
*
|
||||
* @param mixed $structure
|
||||
* @return bool
|
||||
*/
|
||||
public function is($structure): bool
|
||||
{
|
||||
if (is_a($structure, 'Kirby\Cms\StructureObject') === false) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Compares the current object with the given structure object
|
||||
*
|
||||
* @param mixed $structure
|
||||
* @return bool
|
||||
*/
|
||||
public function is($structure): bool
|
||||
{
|
||||
if (is_a($structure, 'Kirby\Cms\StructureObject') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this === $structure;
|
||||
}
|
||||
return $this === $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent Model object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
/**
|
||||
* Returns the parent Model object
|
||||
*
|
||||
* @return \Kirby\Cms\Model
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Content object with the given parent
|
||||
*
|
||||
* @param array|null $content
|
||||
* @return $this
|
||||
*/
|
||||
protected function setContent(array $content = null)
|
||||
{
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the Content object with the given parent
|
||||
*
|
||||
* @param array|null $content
|
||||
* @return $this
|
||||
*/
|
||||
protected function setContent(array $content = null)
|
||||
{
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the object.
|
||||
* The id is required. The structure
|
||||
* class will use the index, if no id is
|
||||
* specified.
|
||||
*
|
||||
* @param string $id
|
||||
* @return $this
|
||||
*/
|
||||
protected function setId(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the id of the object.
|
||||
* The id is required. The structure
|
||||
* class will use the index, if no id is
|
||||
* specified.
|
||||
*
|
||||
* @param string $id
|
||||
* @return $this
|
||||
*/
|
||||
protected function setId(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent Model
|
||||
*
|
||||
* @return $this
|
||||
* @param \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null $parent
|
||||
*/
|
||||
protected function setParent(Model $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parent Model
|
||||
*
|
||||
* @return $this
|
||||
* @param \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null $parent
|
||||
*/
|
||||
protected function setParent(Model $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent Structure collection
|
||||
*
|
||||
* @param \Kirby\Cms\Structure|null $structure
|
||||
* @return $this
|
||||
*/
|
||||
protected function setStructure(Structure $structure = null)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parent Structure collection
|
||||
*
|
||||
* @param \Kirby\Cms\Structure|null $structure
|
||||
* @return $this
|
||||
*/
|
||||
protected function setStructure(Structure $structure = null)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent Structure collection as siblings
|
||||
*
|
||||
* @return \Kirby\Cms\Structure
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
return $this->structure;
|
||||
}
|
||||
/**
|
||||
* Returns the parent Structure collection as siblings
|
||||
*
|
||||
* @return \Kirby\Cms\Structure
|
||||
*/
|
||||
protected function siblingsCollection()
|
||||
{
|
||||
return $this->structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all fields in the object to a
|
||||
* plain associative array. The id is
|
||||
* injected into the array afterwards
|
||||
* to make sure it's always present and
|
||||
* not overloaded in the content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = $this->content()->toArray();
|
||||
$array['id'] = $this->id();
|
||||
/**
|
||||
* Converts all fields in the object to a
|
||||
* plain associative array. The id is
|
||||
* injected into the array afterwards
|
||||
* to make sure it's always present and
|
||||
* not overloaded in the content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = $this->content()->toArray();
|
||||
$array['id'] = $this->id();
|
||||
|
||||
ksort($array);
|
||||
ksort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user