mirror of
https://github.com/dokuwiki/dokuwiki-plugin-dev.git
synced 2026-01-13 20:12:48 +00:00
first version of the plugin wizard interface is working
This commit is contained in:
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/.* export-ignore
|
||||
/_test export-ignore
|
||||
/composer.* export-ignore
|
||||
/www export-ignore
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/vendor/
|
||||
35
Skeletor.php
35
Skeletor.php
@ -113,7 +113,7 @@ class Skeletor
|
||||
* @param string $type
|
||||
* @param string $component
|
||||
*/
|
||||
public function addComponent($type, $component = '')
|
||||
public function addComponent($type, $component = '', $options = [])
|
||||
{
|
||||
if ($this->type !== self::TYPE_PLUGIN) {
|
||||
throw new RuntimeException('Components can only be added to plugins');
|
||||
@ -135,7 +135,10 @@ class Skeletor
|
||||
$self = 'plugin_' . $plugin;
|
||||
}
|
||||
|
||||
$replacements = $this->actionReplacements('EVENT_NAME'); // FIXME accept multiple optional events
|
||||
if($type === 'action') {
|
||||
$replacements = $this->actionReplacements($options);
|
||||
}
|
||||
|
||||
$replacements['@@PLUGIN_COMPONENT_NAME@@'] = $class;
|
||||
$replacements['@@SYNTAX_COMPONENT_NAME@@'] = $self;
|
||||
$this->loadSkeleton($type . '.php', $path, $replacements);
|
||||
@ -219,20 +222,30 @@ class Skeletor
|
||||
/**
|
||||
* Replacements needed for action components.
|
||||
*
|
||||
* @param string $event FIXME support multiple events
|
||||
* @param string[] $event Event names to handle
|
||||
* @return string[]
|
||||
*/
|
||||
protected function actionReplacements($event)
|
||||
protected function actionReplacements($events = [])
|
||||
{
|
||||
$event = strtoupper($event);
|
||||
$fn = 'handle' . str_replace('_', '', ucwords(strtolower($event), '_'));
|
||||
$register = ' $controller->register_hook(\'' . $event . '\', \'AFTER|BEFORE\', $this, \'' . $fn . '\');';
|
||||
$handler = ' public function ' . $fn . '(Doku_Event $event, $param)' . "\n"
|
||||
. " {\n"
|
||||
. " }\n";
|
||||
if (!$events) $events = ['EXAMPLE_EVENT'];
|
||||
|
||||
$register = '';
|
||||
$handler = '';
|
||||
|
||||
$template = file_get_contents(__DIR__ . '/skel/action_handler.php');
|
||||
|
||||
foreach ($events as $event) {
|
||||
$event = strtoupper($event);
|
||||
$fn = 'handle' . str_replace('_', '', ucwords(strtolower($event), '_'));
|
||||
|
||||
$register .= ' $controller->register_hook(\'' . $event .
|
||||
'\', \'AFTER|BEFORE\', $this, \'' . $fn . '\');'. "\n";
|
||||
|
||||
$handler .= str_replace(['@@EVENT@@','@@HANDLER@@'], [$event, $fn], $template);
|
||||
}
|
||||
|
||||
return [
|
||||
'@@REGISTER@@' => $register . "\n ",
|
||||
'@@REGISTER@@' => $register,
|
||||
'@@HANDLERS@@' => $handler,
|
||||
];
|
||||
}
|
||||
|
||||
20
composer.json
Normal file
20
composer.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "dokuwiki/plugin-dev",
|
||||
"description": "DokuWiki dev plugin and wizard",
|
||||
"type": "project",
|
||||
"license": "GPLv2",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"dokuwiki\\plugin\\dev\\": "./"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"splitbrain/php-archive": "^1.3"
|
||||
}
|
||||
}
|
||||
79
composer.lock
generated
Normal file
79
composer.lock
generated
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "40bd143cf974e1a5ab22aa835987ef41",
|
||||
"packages": [
|
||||
{
|
||||
"name": "splitbrain/php-archive",
|
||||
"version": "1.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/splitbrain/php-archive.git",
|
||||
"reference": "d274e5190ba309777926348900cf9578d9e533c9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/splitbrain/php-archive/zipball/d274e5190ba309777926348900cf9578d9e533c9",
|
||||
"reference": "d274e5190ba309777926348900cf9578d9e533c9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-bz2": "*",
|
||||
"ext-zip": "*",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bz2": "For bz2 compression",
|
||||
"ext-iconv": "Used for proper filename encode handling",
|
||||
"ext-mbstring": "Can be used alternatively for handling filename encoding",
|
||||
"ext-zlib": "For zlib compression"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"splitbrain\\PHPArchive\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"description": "Pure-PHP implementation to read and write TAR and ZIP archives",
|
||||
"keywords": [
|
||||
"archive",
|
||||
"extract",
|
||||
"tar",
|
||||
"unpack",
|
||||
"unzip",
|
||||
"zip"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/splitbrain/php-archive/issues",
|
||||
"source": "https://github.com/splitbrain/php-archive/tree/1.3.1"
|
||||
},
|
||||
"time": "2022-03-23T09:21:55+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
93
events.txt
Normal file
93
events.txt
Normal file
@ -0,0 +1,93 @@
|
||||
ACTION_ACT_PREPROCESS
|
||||
ACTION_EXPORT_POSTPROCESS
|
||||
ACTION_HANDLE_SUBSCRIBE
|
||||
ACTION_HEADERS_SEND
|
||||
ACTION_SHOW_REDIRECT
|
||||
AJAX_CALL_UNKNOWN
|
||||
AUTH_ACL_CHECK
|
||||
AUTH_LOGIN_CHECK
|
||||
AUTH_PASSWORD_GENERATE
|
||||
AUTH_USER_CHANGE
|
||||
COMMON_NOTIFY_ADDRESSLIST
|
||||
COMMON_PAGETPL_LOAD
|
||||
COMMON_PAGE_FROMTEMPLATE
|
||||
COMMON_USER_LINK
|
||||
COMMON_WIKIPAGE_SAVE
|
||||
COMMON_WORDBLOCK_BLOCKED
|
||||
CONFUTIL_CDN_SELECT
|
||||
CSS_CACHE_USE
|
||||
CSS_STYLES_INCLUDED
|
||||
DETAIL_STARTED
|
||||
DOKUWIKI_DONE
|
||||
DOKUWIKI_STARTED
|
||||
FEED_DATA_PROCESS
|
||||
FEED_ITEM_ADD
|
||||
FEED_MODE_UNKNOWN
|
||||
FEED_OPTS_POSTPROCESS
|
||||
FETCH_MEDIA_STATUS
|
||||
FORM_QUICKSEARCH_OUTPUT
|
||||
FORM_SEARCH_OUTPUT
|
||||
FULLTEXT_SNIPPET_CREATE
|
||||
HTML_CONFLICTFORM_OUTPUT
|
||||
HTML_DRAFTFORM_OUTPUT
|
||||
HTML_EDITFORM_OUTPUT
|
||||
HTML_EDIT_FORMSELECTION
|
||||
HTML_LOGINFORM_OUTPUT
|
||||
HTML_PAGE_FROMTEMPLATE
|
||||
HTML_RECENTFORM_OUTPUT
|
||||
HTML_REGISTERFORM_OUTPUT
|
||||
HTML_RESENDPWDFORM_OUTPUT
|
||||
HTML_REVISIONSFORM_OUTPUT
|
||||
HTML_SECEDIT_BUTTON
|
||||
HTML_SHOWREV_OUTPUT
|
||||
HTML_SUBSCRIBEFORM_OUTPUT
|
||||
HTML_UPDATEPROFILEFORM_OUTPUT
|
||||
HTML_UPLOADFORM_OUTPUT
|
||||
HTTPCLIENT_REQUEST_SEND
|
||||
INDEXER_PAGE_ADD
|
||||
INDEXER_TASKS_RUN
|
||||
INDEXER_TEXT_PREPARE
|
||||
INDEXER_VERSION_GET
|
||||
INIT_LANG_LOAD
|
||||
IO_NAMESPACE_CREATED
|
||||
IO_NAMESPACE_DELETED
|
||||
IO_WIKIPAGE_READ
|
||||
IO_WIKIPAGE_WRITE
|
||||
JS_CACHE_USE
|
||||
JS_SCRIPT_LIST
|
||||
MAIL_MESSAGE_SEND
|
||||
MANIFEST_SEND
|
||||
MEDIAMANAGER_CONTENT_OUTPUT
|
||||
MEDIAMANAGER_STARTED
|
||||
MEDIA_DELETE_FILE
|
||||
MEDIA_SENDFILE
|
||||
MEDIA_UPLOAD_FINISH
|
||||
MENU_ITEMS_ASSEMBLY
|
||||
PAGEUTILS_ID_HIDEPAGE
|
||||
PARSER_CACHE_USE
|
||||
PARSER_HANDLER_DONE
|
||||
PARSER_METADATA_RENDER
|
||||
PARSER_WIKITEXT_PREPROCESS
|
||||
PLUGIN_CONFIG_PLUGINLIST
|
||||
PLUGIN_PLUGINMANAGER_PLUGINLIST
|
||||
PLUGIN_POPULARITY_DATA_SETUP
|
||||
RENDERER_CONTENT_POSTPROCESS
|
||||
RPC_CALL_ADD
|
||||
SEARCH_QUERY_FULLPAGE
|
||||
SEARCH_QUERY_PAGELOOKUP
|
||||
SEARCH_RESULT_FULLPAGE
|
||||
SEARCH_RESULT_PAGELOOKUP
|
||||
SITEMAP_GENERATE
|
||||
SITEMAP_PING
|
||||
TEMPLATE_PAGETOOLS_DISPLAY
|
||||
TEMPLATE_SITETOOLS_DISPLAY
|
||||
TEMPLATE_USERTOOLS_DISPLAY
|
||||
TOOLBAR_DEFINE
|
||||
TPL_ACTION_GET
|
||||
TPL_ACT_RENDER
|
||||
TPL_ACT_UNKNOWN
|
||||
TPL_CONTENT_DISPLAY
|
||||
TPL_IMG_DISPLAY
|
||||
TPL_METAHEADER_OUTPUT
|
||||
TPL_TOC_RENDER
|
||||
XMLRPC_CALLBACK_REGISTER
|
||||
@ -14,13 +14,6 @@ class @@PLUGIN_COMPONENT_NAME@@ extends \dokuwiki\Extension\ActionPlugin
|
||||
@@REGISTER@@
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME Event handler for
|
||||
*
|
||||
* @param Doku_Event $event event object by reference
|
||||
* @param mixed $param optional parameter passed when event was registered
|
||||
* @return void
|
||||
*/
|
||||
@@HANDLERS@@
|
||||
}
|
||||
|
||||
|
||||
12
skel/action_handler.php
Normal file
12
skel/action_handler.php
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* Event handler for @@EVENT@@
|
||||
*
|
||||
* @see https://www.dokuwiki.org/devel:events:@@EVENT@@
|
||||
* @param Doku_Event $event Event object
|
||||
* @param mixed $param optional parameter passed when event was registered
|
||||
* @return void
|
||||
*/
|
||||
public function @@HANDLER@@(Doku_Event $event, $param) {
|
||||
|
||||
}
|
||||
76
www/PluginWizard.php
Normal file
76
www/PluginWizard.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\dev\www;
|
||||
|
||||
use dokuwiki\plugin\dev\Skeletor;
|
||||
use splitbrain\PHPArchive\ArchiveIllegalCompressionException;
|
||||
use splitbrain\PHPArchive\ArchiveIOException;
|
||||
use splitbrain\PHPArchive\Zip;
|
||||
|
||||
class PluginWizard
|
||||
{
|
||||
|
||||
/**
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!isset($_POST['base'])) return null;
|
||||
|
||||
$skeletor = new Skeletor(
|
||||
Skeletor::TYPE_PLUGIN,
|
||||
$_POST['base'] ?: '', //FIXME clean base
|
||||
$_POST['desc'] ?: '',
|
||||
$_POST['author'] ?: '',
|
||||
$_POST['mail'] ?: '',
|
||||
'',
|
||||
$_POST['url'] ?: ''
|
||||
);
|
||||
$skeletor->addBasics();
|
||||
|
||||
if (!empty($_POST['use_lang'])) $skeletor->addLang();
|
||||
if (!empty($_POST['use_conf'])) $skeletor->addConf();
|
||||
if (!empty($_POST['use_test'])) $skeletor->addTest();
|
||||
|
||||
foreach ($_POST['components'] as $id) {
|
||||
list($type, /*"plugin"*/, /*base*/, $component) = array_pad(explode('_', $id, 4), 4, '');
|
||||
if (isset($_POST['options'][$id])) {
|
||||
$options = array_filter(array_map('trim', explode(',', $_POST['options'][$id])));
|
||||
} else {
|
||||
$options = [];
|
||||
}
|
||||
|
||||
$skeletor->addComponent($type, $component, $options);
|
||||
}
|
||||
|
||||
$zip = new Zip();
|
||||
$zip->setCompression(9);
|
||||
$zip->create();
|
||||
foreach ($skeletor->getFiles() as $file => $content) {
|
||||
$zip->addData($_POST['base'] . '/' . $file, $content);
|
||||
}
|
||||
|
||||
return $zip->getArchive();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get options for all available plugin types
|
||||
*/
|
||||
public function getPluginTypes()
|
||||
{
|
||||
return Skeletor::PLUGIN_TYPES;
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return array_map('trim', file(__DIR__ . '/../events.txt', FILE_IGNORE_NEW_LINES));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
104
www/awesomplete.css
Normal file
104
www/awesomplete.css
Normal file
@ -0,0 +1,104 @@
|
||||
.awesomplete [hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.awesomplete .visually-hidden {
|
||||
position: absolute;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.awesomplete {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.awesomplete > input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.awesomplete > ul {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
min-width: 100%;
|
||||
box-sizing: border-box;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.awesomplete > ul:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.awesomplete > ul {
|
||||
border-radius: .3em;
|
||||
margin: .2em 0 0;
|
||||
background: hsla(0,0%,100%,.9);
|
||||
background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));
|
||||
border: 1px solid rgba(0,0,0,.3);
|
||||
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@supports (transform: scale(0)) {
|
||||
.awesomplete > ul {
|
||||
transition: .3s cubic-bezier(.4,.2,.5,1.4);
|
||||
transform-origin: 1.43em -.43em;
|
||||
}
|
||||
|
||||
.awesomplete > ul[hidden],
|
||||
.awesomplete > ul:empty {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
display: block;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pointer */
|
||||
.awesomplete > ul:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -.43em;
|
||||
left: 1em;
|
||||
width: 0; height: 0;
|
||||
padding: .4em;
|
||||
background: white;
|
||||
border: inherit;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.awesomplete > ul > li {
|
||||
position: relative;
|
||||
padding: .2em .5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.awesomplete > ul > li:hover {
|
||||
background: hsl(200, 40%, 80%);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.awesomplete > ul > li[aria-selected="true"] {
|
||||
background: hsl(205, 40%, 40%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.awesomplete mark {
|
||||
background: hsl(65, 100%, 50%);
|
||||
}
|
||||
|
||||
.awesomplete li:hover mark {
|
||||
background: hsl(68, 100%, 41%);
|
||||
}
|
||||
|
||||
.awesomplete li[aria-selected="true"] mark {
|
||||
background: hsl(86, 100%, 21%);
|
||||
color: inherit;
|
||||
}
|
||||
/*# sourceMappingURL=awesomplete.css.map */
|
||||
3
www/awesomplete.min.js
vendored
Normal file
3
www/awesomplete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
145
www/index.php
Normal file
145
www/index.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$WIZ = new dokuwiki\plugin\dev\www\PluginWizard();
|
||||
try {
|
||||
$archive = $WIZ->handle();
|
||||
if($archive) {
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="plugin.zip"');
|
||||
echo $archive;
|
||||
exit;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// FIXME handle errors
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
?>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>DokuWiki Plugin Wizard</title>
|
||||
<script type="text/javascript">
|
||||
const ACTION_EVENTS = <?php echo json_encode($WIZ->getEvents()); ?>;
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="awesomplete.css" /
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>DokuWiki Plugin Wizard</h1>
|
||||
|
||||
|
||||
<div class="intro">
|
||||
<p>
|
||||
This wizard generates a <a href="https://www.dokuwiki.org/devel:plugins">DokuWiki plugin</a>
|
||||
skeleton to help you get started with coding your plugin.
|
||||
Before using it you should familiarize your self with how plugins in DokuWiki work
|
||||
and determine what components your plugin will need.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To use it, fill in the general plugin info and add plugin components. Once you're
|
||||
done, click "create" and download your plugin skeleton.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Alternatively you can also use the <a href="https://www.dokuwiki.org/plugin:dev">dev plugin</a>.
|
||||
This plugin will also come in handy when editing and extending your plugin later.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<noscript>
|
||||
<div class="nojs">
|
||||
Sorry, this wizard needs JavaScript to do its magic. It will not work with your
|
||||
current setup.
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
|
||||
<form action="index.php" method="post" id="ajax__plugin_wiz">
|
||||
|
||||
<section>
|
||||
<div id="plugin_info">
|
||||
<h2>Plugin Information</h2>
|
||||
|
||||
<label>
|
||||
<span>Plugin base name:</span>
|
||||
<input type="text" name="base" required="required" pattern="^[a-z0-9]+$"
|
||||
placeholder="yourplugin">
|
||||
<small>(lowercase, no special chars)</small>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>A short description of what the plugin does:</span>
|
||||
<input type="text" name="desc" required="required"
|
||||
placeholder="A plugin to flurb the blarg">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>Your name:</span>
|
||||
<input type="text" name="author" required="required" placeholder="Jane Doe">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>Your E-Mail address:</span>
|
||||
<input type="text" name="mail" required="required" placeholder="jane@example.com">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>URL for the plugin:</span>
|
||||
<input type="text" name="url" placeholder="https://www.dokuwiki.org/plugin:yourplugin">
|
||||
<small>(leave empty for default dokuwiki.org location)</small>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" name="use_lang" value="1"/>
|
||||
<span>Use localization</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" name="use_conf" value="1"/>
|
||||
<span>Use configuration</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" name="use_tests" value="1"/>
|
||||
<span>Use unit tests</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="plugin_components">
|
||||
<h2>Add Plugin Components</h2>
|
||||
|
||||
<label>
|
||||
<span>Type:</span>
|
||||
<select>
|
||||
<?php foreach ($WIZ->getPluginTypes() as $type): ?>
|
||||
<option value="<?php echo $type ?>"><?php echo ucfirst($type) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>Add as a Sub-Component:</span>
|
||||
<input type="text" value="" pattern="^[a-z0-9]+$" placeholder="subcomponent"/>
|
||||
<small>(leave empty to add top level)</small>
|
||||
</label>
|
||||
|
||||
<button type="button">Add Component</button>
|
||||
|
||||
<ul id="output"></ul>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<button type="submit" name="plugin_wiz_create">Create and Download<br>Plugin Skeleton</button>
|
||||
|
||||
</form>
|
||||
|
||||
</main>
|
||||
<script src="awesomplete.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
112
www/script.js
Normal file
112
www/script.js
Normal file
@ -0,0 +1,112 @@
|
||||
const plugin = document.getElementById('plugin_info');
|
||||
const component = document.getElementById('plugin_components');
|
||||
const output = document.getElementById('output');
|
||||
updatePluginNameStatus();
|
||||
|
||||
/**
|
||||
* disable the plugin name field when a component has been added
|
||||
*/
|
||||
function updatePluginNameStatus() {
|
||||
plugin.querySelector('input').readOnly = output.children.length > 0;
|
||||
document.querySelector('button[type=submit]').disabled = output.children.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the HTML element for a component
|
||||
*
|
||||
* @param {string} plugin The plugin base name
|
||||
* @param {string} type The component type
|
||||
* @param {string} name The component name
|
||||
* @returns {HTMLLIElement|null} null if the component already exists
|
||||
*/
|
||||
function createComponentElement(plugin, type, name) {
|
||||
let id = `${type}_plugin_${plugin}`;
|
||||
if (name !== '') {
|
||||
id += `_${name}`;
|
||||
}
|
||||
|
||||
if (document.getElementById(`component-${id}`)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.id = `component-${id}`;
|
||||
li.dataset.type = type;
|
||||
li.dataset.name = name;
|
||||
|
||||
const hidden = document.createElement('input');
|
||||
hidden.type = 'hidden';
|
||||
hidden.name = 'components[]';
|
||||
hidden.value = id;
|
||||
li.append(hidden);
|
||||
|
||||
const remove = document.createElement('button');
|
||||
remove.type = 'button';
|
||||
remove.innerText = 'X';
|
||||
remove.title = 'Remove this component';
|
||||
remove.addEventListener('click', function (event) {
|
||||
li.remove();
|
||||
updatePluginNameStatus();
|
||||
});
|
||||
li.append(remove);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.innerText = id;
|
||||
li.append(label);
|
||||
|
||||
|
||||
// add auto completion for events
|
||||
if (type === 'action') {
|
||||
const events = document.createElement('input');
|
||||
events.type = 'text';
|
||||
events.name = `options[${id}]`;
|
||||
events.placeholder = 'EVENTS_TO_HANDLE, ...';
|
||||
li.append(events);
|
||||
new Awesomplete(events, {
|
||||
list: ACTION_EVENTS,
|
||||
filter: function (text, input) {
|
||||
return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]);
|
||||
},
|
||||
|
||||
item: function (text, input) {
|
||||
return Awesomplete.ITEM(text, input.match(/[^,]*$/)[0]);
|
||||
},
|
||||
|
||||
replace: function (text) {
|
||||
var before = this.input.value.match(/^.+,\s*|/)[0];
|
||||
this.input.value = before + text + ", ";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a component to the output list
|
||||
*/
|
||||
component.querySelector('button').addEventListener('click', function (event) {
|
||||
const plugin_base = plugin.querySelector('input'); // first input field is plugin base name
|
||||
const component_type = component.querySelector('select');
|
||||
const component_name = component.querySelector('input');
|
||||
|
||||
if (!plugin_base.validity.valid) {
|
||||
plugin_base.reportValidity();
|
||||
plugin_base.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component_name.validity.valid) {
|
||||
component_name.reportValidity();
|
||||
component_name.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const li = createComponentElement(plugin_base.value, component_type.value, component_name.value);
|
||||
if (!li) return;
|
||||
|
||||
output.appendChild(li);
|
||||
updatePluginNameStatus();
|
||||
});
|
||||
75
www/style.css
Normal file
75
www/style.css
Normal file
@ -0,0 +1,75 @@
|
||||
body, html {
|
||||
font: 16px sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 75em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
form section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2em;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
label > span:first-child {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
label > select,
|
||||
label > input[type="text"] {
|
||||
width: 30em;
|
||||
display: block;
|
||||
font: 16px sans-serif;
|
||||
}
|
||||
|
||||
label > input[type="checkbox"] + span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button, label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#output {
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#output li {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#output li button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#output li span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#output li .awesomplete,
|
||||
#output li input,
|
||||
#output li select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
font-size: 120%;
|
||||
width: 15em;
|
||||
padding: 1em;
|
||||
display: block;
|
||||
margin: 1em auto;
|
||||
}
|
||||
Reference in New Issue
Block a user