NS Playground: fill structure

master
Jean-Christian Paul Denis 2023-01-06 09:31:44 +01:00
parent 58f3ddc3a6
commit a0d854c9ea
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
8 changed files with 269 additions and 122 deletions

18
_admin.php 100644
View File

@ -0,0 +1,18 @@
<?php
/**
* @brief tweakStores, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and Contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
$admin = implode('\\', ['Dotclear', 'Plugin', basename(__DIR__), 'Admin']);
if ($admin::init()) {
$admin::process();
}

19
_config.php 100644
View File

@ -0,0 +1,19 @@
<?php
/**
* @brief tweakStores, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and Contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
$config = implode('\\', ['Dotclear', 'Plugin', basename(__DIR__), 'Config']);
if ($config::init()) {
$config::process();
$config::render();
}

22
_prepend.php 100644
View File

@ -0,0 +1,22 @@
<?php
/**
* @brief tweakStores, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and Contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
$prepend = implode('\\', ['Dotclear', 'Plugin', basename(__DIR__), 'Prepend']);
if (!class_exists($prepend)) {
require implode(DIRECTORY_SEPARATOR, [__DIR__, 'inc', 'Prepend.php']);
if ($prepend::init()) {
$prepend::process();
}
}

View File

@ -10,73 +10,87 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
}
# only superadmin namespace Dotclear\Plugin\tweakStores;
if (!dcCore::app()->auth->isSuperAdmin()) {
return null;
}
# only if activated /* dotclear ns */
dcCore::app()->blog->settings->addNamespace('tweakStores'); use dcCore;
if (!dcCore::app()->blog->settings->tweakStores->active) { use dcPage;
return null;
}
# admin behaviors /* clearbricks ns */
if (dcCore::app()->blog->settings->tweakStores->packman) { use form;
dcCore::app()->addBehavior('packmanBeforeCreatePackage', ['tweakStoresBehaviors', 'packmanBeforeCreatePackage']); use html;
}
dcCore::app()->addBehavior('pluginsToolsHeadersV2', ['tweakStoresBehaviors', 'modulesToolsHeaders']);
dcCore::app()->addBehavior('themesToolsHeadersV2', ['tweakStoresBehaviors', 'modulesToolsHeaders']);
dcCore::app()->addBehavior('pluginsToolsTabsV2', ['tweakStoresBehaviors', 'pluginsToolsTabs']);
dcCore::app()->addBehavior('themesToolsTabsV2', ['tweakStoresBehaviors', 'themesToolsTabs']);
class tweakStoresBehaviors /* php ns */
use Exception;
class Admin
{ {
# create dcstore.xml file on the fly when pack a module protected static $init = false;
public static function packmanBeforeCreatePackage(array $module): void
public static function init(): bool
{ {
tweakStores::writeXML($module['id'], $module, dcCore::app()->blog->settings->tweakStores->file_pattern); if (defined('DC_CONTEXT_ADMIN')
&& dcCore::app()->auth->isSuperAdmin()
&& dcCore::app()->blog->settings->get(basename(__NAMESPACE__))->get('active')
) {
dcCore::app()->auth->user_prefs->addWorkspace('interface');
self::$init = true;
}
return self::$init;
}
public static function process(): ?bool
{
if (!self::$init) {
return false;
}
if (dcCore::app()->blog->settings->get(basename(__NAMESPACE__))->get('packman')) {
// create dcstore.xml file on the fly when plugin packman pack a module
dcCore::app()->addBehavior('packmanBeforeCreatePackage', function (array $module): void {
Core::writeXML($module['id'], $module, dcCore::app()->blog->settings->get(basename(__NAMESPACE__))->get('file_pattern'));
});
}
dcCore::app()->addBehaviors([
// addd some js
'pluginsToolsHeadersV2' => [self::class, 'modulesToolsHeaders'],
'themesToolsHeadersV2' => [self::class, 'modulesToolsHeaders'],
// admin plugins page tab
'pluginsToolsTabsV2' => function (): void {
self::modulesToolsTabs(dcCore::app()->plugins->getModules(), explode(',', DC_DISTRIB_PLUGINS), dcCore::app()->adminurl->get('admin.plugins'));
},
// admin themes page tab
'themesToolsTabsV2' => function (): void {
self::modulesToolsTabs(dcCore::app()->themes->getModules(), explode(',', DC_DISTRIB_THEMES), dcCore::app()->adminurl->get('admin.blog.theme'));
},
]);
return true;
} }
# addd some js
public static function modulesToolsHeaders(bool $is_plugin): string public static function modulesToolsHeaders(bool $is_plugin): string
{ {
dcCore::app()->auth->user_prefs->addWorkspace('interface');
return return
dcPage::jsVars(['dotclear.ts_copied' => __('Copied to clipboard')]) . dcPage::jsJson('ts_copied', ['alert' => __('Copied to clipboard')]) .
dcPage::jsLoad(dcPage::getPF('tweakStores/js/admin.js')) . dcPage::jsModuleLoad(basename(__NAMESPACE__) . '/js/admin.js') .
( (
!dcCore::app()->auth->user_prefs->interface->colorsyntax ? '' : !dcCore::app()->auth->user_prefs->interface->colorsyntax ? '' :
dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->interface->colorsyntax_theme) . dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->interface->colorsyntax_theme) .
dcPage::jsLoad(dcPage::getPF('tweakStores/js/cms.js')) dcPage::jsModuleLoad(basename(__NAMESPACE__) . '/js/cms.js')
); );
} }
# admin plugins page tab
public static function pluginsToolsTabs(): void
{
self::modulesToolsTabs(dcCore::app()->plugins->getModules(), explode(',', DC_DISTRIB_PLUGINS), dcCore::app()->adminurl->get('admin.plugins') . '#tweakStores');
}
# admin themes page tab
public static function themesToolsTabs(): void
{
self::modulesToolsTabs(dcCore::app()->themes->getModules(), explode(',', DC_DISTRIB_THEMES), dcCore::app()->adminurl->get('admin.blog.theme') . '#tweakStores');
}
# generic page tab
protected static function modulesToolsTabs(array $modules, array $excludes, string $page_url): void protected static function modulesToolsTabs(array $modules, array $excludes, string $page_url): void
{ {
dcCore::app()->auth->user_prefs->addWorkspace('interface'); $page_url .= '#' . basename(__NAMESPACE__);
$user_ui_colorsyntax = dcCore::app()->auth->user_prefs->interface->colorsyntax; $user_ui_colorsyntax = dcCore::app()->auth->user_prefs->interface->colorsyntax;
$user_ui_colorsyntax_theme = dcCore::app()->auth->user_prefs->interface->colorsyntax_theme; $user_ui_colorsyntax_theme = dcCore::app()->auth->user_prefs->interface->colorsyntax_theme;
$combo = self::comboModules($modules, $excludes); $combo = self::comboModules($modules, $excludes);
$file_pattern = dcCore::app()->blog->settings->tweakStores->file_pattern; $file_pattern = dcCore::app()->blog->settings->get(basename(__NAMESPACE__))->get('file_pattern');
# check dcstore repo # check dcstore repo
$url = ''; $url = '';
@ -110,7 +124,7 @@ class tweakStoresBehaviors
# generate xml code # generate xml code
if (!empty($_POST['buildxml_id']) && in_array($_POST['buildxml_id'], $combo)) { if (!empty($_POST['buildxml_id']) && in_array($_POST['buildxml_id'], $combo)) {
$xml_content = tweakStores::generateXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern); $xml_content = Core::generateXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern);
} }
# write dcstore.xml file # write dcstore.xml file
@ -118,14 +132,14 @@ class tweakStoresBehaviors
if (empty($_POST['your_pwd']) || !dcCore::app()->auth->checkPassword($_POST['your_pwd'])) { if (empty($_POST['your_pwd']) || !dcCore::app()->auth->checkPassword($_POST['your_pwd'])) {
dcCore::app()->error->add(__('Password verification failed')); dcCore::app()->error->add(__('Password verification failed'));
} else { } else {
$ret = tweakStores::writeXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern); $ret = Core::writeXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern);
if (!empty(tweakStores::$failed)) { if (!empty(Core::$failed)) {
dcCore::app()->error->add(implode(' ', tweakStores::$failed)); dcCore::app()->error->add(implode(' ', Core::$failed));
} }
} }
} }
echo echo
'<div class="multi-part" id="tweakStores" title="' . __('Tweak stores') . '">' . '<div class="multi-part" id="' . basename(__NAMESPACE__) . '" title="' . dcCore::app()->plugins->moduleInfo(basename(__NAMESPACE__), 'name') . '">' .
'<h3>' . __('Tweak third-party repositories') . '</h3>'; '<h3>' . __('Tweak third-party repositories') . '</h3>';
if (!empty($_POST['write_xml'])) { if (!empty($_POST['write_xml'])) {
@ -162,7 +176,7 @@ class tweakStoresBehaviors
( (
empty($file_content) ? '' : empty($file_content) ? '' :
'<pre>' . form::textArea('file_xml', 165, 14, [ '<pre>' . form::textArea('file_xml', 165, 14, [
'default' => html::escapeHTML(tweakStores::prettyXML($file_content)), 'default' => html::escapeHTML(Core::prettyXML($file_content)),
'class' => 'maximal', 'class' => 'maximal',
'extra_html' => 'readonly="true"', 'extra_html' => 'readonly="true"',
]) . '</pre>' . ]) . '</pre>' .
@ -177,7 +191,7 @@ class tweakStoresBehaviors
if (empty($file_pattern)) { if (empty($file_pattern)) {
echo sprintf( echo sprintf(
'<div class="fieldset"><h4>' . __('Generate xml code') . '</h4><p class="info"><a href="%s">%s</a></p></div>', '<div class="fieldset"><h4>' . __('Generate xml code') . '</h4><p class="info"><a href="%s">%s</a></p></div>',
dcCore::app()->adminurl->get('admin.plugins', ['module' => 'tweakStores', 'conf' => 1, 'redir' => $page_url]), dcCore::app()->adminurl->get('admin.plugins', ['module' => basename(__NAMESPACE__), 'conf' => 1, 'redir' => $page_url]),
__('You must configure zip file pattern to complete xml code automatically.') __('You must configure zip file pattern to complete xml code automatically.')
); );
} else { } else {
@ -197,19 +211,19 @@ class tweakStoresBehaviors
'<form method="post" action="' . $page_url . '" id="writexml" class="fieldset">' . '<form method="post" action="' . $page_url . '" id="writexml" class="fieldset">' .
'<h4>' . sprintf(__('Generated code for module: %s'), html::escapeHTML($_POST['buildxml_id'])) . '</h4>'; '<h4>' . sprintf(__('Generated code for module: %s'), html::escapeHTML($_POST['buildxml_id'])) . '</h4>';
if (!empty(tweakStores::$failed)) { if (!empty(Core::$failed)) {
echo '<p class="info">' . sprintf(__('Failed to parse XML code: %s'), implode(', ', tweakStores::$failed)) . '</p> '; echo '<p class="info">' . sprintf(__('Failed to parse XML code: %s'), implode(', ', Core::$failed)) . '</p> ';
} }
if (!empty(tweakStores::$notice)) { if (!empty(Core::$notice)) {
echo '<p class="info">' . sprintf(__('Code is not fully filled: %s'), implode(', ', tweakStores::$notice)) . '</p> '; echo '<p class="info">' . sprintf(__('Code is not fully filled: %s'), implode(', ', Core::$notice)) . '</p> ';
} }
if (!empty($xml_content)) { if (!empty($xml_content)) {
if (empty(tweakStores::$failed) && empty(tweakStores::$notice)) { if (empty(Core::$failed) && empty(Core::$notice)) {
echo '<p class="info">' . __('Code is complete') . '</p>'; echo '<p class="info">' . __('Code is complete') . '</p>';
} }
echo echo
'<pre>' . form::textArea('gen_xml', 165, 14, [ '<pre>' . form::textArea('gen_xml', 165, 14, [
'default' => html::escapeHTML(tweakStores::prettyXML($xml_content)), 'default' => html::escapeHTML(Core::prettyXML($xml_content)),
'class' => 'maximal', 'class' => 'maximal',
'extra_html' => 'readonly="true"', 'extra_html' => 'readonly="true"',
]) . '</pre>' . ]) . '</pre>' .
@ -218,7 +232,7 @@ class tweakStoresBehaviors
dcPage::jsRunCodeMirror('editor', 'gen_xml', 'dotclear', $user_ui_colorsyntax_theme) dcPage::jsRunCodeMirror('editor', 'gen_xml', 'dotclear', $user_ui_colorsyntax_theme)
); );
if (empty(tweakStores::$failed) if (empty(Core::$failed)
&& $modules[$_POST['buildxml_id']]['root_writable'] && $modules[$_POST['buildxml_id']]['root_writable']
&& dcCore::app()->auth->isSuperAdmin() && dcCore::app()->auth->isSuperAdmin()
) { ) {
@ -234,13 +248,13 @@ class tweakStoresBehaviors
] ]
) . '</p>' . ) . '</p>' .
'<p><input type="submit" name="write_xml" value="' . __('Save to module directory') . '" /> ' . '<p><input type="submit" name="write_xml" value="' . __('Save to module directory') . '" /> ' .
'<a class="hidden-if-no-js button" href="#tweakStores" id="ts_copy_button">' . __('Copy to clipboard') . '</a>' . '<a class="hidden-if-no-js button" href="#' . basename(__NAMESPACE__) . '" id="ts_copy_button">' . __('Copy to clipboard') . '</a>' .
form::hidden('buildxml_id', $_POST['buildxml_id']) . form::hidden('buildxml_id', $_POST['buildxml_id']) .
dcCore::app()->formNonce() . '</p>'; dcCore::app()->formNonce() . '</p>';
} }
echo sprintf( echo sprintf(
'<p class="info"><a href="%s">%s</a></p>', '<p class="info"><a href="%s">%s</a></p>',
dcCore::app()->adminurl->get('admin.plugins', ['module' => 'tweakStores', 'conf' => 1, 'redir' => $page_url]), dcCore::app()->adminurl->get('admin.plugins', ['module' => basename(__NAMESPACE__), 'conf' => 1, 'redir' => $page_url]),
__('You can edit zip file pattern from configuration page.') __('You can edit zip file pattern from configuration page.')
); );
} }

View File

@ -10,67 +10,93 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_MODULE')) { declare(strict_types=1);
return null;
}
dcPage::checkSuper(); namespace Dotclear\Plugin\tweakStores;
$redir = empty($_REQUEST['redir']) ? /* dotclear ns */
dcCore::app()->admin->list->getURL() . '#plugins' : $_REQUEST['redir']; use dcCore;
use dcPage;
# -- Get settings -- /* clearbricks ns */
dcCore::app()->blog->settings->addNamespace('tweakStores'); use form;
$s = dcCore::app()->blog->settings->tweakStores; use http;
$tweakStores_active = $s->active; /* php ns */
$tweakStores_packman = $s->packman; use Exception;
$tweakStores_file_pattern = $s->file_pattern;
# -- Set settings -- class Config
if (!empty($_POST['save'])) { {
try { protected static $init = false;
$tweakStores_active = !empty($_POST['tweakStores_active']);
$tweakStores_packman = !empty($_POST['tweakStores_packman']);
$tweakStores_file_pattern = $_POST['tweakStores_file_pattern'];
$s->put('active', $tweakStores_active); public static function init(): bool
$s->put('packman', $tweakStores_packman); {
$s->put('file_pattern', $tweakStores_file_pattern); if (defined('DC_CONTEXT_ADMIN') && defined('DC_CONTEXT_MODULE')) {
dcPage::checkSuper();
self::$init = true;
}
dcPage::addSuccessNotice( return self::$init;
__('Configuration successfully updated') }
);
http::redirect( public static function process(): ?bool
dcCore::app()->admin->list->getURL('module=tweakStores&conf=1&redir=' . dcCore::app()->admin->list->getRedir()) {
); if (!self::$init) {
} catch (Exception $e) { return false;
dcCore::app()->error->add($e->getMessage()); }
if (empty($_POST['save'])) {
return null;
}
try {
$s = dcCore::app()->blog->settings->get(basename(__NAMESPACE__));
$s->put('active', !empty($_POST['s_active']));
$s->put('packman', !empty($_POST['s_packman']));
$s->put('file_pattern', $_POST['s_file_pattern']);
dcPage::addSuccessNotice(
__('Configuration successfully updated')
);
http::redirect(
dcCore::app()->admin->__get('list')->getURL('module=' . basename(__NAMESPACE__) . '&conf=1&redir=' . dcCore::app()->admin->__get('list')->getRedir())
);
return true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
return null;
}
public static function render(): void
{
$s = dcCore::app()->blog->settings->get(basename(__NAMESPACE__));
echo '
<div class="fieldset">
<h4>' . dcCore::app()->plugins->moduleInfo(basename(__NAMESPACE__), 'name') . '</h4>
<p><label class="classic" for="s_active">' .
form::checkbox('s_active', 1, (bool) $s->get('active')) . ' ' .
__('Enable plugin') . '</label></p>
<p class="form-note">' . __('If enabled, new tab "Tweak stores" allows your to perfom actions relative to third-party repositories.') . '</p>
<p><label class="classic" for="s_packman">' .
form::checkbox('s_packman', 1, (bool) $s->get('packman')) . ' ' .
__('Enable packman behaviors') . '</label></p>
<p class="form-note">' . __('If enabled, plugin pacKman will (re)generate on the fly dcstore.xml file at root directory of the module.') . '</p>
<p><label class="classic" for="s_file_pattern">' . __('Predictable URL to zip file on the external repository') .
form::field('s_file_pattern', 65, 255, (string) $s->get('file_pattern'), 'maximal') . '
</label></p>
<p class="form-note">' .
__('You can use widcard like %author%, %type%, %id%, %version%.') . '<br /> ' .
__('For example on github https://github.com/MyGitName/%id%/releases/download/v%version%/%type%-%id%.zip') . '<br />' .
__('Note: on github, you must create a release and join to it the module zip file.') . '
</p>
</div>';
} }
} }
# -- Display form --
echo '
<div class="fieldset">
<h4>' . __('Tweak store') . '</h4>
<p><label class="classic" for="tweakStores_active">' .
form::checkbox('tweakStores_active', 1, $tweakStores_active) . ' ' .
__('Enable plugin') . '</label></p>
<p class="form-note">' . __('If enabled, new tab "Tweak stores" allows your to perfom actions relative to third-party repositories.') . '</p>
<p><label class="classic" for="tweakStores_packman">' .
form::checkbox('tweakStores_packman', 1, $tweakStores_packman) . ' ' .
__('Enable packman behaviors') . '</label></p>
<p class="form-note">' . __('If enabled, plugin pacKman will (re)generate on the fly dcstore.xml file at root directory of the module.') . '</p>
<p><label class="classic" for="tweakStores_file_pattern">' . __('Predictable URL to zip file on the external repository') .
form::field('tweakStores_file_pattern', 65, 255, $tweakStores_file_pattern, 'maximal') . '
</label></p>
<p class="form-note">' .
__('You can use widcard like %author%, %type%, %id%, %version%.') . '<br /> ' .
__('For example on github https://github.com/MyGitName/%id%/releases/download/v%version%/%type%-%id%.zip') . '<br />' .
__('Note: on github, you must create a release and join to it the module zip file.') . '
</p>
</div>';

View File

@ -10,7 +10,20 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class tweakStores declare(strict_types=1);
namespace Dotclear\Plugin\tweakStores;
/* clearbricks ns */
use files;
use text;
use xmlTag;
/* php ns */
use DOMDocument;
use Exception;
class Core
{ {
/** @var array List of notice messages */ /** @var array List of notice messages */
public static $notice = []; public static $notice = [];

View File

@ -10,8 +10,41 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
Clearbricks::lib()->autoload(['tweakStores' => __DIR__ . '/inc/class.tweakstores.php']); namespace Dotclear\Plugin\tweakStores;
/* clearbricks ns */
use Clearbricks;
class Prepend
{
private const LIBS = [
'Admin',
'Config',
'Core',
];
protected static $init = false;
public static function init(): bool
{
self::$init = defined('DC_RC_PATH');
return self::$init;
}
public static function process(): ?bool
{
if (!self::$init) {
return false;
}
foreach (self::LIBS as $lib) {
Clearbricks::lib()->autoload([
implode('\\', ['Dotclear','Plugin', basename(__NAMESPACE__), $lib]) => __DIR__ . DIRECTORY_SEPARATOR . $lib . '.php',
]);
}
return true;
}
}

View File

@ -1,6 +1,8 @@
/*global $, dotclear */ /*global $, dotclear */
'use strict'; 'use strict';
Object.assign(dotclear.msg, dotclear.getData('ts_copied'));
$(function(){ $(function(){
$("#ts_copy_button").click(function() { $("#ts_copy_button").click(function() {
var style = $("#gen_xml").attr('style'); var style = $("#gen_xml").attr('style');
@ -14,7 +16,7 @@ $(function(){
$("#gen_xml").removeAttr("contenteditable").attr('style', style); $("#gen_xml").removeAttr("contenteditable").attr('style', style);
$("#ts_copy_button").focus(); $("#ts_copy_button").focus();
alert(dotclear.ts_copied); alert(dotclear.msg.alert);
return false; return false;
}); });
}); });