Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.83% covered (warning)
70.83%
17 / 24
70.83% covered (warning)
70.83%
17 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
Registrar
70.83% covered (warning)
70.83%
17 / 24
70.83% covered (warning)
70.83%
17 / 24
38.29
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 option
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 field
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fieldSchema
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 nodeSchema
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 collectionSchema
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 node
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 section
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 collection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dashboardCard
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 user
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 role
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 allow
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 migrations
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sql
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 assets
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 blockType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 control
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 templates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 panelPage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 css
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 js
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 register
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 routes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Plugin;
6
7use Celema\Container\Entry;
8use Closure;
9use Cosray\Bootstrap;
10use Cosray\Collection\Ref;
11use Cosray\Collection\Schema\Handler as CollectionHandler;
12use Cosray\Config;
13use Cosray\Contract\Block;
14use Cosray\Contract\DashboardCard;
15use Cosray\Field\Schema\Handler as FieldHandler;
16use Cosray\Node\Schema\Handler as NodeHandler;
17use Cosray\Section;
18
19/**
20 * Registration facade handed to each plugin during boot.
21 */
22final class Registrar
23{
24    public function __construct(
25        private readonly Bootstrap $bootstrap,
26        public readonly string $id,
27        public readonly Config $config,
28    ) {}
29
30    /**
31     * An app-config option from this plugin's namespace: option('currency')
32     * reads the '{plugin-id}.currency' settings key. Returns $default when
33     * the app configures nothing — a plugin must work with zero
34     * configuration, so defaults live at the read site, not in the app.
35     */
36    public function option(string $key, mixed $default = null): mixed
37    {
38        return $this->config->get("{$this->id}.{$key}", $default);
39    }
40
41    /** @param class-string<\Cosray\Field\Field> $class */
42    public function field(string $class, string ...$aliases): void
43    {
44        $this->bootstrap->fields()->add($class, ...$aliases);
45    }
46
47    /** @param class-string $attribute */
48    public function fieldSchema(string $attribute, FieldHandler $handler): void
49    {
50        $this->bootstrap->fieldSchemas()->register($attribute, $handler);
51    }
52
53    /** @param class-string $attribute */
54    public function nodeSchema(string $attribute, NodeHandler $handler): void
55    {
56        $this->bootstrap->nodeSchemas()->register($attribute, $handler);
57    }
58
59    /** @param class-string $attribute */
60    public function collectionSchema(string $attribute, CollectionHandler $handler): void
61    {
62        $this->bootstrap->collectionSchemas()->registry()->register($attribute, $handler);
63    }
64
65    /** @param class-string $class */
66    public function node(string $class): void
67    {
68        $this->bootstrap->node($class);
69    }
70
71    public function section(string $name): Section
72    {
73        return $this->bootstrap->section($name);
74    }
75
76    /** @param class-string $class */
77    public function collection(string $class): Ref
78    {
79        return $this->bootstrap->collection($class);
80    }
81
82    /** @param class-string<DashboardCard>|DashboardCard $card */
83    public function dashboardCard(string|DashboardCard $card): void
84    {
85        $this->bootstrap->dashboard->add($card);
86    }
87
88    /** @param class-string<\Cosray\User> $class */
89    public function user(string $class): void
90    {
91        $this->bootstrap->user($class);
92    }
93
94    public function role(string $name, string $label): void
95    {
96        $this->bootstrap->policy()->role($name, $label);
97    }
98
99    /**
100     * Principals are 'everyone', 'authenticated', 'role:{name}',
101     * 'type:{handle}' and 'user:{uid}'.
102     */
103    public function allow(string $principal, string ...$permissions): void
104    {
105        $this->bootstrap->policy()->allow($principal, ...$permissions);
106    }
107
108    public function migrations(string $dir): void
109    {
110        $this->bootstrap->addMigrations($dir);
111    }
112
113    public function sql(string $dir): void
114    {
115        $this->bootstrap->addSql($dir);
116    }
117
118    /**
119     * Serve prebuilt plugin assets from $dir under
120     * `{panel}/vendor/{pluginId}/...`.
121     */
122    public function assets(string $dir): void
123    {
124        $this->bootstrap->addAssets($this->id, $dir);
125    }
126
127    /**
128     * Add a block type to the default offer list of Blocks fields.
129     *
130     * @param class-string<Block> $class
131     */
132    public function blockType(string $class): void
133    {
134        $this->bootstrap->blockType($class);
135    }
136
137    /**
138     * Register a named custom control rendered by a custom element.
139     * Fields (from any plugin or the project) can then use the name in
140     * their control() definition. Later registrations win, so a plugin
141     * may replace a built-in editor. The module is served from this
142     * plugin's asset dir.
143     */
144    public function control(string $name, string $tag, string $module): void
145    {
146        $this->bootstrap->controls()->register($name, $tag, "{$this->id}/{$module}");
147    }
148
149    /**
150     * Register $dir as template namespace `{pluginId}:` for the given
151     * renderer ('panel' or 'view'). Templates are addressed as
152     * '{pluginId}:template/path'.
153     */
154    public function templates(string $dir, string $renderer = 'panel'): void
155    {
156        $this->bootstrap->addTemplates($this->id, $dir, $renderer);
157    }
158
159    /**
160     * Register a page inside the panel chrome: session, auth and the
161     * panel renderer are applied like for built-in pages. The endpoint
162     * controller should extend Cosray\Controller\Panel\Panel and
163     * return $this->context([...]) so the shell gets its data.
164     *
165     * @param mixed $endpoint route endpoint, e.g. [Controller::class, 'method']
166     */
167    public function panelPage(string $pattern, mixed $endpoint, string $template, string $name): void
168    {
169        $this->bootstrap->addPanelPage($pattern, $endpoint, $template, "{$this->id}.{$name}");
170    }
171
172    /**
173     * A stylesheet loaded on every panel page. App-wide chrome styling
174     * only — field controls load their assets lazily via element
175     * descriptors.
176     */
177    public function css(string $url): void
178    {
179        $this->bootstrap->panelExtras()->addCss($url);
180    }
181
182    /**
183     * A script loaded once per full panel document and NOT re-run across
184     * htmx navigations: write delegated, idempotent listeners — see
185     * "Panel scripts across navigation" in the README.
186     */
187    public function js(string $url, bool $module = true): void
188    {
189        $this->bootstrap->panelExtras()->addJs($url, $module);
190    }
191
192    /**
193     * @param non-empty-string $key
194     * @param class-string|object $value
195     */
196    public function register(string $key, object|string $value): Entry
197    {
198        return $this->bootstrap->addService($key, $value);
199    }
200
201    /** @param Closure(\Celema\Core\App): void $routes */
202    public function routes(Closure $routes): void
203    {
204        $this->bootstrap->addRoutes($routes);
205    }
206}