Update Kirby and add password guard

This commit is contained in:
isUnknown 2026-02-09 17:05:41 +01:00
parent aaf1aa7890
commit 55d4e45891
987 changed files with 160116 additions and 66454 deletions

View file

@ -1,241 +1,217 @@
<?php
use Kirby\Cms\File;
use Kirby\Panel\Collector\FilesCollector;
use Kirby\Panel\Ui\Item\FileItem;
use Kirby\Panel\Ui\Upload;
use Kirby\Toolkit\I18n;
return [
'mixins' => [
'empty',
'headline',
'help',
'layout',
'min',
'max',
'pagination',
'parent',
],
'props' => [
/**
* Enables/disables reverse sorting
*/
'flip' => function (bool $flip = false) {
return $flip;
},
/**
* Image options to control the source and look of file previews
*/
'image' => function ($image = null) {
return $image ?? [];
},
/**
* Optional info text setup. Info text is shown on the right (lists) or below (cards) the filename.
*/
'info' => function (string $info = null) {
return $info;
},
/**
* The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: `tiny`, `small`, `medium`, `large`, `huge`
*/
'size' => function (string $size = 'auto') {
return $size;
},
/**
* Enables/disables manual sorting
*/
'sortable' => function (bool $sortable = true) {
return $sortable;
},
/**
* Overwrites manual sorting and sorts by the given field and sorting direction (i.e. `filename desc`)
*/
'sortBy' => function (string $sortBy = null) {
return $sortBy;
},
/**
* Filters all files by template and also sets the template, which will be used for all uploads
*/
'template' => function (string $template = null) {
return $template;
},
/**
* Setup for the main text in the list or cards. By default this will display the filename.
*/
'text' => function (string $text = '{{ file.filename }}') {
return $text;
}
],
'computed' => [
'accept' => function () {
if ($this->template) {
$file = new File([
'filename' => 'tmp',
'template' => $this->template
]);
'mixins' => [
'batch',
'details',
'empty',
'headline',
'help',
'layout',
'min',
'max',
'pagination',
'parent',
'search',
'sort'
],
'props' => [
/**
* Option to switch off the upload button
*/
'create' => function (bool $create = true) {
return $create;
},
/**
* Filters pages by a query. Sorting will be disabled
*/
'query' => function (string|null $query = null) {
return $query;
},
/**
* Filters all files by template and also sets the template, which will be used for all uploads
*/
'template' => function (string|null $template = null) {
return $template;
},
/**
* Setup for the main text in the list or cards. By default this will display the filename.
*/
'text' => function ($text = '{{ file.filename }}') {
return I18n::translate($text, $text);
},
],
'computed' => [
'accept' => function () {
if ($this->template) {
$file = new File([
'filename' => 'tmp',
'parent' => $this->model(),
'template' => $this->template
]);
return $file->blueprint()->accept()['mime'] ?? '*';
}
return $file->blueprint()->acceptAttribute();
}
return null;
},
'parent' => function () {
return $this->parentModel();
},
'files' => function () {
$files = $this->parent->files()->template($this->template);
return null;
},
'parent' => function () {
return $this->parentModel();
},
'collector' => function () {
return $this->collector ??= new FilesCollector(
flip: $this->flip(),
limit: $this->limit(),
page: $this->page() ?? 1,
parent: $this->parent(),
query: $this->query(),
search: $this->searchterm(),
sortBy: $this->sortBy(),
template: $this->template(),
);
},
'models' => function () {
return $this->collector()->models();
},
'modelsPaginated' => function () {
return $this->collector()->models(paginated: true);
},
'files' => function () {
return $this->models;
},
'data' => function () {
$data = [];
$dragTextIsAbsolute = $this->model->is($this->parent) === false;
// filter out all protected files
$files = $files->filterBy('isReadable', true);
foreach ($this->modelsPaginated() as $file) {
$item = (new FileItem(
file: $file,
dragTextIsAbsolute: $dragTextIsAbsolute,
image: $this->image,
layout: $this->layout,
info: $this->info,
text: $this->text,
))->props();
if ($this->sortBy) {
$files = $files->sortBy(...$files::sortArgs($this->sortBy));
} elseif ($this->sortable === true) {
$files = $files->sortBy('sort', 'asc', 'filename', 'asc');
}
if ($this->layout === 'table') {
$item = $this->columnsValues($item, $file);
}
// flip
if ($this->flip === true) {
$files = $files->flip();
}
$data[] = $item;
}
// apply the default pagination
$files = $files->paginate([
'page' => $this->page,
'limit' => $this->limit,
'method' => 'none' // the page is manually provided
]);
return $data;
},
'total' => function () {
return $this->models()->count();
},
'errors' => function () {
$errors = [];
return $files;
},
'data' => function () {
$data = [];
if ($this->validateMax() === false) {
$errors['max'] = I18n::template('error.section.files.max.' . I18n::form($this->max), [
'max' => $this->max,
'section' => $this->headline
]);
}
// the drag text needs to be absolute when the files come from
// a different parent model
$dragTextAbsolute = $this->model->is($this->parent) === false;
if ($this->validateMin() === false) {
$errors['min'] = I18n::template('error.section.files.min.' . I18n::form($this->min), [
'min' => $this->min,
'section' => $this->headline
]);
}
foreach ($this->files as $file) {
$image = $file->panelImage($this->image);
if (empty($errors) === true) {
return [];
}
$data[] = [
'dragText' => $file->dragText('auto', $dragTextAbsolute),
'extension' => $file->extension(),
'filename' => $file->filename(),
'id' => $file->id(),
'icon' => $file->panelIcon($image),
'image' => $image,
'info' => $file->toString($this->info ?? false),
'link' => $file->panelUrl(true),
'mime' => $file->mime(),
'parent' => $file->parent()->panelPath(),
'text' => $file->toString($this->text),
'url' => $file->url(),
];
}
return [
$this->name => [
'label' => $this->headline,
'message' => $errors,
]
];
},
'pagination' => function () {
return $this->pagination();
},
'upload' => function () {
if ($this->create === false) {
return false;
}
return $data;
},
'total' => function () {
return $this->files->pagination()->total();
},
'errors' => function () {
$errors = [];
if ($this->isFull() === true) {
return false;
}
if ($this->validateMax() === false) {
$errors['max'] = I18n::template('error.section.files.max.' . I18n::form($this->max), [
'max' => $this->max,
'section' => $this->headline
]);
}
$settings = new Upload(
api: $this->parent->apiUrl(true) . '/files',
accept: $this->accept,
max: $this->max ? $this->max - $this->total : null,
preview: $this->image,
sort: $this->sortable === true ? $this->total + 1 : null,
template: $this->template,
);
if ($this->validateMin() === false) {
$errors['min'] = I18n::template('error.section.files.min.' . I18n::form($this->min), [
'min' => $this->min,
'section' => $this->headline
]);
}
return $settings->props();
}
],
// @codeCoverageIgnoreStart
'api' => function () {
return [
[
'pattern' => 'sort',
'method' => 'PATCH',
'action' => function () {
$this->section()->model()->files()->changeSort(
$this->requestBody('files'),
$this->requestBody('index')
);
if (empty($errors) === true) {
return [];
}
return [
$this->name => [
'label' => $this->headline,
'message' => $errors,
]
];
},
'link' => function () {
$modelLink = $this->model->panelUrl(true);
$parentLink = $this->parent->panelUrl(true);
if ($modelLink !== $parentLink) {
return $parentLink;
}
},
'pagination' => function () {
return $this->pagination();
},
'sortable' => function () {
if ($this->sortable === false) {
return false;
}
if ($this->sortBy !== null) {
return false;
}
if ($this->flip === true) {
return false;
}
return true;
},
'upload' => function () {
if ($this->isFull() === true) {
return false;
}
// count all uploaded files
$total = count($this->data);
$max = $this->max ? $this->max - $total : null;
if ($this->max && $total === $this->max - 1) {
$multiple = false;
} else {
$multiple = true;
}
return [
'accept' => $this->accept,
'multiple' => $multiple,
'max' => $max,
'api' => $this->parent->apiUrl(true) . '/files',
'attributes' => array_filter([
'template' => $this->template
])
];
}
],
'toArray' => function () {
return [
'data' => $this->data,
'errors' => $this->errors,
'options' => [
'accept' => $this->accept,
'apiUrl' => $this->parent->apiUrl(true),
'empty' => $this->empty,
'headline' => $this->headline,
'help' => $this->help,
'layout' => $this->layout,
'link' => $this->link,
'max' => $this->max,
'min' => $this->min,
'size' => $this->size,
'sortable' => $this->sortable,
'upload' => $this->upload
],
'pagination' => $this->pagination
];
}
return true;
}
],
[
'pattern' => 'delete',
'method' => 'DELETE',
'action' => function () {
return $this->section()->deleteSelected(
ids: $this->requestBody('ids'),
);
}
]
];
},
// @codeCoverageIgnoreEnd
'toArray' => function () {
return [
'data' => $this->data,
'errors' => $this->errors,
'options' => [
'accept' => $this->accept,
'apiUrl' => $this->parent->apiUrl(true) . '/sections/' . $this->name,
'batch' => $this->batch,
'columns' => $this->columnsWithTypes(),
'empty' => $this->empty,
'headline' => $this->headline,
'help' => $this->help,
'layout' => $this->layout,
'link' => $this->link(),
'max' => $this->max,
'min' => $this->min,
'search' => $this->search,
'size' => $this->size,
'sortable' => $this->sortable,
'upload' => $this->upload
],
'pagination' => $this->pagination
];
}
];