Compare commits

...

3 Commits
v1.0 ... master

Author SHA1 Message Date
Jean-Christian Paul Denis 51af8043c6
code review 2023-11-04 22:07:38 +01:00
Jean-Christian Paul Denis aeecb70024
update README 2023-10-21 14:44:26 +02:00
Jean-Christian Paul Denis 828d3905d3
upgrade to dotclear 2.28 2023-10-21 14:41:39 +02:00
9 changed files with 217 additions and 136 deletions

View File

@ -1,3 +1,16 @@
tweakStores 1.2 - 2023.11.04
===========================================================
* Require Dotclear 2.28
* Require PHP 8.1
* Code review
tweakStores 1.1 - 2023.10.21
===========================================================
* Require Dotclear 2.28
* Require PHP 8.1
* Upgrade to Dotclear 2.28
* Code review (phpstan)
tweakStores 1.0 - 2023.08.14
===========================================================
* Require Dotclear 2.27

View File

@ -1,25 +1,22 @@
# README
[![Release](https://img.shields.io/badge/release-1.0-a2cbe9.svg)](https://git.dotclear.watch/JcDenis/tweakStores/releases)
[![Date](https://img.shields.io/badge/date-2023.08.15-c44d58.svg)](https://git.dotclear.watch/JcDenis/tweakStores/releases)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.27-137bbb.svg)](https://fr.dotclear.org/download)
[![Release](https://img.shields.io/badge/release-1.2-a2cbe9.svg)](https://git.dotclear.watch/JcDenis/tweakStores/releases)
![Date](https://img.shields.io/badge/date-2023.11.04-c44d58.svg)
[![Dotclear](https://img.shields.io/badge/dotclear-v2.28-137bbb.svg)](https://fr.dotclear.org/download)
[![Dotaddict](https://img.shields.io/badge/dotaddict-official-9ac123.svg)](https://plugins.dotaddict.org/dc2/details/tweakStores)
[![License](https://img.shields.io/github/license/JcDenis/tweakStores)](https://git.dotclear.watch/JcDenis/tweakStores/blob/master/LICENSE)
[![License](https://img.shields.io/badge/license-GPL--2.0-ececec.svg)](https://git.dotclear.watch/JcDenis/tweakStores/src/branch/master/LICENSE)
## WHAT IS TWEAKSTORES ?
## ABOUT
"tweakStores" is a plugin for the open-source
web publishing software called Dotclear.
_tweakStores_ is a plugin for the open-source web publishing software called [Dotclear](https://www.dotclear.org).
It helps devs to manage dcstore.xml file.
> Help devs to manage dcstore.xml file.
## REQUIREMENTS
_tweakStores_ requires:
* superadmin permissions
* Dotclear 2.27
* Dotclear 2.28
* PHP 8.1+
* Dotclear superadmin permissions
## USAGE
@ -32,9 +29,10 @@ package creation.
## LINKS
* License : [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html)
* Source & contribution : [Gitea Page](https://git.dotclear.watch/JcDenis/tweakStores) or [GitHub Page](https://github.com/JcDenis/tweakStores)
* Packages & details: [Gitea Page](https://git.dotclear.watch/JcDenis/tweakStores/releases) or [Dotaddict Page](https://plugins.dotaddict.org/dc2/details/tweakStores)
* [License](https://git.dotclear.watch/JcDenis/tweakStores/src/branch/master/LICENSE)
* [Packages & details](https://git.dotclear.watch/JcDenis/tweakStores/releases) (or on [Dotaddict](https://plugins.dotaddict.org/dc2/details/tweakStores))
* [Sources & contributions](https://git.dotclear.watch/JcDenis/tweakStores) (or on [GitHub](https://github.com/JcDenis/tweakStores))
* [Issues & security](https://git.dotclear.watch/JcDenis/tweakStores/issues) (or on [GitHub](https://github.com/JcDenis/tweakStores/issues))
## CONTRIBUTORS

View File

@ -1,30 +1,26 @@
<?php
/**
* @brief tweakStores, a plugin for Dotclear 2
* @file
* @brief The plugin tweakStores definition
* @ingroup tweakStores
*
* @package Dotclear
* @subpackage Plugin
* @defgroup tweakStores Plugin tweakStores.
*
* @author Jean-Christian Denis and Contributors
* Helper to manage external repositories.
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
declare(strict_types=1);
$this->registerModule(
'Tweak stores',
'Helper to manage external repositories',
'Jean-Christian Denis and Contributors',
'1.0',
'1.2',
[
'requires' => [
['php', '8.1'],
['core', '2.27'],
],
'permissions' => null,
'requires' => [['core', '2.28']],
'permissions' => 'My',
'type' => 'plugin',
'support' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/issues',
'details' => 'https://git.dotclear.watch/JcDenis/' . basename(__DIR__) . '/src/branch/master/README.md',

View File

@ -2,11 +2,11 @@
<modules xmlns:da="http://dotaddict.org/da/">
<module id="tweakStores">
<name>Tweak stores</name>
<version>1.0</version>
<version>1.2</version>
<author>Jean-Christian Denis and Contributors</author>
<desc>Helper to manage external repositories</desc>
<file>https://git.dotclear.watch/JcDenis/tweakStores/releases/download/v1.0/plugin-tweakStores.zip</file>
<da:dcmin>2.27</da:dcmin>
<file>https://git.dotclear.watch/JcDenis/tweakStores/releases/download/v1.2/plugin-tweakStores.zip</file>
<da:dcmin>2.28</da:dcmin>
<da:details>https://git.dotclear.watch/JcDenis/tweakStores/src/branch/master/README.md</da:details>
<da:support>https://git.dotclear.watch/JcDenis/tweakStores/issues</da:support>
</module>

View File

@ -1,22 +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);
namespace Dotclear\Plugin\tweakStores;
use dcCore;
use Dotclear\App;
use Dotclear\Core\Process;
/**
* @brief tweakStores backend class.
* @ingroup tweakStores
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Backend extends Process
{
public static function init(): bool
@ -30,15 +27,15 @@ class Backend extends Process
return false;
}
dcCore::app()->addBehaviors([
App::behavior()->addBehaviors([
// addd some js
'pluginsToolsHeadersV2' => [BackendBehaviors::class, 'modulesToolsHeaders'],
'themesToolsHeadersV2' => [BackendBehaviors::class, 'modulesToolsHeaders'],
'pluginsToolsHeadersV2' => BackendBehaviors::modulesToolsHeaders(...),
'themesToolsHeadersV2' => BackendBehaviors::modulesToolsHeaders(...),
// admin modules page tab
'pluginsToolsTabsV2' => [BackendBehaviors::class, 'pluginsToolsTabsV2'],
'themesToolsTabsV2' => [BackendBehaviors::class, 'themesToolsTabsV2'],
'pluginsToolsTabsV2' => BackendBehaviors::pluginsToolsTabsV2(...),
'themesToolsTabsV2' => BackendBehaviors::themesToolsTabsV2(...),
// add to plugin pacKman
'packmanBeforeCreatePackage' => [BackendBehaviors::class, 'packmanBeforeCreatePackage'],
'packmanBeforeCreatePackage' => BackendBehaviors::packmanBeforeCreatePackage(...),
]);
return true;

View File

@ -1,22 +1,12 @@
<?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);
namespace Dotclear\Plugin\tweakStores;
use dcCore;
use dcModuleDefine;
use dcModules;
use Dotclear\App;
use Dotclear\Module\ModuleDefine;
use Dotclear\Interface\Module\ModulesInterface;
use Dotclear\Core\Backend\{
Notices,
Page
@ -43,17 +33,41 @@ use Dotclear\Helper\Text;
use DOMDocument;
use Exception;
/**
* @brief tweakStores backend behaviors class.
* @ingroup tweakStores
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class BackendBehaviors
{
/** @var array List of notice messages */
/**
* List of notice messages.
*
* @var array<int, string> $notice
*/
private static $notice = [];
/** @var array List of failed messages */
/**
* List of failed messages.
*
* @var array<int, string> $failed
*/
private static $failed = [];
/** @var Settings Module settings */
/**
* Module settings.
*
* @var Settings $settings
*/
private static $settings;
/**
* Get settings instance.
*
* @return Settings Settings helper instance
*/
private static function settings(): Settings
{
if (!(self::$settings instanceof Settings)) {
@ -63,6 +77,13 @@ class BackendBehaviors
return self::$settings;
}
/**
* Plugin pacKman behavior.
*
* Add dcstore.xml file on the fly on pacKman package creation.
*
* @param array<string, string> $module
*/
public static function packmanBeforeCreatePackage(array $module): void
{
if (self::settings()->packman) {
@ -70,27 +91,37 @@ class BackendBehaviors
}
// move from array to dcModuleDefine object
$modules = $module['type'] == 'theme' ? dcCore::app()->themes : dcCore::app()->plugins;
$modules = $module['type'] == 'theme' ? App::themes() : App::plugins();
$define = $modules->getDefine($module['id']);
self::writeXML($define, self::settings()->file_pattern);
}
/**
* Add list headers and save settings.
*
* @param bool $is_theme Is on themes list
*
* @return string HTML header code
*/
public static function modulesToolsHeaders(bool $is_theme): string
{
//save settings (before page header sent)
if (!empty($_POST['tweakstore_save'])) {
try {
foreach (self::settings()->dump() as $key => $value) {
if (!is_string($key)) {
continue;
}
self::settings()->set($key, $_POST['ts_' . $key] ?? $value);
}
Notices::addSuccessNotice(
__('Configuration successfully updated')
);
dcCore::app()->admin->url->redirect($is_theme ? 'admin.blog.theme' : 'admin.plugins', ['tab' => My::id()]);
App::backend()->url()->redirect($is_theme ? 'admin.blog.theme' : 'admin.plugins', ['tab' => My::id()]);
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
App::error()->add($e->getMessage());
}
}
@ -98,28 +129,40 @@ class BackendBehaviors
Page::jsJson('tweakstore_copied', ['alert' => __('Copied to clipboard')]) .
My::jsLoad('backend') .
(
!dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax') ? '' :
Page::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme')) .
!App::auth()->prefs()->get('interface')->get('colorsyntax') ? '' :
Page::jsLoadCodeMirror(App::auth()->prefs()->get('interface')->get('colorsyntax_theme')) .
My::jsLoad('cms')
);
}
/**
* Plugins tab.
*/
public static function pluginsToolsTabsV2(): void
{
self::modulesToolsTabs(dcCore::app()->plugins, (string) dcCore::app()->admin->url->get('admin.plugins'));
self::modulesToolsTabs(App::plugins(), (string) App::backend()->url()->get('admin.plugins'));
}
/**
* Themes tab.
*/
public static function themesToolsTabsV2(): void
{
self::modulesToolsTabs(dcCore::app()->themes, (string) dcCore::app()->admin->url->get('admin.blog.theme'));
self::modulesToolsTabs(App::themes(), (string) App::backend()->url()->get('admin.blog.theme'));
}
private static function modulesToolsTabs(dcModules $modules, string $page_url): void
/**
* Modules tab.
*
* @param ModulesInterface $modules The modules
* @param string $page_url The page URL
*/
private static function modulesToolsTabs(ModulesInterface $modules, string $page_url): void
{
// settings
$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');
$user_ui_colorsyntax = App::auth()->prefs()->get('interface')->get('colorsyntax');
$user_ui_colorsyntax_theme = App::auth()->prefs()->get('interface')->get('colorsyntax_theme');
$file_pattern = self::settings()->file_pattern;
$local_content = $distant_content = '';
@ -162,12 +205,12 @@ class BackendBehaviors
// write dcstore.xml file
if (!empty($_POST['tweakstore_write'])) {
if (empty($_POST['your_pwd']) || !dcCore::app()->auth->checkPassword($_POST['your_pwd'])) {
dcCore::app()->error->add(__('Password verification failed'));
if (empty($_POST['your_pwd']) || !App::auth()->checkPassword($_POST['your_pwd'])) {
App::error()->add(__('Password verification failed'));
} else {
self::writeXML($module, self::settings()->file_pattern);
if (!empty(self::$failed)) {
dcCore::app()->error->add(implode(' ', self::$failed));
App::error()->add(implode(' ', self::$failed));
}
}
}
@ -252,16 +295,16 @@ class BackendBehaviors
);
if ($module->get('root_writable')
&& dcCore::app()->auth->isSuperAdmin()
&& App::auth()->isSuperAdmin()
) {
echo
(new Para())->class('field')->items([
(new Label(__('Your password:')))->for('tweakstore_pwd')->class('required'),
(new Password(['your_pwd', 'tweakstore_pwd']))->size(20)->maxlenght(255)->required(true)->placeholder(__('Password'))->autocomplete('current-password'),
(new Password(['your_pwd', 'tweakstore_pwd']))->size(20)->maxlength(255)->required(true)->placeholder(__('Password'))->autocomplete('current-password'),
])->render() .
'<p><input type="submit" name="tweakstore_write" id="tweakstore_write" value="' . __('Save to module directory') . '" /> ' .
'<a class="hidden-if-no-js button" href="#' . My::id() . '" id="tweakstore_copy">' . __('Copy to clipboard') . '</a>' .
dcCore::app()->formNonce() . '</p>';
App::nonce()->getFormNonce() . '</p>';
}
echo
@ -273,7 +316,7 @@ class BackendBehaviors
(new Para())->items([
(new Submit('tweakstore_submit'))->value(__('Check')),
(new Hidden('tweakstore_do', '1')),
dcCore::app()->formNonce(false),
App::nonce()->formNonce(),
])->render() .
'</form>' .
@ -286,7 +329,7 @@ class BackendBehaviors
// 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)->class('maximal')->value(self::settings()->file_pattern),
(new Input('ts_file_pattern'))->size(65)->maxlength(255)->class('maximal')->value(self::settings()->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'),
@ -301,7 +344,7 @@ class BackendBehaviors
(new Para())->items([
(new Submit('tweakstore_save'))->value(__('Save')),
dcCore::app()->formNonce(false),
App::nonce()->formNonce(),
])->render() .
'</div>' .
@ -309,8 +352,12 @@ class BackendBehaviors
'</div>';
}
# create list of module for combo and remove official modules
private static function comboModules(dcModules $modules): array
/**
* Create list of module for combo and remove official modules.
*
* @return array<string, string>
*/
private static function comboModules(ModulesInterface $modules): array
{
$combo = [];
foreach ($modules->getDefines() as $module) {
@ -325,7 +372,15 @@ class BackendBehaviors
return array_merge([__('Select a module') => '0'], $combo);
}
private static function parseFilePattern(dcModuleDefine $module, string $file_pattern): string
/**
* Parse dcstore content.
*
* @param ModuleDefine $module The module
* @param string $file_pattern The file pattern
*
* @return string The parsed file content
*/
private static function parseFilePattern(ModuleDefine $module, string $file_pattern): string
{
return Text::tidyURL(str_replace(
[
@ -344,7 +399,15 @@ class BackendBehaviors
));
}
private static function generateXML(dcModuleDefine $module, string $file_pattern): string
/**
* Generate XML content from module.
*
* @param ModuleDefine $module The module
* @param string $file_pattern The file pattern
*
* @return string The XML content
*/
private static function generateXML(ModuleDefine $module, string $file_pattern): string
{
$rsp = new XmlTag('module');
@ -358,10 +421,10 @@ class BackendBehaviors
$rsp->insertAttr('id', $module->get('id'));
# name
if (empty($module->get('name'))) {
if (empty($module->get('label'))) {
self::$failed[] = 'no module name set in _define.php';
}
$rsp->insertNode(new XmlTag('name', $module->get('name')));
$rsp->insertNode(new XmlTag('name', $module->get('label')));
# version
if (empty($module->get('version'))) {
@ -437,7 +500,15 @@ class BackendBehaviors
return self::prettyXML($res->toXML());
}
private static function writeXML(dcModuleDefine $module, string $file_pattern): bool
/**
* Write XML content to dcstore file.
*
* @param ModuleDefine $module The module
* @param string $file_pattern The file pattern
*
* @return bool True on success
*/
private static function writeXML(ModuleDefine $module, string $file_pattern): bool
{
self::$failed = [];
if (!$module->get('root_writable')) {
@ -459,6 +530,13 @@ class BackendBehaviors
return true;
}
/**
* Arrange XML content.
*
* @param string $str The content
*
* @return string The pretty content
*/
private static function prettyXML(string $str): string
{
if (class_exists('DOMDocument')) {

View File

@ -1,29 +1,27 @@
<?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);
namespace Dotclear\Plugin\tweakStores;
use dcCore;
use Dotclear\App;
use Dotclear\Module\MyPlugin;
/**
* This module definitions.
* @brief tweakStores My helper.
* @ingroup tweakStores
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class My extends MyPlugin
{
public static function checkCustomContext(int $context): ?bool
{
return $context === My::BACKEND ? dcCore::app()->auth->isSuperAdmin() : null;
return match ($context) {
// Limit ot super admin
self::MODULE => App::auth()->isSuperAdmin(),
default => null,
};
}
}

View File

@ -1,25 +1,30 @@
<?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);
namespace Dotclear\Plugin\tweakStores;
/**
* @brief tweakStores settings helper.
* @ingroup tweakStores
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Settings
{
// Enable plugin pacKman behavior
/**
* Enable plugin pacKman behavior.
*
* @var bool $packman
*/
public readonly bool $packman;
// Predictable dcstore url
/**
* Predictable dcstore url.
*
* @var string $v
*/
public readonly string $file_pattern;
/**
@ -37,7 +42,7 @@ class Settings
* @param string $key The setting ID
* @param mixed $value The setting value
*
* @return bool True on success
* @return bool True on success
*/
public function set(string $key, mixed $value): bool
{
@ -54,7 +59,7 @@ class Settings
/**
* List defined settings keys
*
* @return array The settings keys
* @return array<string, bool|string> The settings keys
*/
public function dump(): array
{

View File

@ -1,23 +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);
namespace Dotclear\Plugin\tweakStores;
use dcCore;
use Dotclear\Core\Process;
use Dotclear\Plugin\Uninstaller\Uninstaller;
/**
* @brief tweakStores uninstall class.
* @ingroup tweakStores
*
* @author Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
class Uninstall extends Process
{
public static function init(): bool
@ -27,7 +23,7 @@ class Uninstall extends Process
public static function process(): bool
{
if (!self::status() || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
if (!self::status()) {
return false;
}