Skip to content

Commit 3f0b56d

Browse files
committed
feat: Models picker dialogs classes
1 parent fe6ad7c commit 3f0b56d

7 files changed

+1065
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Kirby\Panel\Controller\Dialog;
4+
5+
use Kirby\Cms\File;
6+
use Kirby\Cms\ModelWithContent;
7+
use Kirby\Panel\Collector\FilesCollector;
8+
9+
/**
10+
* Controls the Panel dialog for selecting files
11+
*
12+
* @package Kirby Panel
13+
* @author Bastian Allgeier <[email protected]>
14+
* @link https://getkirby.com
15+
* @copyright Bastian Allgeier
16+
* @license https://getkirby.com/license
17+
* @since 6.0.0
18+
* @unstable
19+
*/
20+
class FilesPickerDialogController extends ModelsPickerDialogController
21+
{
22+
protected const TYPE = 'files';
23+
24+
protected FilesCollector $collector;
25+
26+
public function __construct(
27+
ModelWithContent $model,
28+
bool $hasSearch = true,
29+
array|null $image = [],
30+
string|null $info = null,
31+
string $layout = 'list',
32+
public int|null $limit = null,
33+
int|null $max = null,
34+
bool $multiple = true,
35+
public string|null $query = null,
36+
string|null $size = null,
37+
string|null $text = null
38+
) {
39+
parent::__construct(
40+
model: $model,
41+
hasSearch: $hasSearch,
42+
image: $image,
43+
info: $info,
44+
layout: $layout,
45+
max: $max,
46+
multiple: $multiple,
47+
size: $size,
48+
text: $text,
49+
);
50+
}
51+
52+
public function collector(): FilesCollector
53+
{
54+
return $this->collector ??= new FilesCollector(
55+
limit: $this->limit,
56+
page: $this->page,
57+
parent: $this->model,
58+
query: $this->query(),
59+
search: $this->search,
60+
);
61+
}
62+
63+
public function find(string $id): File|null
64+
{
65+
return $this->kirby->file($id, $this->model);
66+
}
67+
68+
public function query(): string
69+
{
70+
if ($this->query !== null) {
71+
return $this->query;
72+
}
73+
74+
if ($this->model instanceof File) {
75+
return 'file.siblings';
76+
}
77+
78+
return $this->model::CLASS_ALIAS . '.files';
79+
}
80+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Kirby\Panel\Controller\Dialog;
4+
5+
use Kirby\Cms\ModelWithContent;
6+
use Kirby\Panel\Collector\ModelsCollector;
7+
use Kirby\Panel\Controller\DialogController;
8+
use Kirby\Panel\Ui\Dialog;
9+
use Kirby\Toolkit\Str;
10+
11+
/**
12+
* Controls a Panel dialog for selecting
13+
* models (pages, files, users)
14+
*
15+
* @package Kirby Panel
16+
* @author Bastian Allgeier <[email protected]>
17+
* @link https://getkirby.com
18+
* @copyright Bastian Allgeier
19+
* @license https://getkirby.com/license
20+
* @since 6.0.0
21+
* @unstable
22+
*/
23+
abstract class ModelsPickerDialogController extends DialogController
24+
{
25+
protected const TYPE = 'models';
26+
27+
public int $page = 1;
28+
public string|null $search = null;
29+
30+
public function __construct(
31+
public ModelWithContent $model,
32+
public bool $hasSearch = true,
33+
public array|null $image = [],
34+
public string|null $info = null,
35+
public string $layout = 'list',
36+
public int|null $max = null,
37+
public bool $multiple = true,
38+
public string|null $size = null,
39+
public string|null $text = null,
40+
) {
41+
parent::__construct();
42+
43+
$this->page = $this->request->get('page', 1);
44+
$this->search = $this->request->get('search');
45+
}
46+
47+
/**
48+
* Returns the collector to retrieve the
49+
* queried, searched, sorted and paginated models
50+
*/
51+
abstract public function collector(): ModelsCollector;
52+
53+
/**
54+
* Finds a model by its ID
55+
*/
56+
abstract public function find(string $id): ModelWithContent|null;
57+
58+
/**
59+
* Returns the picker data for a model
60+
*/
61+
public function item(ModelWithContent $model): array
62+
{
63+
return $model->panel()->pickerData([
64+
'image' => $this->image,
65+
'info' => $this->info,
66+
'layout' => $this->layout,
67+
'text' => $this->text,
68+
]);
69+
}
70+
71+
/**
72+
* Fetches all items for the picker
73+
*/
74+
public function items(): array
75+
{
76+
return $this->collector()->models(paginated: true)->values(
77+
fn (ModelWithContent $model) => $this->item($model)
78+
);
79+
}
80+
81+
/**
82+
* Load the picker dialog
83+
*/
84+
public function load(): Dialog
85+
{
86+
return new Dialog(...$this->props());
87+
}
88+
89+
/**
90+
* Returns the props for the dialog
91+
*/
92+
public function props(): array
93+
{
94+
return [
95+
'component' => 'k-' . static::TYPE . '-picker-dialog',
96+
'hasSearch' => $this->hasSearch,
97+
'items' => $this->items(),
98+
'layout' => $this->layout,
99+
'max' => $this->max,
100+
'multiple' => $this->multiple,
101+
'pagination' => $this->collector()->pagination()->toArray(),
102+
'size' => $this->size,
103+
'value' => Str::split($this->request->get('value', ''))
104+
];
105+
}
106+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
namespace Kirby\Panel\Controller\Dialog;
4+
5+
use Kirby\Cms\ModelWithContent;
6+
use Kirby\Cms\Page;
7+
use Kirby\Cms\Pages;
8+
use Kirby\Cms\Site;
9+
use Kirby\Panel\Collector\PagesCollector;
10+
11+
/**
12+
* Controls the Panel dialog for selecting pages
13+
*
14+
* @package Kirby Panel
15+
* @author Bastian Allgeier <[email protected]>
16+
* @link https://getkirby.com
17+
* @copyright Bastian Allgeier
18+
* @license https://getkirby.com/license
19+
* @since 6.0.0
20+
* @unstable
21+
*/
22+
class PagesPickerDialogController extends ModelsPickerDialogController
23+
{
24+
protected const TYPE = 'pages';
25+
26+
protected PagesCollector $collector;
27+
28+
public function __construct(
29+
ModelWithContent $model,
30+
bool $hasSearch = true,
31+
array|null $image = [],
32+
string|null $info = null,
33+
string $layout = 'list',
34+
public int|null $limit = null,
35+
int|null $max = null,
36+
bool $multiple = true,
37+
public string|null $query = null,
38+
public bool|null $subpages = true,
39+
string|null $size = null,
40+
string|null $text = null,
41+
) {
42+
parent::__construct(
43+
model: $model,
44+
hasSearch: $hasSearch,
45+
image: $image,
46+
info: $info,
47+
layout: $layout,
48+
max: $max,
49+
multiple: $multiple,
50+
size: $size,
51+
text: $text
52+
);
53+
}
54+
55+
public function collector(): PagesCollector
56+
{
57+
return $this->collector ??= new class (
58+
limit: $this->limit,
59+
page: $this->page,
60+
parent: $this->model,
61+
query: $this->query(),
62+
search: $this->search,
63+
) extends PagesCollector {
64+
// if the query only returns a site or page object
65+
// instead of a pages collection, use its children
66+
protected function collectByQuery(): Pages
67+
{
68+
$pages = $this->parent()->query($this->query);
69+
70+
if ($pages instanceof Site || $pages instanceof Page) {
71+
return $pages->children();
72+
}
73+
74+
return $pages ?? new Pages([]);
75+
}
76+
};
77+
}
78+
79+
public function find(string $id): Page|null
80+
{
81+
return $this->kirby->page($id);
82+
}
83+
84+
/**
85+
* Returns the parent model object that
86+
* is currently selected in the page picker.
87+
* It normally starts at the site, but can
88+
* also be any subpage. When a query is given
89+
* and subpage navigation is deactivated,
90+
* there will be no model available at all.
91+
*/
92+
public function parent(): array|null
93+
{
94+
// no subpages navigation = no current parent
95+
if ($this->subpages === false) {
96+
return null;
97+
}
98+
99+
if ($parent = $this->request->get('parent')) {
100+
$parent = $this->find($parent);
101+
} else {
102+
$parent = $this->root();
103+
}
104+
105+
if ($parent === null) {
106+
return null;
107+
}
108+
109+
// the current parent is the site or the top-most page has been reached.
110+
// the missing id indicates that there's nothing above
111+
if (
112+
$parent instanceof Site ||
113+
$parent->id() === $this->root()->id()
114+
) {
115+
return [
116+
'id' => null,
117+
'parent' => null,
118+
'title' => $parent->title()->value()
119+
];
120+
}
121+
122+
return [
123+
'id' => $parent->id(),
124+
'parent' => $parent->parentModel()->id(),
125+
'title' => $parent->title()->value()
126+
];
127+
}
128+
129+
public function props(): array
130+
{
131+
return [
132+
...parent::props(),
133+
'parent' => $this->parent()
134+
];
135+
}
136+
137+
public function query(): string
138+
{
139+
// if a current parent is present, use its children
140+
if ($current = $this->request->get('parent')) {
141+
return 'page("' . $current . '").children';
142+
}
143+
144+
return $this->query ?? 'site.children';
145+
}
146+
147+
/**
148+
* Calculates the top-most model (page or site)
149+
* that can be accessed when navigating
150+
* through pages.
151+
*/
152+
public function root(): Page|Site
153+
{
154+
if ($this->query === null) {
155+
return $this->site;
156+
}
157+
158+
$parent = $this->model->query($this->query);
159+
160+
if ($parent instanceof Pages) {
161+
$parent = $parent->parent();
162+
}
163+
164+
return $parent ?? $this->site;
165+
}
166+
}

0 commit comments

Comments
 (0)