From dae470074c873a303f82bf85dc1728b9eb2a431a Mon Sep 17 00:00:00 2001 From: Jean-Christian Denis Date: Sun, 19 Mar 2023 21:17:24 +0100 Subject: [PATCH] use namespace --- _admin.php | 18 -- _config.php | 19 -- _prepend.php | 22 -- inc/Core.php | 239 ---------------------- inc/Prepend.php | 50 ----- src/Backend.php | 264 ++---------------------- src/BackendBehaviors.php | 421 +++++++++++++++++++++++++++++++++++++++ src/Config.php | 109 +++++----- src/My.php | 50 +++++ src/Settings.php | 76 +++++++ 10 files changed, 622 insertions(+), 646 deletions(-) delete mode 100644 _admin.php delete mode 100644 _config.php delete mode 100644 _prepend.php delete mode 100644 inc/Core.php delete mode 100644 inc/Prepend.php create mode 100644 src/BackendBehaviors.php create mode 100644 src/My.php create mode 100644 src/Settings.php diff --git a/_admin.php b/_admin.php deleted file mode 100644 index 065bd9f..0000000 --- a/_admin.php +++ /dev/null @@ -1,18 +0,0 @@ - '', - 'author' => '', - 'version' => 0, - 'current_version' => 0, - 'root' => '', - 'root_writable' => false, - 'permissions' => null, - 'parent' => null, - 'priority' => 1000, - 'standalone_config' => false, - 'support' => '', - 'section' => '', - 'tags' => '', - 'details' => '', - 'sshot' => '', - 'score' => 0, - 'type' => null, - 'requires' => [], - 'settings' => [], - 'repository' => '', - 'dc_min' => 0, - ], - # Module's values - $module, - # Clean up values - [ - 'id' => $id, - 'sid' => self::sanitizeString($id), - 'label' => $label, - 'name' => $name, - 'oname' => $oname, - 'sname' => self::sanitizeString($name), - ] - ); - } - - # taken from lib.moduleslist.php - public static function sanitizeString(string $str): string - { - return (string) preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str)); - } - - public static function parseFilePattern(string $id, array $module, string $file_pattern): string - { - $module = self::sanitizeModule($id, $module); - - return text::tidyURL(str_replace( - [ - '%type%', - '%id%', - '%version%', - '%author%', - ], - [ - $module['type'], - $module['id'], - $module['version'], - $module['author'], - ], - $file_pattern - )); - } - - public static function generateXML(string $id, array $module, string $file_pattern): string - { - if (!is_array($module) || empty($module)) { - return ''; - } - $module = self::sanitizeModule($id, $module); - $rsp = new xmlTag('module'); - - self::$notice = []; - self::$failed = []; - - # id - if (empty($module['id'])) { - self::$failed[] = 'unknow module'; - } - $rsp->id = $module['id']; - - # name - if (empty($module['name'])) { - self::$failed[] = 'no module name set in _define.php'; - } - $rsp->name($module['oname']); - - # version - if (empty($module['version'])) { - self::$failed[] = 'no module version set in _define.php'; - } - $rsp->version($module['version']); - - # author - if (empty($module['author'])) { - self::$failed[] = 'no module author set in _define.php'; - } - $rsp->author($module['author']); - - # desc - if (empty($module['desc'])) { - self::$failed[] = 'no module description set in _define.php'; - } - $rsp->desc($module['desc']); - - # repository - if (empty($module['repository'])) { - self::$failed[] = 'no repository set in _define.php'; - } - - # file - $file_pattern = self::parseFilePattern($id, $module, $file_pattern); - if (empty($file_pattern)) { - self::$failed[] = 'no zip file pattern set in Tweak Store configuration'; - } - $rsp->file($file_pattern); - - # da dc_min or requires core - if (!empty($module['requires']) && is_array($module['requires'])) { - foreach ($module['requires'] as $req) { - if (!is_array($req)) { - $req = [$req]; - } - if ($req[0] == 'core') { - $module['dc_min'] = $req[1]; - - break; - } - } - } - if (empty($module['dc_min'])) { - self::$notice[] = 'no minimum dotclear version'; - } else { - $rsp->insertNode(new xmlTag('da:dcmin', $module['dc_min'])); - } - - # details - if (empty($module['details'])) { - self::$notice[] = 'no details URL'; - } else { - $rsp->insertNode(new xmlTag('da:details', $module['details'])); - } - - # section - if (!empty($module['section'])) { - $rsp->insertNode(new xmlTag('da:section', $module['section'])); - } - - # support - if (empty($module['support'])) { - self::$notice[] = 'no support URL'; - } else { - $rsp->insertNode(new xmlTag('da:support', $module['support'])); - } - - $res = new xmlTag('modules', $rsp); - $res->insertAttr('xmlns:da', 'http://dotaddict.org/da/'); - - return self::prettyXML($res->toXML()); - } - - public static function writeXML(string $id, array $module, string $file_pattern): bool - { - self::$failed = []; - if (!$module['root_writable']) { - return false; - } - $content = self::generateXML($id, $module, $file_pattern); - if (!empty(self::$failed)) { - return false; - } - - try { - files::putContent($module['root'] . '/dcstore.xml', $content); - } catch (Exception $e) { - self::$failed[] = $e->getMessage(); - - return false; - } - - return true; - } - - public static function prettyXML(string $str): string - { - if (class_exists('DOMDocument')) { - $dom = new DOMDocument('1.0'); - $dom->preserveWhiteSpace = false; - $dom->formatOutput = true; - $dom->loadXML($str); - - return (string) $dom->saveXML(); - } - - return (string) str_replace('><', ">\n<", $str); - } -} diff --git a/inc/Prepend.php b/inc/Prepend.php deleted file mode 100644 index e66f63f..0000000 --- a/inc/Prepend.php +++ /dev/null @@ -1,50 +0,0 @@ -autoload([ - __NAMESPACE__ . '\\' . $lib => __DIR__ . DIRECTORY_SEPARATOR . $lib . '.php', - ]); - } - - return true; - } -} diff --git a/src/Backend.php b/src/Backend.php index 2d5a82b..9815955 100644 --- a/src/Backend.php +++ b/src/Backend.php @@ -14,270 +14,38 @@ declare(strict_types=1); namespace Dotclear\Plugin\tweakStores; -/* dotclear ns */ use dcCore; -use dcPage; +use dcNsProcess; -/* clearbricks ns */ -use form; -use html; - -/* php ns */ -use Exception; - -class Admin +class Backend extends dcNsProcess { - private static $pid = ''; - protected static $init = false; - public static function init(): bool { - 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::$pid = basename(dirname(__DIR__)); - self::$init = true; - } + static::$init = My::phpCompliant() + && defined('DC_CONTEXT_ADMIN') + && dcCore::app()->auth->isSuperAdmin() + && dcCore::app()->blog->settings->get(My::id())->get('active'); - return self::$init; + return static::$init; } - public static function process(): ?bool + public static function process(): bool { - if (!self::$init) { + if (!static::$init) { return false; } - if (dcCore::app()->blog->settings->get(self::$pid)->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(self::$pid)->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')); - }, + 'pluginsToolsHeadersV2' => [BackendBehaviors::class, 'modulesToolsHeaders'], + 'themesToolsHeadersV2' => [BackendBehaviors::class, 'modulesToolsHeaders'], + // admin modules page tab + 'pluginsToolsTabsV2' => [BackendBehaviors::class, 'pluginsToolsTabsV2'], + 'themesToolsTabsV2' => [BackendBehaviors::class, 'themesToolsTabsV2'], + // add to plugin pacKman + 'packmanBeforeCreatePackage' => [BackendBehaviors::class, 'packmanBeforeCreatePackage'], ]); return true; } - - public static function modulesToolsHeaders(bool $is_plugin): string - { - return - dcPage::jsJson('ts_copied', ['alert' => __('Copied to clipboard')]) . - dcPage::jsModuleLoad(self::$pid . '/js/admin.js') . - ( - !dcCore::app()->auth->user_prefs->interface->colorsyntax ? '' : - dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->interface->colorsyntax_theme) . - dcPage::jsModuleLoad(self::$pid . '/js/cms.js') - ); - } - - protected static function modulesToolsTabs(array $modules, array $excludes, string $page_url): void - { - $page_url .= '#' . self::$pid; - $user_ui_colorsyntax = dcCore::app()->auth->user_prefs->interface->colorsyntax; - $user_ui_colorsyntax_theme = dcCore::app()->auth->user_prefs->interface->colorsyntax_theme; - $combo = self::comboModules($modules, $excludes); - $file_pattern = dcCore::app()->blog->settings->get(self::$pid)->get('file_pattern'); - - # check dcstore repo - $url = ''; - if (!empty($_POST['checkxml_id']) && in_array($_POST['checkxml_id'], $combo)) { - if (empty($modules[$_POST['checkxml_id']]['repository'])) { - $url = __('This module has no repository set in its _define.php file.'); - } else { - try { - $url = $modules[$_POST['checkxml_id']]['repository']; - if (false === strpos($url, 'dcstore.xml')) { - $url .= '/dcstore.xml'; - } - if (function_exists('curl_init')) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_REFERER, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - $file_content = curl_exec($ch); - curl_close($ch); - } else { - $file_content = file_get_contents($url); - } - } catch (Exception $e) { - $file_content = __('Failed to read third party repository'); - } - } - } - - # generate xml code - if (!empty($_POST['buildxml_id']) && in_array($_POST['buildxml_id'], $combo)) { - $xml_content = Core::generateXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern); - } - - # write dcstore.xml file - if (!empty($_POST['write_xml'])) { - if (empty($_POST['your_pwd']) || !dcCore::app()->auth->checkPassword($_POST['your_pwd'])) { - dcCore::app()->error->add(__('Password verification failed')); - } else { - $ret = Core::writeXML($_POST['buildxml_id'], $modules[$_POST['buildxml_id']], $file_pattern); - if (!empty(Core::$failed)) { - dcCore::app()->error->add(implode(' ', Core::$failed)); - } - } - } - echo - '
' . - '

' . __('Tweak third-party repositories') . '

'; - - if (!empty($_POST['write_xml'])) { - if (dcCore::app()->error->flag()) { - echo dcCore::app()->error->toHTML(); - } else { - echo '

' . __('File successfully written') . '

'; - } - } - if (count($combo) < 2) { - echo - '
' . __('There is no module to tweak') . '
' . - '
'; - - return; - } - - echo - '
' . - '

' . __('Check repository') . '

' . - '

' . __('This checks if dcstore.xml file is present on third party repository.') . '

' . - '

' . - form::combo('checkxml_id', $combo, empty($_POST['checkxml_id']) ? '-' : html::escapeHTML($_POST['checkxml_id'])) . - '

' . - '

' . - dcCore::app()->formNonce() . '

' . - '
'; - - if (!empty($url)) { - echo - '
' . - '

' . __('Repositiory contents') . '

' . - '

' . $url . '

' . - ( - empty($file_content) ? '' : - '
' . form::textArea('file_xml', 165, 14, [
-                    'default'    => html::escapeHTML(Core::prettyXML($file_content)),
-                    'class'      => 'maximal',
-                    'extra_html' => 'readonly="true"',
-                ]) . '
' . - ( - !$user_ui_colorsyntax ? '' : - dcPage::jsRunCodeMirror('editor', 'file_xml', 'dotclear', $user_ui_colorsyntax_theme) - ) - ) . - '
'; - } - - if (empty($file_pattern)) { - echo sprintf( - '

' . __('Generate xml code') . '

%s

', - dcCore::app()->adminurl->get('admin.plugins', ['module' => self::$pid, 'conf' => 1, 'redir' => $page_url]), - __('You must configure zip file pattern to complete xml code automatically.') - ); - } else { - echo - '
' . - '

' . __('Generate xml code') . '

' . - '

' . __('This helps to generate content of dcstore.xml for seleted module.') . '

' . - '

' . - form::combo('buildxml_id', $combo, empty($_POST['buildxml_id']) ? '-' : html::escapeHTML($_POST['buildxml_id'])) . - '

' . - '

' . - dcCore::app()->formNonce() . '

' . - '
'; - } - if (!empty($_POST['buildxml_id'])) { - echo - '
' . - '

' . sprintf(__('Generated code for module: %s'), html::escapeHTML($_POST['buildxml_id'])) . '

'; - - if (!empty(Core::$failed)) { - echo '

' . sprintf(__('Failed to parse XML code: %s'), implode(', ', Core::$failed)) . '

'; - } - if (!empty(Core::$notice)) { - echo '

' . sprintf(__('Code is not fully filled: %s'), implode(', ', Core::$notice)) . '

'; - } - if (!empty($xml_content)) { - if (empty(Core::$failed) && empty(Core::$notice)) { - echo '

' . __('Code is complete') . '

'; - } - echo - '
' . form::textArea('gen_xml', 165, 14, [
-                    'default'    => html::escapeHTML(Core::prettyXML($xml_content)),
-                    'class'      => 'maximal',
-                    'extra_html' => 'readonly="true"',
-                ]) . '
' . - ( - !$user_ui_colorsyntax ? '' : - dcPage::jsRunCodeMirror('editor', 'gen_xml', 'dotclear', $user_ui_colorsyntax_theme) - ); - - if (empty(Core::$failed) - && $modules[$_POST['buildxml_id']]['root_writable'] - && dcCore::app()->auth->isSuperAdmin() - ) { - echo - '

' . - form::password( - ['your_pwd', 'your_pwd2'], - 20, - 255, - [ - 'extra_html' => 'required placeholder="' . __('Password') . '"', - 'autocomplete' => 'current-password', - ] - ) . '

' . - '

' . - '' . __('Copy to clipboard') . '' . - form::hidden('buildxml_id', $_POST['buildxml_id']) . - dcCore::app()->formNonce() . '

'; - } - echo sprintf( - '

%s

', - dcCore::app()->adminurl->get('admin.plugins', ['module' => self::$pid, 'conf' => 1, 'redir' => $page_url]), - __('You can edit zip file pattern from configuration page.') - ); - } - echo - '
'; - } - echo - ''; - } - - # create list of module for combo and remove official modules - protected static function comboModules(array $modules, array $excludes): array - { - $combo = [__('Select a module') => '0']; - foreach ($modules as $id => $module) { - if (in_array($id, $excludes)) { - continue; - } - $combo[$module['name'] . ' ' . $module['version']] = $id; - } - - return $combo; - } } diff --git a/src/BackendBehaviors.php b/src/BackendBehaviors.php new file mode 100644 index 0000000..cfdb8a3 --- /dev/null +++ b/src/BackendBehaviors.php @@ -0,0 +1,421 @@ +blog->settings->get(My::id())->get('packman')) { + return; + } + + // move from array to dcModuleDefine object + $modules = $module['type'] == 'theme' ? dcCore::app()->themes : dcCore::app()->plugins; + $define = $modules->getDefine($module['id']); + + self::writeXML($define, dcCore::app()->blog->settings->get(My::id())->get('file_pattern')); + } + + public static function modulesToolsHeaders(bool $is_plugin): string + { + return + dcPage::jsJson('ts_copied', ['alert' => __('Copied to clipboard')]) . + dcPage::jsModuleLoad(My::id() . '/js/backend.js') . + ( + !dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax') ? '' : + dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme')) . + dcPage::jsModuleLoad(My::id() . '/js/cms.js') + ); + } + + public static function pluginsToolsTabsV2(): void + { + self::modulesToolsTabs(dcCore::app()->plugins, explode(',', DC_DISTRIB_PLUGINS), dcCore::app()->adminurl->get('admin.plugins')); + } + + public static function themesToolsTabsV2(): void + { + self::modulesToolsTabs(dcCore::app()->themes, explode(',', DC_DISTRIB_THEMES), dcCore::app()->adminurl->get('admin.blog.theme')); + } + + private static function modulesToolsTabs(dcModules $modules, array $excludes, string $page_url): void + { + $page_url .= '#' . My::id(); + $user_ui_colorsyntax = dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax'); + $user_ui_colorsyntax_theme = dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme'); + $file_pattern = (new Settings())->file_pattern; + + $module = $modules->getDefine($_POST['ts_id'] ?? '-'); + $combo = self::comboModules($modules, $excludes); + $form = '

' . + form::combo('ts_id', $combo, $module->isDefined() ? html::escapeHTML($module->get('id')) : '-') . + '

'; + + # check dcstore repo + $url = ''; + if (!empty($_POST['check_xml']) && $module->isDefined()) { + if (empty($module->get('repository'))) { + $url = __('This module has no repository set in its _define.php file.'); + } else { + try { + $url = $module->get('repository'); + if (false === strpos($url, 'dcstore.xml')) { + $url .= '/dcstore.xml'; + } + if (function_exists('curl_init')) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_REFERER, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $file_content = curl_exec($ch); + curl_close($ch); + } else { + $file_content = file_get_contents($url); + } + } catch (Exception $e) { + $file_content = __('Failed to read third party repository'); + } + } + } + + # generate xml code + if (!empty($_POST['build_xml']) && $module->isDefined()) { + $xml_content = self::generateXML($module, $file_pattern); + } + + # write dcstore.xml file + if (!empty($_POST['write_xml'])) { + if (empty($_POST['your_pwd']) || !dcCore::app()->auth->checkPassword($_POST['your_pwd'])) { + dcCore::app()->error->add(__('Password verification failed')); + } else { + $ret = self::writeXML($module, $file_pattern); + if (!empty(self::$failed)) { + dcCore::app()->error->add(implode(' ', self::$failed)); + } + } + } + echo + '
' . + '

' . __('Tweak third-party repositories') . '

'; + + if (!empty($_POST['write_xml'])) { + if (dcCore::app()->error->flag()) { + echo dcCore::app()->error->toHTML(); + } else { + echo '

' . __('File successfully written') . '

'; + } + } + if (count($combo) < 2) { + echo + '
' . __('There is no module to tweak') . '
' . + '
'; + + return; + } + + echo + '
' . + '

' . __('Check repository') . '

' . + '

' . __('This checks if dcstore.xml file is present on third party repository.') . '

' . + $form . + '

' . + dcCore::app()->formNonce() . '

' . + '
'; + + if (!empty($url)) { + echo + '
' . + '

' . __('Repositiory contents') . '

' . + '

' . $url . '

' . + ( + empty($file_content) ? '' : + '
' . form::textArea('file_xml', 165, 14, [
+                    'default'    => html::escapeHTML(self::prettyXML($file_content)),
+                    'class'      => 'maximal',
+                    'extra_html' => 'readonly="true"',
+                ]) . '
' . + ( + !$user_ui_colorsyntax ? '' : + dcPage::jsRunCodeMirror('editor', 'file_xml', 'dotclear', $user_ui_colorsyntax_theme) + ) + ) . + '
'; + } + + if (empty($file_pattern)) { + echo sprintf( + '

' . __('Generate xml code') . '

%s

', + dcCore::app()->adminurl->get('admin.plugins', ['module' => My::id(), 'conf' => 1, 'redir' => $page_url]), + __('You must configure zip file pattern to complete xml code automatically.') + ); + } else { + echo + '
' . + '

' . __('Generate xml code') . '

' . + '

' . __('This helps to generate content of dcstore.xml for seleted module.') . '

' . + $form . + '

' . + dcCore::app()->formNonce() . '

' . + '
'; + } + if (!empty($_POST['build_xml'])) { + echo + '
' . + '

' . sprintf(__('Generated code for module: %s'), html::escapeHTML($module->get('id'))) . '

'; + + if (!empty(self::$failed)) { + echo '

' . sprintf(__('Failed to parse XML code: %s'), implode(', ', self::$failed)) . '

'; + } + if (!empty(self::$notice)) { + echo '

' . sprintf(__('Code is not fully filled: %s'), implode(', ', self::$notice)) . '

'; + } + if (!empty($xml_content)) { + if (empty(self::$failed) && empty(self::$notice)) { + echo '

' . __('Code is complete') . '

'; + } + echo + '
' . form::textArea('gen_xml', 165, 14, [
+                    'default'    => html::escapeHTML(self::prettyXML($xml_content)),
+                    'class'      => 'maximal',
+                    'extra_html' => 'readonly="true"',
+                ]) . '
' . + ( + !$user_ui_colorsyntax ? '' : + dcPage::jsRunCodeMirror('editor', 'gen_xml', 'dotclear', $user_ui_colorsyntax_theme) + ); + + if (empty(self::$failed) + && $module->get('root_writable') + && dcCore::app()->auth->isSuperAdmin() + ) { + echo + '

' . + form::password( + ['your_pwd', 'your_pwd2'], + 20, + 255, + [ + 'extra_html' => 'required placeholder="' . __('Password') . '"', + 'autocomplete' => 'current-password', + ] + ) . '

' . + '

' . + '' . __('Copy to clipboard') . '' . + form::hidden('ts_id', $_POST['ts_id']) . + dcCore::app()->formNonce() . '

'; + } + echo sprintf( + '

%s

', + dcCore::app()->adminurl->get('admin.plugins', ['module' => My::id(), 'conf' => 1, 'redir' => $page_url]), + __('You can edit zip file pattern from configuration page.') + ); + } + echo + '
'; + } + echo + ''; + } + + # create list of module for combo and remove official modules + private static function comboModules(dcModules $modules, array $excludes): array + { + $combo = [__('Select a module') => '0']; + foreach ($modules->getDefines() as $module) { + if (in_array($module->get('id'), $excludes)) { + continue; + } + $combo[$module->get('name') . ' ' . $module->get('version')] = $module->get('id'); + } + + return $combo; + } + + private static function parseFilePattern(dcModuleDefine $module, string $file_pattern): string + { + return text::tidyURL(str_replace( + [ + '%type%', + '%id%', + '%version%', + '%author%', + ], + [ + $module->get('type'), + $module->get('id'), + $module->get('version'), + $module->get('author'), + ], + $file_pattern + )); + } + + private static function generateXML(dcModuleDefine $module, string $file_pattern): string + { + $rsp = new xmlTag('module'); + + self::$notice = []; + self::$failed = []; + + # id + if (!$module->isDefined()) { + self::$failed[] = 'unknow module'; + } + $rsp->id = $module->get('id'); + + # name + if (empty($module->get('name'))) { + self::$failed[] = 'no module name set in _define.php'; + } + $rsp->name($module->get('name')); + + # version + if (empty($module->get('version'))) { + self::$failed[] = 'no module version set in _define.php'; + } + $rsp->version($module->get('version')); + + # author + if (empty($module->get('author'))) { + self::$failed[] = 'no module author set in _define.php'; + } + $rsp->author($module->get('author')); + + # desc + if (empty($module->get('desc'))) { + self::$failed[] = 'no module description set in _define.php'; + } + $rsp->desc($module->get('desc')); + + # repository + if (empty($module->get('repository'))) { + self::$failed[] = 'no repository set in _define.php'; + } + + # file + $file_pattern = self::parseFilePattern($module, $file_pattern); + if (empty($file_pattern)) { + self::$failed[] = 'no zip file pattern set in Tweak Store configuration'; + } + $rsp->file($file_pattern); + + # da dc_min or requires core + if (!empty($module->get('requires')) && is_array($module->get('requires'))) { + foreach ($module->get('requires') as $req) { + if (!is_array($req)) { + $req = [$req]; + } + if ($req[0] == 'core') { + $module->set('dc_min', $req[1]); + + break; + } + } + } + if (empty($module->get('dc_min'))) { + self::$notice[] = 'no minimum dotclear version'; + } else { + $rsp->insertNode(new xmlTag('da:dcmin', $module->get('dc_min'))); + } + + # details + if (empty($module->get('details'))) { + self::$notice[] = 'no details URL'; + } else { + $rsp->insertNode(new xmlTag('da:details', $module->get('details'))); + } + + # section + if (!empty($module->get('section'))) { + $rsp->insertNode(new xmlTag('da:section', $module->get('section'))); + } + + # support + if (empty($module->get('support'))) { + self::$notice[] = 'no support URL'; + } else { + $rsp->insertNode(new xmlTag('da:support', $module->get('support'))); + } + + $res = new xmlTag('modules', $rsp); + $res->insertAttr('xmlns:da', 'http://dotaddict.org/da/'); + + return self::prettyXML($res->toXML()); + } + + private static function writeXML(dcModuleDefine $module, string $file_pattern): bool + { + self::$failed = []; + if (!$module->get('root_writable')) { + return false; + } + $content = self::generateXML($module, $file_pattern); + if (!empty(self::$failed)) { + return false; + } + + try { + files::putContent($module->get('root') . DIRECTORY_SEPARATOR . 'dcstore.xml', $content); + } catch (Exception $e) { + self::$failed[] = $e->getMessage(); + + return false; + } + + return true; + } + + private static function prettyXML(string $str): string + { + if (class_exists('DOMDocument')) { + $dom = new DOMDocument('1.0'); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML($str); + + return (string) $dom->saveXML(); + } + + return (string) str_replace('><', ">\n<", $str); + } +} diff --git a/src/Config.php b/src/Config.php index fdaff7d..ac36b9b 100644 --- a/src/Config.php +++ b/src/Config.php @@ -16,89 +16,98 @@ namespace Dotclear\Plugin\tweakStores; /* dotclear ns */ use dcCore; +use dcNsProcess; use dcPage; - -/* clearbricks ns */ -use form; -use http; +use Dotclear\Helper\Html\Form\{ + Checkbox, + Div, + Fieldset, + Input, + Label, + Legend, + Note, + Para +}; /* php ns */ use Exception; -class Config +class Config extends dcNsProcess { - private static $pid = ''; - protected static $init = false; - public static function init(): bool { - if (defined('DC_CONTEXT_ADMIN') && defined('DC_CONTEXT_MODULE')) { - dcPage::checkSuper(); - self::$pid = basename(dirname(__DIR__)); - self::$init = true; - } + static::$init = My::phpCompliant() + && defined('DC_CONTEXT_ADMIN') + && defined('DC_CONTEXT_MODULE') + && dcCore::app()->auth->isSuperAdmin(); - return self::$init; + return static::$init; } - public static function process(): ?bool + public static function process(): bool { - if (!self::$init) { + if (!static::$init) { return false; } if (empty($_POST['save'])) { - return null; + return true; } + $s = new Settings(); + try { - $s = dcCore::app()->blog->settings->get(self::$pid); - $s->put('active', !empty($_POST['s_active'])); - $s->put('packman', !empty($_POST['s_packman'])); - $s->put('file_pattern', $_POST['s_file_pattern']); + foreach ($s->listSettings() as $key) { + $s->writeSetting($key, $_POST['ts_' . $key] ?? ''); + } dcPage::addSuccessNotice( __('Configuration successfully updated') ); - http::redirect( - dcCore::app()->admin->__get('list')->getURL('module=' . self::$pid . '&conf=1&redir=' . dcCore::app()->admin->__get('list')->getRedir()) + dcCore::app()->adminurl->redirect( + 'admin.plugins', + ['module' => My::id(), 'conf' => 1, 'redir' => dcCore::app()->admin->__get('list')->getRedir()] ); - - return true; } catch (Exception $e) { dcCore::app()->error->add($e->getMessage()); } - return null; + return true; } public static function render(): void { - $s = dcCore::app()->blog->settings->get(self::$pid); + if (!static::$init) { + return; + } - echo ' -
-

' . dcCore::app()->plugins->moduleInfo(self::$pid, 'name') . '

+ $s = new Settings(); -

-

' . __('If enabled, new tab "Tweak stores" allows your to perfom actions relative to third-party repositories.') . '

- -

-

' . __('If enabled, plugin pacKman will (re)generate on the fly dcstore.xml file at root directory of the module.') . '

- -

-

' . - __('You can use widcard like %author%, %type%, %id%, %version%.') . '
' . - __('For example on github https://github.com/MyGitName/%id%/releases/download/v%version%/%type%-%id%.zip') . '
' . - __('Note: on github, you must create a release and join to it the module zip file.') . ' -

- -
'; + echo (new Div())->items([ + (new Fieldset())->class('fieldset')->legend(new Legend(__('Interface')))->fields([ + // s_active + (new Para())->items([ + (new Checkbox('ts_active', $s->active))->value(1), + (new Label(__('Enable plugin'), Label::OUTSIDE_LABEL_AFTER))->for('ts_active')->class('classic'), + ]), + (new Note())->text(__('If enabled, new tab "Tweak stores" allows your to perfom actions relative to third-party repositories.'))->class('form-note'), + // s_file_pattern + (new Para())->items([ + (new Label(__('Predictable URL to zip file on the external repository')))->for('ts_file_pattern'), + (new Input('ts_file_pattern'))->size(65)->maxlenght(255)->value($s->file_pattern), + ]), + (new Note())->text(__('You can use widcard like %author%, %type%, %id%, %version%.'))->class('form-note'), + (new Note())->text(__('For example on github https://github.com/MyGitName/%id%/releases/download/v%version%/%type%-%id%.zip'))->class('form-note'), + (new Note())->text(__('Note: on github, you must create a release and join to it the module zip file.'))->class('form-note'), + ]), + (new Fieldset())->class('fieldset')->legend(new Legend(__('Behaviors')))->fields([ + // s_packman + (new Para())->items([ + (new Checkbox('ts_packman', $s->packman))->value(1), + (new Label(__('Enable packman behaviors'), Label::OUTSIDE_LABEL_AFTER))->for('ts_packman')->class('classic'), + ]), + (new Note())->text(__('If enabled, plugin pacKman will (re)generate on the fly dcstore.xml file at root directory of the module.'))->class('form-note'), + ]), + ])->render(); } } diff --git a/src/My.php b/src/My.php new file mode 100644 index 0000000..ab6211e --- /dev/null +++ b/src/My.php @@ -0,0 +1,50 @@ +plugins->moduleInfo(self::id(), 'name')); + } + + /** + * Check php version + */ + public static function phpCompliant(): bool + { + return version_compare(phpversion(), self::PHP_MIN, '>='); + } +} diff --git a/src/Settings.php b/src/Settings.php new file mode 100644 index 0000000..6fd8cda --- /dev/null +++ b/src/Settings.php @@ -0,0 +1,76 @@ +blog->settings->get(My::id()); + + $this->active = (bool) ($s->get('active') ?? false); + $this->packman = (bool) ($s->get('packman') ?? false); + $this->file_pattern = (string) ($s->get('file_pattern') ?? ''); + } + + public function getSetting(string $key): mixed + { + return $this->{$key} ?? null; + } + + /** + * Overwrite a plugin settings (in db) + * + * @param string $key The setting ID + * @param mixed $value The setting value + * + * @return bool True on success + */ + public function writeSetting(string $key, mixed $value): bool + { + if (property_exists($this, $key) && settype($value, gettype($this->{$key})) === true) { + dcCore::app()->blog->settings->get(My::id())->drop($key); + dcCore::app()->blog->settings->get(My::id())->put($key, $value, gettype($this->{$key}), '', true, true); + + return true; + } + + return false; + } + + /** + * List defined settings keys + * + * @return array The settings keys + */ + public function listSettings(): array + { + return array_keys(get_class_vars(Settings::class)); + } +}