mirror of
https://code.castopod.org/adaures/castopod
synced 2025-06-06 18:31:05 +00:00
docs(plugins): fill up rest of manifest and hooks reference + creating a plugin
This commit is contained in:
parent
cc6495dc7c
commit
e417d45b14
@ -12,15 +12,15 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.7.0",
|
"@astrojs/check": "^0.7.0",
|
||||||
"@astrojs/starlight": "^0.22.4",
|
"@astrojs/starlight": "^0.24.0",
|
||||||
"@astrojs/starlight-tailwind": "^2.0.2",
|
"@astrojs/starlight-tailwind": "^2.0.3",
|
||||||
"@astrojs/tailwind": "^5.1.0",
|
"@astrojs/tailwind": "^5.1.0",
|
||||||
"@fontsource/inter": "^5.0.18",
|
"@fontsource/inter": "^5.0.18",
|
||||||
"@fontsource/rubik": "^5.0.20",
|
"@fontsource/rubik": "^5.0.20",
|
||||||
"astro": "^4.8.6",
|
"astro": "^4.10.1",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"cssnano": "^7.0.1",
|
"cssnano": "^7.0.2",
|
||||||
"postcss-preset-env": "^9.5.13",
|
"postcss-preset-env": "^9.5.14",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
|
963
docs/pnpm-lock.yaml
generated
963
docs/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,24 +2,29 @@
|
|||||||
title: Creating a Plugin
|
title: Creating a Plugin
|
||||||
---
|
---
|
||||||
|
|
||||||
import { FileTree, Steps } from "@astrojs/starlight/components";
|
import { FileTree, Steps, Badge } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
In order to get started, you first need to
|
In order to get started, you first need to
|
||||||
[setup your Castopod dev environment](https://code.castopod.org/adaures/castopod/-/blob/develop/CONTRIBUTING-DEV.md).
|
[setup your Castopod dev environment](https://code.castopod.org/adaures/castopod/-/blob/develop/CONTRIBUTING-DEV.md).
|
||||||
|
|
||||||
## Using the create command
|
## 1. Create the plugin folder
|
||||||
|
|
||||||
To quickly get you started, you can create a plugin using the following CLI
|
You'll first need to create your [plugin folder](./#plugin-folder-structure) in
|
||||||
command:
|
the `plugins/` directory.
|
||||||
|
|
||||||
|
### Using the create command <Badge text="Recommended" size="small" />
|
||||||
|
|
||||||
|
To quickly get you started, you can have a folder generated for you using the
|
||||||
|
following CLI command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
php spark plugins:create
|
php spark plugins:create
|
||||||
```
|
```
|
||||||
|
|
||||||
👉 Follow the CLI instructions: you will be prompted for metadata and hooks
|
👉 You will be prompted for metadata and hooks usage to have a skeleton plugin
|
||||||
definitions to generate the [plugin folder](./#plugin-folder-structure) for you.
|
project generated for you!
|
||||||
|
|
||||||
## Manual setup
|
### Manual setup
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
1. create a plugin folder inside a vendor directory
|
1. create a plugin folder inside a vendor directory
|
||||||
@ -54,3 +59,58 @@ definitions to generate the [plugin folder](./#plugin-folder-structure) for you.
|
|||||||
</FileTree>
|
</FileTree>
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
|
## 2. Build your plugin
|
||||||
|
|
||||||
|
Now that your plugin folder is set, you can start working on your Plugin's logic
|
||||||
|
by implementing [the hooks](./hooks) needed.
|
||||||
|
|
||||||
|
### Settings forms
|
||||||
|
|
||||||
|
You can prompt users for data through settings forms.
|
||||||
|
|
||||||
|
These forms can be built declaratively using the
|
||||||
|
[settings attribute](./manifest#settings) in your manifest.
|
||||||
|
|
||||||
|
```json
|
||||||
|
// manifest.json
|
||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"general": {
|
||||||
|
"field-key": {
|
||||||
|
"type": "text",
|
||||||
|
"label": "Enter a text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"podcast": {
|
||||||
|
"field-key": {
|
||||||
|
"type": "text",
|
||||||
|
"label": "Enter a text for this podcast"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"episode": {
|
||||||
|
"field-key": {
|
||||||
|
"type": "type",
|
||||||
|
"label": "Enter a text for this episode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example will generate settings forms at 3 levels:
|
||||||
|
|
||||||
|
- `general`: a general form to prompt data to be used by the plugin
|
||||||
|
- `podcast`: a form for each podcast to prompt for podcast specific data
|
||||||
|
- `episode`: a form for each episode to prompt for episode specific data
|
||||||
|
|
||||||
|
The data can then be accessed in the Plugin class methods via helper methods
|
||||||
|
taking in the field key:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$this->getGeneralSetting('field-key');
|
||||||
|
|
||||||
|
$this->getPodcastSetting($podcast->id, 'field-key');
|
||||||
|
|
||||||
|
$this->getEpisodeSetting($episode->id, 'field-key');
|
||||||
|
```
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: BasePlugin
|
|
||||||
---
|
|
@ -2,8 +2,8 @@
|
|||||||
title: Hooks reference
|
title: Hooks reference
|
||||||
---
|
---
|
||||||
|
|
||||||
Hooks are methods that live in the Plugin class, they are executed in parts of
|
Hooks are methods of the Plugin class, they are executed in parts of the
|
||||||
the Castopod codebase.
|
Castopod codebase.
|
||||||
|
|
||||||
## List
|
## List
|
||||||
|
|
||||||
@ -17,6 +17,11 @@ the Castopod codebase.
|
|||||||
|
|
||||||
### rssBeforeChannel
|
### rssBeforeChannel
|
||||||
|
|
||||||
|
This hook is executed just before rendering the `<channel>` tag in the Podcast
|
||||||
|
RSS feed using the given Podcast object.
|
||||||
|
|
||||||
|
Here is a good place to alter the Podcast object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function rssBeforeChannel(Podcast $podcast): void
|
public function rssBeforeChannel(Podcast $podcast): void
|
||||||
{
|
{
|
||||||
@ -26,8 +31,13 @@ public function rssBeforeChannel(Podcast $podcast): void
|
|||||||
|
|
||||||
### rssAfterChannel
|
### rssAfterChannel
|
||||||
|
|
||||||
|
This hook is executed after rendering all of the `<channel>` tags in the Podcast
|
||||||
|
RSS feed.
|
||||||
|
|
||||||
|
Here is a good place to add new tags to the generated channel.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $rss): void
|
public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void
|
||||||
{
|
{
|
||||||
// …
|
// …
|
||||||
}
|
}
|
||||||
@ -35,6 +45,11 @@ public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $rss): void
|
|||||||
|
|
||||||
### rssBeforeItem
|
### rssBeforeItem
|
||||||
|
|
||||||
|
This hook is executed before rendering an `<item>` tag in the Podcast RSS feed
|
||||||
|
using the given Episode object.
|
||||||
|
|
||||||
|
Here is a good place to alter the Episode object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function rssBeforeItem(Episode $episode): void
|
public function rssBeforeItem(Episode $episode): void
|
||||||
{
|
{
|
||||||
@ -44,8 +59,13 @@ public function rssBeforeItem(Episode $episode): void
|
|||||||
|
|
||||||
### rssAfterItem
|
### rssAfterItem
|
||||||
|
|
||||||
|
This hook is executed after rendering an `<item>`'s tags in the Podcast RSS
|
||||||
|
feed.
|
||||||
|
|
||||||
|
Here is a good place to add new tags to the generated item.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function rssAfterItem(Epsiode $episode, SimpleRSSElement $rss): void
|
public function rssAfterItem(Epsiode $episode, SimpleRSSElement $item): void
|
||||||
{
|
{
|
||||||
// …
|
// …
|
||||||
}
|
}
|
||||||
@ -53,6 +73,11 @@ public function rssAfterItem(Epsiode $episode, SimpleRSSElement $rss): void
|
|||||||
|
|
||||||
### siteHead
|
### siteHead
|
||||||
|
|
||||||
|
This hook is executed in the public pages' `<head>` tag.
|
||||||
|
|
||||||
|
This is a good place to add meta tags and third-party scripts to Castopod's
|
||||||
|
public pages.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function siteHead(): void
|
public function siteHead(): void
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: Castopod Plugins
|
title: Castopod Plugins
|
||||||
---
|
---
|
||||||
|
|
||||||
import { FileTree, Aside } from "@astrojs/starlight/components";
|
import { FileTree, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Plugins are ways to extend Castopod's core features.
|
Plugins are ways to extend Castopod's core features.
|
||||||
|
|
||||||
@ -16,13 +16,13 @@ Plugins are ways to extend Castopod's core features.
|
|||||||
- fr.json
|
- fr.json
|
||||||
- …
|
- …
|
||||||
- icon.svg
|
- icon.svg
|
||||||
- [manifest.json](./manifest) // required
|
- manifest.json // required
|
||||||
- [Plugin.php](#plugin-class) // required
|
- Plugin.php // required
|
||||||
- README.md
|
- README.md
|
||||||
|
|
||||||
</FileTree>
|
</FileTree>
|
||||||
|
|
||||||
Plugins reside in the `plugins` folder under a **vendor** folder, ie. the
|
Plugins reside in the `plugins/` directory under a `vendor/` folder, ie. the
|
||||||
organisation or person who authored the plugin.
|
organisation or person who authored the plugin.
|
||||||
|
|
||||||
<FileTree>
|
<FileTree>
|
||||||
@ -35,15 +35,16 @@ organisation or person who authored the plugin.
|
|||||||
|
|
||||||
</FileTree>
|
</FileTree>
|
||||||
|
|
||||||
### manifest.json (required)
|
### Plugin manifest (required)
|
||||||
|
|
||||||
The plugin manifest is a JSON file containing your plugin's metadata and
|
The plugin manifest is a JSON file containing the plugin's metadata and
|
||||||
permissions.
|
declarations.
|
||||||
|
|
||||||
This file will determine whether a plugin is valid or not. The minimal required
|
This file will determine whether a plugin is valid or not. The minimal required
|
||||||
data being:
|
data being:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
// manifest.json
|
||||||
{
|
{
|
||||||
"name": "acme/hello-world",
|
"name": "acme/hello-world",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
@ -52,12 +53,12 @@ data being:
|
|||||||
|
|
||||||
Checkout the [manifest.json reference](./manifest).
|
Checkout the [manifest.json reference](./manifest).
|
||||||
|
|
||||||
<h3 id="plugin-class">Plugin class (required)</h3>
|
### Plugin class (required)
|
||||||
|
|
||||||
This is where your plugin's logic will live.
|
This is where the plugin's logic lives.
|
||||||
|
|
||||||
The Plugin class must extend Castopod's BasePlugin class and implement one or
|
The Plugin class extends Castopod's BasePlugin class and implements one or more
|
||||||
multiple [Hooks](./hooks) (methods).
|
[Hooks](./hooks) (methods) intended to be run throughout Castopod's codebase.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Plugin.php
|
// Plugin.php
|
||||||
@ -69,7 +70,11 @@ use Modules\Plugins\Core\BasePlugin;
|
|||||||
|
|
||||||
class AcmeHelloWorldPlugin extends BasePlugin
|
class AcmeHelloWorldPlugin extends BasePlugin
|
||||||
{
|
{
|
||||||
|
// this rssBeforeChannel method is a Hook
|
||||||
|
public function rssBeforeChannel(Podcast $podcast): void
|
||||||
|
{
|
||||||
// …
|
// …
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -85,14 +90,14 @@ For example, a plugin living under the `acme/hello-world` folder must be named
|
|||||||
|
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
### README.md
|
### Plugin README
|
||||||
|
|
||||||
The `README.md` file is loaded into the plugin's view page for the user to
|
The `README.md` file is loaded into the plugin's view page for the user to read
|
||||||
read.
|
through.
|
||||||
It should be used for any additional information to help guide the user in using
|
It should be used for any additional information to help guide the user in using
|
||||||
the plugin.
|
the plugin.
|
||||||
|
|
||||||
### icon.svg
|
### Plugin icon
|
||||||
|
|
||||||
The plugin icon is displayed next to its title, it is an SVG file intended to
|
The plugin icon is displayed next to its title, it is an SVG file intended to
|
||||||
give a graphical representation of the plugin.
|
give a graphical representation of the plugin.
|
||||||
@ -101,8 +106,8 @@ The icon should be squared, and be legible in a 64px by 64px circle.
|
|||||||
|
|
||||||
### Internationalization (i18n)
|
### Internationalization (i18n)
|
||||||
|
|
||||||
Translation strings live under the `i18n` folder. Translation files are JSON
|
Plugins can be translated. Translation strings live inside the `i18n` folder.
|
||||||
files named as locale keys:
|
Translation files are JSON files named as locale keys:
|
||||||
|
|
||||||
<FileTree>
|
<FileTree>
|
||||||
|
|
||||||
@ -118,16 +123,45 @@ Supported locales are:
|
|||||||
`br`,`ca`,`de`,`en`,`es`,`fr`,`nn-no`,`pl`,`pt-br`,`sr-latn`,`zh-hans`.
|
`br`,`ca`,`de`,`en`,`es`,`fr`,`nn-no`,`pl`,`pt-br`,`sr-latn`,`zh-hans`.
|
||||||
|
|
||||||
The translation strings allow you to translate the title, description and
|
The translation strings allow you to translate the title, description and
|
||||||
settings keys.
|
settings keys (ie. labels, hints, helpers, etc.).
|
||||||
|
|
||||||
```json
|
<Tabs>
|
||||||
{
|
<TabItem label="English">
|
||||||
|
```json
|
||||||
|
// i18n/en.json
|
||||||
|
{
|
||||||
"title": "Hello, World!",
|
"title": "Hello, World!",
|
||||||
"description": "A Castopod plugin to greet the world!",
|
"description": "A Castopod plugin to greet the world!",
|
||||||
"settings": {
|
"settings": {
|
||||||
"general": {},
|
"general": {
|
||||||
|
"field-key": {
|
||||||
|
"label": "Enter a text",
|
||||||
|
"hint": "You can enter any type of character."
|
||||||
|
}
|
||||||
|
},
|
||||||
"podcast": {},
|
"podcast": {},
|
||||||
"episode": {}
|
"episode": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="French">
|
||||||
|
```json
|
||||||
|
// i18n/fr.json
|
||||||
|
{
|
||||||
|
"title": "Bonjour, le Monde !",
|
||||||
|
"description": "Un plugin castopod pour saluer le monde !",
|
||||||
|
"settings": {
|
||||||
|
"general": {
|
||||||
|
"field-key": {
|
||||||
|
"label": "Saisissez un texte",
|
||||||
|
"hint": "Vous pouvez saisir n'importe quel type de caractère."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"podcast": {},
|
||||||
|
"episode": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
@ -7,7 +7,14 @@ a JSON file.
|
|||||||
|
|
||||||
### name (required)
|
### name (required)
|
||||||
|
|
||||||
The plugin name, including 'vendor-name/' prefix.
|
The plugin name, including 'vendor-name/' prefix. Examples:
|
||||||
|
|
||||||
|
- acme/hello-world
|
||||||
|
- adaures/click
|
||||||
|
|
||||||
|
The name must be lowercase and consist of words separated by `-`, `.` or `_`.
|
||||||
|
The complete name should match
|
||||||
|
`^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*$`.
|
||||||
|
|
||||||
### version (required)
|
### version (required)
|
||||||
|
|
||||||
@ -15,8 +22,8 @@ The plugin's semantic version (eg. 1.0.0) - see https://semver.org/
|
|||||||
|
|
||||||
### description
|
### description
|
||||||
|
|
||||||
The plugin's description. This helps people discover your plugin as it's listed
|
The plugin's description. This helps people discover your plugin when listed in
|
||||||
in repositories
|
repositories.
|
||||||
|
|
||||||
### authors
|
### authors
|
||||||
|
|
||||||
@ -25,7 +32,7 @@ a required "name" field and optional "email" and "url" fields:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Jean D'eau",
|
"name": "Jean Deau",
|
||||||
"email": "jean.deau@example.com",
|
"email": "jean.deau@example.com",
|
||||||
"url": "https://example.com/"
|
"url": "https://example.com/"
|
||||||
}
|
}
|
||||||
@ -34,7 +41,7 @@ a required "name" field and optional "email" and "url" fields:
|
|||||||
Or you can shorten the object into a single string:
|
Or you can shorten the object into a single string:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"Jean D'eau <jean.deau@example.com> (https://example.com/)"
|
"Jean Deau <jean.deau@example.com> (https://example.com/)"
|
||||||
```
|
```
|
||||||
|
|
||||||
### homepage
|
### homepage
|
||||||
@ -43,17 +50,79 @@ The URL to the project homepage.
|
|||||||
|
|
||||||
### license
|
### license
|
||||||
|
|
||||||
You should specify a license for your plugin so that people know how they are
|
Specify a license for your plugin so that people know how they are permitted to
|
||||||
permitted to use it, and any restrictions you're placing on it.
|
use it, and any restrictions you're placing on it.
|
||||||
|
|
||||||
### private
|
### private
|
||||||
|
|
||||||
|
Whether or not to publish the plugin in public directories. If set to `true`,
|
||||||
|
directories should refuse to publish the plugin.
|
||||||
|
|
||||||
### keywords
|
### keywords
|
||||||
|
|
||||||
|
Array of strings to help your plugin get discovered when listed in repositories.
|
||||||
|
|
||||||
### hooks
|
### hooks
|
||||||
|
|
||||||
|
List of hooks used by the plugin. If the hook is not specified, Castopod will
|
||||||
|
not run it.
|
||||||
|
|
||||||
### settings
|
### settings
|
||||||
|
|
||||||
|
Declare settings forms for persisting user data. The plugin's settings forms can
|
||||||
|
be declared at three levels: `general`, `podcast`, and `episode`.
|
||||||
|
|
||||||
|
Each level accepts one or more fields, identified by a key.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"general": { // general settings form
|
||||||
|
"field-key": {
|
||||||
|
"type": "text", // default field type: a text input
|
||||||
|
"label": "Enter a text"
|
||||||
|
},
|
||||||
|
…
|
||||||
|
},
|
||||||
|
"podcast": {…}, // settings form for each podcast
|
||||||
|
"episode": {…}, // settings form for each episode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `general`, `podcast`, and `episode` settings are of `Fields` object with
|
||||||
|
each property being a field key and the value being a `Field` object.
|
||||||
|
|
||||||
|
#### Field object
|
||||||
|
|
||||||
|
A field is a form element:
|
||||||
|
|
||||||
|
| Property | Type | Note |
|
||||||
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
||||||
|
| `type` | `checkbox` \| `datetime` \| `email` \| `markdown` \| `number` \| `radio-group` \| `select-multiple` \| `select` \| `text` \| `textarea` \| `toggler` \| `url` | Default is `text` |
|
||||||
|
| `label` (required) | `string` | Can be translated (see i18n) |
|
||||||
|
| `hint` | `string` | Can be translated (see i18n) |
|
||||||
|
| `helper` | `string` | Can be translated (see i18n) |
|
||||||
|
| `optional` | `boolean` | Default is `false` |
|
||||||
|
| `options` | `Options` | Required for `radio-group`, `select-multiple`, and `select` types. |
|
||||||
|
|
||||||
|
#### Options object
|
||||||
|
|
||||||
|
The `Options` object properties are option keys and the value is an `Option`.
|
||||||
|
|
||||||
|
##### Option object
|
||||||
|
|
||||||
|
| Property | Type | Note |
|
||||||
|
| ------------------ | -------- | ---------------------------- |
|
||||||
|
| `label` (required) | `string` | Can be translated (see i18n) |
|
||||||
|
| `hint` | `string` | Can be translated (see i18n) |
|
||||||
|
|
||||||
### files
|
### files
|
||||||
|
|
||||||
|
Array of file patterns that describes the entries to be included when your
|
||||||
|
plugin is installed.
|
||||||
|
|
||||||
### repository
|
### repository
|
||||||
|
|
||||||
|
Repository where the plugin's code lives. Helpful for people who want to
|
||||||
|
contribute.
|
||||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Modules\Plugins\Manifest;
|
namespace Modules\Plugins\Manifest;
|
||||||
|
|
||||||
|
use Override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $key
|
* @property string $key
|
||||||
* @property 'text'|'email'|'url'|'markdown'|'number'|'switch' $type
|
* @property 'text'|'email'|'url'|'markdown'|'number'|'switch' $type
|
||||||
@ -45,6 +47,22 @@ class Field extends ManifestObject
|
|||||||
*/
|
*/
|
||||||
protected array $options = [];
|
protected array $options = [];
|
||||||
|
|
||||||
|
#[Override]
|
||||||
|
public function loadData(array $data): void
|
||||||
|
{
|
||||||
|
if (array_key_exists('options', $data)) {
|
||||||
|
$newOptions = [];
|
||||||
|
foreach ($data['options'] as $key => $option) {
|
||||||
|
$option['value'] = $key;
|
||||||
|
$newOptions[] = $option;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['options'] = $newOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::loadData($data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{label:string,value:string,hint:string}[]
|
* @return array{label:string,value:string,hint:string}[]
|
||||||
*/
|
*/
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Modules\Plugins\Manifest;
|
|
||||||
|
|
||||||
use Override;
|
|
||||||
|
|
||||||
class Fields extends ManifestObject
|
|
||||||
{
|
|
||||||
#[Override]
|
|
||||||
public function loadData(array $data): void
|
|
||||||
{
|
|
||||||
dd($data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -195,12 +195,11 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"type": "array",
|
"type": "object",
|
||||||
"items": {
|
"patternProperties": {
|
||||||
"$ref": "#/$defs/option"
|
"^[A-Za-z0-9]+[\\w\\-\\:\\.]*$": { "$ref": "#/$defs/option" }
|
||||||
},
|
},
|
||||||
"minItems": 1,
|
"additionalProperties": false
|
||||||
"uniqueItems": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["label"],
|
"required": ["label"],
|
||||||
@ -215,14 +214,11 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"value": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"hint": {
|
"hint": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["label", "value"],
|
"required": ["label"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"field-multiple-implies-options-is-required": {
|
"field-multiple-implies-options-is-required": {
|
||||||
|
@ -14,129 +14,107 @@
|
|||||||
"keywords": ["seo", "analytics"],
|
"keywords": ["seo", "analytics"],
|
||||||
"hooks": ["rssAfterChannel"],
|
"hooks": ["rssAfterChannel"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"general": [
|
"general": {
|
||||||
{
|
"name": {
|
||||||
"type": "radio-group",
|
"type": "radio-group",
|
||||||
"key": "name",
|
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"options": [
|
"options": {
|
||||||
{ "label": "Foo", "value": "foo", "hint": "This is a hint." },
|
"foo": { "label": "Foo", "hint": "This is a hint." },
|
||||||
{ "label": "Bar", "value": "bar" }
|
"bar": { "label": "Bar" }
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
{
|
"email": {
|
||||||
"type": "email",
|
"type": "email",
|
||||||
"key": "email",
|
|
||||||
"label": "Email"
|
"label": "Email"
|
||||||
},
|
},
|
||||||
{
|
"url": {
|
||||||
"type": "url",
|
"type": "url",
|
||||||
"key": "url",
|
|
||||||
"label": "Your website URL"
|
"label": "Your website URL"
|
||||||
},
|
},
|
||||||
{
|
"toggler": {
|
||||||
"type": "toggler",
|
"type": "toggler",
|
||||||
"key": "toggler",
|
|
||||||
"label": "Toggle this?"
|
"label": "Toggle this?"
|
||||||
},
|
},
|
||||||
{
|
"number": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"key": "number",
|
|
||||||
"label": "Number"
|
"label": "Number"
|
||||||
},
|
},
|
||||||
{
|
"datetime": {
|
||||||
"type": "datetime",
|
"type": "datetime",
|
||||||
"key": "datetime",
|
|
||||||
"label": "Enter a date",
|
"label": "Enter a date",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
{
|
"select": {
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"key": "select",
|
|
||||||
"label": "Select something",
|
"label": "Select something",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
"select-multiple": {
|
||||||
"type": "select-multiple",
|
"type": "select-multiple",
|
||||||
"key": "select-multiple",
|
|
||||||
"label": "Select multiple things",
|
"label": "Select multiple things",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
"radio-group": {
|
||||||
"type": "radio-group",
|
"type": "radio-group",
|
||||||
"key": "radio-group",
|
|
||||||
"label": "Radio Group",
|
"label": "Radio Group",
|
||||||
"helper": "This is a helper.",
|
"helper": "This is a helper.",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
"texting": {
|
||||||
"type": "textarea",
|
"type": "textarea",
|
||||||
"key": "texting",
|
|
||||||
"label": "Your text",
|
"label": "Your text",
|
||||||
"hint": "This is a hint."
|
"hint": "This is a hint."
|
||||||
},
|
},
|
||||||
{
|
"hello": {
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"key": "hello",
|
|
||||||
"label": "Name Podcast",
|
"label": "Name Podcast",
|
||||||
"hint": "This is a hint.",
|
"hint": "This is a hint.",
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"podcast": [
|
"podcast": {
|
||||||
{
|
"name": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"key": "name",
|
|
||||||
"label": "Name Podcast",
|
"label": "Name Podcast",
|
||||||
"hint": "This is a hint."
|
"hint": "This is a hint."
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"episode": [
|
"episode": {
|
||||||
{
|
"name": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"key": "name",
|
|
||||||
"label": "Name Episode",
|
"label": "Name Episode",
|
||||||
"helper": "This is a helper."
|
"helper": "This is a helper."
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "radio-group",
|
"type": "radio-group",
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"options": [
|
"options": {
|
||||||
{ "label": "Foo", "value": "foo", "hint": "This is a hint." },
|
"foo": { "label": "Foo", "hint": "This is a hint." },
|
||||||
{ "label": "Bar", "value": "bar" },
|
"bar": { "label": "Bar" },
|
||||||
{ "label": "Baz", "value": "baz" }
|
"baz": { "label": "Baz" }
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"type": "email",
|
"type": "email",
|
||||||
@ -48,57 +48,48 @@
|
|||||||
"select": {
|
"select": {
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"label": "Select something",
|
"label": "Select something",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"select-multiple": {
|
"select-multiple": {
|
||||||
"type": "select-multiple",
|
"type": "select-multiple",
|
||||||
"label": "Select multiple things",
|
"label": "Select multiple things",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"radio-group": {
|
"radio-group": {
|
||||||
"type": "radio-group",
|
"type": "radio-group",
|
||||||
"label": "Radio Group",
|
"label": "Radio Group",
|
||||||
"helper": "This is a helper.",
|
"helper": "This is a helper.",
|
||||||
"options": [
|
"options": {
|
||||||
{
|
"foo": {
|
||||||
"label": "Foo",
|
"label": "Foo"
|
||||||
"value": "foo"
|
|
||||||
},
|
},
|
||||||
{
|
"bar": {
|
||||||
"label": "Bar",
|
"label": "Bar"
|
||||||
"value": "bar"
|
|
||||||
},
|
},
|
||||||
{
|
"baz": {
|
||||||
"label": "Baz",
|
"label": "Baz"
|
||||||
"value": "baz"
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"textarea": {
|
"textarea": {
|
||||||
"type": "textarea",
|
"type": "textarea",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user