split Task class properties

This commit is contained in:
Jean-Christian Denis 2023-04-29 00:34:01 +02:00
parent effb961f6c
commit 0c304755e1
Signed by: JcDenis
GPG key ID: 1B5B8C5B90B6C951
22 changed files with 909 additions and 858 deletions

View file

@ -19,7 +19,6 @@ use dcCore;
use dcPage;
use dcFavorites;
use dcNsProcess;
use Dotclear\App;
use Dotclear\Helper\File\Files;
/**
@ -71,15 +70,23 @@ class Backend extends dcNsProcess
);
},
// Add actions to improve
'improveTaskAdd' => function (Tasks $actions): void {
$dir = __DIR__ . DIRECTORY_SEPARATOR . 'Task' . DIRECTORY_SEPARATOR;
foreach (Files::scandir($dir) as $file) {
if (str_ends_with($file, '.php') && is_file($dir . $file)) {
$class = __NAMESPACE__ . '\\Task\\' . basename($file, '.php');
$actions->add(new $class());
}
}
// Add taks to improve
'improveTaskAdd' => function (Tasks $tasks): void {
$tasks
->add(new Task\CssHeader())
->add(new Task\DcDeprecated())
->add(new Task\DcStore())
->add(new Task\EndOfFile())
->add(new Task\GitShields())
->add(new Task\LicenseFile())
->add(new Task\NewLine())
->add(new Task\PhpCsFixer())
->add(new Task\PhpHeader())
->add(new Task\PhpStan())
->add(new Task\Po2Php())
->add(new Task\Tab())
->add(new Task\Zip())
;
},
]);

View file

@ -97,10 +97,10 @@ class Config extends dcNsProcess
$items = [];
$settings = dcCore::app()->blog->settings->get(My::id());
foreach ($improve->tasks->dump() as $action) {
foreach ($improve->tasks->dump() as $task) {
$items[] = (new Para())->items([
(new Checkbox(['disabled[]', 'disabled_' . $action->id()], $action->isDisabled()))->value($action->id()),
(new Label($action->id(), Label::OUTSIDE_LABEL_AFTER))->class('classic')->for('disabled_' . $action->id()),
(new Checkbox(['disabled[]', 'disabled_' . $task->properties->id], $task->isDisabled()))->value($task->properties->id),
(new Label($task->properties->id, Label::OUTSIDE_LABEL_AFTER))->class('classic')->for('disabled_' . $task->properties->id),
]);
}

View file

@ -14,7 +14,6 @@ declare(strict_types=1);
namespace Dotclear\Plugin\improve;
use ArrayObject;
use dcCore;
use dcLog;
use dcModuleDefine;
@ -32,9 +31,6 @@ class Core
/** @var Tasks $tasks The tasks stack instance */
public readonly Tasks $tasks;
/** @var array<int,string> $disabled Disabled tasks modules */
private $disabled = [];
/** @var array<string,array> $logs Logs by actions modules */
private $logs = [];
@ -55,8 +51,10 @@ class Core
protected function __construct()
{
$this->tasks = new Tasks();
// mark some tasks as disabled (by settings)
$disable = explode(';', (string) dcCore::app()->blog?->settings->get(My::id())->get('disabled'));
foreach($disable as $id) {
foreach ($disable as $id) {
$this->tasks->get($id)?->disable();
}
}
@ -153,26 +151,26 @@ class Core
return $lines;
}
public function fixModule(dcModuleDefine $module, array $actions): float
public function fixModule(dcModuleDefine $module, array $tasks): float
{
$time_start = microtime(true);
$workers = [];
foreach ($actions as $action) {
if ($this->tasks->get($action)?->isConfigured()
&& $this->tasks->get($action)?->isDisabled() === false
foreach ($tasks as $id) {
if (!$this->tasks->get($id)?->isDisabled()
&& $this->tasks->get($id)?->isConfigured()
) {
$workers[] = $this->tasks->get($action);
$workers[] = $this->tasks->get($id);
}
}
foreach ($workers as $action) {
foreach ($workers as $task) {
// trace all path and action in logs
$this->logs[My::id()][__('Begin')][] = $action->id();
$this->logs[My::id()][__('Begin')][] = $task->properties->id;
// info: set current module
$action->setModule($module);
$action->setPath(__('Begin'), '', true);
$task->setModule($module);
$task->setPath(__('Begin'), '', true);
// action: open module
$action->openModule();
$task->openModule();
}
if (!$module->get('root_writable') || !is_writable($module->get('root'))) {
throw new Exception(__('Module path is not writable'));
@ -182,61 +180,63 @@ class Core
if (!file_exists($file[0])) {
continue;
}
foreach ($workers as $action) {
foreach ($workers as $task) {
// trace all path and action in logs
$this->logs[My::id()][$file[0]][] = $action->id();
$this->logs[My::id()][$file[0]][] = $task->properties->id;
// info: set current path
$action->setPath($file[0], $file[1], $file[2]);
$task->setPath($file[0], $file[1], $file[2]);
}
if (!$file[2]) {
foreach ($workers as $action) {
foreach ($workers as $task) {
// action: open a directory. full path
$action->openDirectory();
$task->openDirectory();
}
} else {
foreach ($workers as $action) {
foreach ($workers as $task) {
// action: before openning a file. full path, extension
$action->openFile();
$task->openFile();
}
if (in_array($file[1], self::$readfile_extensions)) {
if (false !== ($content = file_get_contents($file[0]))) {
$no_content = empty($content);
foreach ($workers as $action) {
foreach ($workers as $task) {
// action: read a file content. full path, extension, content
$action->readFile($content);
$task->readFile($content);
if (empty($content) && !$no_content) {
throw new Exception(sprintf(
__('File content has been removed: %s by %s'),
$file[0],
$action->name()
$task->properties->name
));
}
}
Files::putContent($file[0], $content);
}
foreach ($workers as $action) {
foreach ($workers as $task) {
// action: after closing a file. full path, extension
$action->closeFile();
$task->closeFile();
}
}
}
}
foreach ($workers as $action) {
foreach ($workers as $task) {
// trace all path and action in logs
$this->logs[My::id()][__('End')][] = $action->id();
$this->logs[My::id()][__('End')][] = $task->properties->id;
// info: set current module
$action->setPath(__('End'), '', true);
$task->setPath(__('End'), '', true);
// action: close module
$action->closeModule();
$task->closeModule();
}
// info: get acions reports
foreach ($workers as $action) {
$this->logs[$action->id()] = $action->getLogs();
foreach ($workers as $task) {
$logs = [];
foreach ($this->has_log as $type => $v) {
if ($action->hasLog($type)) {
if (!$task->{$type}->empty()) {
$logs[$type] = $task->{$type}->dump();
$this->has_log[$type] = true;
}
}
$this->logs[$task->properties->id] = $logs;
}
return round(microtime(true) - $time_start, 5);

View file

@ -42,14 +42,14 @@ use Exception;
*/
class Manage extends dcNsProcess
{
/** @var Core $improve improve core instance */
private static $improve = null;
/** @var string $type Current module(s) type */
private static $type = 'plugin';
private static string $type = 'plugin';
/** @var string $module Current module id */
private static $module = '-';
/** @var Action|null $action Current action module */
private static $action = null;
private static string $module = '-';
/** @var null|Task $task Current action module */
private static ?Task $task = null;
public static function init(): bool
{
@ -61,6 +61,212 @@ class Manage extends dcNsProcess
return static::$init;
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
self::$type = self::getType();
self::$module = self::getModule();
self::$task = self::getTask();
$log_id = '';
$done = self::setPreferences();
if (!empty($_POST['fix'])) {
if (empty($_POST['actions'])) {
dcAdminNotices::addWarningNotice(__('No action selected'));
} elseif (self::$module == '-') {
dcAdminNotices::addWarningNotice(__('No module selected'));
} else {
try {
$time = Core::instance()->fixModule(
self::$type == 'plugin' ? dcCore::app()->plugins->getDefine(self::$module) : dcCore::app()->themes->getDefine(self::$module),
$_POST['actions']
);
$log_id = Core::instance()->writeLogs();
dcCore::app()->blog?->triggerBlog();
if (Core::instance()->hasLog('error')) {
$notice = ['type' => dcAdminNotices::NOTICE_ERROR, 'msg' => __('Fix of "%s" complete in %s secondes with errors')];
} elseif (Core::instance()->hasLog('warning')) {
$notice = ['type' => dcAdminNotices::NOTICE_WARNING, 'msg' => __('Fix of "%s" complete in %s secondes with warnings')];
} elseif (Core::instance()->hasLog('success')) {
$notice = ['type' => dcAdminNotices::NOTICE_SUCCESS, 'msg' => __('Fix of "%s" complete in %s secondes')];
} else {
$notice = ['type' => dcAdminNotices::NOTICE_SUCCESS, 'msg' => __('Fix of "%s" complete in %s secondes without messages')];
}
dcAdminNotices::addNotice($notice['type'], sprintf($notice['msg'], self::$module, $time));
$done = true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
$done = false;
}
}
}
if ($done) {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), ['type' => self::$type, 'module' => self::$module, 'upd' => $log_id]);
}
return true;
}
public static function render(): void
{
if (!static::$init) {
return;
}
dcPage::openModule(
My::name(),
dcPage::jsModuleLoad(My::id() . '/js/index.js') .
(self::$task === null ? '' : self::$task->header())
);
echo
dcPage::breadcrumb([
__('Plugins') => '',
My::name() => '',
empty($_REQUEST['config']) ? (self::$type == 'theme' ? __('Themes actions') : __('Plugins actions')) : __('Configure module') => '',
]) .
dcPage::notices();
if (empty($_REQUEST['config'])) {
self::displayActions();
} else {
self::displayConfigurator();
}
dcPage::closeModule();
}
private static function displayConfigurator(): void
{
$back_url = $_REQUEST['redir'] ?? dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type]);
if (null === self::$task) {
echo '
<p class="warning">' . __('Unknow module') . '</p>
<p><a class="back" href="' . $back_url . '">' . __('Back') . '</a></p>';
} else {
$redir = $_REQUEST['redir'] ?? dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type, 'config' => self::$task->properties->id]);
$res = self::$task->configure($redir);
echo '
<h3>' . sprintf(__('Configure module "%s"'), self::$task->properties->name) . '</h3>
<p><a class="back" href="' . $back_url . '">' . __('Back') . '</a></p>
<h4>' . Html::escapeHTML(self::$task->properties->description) . '</h4>' .
(new Form('form-actions'))->method('post')->action(dcCore::app()->adminurl?->get('admin.plugin.' . My::id()))->fields([
empty($res) ? (new Text('p', __('Nothing to configure')))->class('message') : (new Text('', $res)),
(new Para())->class('clear')->items([
(new Submit(['save']))->value(__('Save')),
(new Hidden('type', self::$type)),
(new Hidden('config', self::$task->properties->id)),
(new Hidden('redir', $redir)),
dcCore::app()->formNonce(false),
]),
])->render();
}
}
private static function displayActions(): void
{
echo
(new Form('improve_menu'))->method('get')->action(dcCore::app()->adminurl?->get('admin.plugin.' . My::id()))->fields([
(new Para())->class('anchor-nav')->items([
(new Label(__('Goto:'), Label::OUTSIDE_LABEL_BEFORE))->for('type')->class('classic'),
(new Select('type'))->default(self::$type)->items([__('Plugins') => 'plugin', __('Themes') => 'theme']),
(new Submit('simenu'))->value(__('Save')),
(new Hidden('p', My::id())),
]),
])->render();
$combo_modules = self::comboModules();
if (count($combo_modules) == 1) {
echo '<p class="message">' . __('No module to manage') . '</p>';
} else {
echo '<p /><form action="' . dcCore::app()->adminurl?->get('admin.plugin.' . My::id()) . '" method="post" id="form-actions">' .
'<table><caption>' . __('List of available tasks') . '</caption><thead><tr>' .
'<th colspan="2" class="first">' . __('Task') . '</td>' .
'<th scope="col">' . __('Description') . '</td>' .
'<th scope="col">' . __('Configuration') . '</td>' .
(DC_DEBUG ? '<th scope="col">' . __('Priority') . '</td>' : '') . /* @phpstan-ignore-line */
'</tr></thead><tbody>';
foreach (Core::instance()->tasks->dump() as $task) {
if ($task->isDisabled() || !in_array(self::$type, $task->properties->types)) {
continue;
}
echo
'<tr class="line' . ($task->isConfigured() ? '' : ' offline') . '">' .
'<td class="minimal">' .
(new Checkbox(
['actions[]', 'action_' . $task->properties->id],
in_array($task->properties->id, self::getPreference()) && $task->isConfigured()
))->value($task->properties->id)->disabled(!$task->isConfigured())->render() .
'</td>' .
'<td class="minimal nowrap">' .
(new Label(Html::escapeHTML($task->properties->name), Label::OUTSIDE_LABEL_AFTER))->for('action_' . $task->properties->id)->class('classic')->render() .
'</td>' .
'<td class="maximal">' . $task->properties->description . '</td>' .
'<td class="minimal nowrap modules">' . (
false === $task->properties->configurator ? '' :
'<a class="module-config" href="' . dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type, 'config' => $task->properties->id]) .
'" title="' . sprintf(__("Configure action '%s'"), $task->properties->name) . '">' . __('Configure') . '</a>'
) . '</td>' .
(DC_DEBUG ? '<td class="minimal"><span class="debug">' . $task->properties->priority . '</span></td>' : '') . /* @phpstan-ignore-line */
'</tr>';
}
echo '</tbody></table>' .
(new Div())->class('two-cols')->items([
(new Para())->class('col left')->items([
(new Checkbox('save_preferences', !empty($_POST['save_preferences'])))->value(1),
(new Label(__('Save fields selection as preference'), Label::OUTSIDE_LABEL_AFTER))->for('save_preferences')->class('classic'),
]),
(new Para())->class('col right')->items([
(new Label(__('Select a module:'), Label::OUTSIDE_LABEL_BEFORE))->for('module')->class('classic'),
(new Select('module'))->default(self::$module)->items($combo_modules),
(new Submit('fix'))->value(__('Fix it')),
(new Hidden(['type'], self::$type)),
dcCore::app()->formNonce(false),
]),
])->render() .
'<br class="clear" />
</form>';
if (!empty($_REQUEST['upd']) && !dcCore::app()->blog?->settings->get(My::id())->get('nodetails')) {
$logs = Core::instance()->parseLogs((int) $_REQUEST['upd']);
if (!empty($logs)) {
echo '<div class="fieldset"><h4>' . __('Details') . '</h4>';
foreach ($logs as $path => $types) {
echo '<h5>' . $path . '</h5>';
foreach ($types as $type => $tools) {
echo '<div class="' . $type . '"><ul>';
foreach ($tools as $tool => $msgs) {
$a = Core::instance()->tasks->get($tool);
if (null !== $a) {
echo '<li>' . $a->properties->name . '<ul>';
foreach ($msgs as $msg) {
echo '<li>' . $msg . '</li>';
}
}
echo '</ul></li>';
}
echo '</ul></div>';
}
echo '';
}
echo '</div>';
}
}
}
}
private static function getType(): string
{
return $_REQUEST['type'] ?? 'plugin';
@ -76,9 +282,9 @@ class Manage extends dcNsProcess
return $module;
}
private static function getAction(): ?Action
private static function getTask(): ?Task
{
return empty($_REQUEST['config']) ? null : self::$improve->tasks->get($_REQUEST['config']);
return empty($_REQUEST['config']) ? null : Core::instance()->tasks->get($_REQUEST['config']);
}
private static function getPreference(bool $all = false): array
@ -105,9 +311,9 @@ class Manage extends dcNsProcess
$preferences = self::getPreference(true);
$preferences[self::$type] = [];
if (!empty($_POST['actions'])) {
foreach (self::$improve->tasks->dump() as $action) {
if (!$action->isDisabled() && in_array(self::$type, $action->types()) && in_array($action->id(), $_POST['actions'])) {
$preferences[self::$type][] = $action->id();
foreach (Core::instance()->tasks->dump() as $task) {
if (!$task->isDisabled() && in_array(self::$type, $task->properties->types) && in_array($task->properties->id, $_POST['actions'])) {
$preferences[self::$type][] = $task->properties->id;
}
}
}
@ -151,211 +357,4 @@ class Manage extends dcNsProcess
return array_merge([__('Select a module') => '-'], $combo_modules);
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
self::$improve = Core::instance();
self::$type = self::getType();
self::$module = self::getModule();
self::$action = self::getAction();
$log_id = '';
$done = self::setPreferences();
if (!empty($_POST['fix'])) {
if (empty($_POST['actions'])) {
dcAdminNotices::addWarningNotice(__('No action selected'));
} elseif (self::$module == '-') {
dcAdminNotices::addWarningNotice(__('No module selected'));
} else {
try {
$time = self::$improve->fixModule(
self::$type == 'plugin' ? dcCore::app()->plugins->getDefine(self::$module) : dcCore::app()->themes->getDefine(self::$module),
$_POST['actions']
);
$log_id = self::$improve->writeLogs();
dcCore::app()->blog?->triggerBlog();
if (self::$improve->hasLog('error')) {
$notice = ['type' => dcAdminNotices::NOTICE_ERROR, 'msg' => __('Fix of "%s" complete in %s secondes with errors')];
} elseif (self::$improve->hasLog('warning')) {
$notice = ['type' => dcAdminNotices::NOTICE_WARNING, 'msg' => __('Fix of "%s" complete in %s secondes with warnings')];
} elseif (self::$improve->hasLog('success')) {
$notice = ['type' => dcAdminNotices::NOTICE_SUCCESS, 'msg' => __('Fix of "%s" complete in %s secondes')];
} else {
$notice = ['type' => dcAdminNotices::NOTICE_SUCCESS, 'msg' => __('Fix of "%s" complete in %s secondes without messages')];
}
dcAdminNotices::addNotice($notice['type'], sprintf($notice['msg'], self::$module, $time));
$done = true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
$done = false;
}
}
}
if ($done) {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), ['type' => self::$type, 'module' => self::$module, 'upd' => $log_id]);
}
return true;
}
public static function render(): void
{
if (!static::$init) {
return;
}
dcPage::openModule(
My::name(),
dcPage::jsModuleLoad(My::id() . '/js/index.js') .
(self::$action === null ? '' : self::$action->header())
);
echo
dcPage::breadcrumb([
__('Plugins') => '',
My::name() => '',
empty($_REQUEST['config']) ? (self::$type == 'theme' ? __('Themes actions') : __('Plugins actions')) : __('Configure module') => '',
]) .
dcPage::notices();
if (empty($_REQUEST['config'])) {
self::displayActions();
} else {
self::displayConfigurator();
}
dcPage::closeModule();
}
private static function displayConfigurator(): void
{
$back_url = $_REQUEST['redir'] ?? dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type]);
if (null === self::$action) {
echo '
<p class="warning">' . __('Unknow module') . '</p>
<p><a class="back" href="' . $back_url . '">' . __('Back') . '</a></p>';
} else {
$redir = $_REQUEST['redir'] ?? dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type, 'config' => self::$action->id()]);
$res = self::$action->configure($redir);
echo '
<h3>' . sprintf(__('Configure module "%s"'), self::$action->name()) . '</h3>
<p><a class="back" href="' . $back_url . '">' . __('Back') . '</a></p>
<h4>' . Html::escapeHTML(self::$action->description()) . '</h4>' .
(new Form('form-actions'))->method('post')->action(dcCore::app()->adminurl?->get('admin.plugin.' . My::id()))->fields([
empty($res) ? (new Text('p', __('Nothing to configure')))->class('message') : (new Text('', $res)),
(new Para())->class('clear')->items([
(new Submit(['save']))->value(__('Save')),
(new Hidden('type', self::$type)),
(new Hidden('config', self::$action->id())),
(new Hidden('redir', $redir)),
dcCore::app()->formNonce(false),
]),
])->render();
}
}
private static function displayActions(): void
{
echo
(new Form('improve_menu'))->method('get')->action(dcCore::app()->adminurl?->get('admin.plugin.' . My::id()))->fields([
(new Para())->class('anchor-nav')->items([
(new Label(__('Goto:'), Label::OUTSIDE_LABEL_BEFORE))->for('type')->class('classic'),
(new Select('type'))->default(self::$type)->items([__('Plugins') => 'plugin', __('Themes') => 'theme']),
(new Submit('simenu'))->value(__('Save')),
(new Hidden('p', My::id())),
]),
])->render();
$combo_modules = self::comboModules();
if (count($combo_modules) == 1) {
echo '<p class="message">' . __('No module to manage') . '</p>';
} else {
echo '<form action="' . dcCore::app()->adminurl?->get('admin.plugin.' . My::id()) . '" method="post" id="form-actions">' .
'<table><caption class="hidden">' . __('Actions') . '</caption><thead><tr>' .
'<th colspan="2" class="first">' . __('Action') . '</td>' .
'<th scope="col">' . __('Description') . '</td>' .
'<th scope="col">' . __('Configuration') . '</td>' .
(DC_DEBUG ? '<th scope="col">' . __('Priority') . '</td>' : '') . /* @phpstan-ignore-line */
'</tr></thead><tbody>';
foreach (self::$improve->tasks->dump() as $action) {
if ($action->isDisabled() || !in_array(self::$type, $action->types())) {
continue;
}
echo
'<tr class="line' . ($action->isConfigured() ? '' : ' offline') . '">' .
'<td class="minimal">' .
(new Checkbox(
['actions[]', 'action_' . $action->id()],
in_array($action->id(), self::getPreference()) && $action->isConfigured()
))->value($action->id())->disabled(!$action->isConfigured())->render() .
'</td>' .
'<td class="minimal nowrap">' .
(new Label(Html::escapeHTML($action->name()), Label::OUTSIDE_LABEL_AFTER))->for('action_' . $action->id())->class('classic')->render() .
'</td>' .
'<td class="maximal">' . $action->description() . '</td>' .
'<td class="minimal nowrap modules">' . (
false === $action->configurator() ? '' :
'<a class="module-config" href="' . dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => self::$type, 'config' => $action->id()]) .
'" title="' . sprintf(__("Configure action '%s'"), $action->name()) . '">' . __('Configure') . '</a>'
) . '</td>' .
(DC_DEBUG ? '<td class="minimal"><span class="debug">' . $action->priority() . '</span></td>' : '') . /* @phpstan-ignore-line */
'</tr>';
}
echo '</tbody></table>' .
(new Div())->class('two-cols')->items([
(new Para())->class('col left')->items([
(new Checkbox('save_preferences', !empty($_POST['save_preferences'])))->value(1),
(new Label(__('Save fields selection as preference'), Label::OUTSIDE_LABEL_AFTER))->for('save_preferences')->class('classic'),
]),
(new Para())->class('col right')->items([
(new Label(__('Select a module:'), Label::OUTSIDE_LABEL_BEFORE))->for('module')->class('classic'),
(new Select('module'))->default(self::$module)->items($combo_modules),
(new Submit('fix'))->value(__('Fix it')),
(new Hidden(['type'], self::$type)),
dcCore::app()->formNonce(false),
]),
])->render() .
'<br class="clear" />
</form>';
if (!empty($_REQUEST['upd']) && !dcCore::app()->blog?->settings->get(My::id())->get('nodetails')) {
$logs = self::$improve->parseLogs((int) $_REQUEST['upd']);
if (!empty($logs)) {
echo '<div class="fieldset"><h4>' . __('Details') . '</h4>';
foreach ($logs as $path => $types) {
echo '<h5>' . $path . '</h5>';
foreach ($types as $type => $tools) {
echo '<div class="' . $type . '"><ul>';
foreach ($tools as $tool => $msgs) {
$a = self::$improve->tasks->get($tool);
if (null !== $a) {
echo '<li>' . $a->name() . '<ul>';
foreach ($msgs as $msg) {
echo '<li>' . $msg . '</li>';
}
}
echo '</ul></li>';
}
echo '</ul></div>';
}
echo '';
}
echo '</div>';
}
}
}
}
}

View file

@ -14,101 +14,65 @@ declare(strict_types=1);
namespace Dotclear\Plugin\improve;
use ArrayObject;
use dcCore;
use dcModuleDefine;
use dcPage;
use Dotclear\Helper\Network\Http;
/**
* Improve action class helper
*/
abstract class AbstractTask
abstract class Task
{
/** @var TaskDescriptor Task descriptor instance */
public readonly TaskDescriptor $properties;
/** @var TaskMessages Task success messages instance */
public readonly TaskMessages $success;
/** @var TaskMessages Task warning messages instance */
public readonly TaskMessages $warning;
/** @var TaskMessages Task error messages instance */
public readonly TaskMessages $error;
/** @var TaskSettings Task settings instance */
protected readonly TaskSettings $settings;
/** @var dcModuleDefine Current module */
protected $module;
/** @var string Current full path */
protected $path_full = '';
/** @var string Current file extension */
protected $path_extension = '';
/** @var boolean Current path is directory */
protected $path_is_dir = null;
/** @var string The child class name */
private $class_name = '';
/** @var array<string, array> Messages logs */
private $logs = ['success' => [], 'warning' => [], 'error' => []];
/** @var array<string> Action module settings */
private $settings = [];
/** @var array List of allowed properties */
protected static $allowed_properties = ['id', 'name', 'description', 'priority', 'configurator', 'types'];
protected dcModuleDefine $module;
/** @var bool Is disabled action */
private $disabled = false;
private bool $disabled = false;
/** @var string Module id */
private $id = '';
/** @var string Current full path */
protected string $path_full = '';
/** @var string Module name */
private $name = '';
/** @var string Current file extension */
protected string $path_extension = '';
/** @var string Module description */
private $description = '';
/** @var integer Module id */
private $priority = 500;
/** @var boolean Module has config page */
private $configurator = false;
/** @var array Module supported types */
private $types = ['plugin'];
/** @var null|bool Current path is directory */
protected ?bool $path_is_dir = null;
/**
* Action constructor inits properties and settings of a child class.
*/
final public function __construct()
{
$this->class_name = str_replace(__NAMESPACE__ . '\\Task\\', '', get_called_class());
$this->success = new TaskMessages();
$this->warning = new TaskMessages();
$this->error = new TaskMessages();
$this->properties = $this->getProperties();
$this->settings = new TaskSettings($this->properties->id);
$this->module = new dcModuleDefine('undefined');
$settings = dcCore::app()->blog?->settings->get(My::id())->get('settings_' . $this->class_name);
if (null != $settings) {
$settings = json_decode($settings, true);
}
$this->settings = is_array($settings) ? $settings : [];
$this->init();
// can overload priority by settings
if (1 < ($p = (int) dcCore::app()->blog?->settings->get(My::id())->get('priority_' . $this->class_name))) {
$this->priority = $p;
}
}
/**
* Set action as disabled.
*/
final public function disable()
{
$this->disabled = true;
}
/**
* Check if actio is disabled.
* Get task description.
*
* @return bool True on disabled
* @return TaskDescriptor The task description
*/
final public function isDisabled()
{
return $this->disabled;
}
abstract protected function getProperties(): TaskDescriptor;
/**
* Action initialisation function.
@ -120,135 +84,47 @@ abstract class AbstractTask
*/
abstract protected function init(): bool;
/// @name Properties methods
//@{
/**
* Get a definition property of action class
* Get a setting.
*
* @param string $key a property or setting id
* @param string $key The setting ID
*
* @return mixed Value of property or setting of action.
*/
final public function get(string $key)
{
if (isset($this->settings[$key])) {
return $this->settings[$key];
}
return null;
}
/** Get action module id */
final public function id(): string
{
return $this->id;
}
/** Get action module name */
final public function name(): string
{
return $this->name;
}
/** Get action module description */
final public function description(): string
{
return $this->description;
}
/** Get action module priority */
final public function priority(): int
{
return $this->priority;
}
/** Get action module configuration url if any */
final public function configurator(): bool
{
return $this->configurator;
}
/** Get action module supported types */
final public function types(): array
{
return $this->types;
return $this->settings->get($key);
}
/**
* Set properties of action class
*
* @param array $properties Properties
*
* @return boolean Success
* Set task as disabled.
*/
final protected function setProperties(array $properties): bool
final public function disable()
{
foreach ($properties as $key => $value) {
if (in_array($key, self::$allowed_properties)) {
$this->{$key} = $value;
}
}
return true;
}
//@}
/// @name Settings methods
//@{
/**
* Get a settings of action class
*
* @param string $setting a settings id
*
* @return mixed A setting of action.
*/
final protected function getSetting(string $setting)
{
return $this->settings[$setting] ?? null;
$this->disabled = true;
}
/**
* Set one or more setting of action class
* Check if task is disabled.
*
* @param mixed $settings one or more settings
* @param mixed $value value for a single setting
*
* @return mixed A setting of action.
* @return bool True on disabled
*/
final protected function setSettings($settings, $value = null)
final public function isDisabled()
{
$settings = is_array($settings) ? $settings : [$settings => $value];
foreach ($settings as $k => $v) {
$this->settings[$k] = $v;
}
return true;
return $this->disabled;
}
/**
* Redirection after settings update
* Do HTTP redirection.
*
* This save settings update before redirect.
* Used after settings form validation to save settings.
*
* @param string $url redirect url after settings update
* @param string $url The URL redirection
*/
final protected function redirect(string $url): bool
final protected function redirect(string $url): void
{
if (!is_null(dcCore::app()->blog)) {
dcCore::app()->blog->settings->get(My::id())->put(
'settings_' . $this->class_name,
json_encode($this->settings),
'string',
null,
true,
true
);
dcCore::app()->blog->triggerBlog();
dcPage::addSuccessNotice(__('Configuration successfully updated'));
}
$this->settings->save();
Http::redirect($url);
return true;
}
/**
@ -285,7 +161,6 @@ abstract class AbstractTask
{
return null;
}
//@}
/**
* Set in class var current module definitions.
@ -314,6 +189,10 @@ abstract class AbstractTask
$this->path_extension = $path_extension;
$this->path_is_dir = $path_is_dir;
$this->success->path($path_full);
$this->warning->path($path_full);
$this->error->path($path_full);
return true;
}
@ -374,133 +253,4 @@ abstract class AbstractTask
return null;
}
//@}
/// @name Logs methods
//@{
/**
* Set an action log.
*
* Log must be use every time an action something happen.
*
* @param string $type type of message, can be error, warning, succes
* @param string $message message to log
*
* @return boolean True if message is logged.
*/
final public function setLog(string $type, string $message): bool
{
if (empty($this->path_full) || !array_key_exists($type, $this->logs)) {
return false;
}
$this->logs[$type][$this->path_full][] = $message;
return true;
}
/**
* Check if action class has log of given type.
*
* @param string $type type of message, can be error, warning, succes
*
* @return boolean True if messages exist.
*/
final public function hasLog(string $type): bool
{
return array_key_exists($type, $this->logs) && !empty($this->logs[$type]);
}
/**
* Get action logs.
*
* @param string|null $type type of message, can be error, warning, succes
*
* @return array Arry of given type of log or all if type is null
*/
final public function getLogs($type = null): array
{
if (null === $type) {
return $this->logs;
}
if (empty($this->path_full)
|| !array_key_exists($type, $this->logs)
|| !array_key_exists($this->path_full, $this->logs[$type])
) {
return [];
}
return $this->logs[$type][$this->path_full];
}
/**
* Set a log of type error.
*/
final public function setError(string $message): bool
{
return $this->setLog('error', $message);
}
/**
* Check logs of type error exists.
*/
final public function hasError(): bool
{
return !empty($this->getLogs('error'));
}
/**
* Get logs of type error.
*/
final public function getErrors(): array
{
return $this->getLogs('error');
}
/**
* Set a log of type warning.
*/
final public function setWarning(string $message): bool
{
return $this->setLog('warning', $message);
}
/**
* Check logs of type error warnings.
*/
final public function hasWarning(): bool
{
return !empty($this->getLogs('warning'));
}
/**
* Get logs of type warning.
*/
final public function getWarnings(): array
{
return $this->getLogs('warning');
}
/**
* Set a log of type success.
*/
final public function setSuccess(string $message): bool
{
return $this->setLog('success', $message);
}
/**
* Check logs of type error success.
*/
final public function hasSuccess(): bool
{
return !empty($this->getLogs('success'));
}
/**
* Get logs of type success.
*/
final public function getSuccess(): array
{
return $this->getLogs('success');
}
//@}
}

View file

@ -27,13 +27,16 @@ use Dotclear\Helper\Html\Form\{
Textarea
};
use Dotclear\Helper\Html\Html;
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
use Exception;
/**
* Improve action module php header
*/
class cssheader extends AbstractTask
class CssHeader extends Task
{
/** @var string Exemple of header */
private static $exemple = <<<EOF
@ -73,17 +76,20 @@ class cssheader extends AbstractTask
/** @var string Settings bloc content */
private $bloc_content = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'cssheader',
name: __('CSS header'),
description: __('Add or remove phpdoc header bloc from css file'),
configurator: true,
types: ['plugin', 'theme'],
priority: 340
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'cssheader',
'name' => __('CSS header'),
'description' => __('Add or remove phpdoc header bloc from css file'),
'priority' => 340,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$this->action_bloc = [
__('Do nothing') => 0,
__('Add bloc if it does not exist') => 'create',
@ -92,7 +98,7 @@ class cssheader extends AbstractTask
__('Remove existing bloc header') => 'remove',
];
$bloc_content = $this->getSetting('bloc_content');
$bloc_content = $this->settings->get('bloc_content');
$this->bloc_content = is_string($bloc_content) ? $bloc_content : '';
return true;
@ -100,13 +106,13 @@ class cssheader extends AbstractTask
public function isConfigured(): bool
{
return !empty($this->getSetting('bloc_action'));
return !empty($this->settings->get('bloc_action'));
}
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'bloc_action' => !empty($_POST['bloc_action']) ? $_POST['bloc_action'] : '',
'bloc_content' => !empty($_POST['bloc_content']) ? $_POST['bloc_content'] : '',
'exclude_locales' => !empty($_POST['exclude_locales']),
@ -121,16 +127,16 @@ class cssheader extends AbstractTask
// bloc_action
(new Para())->items([
(new Label(__('Action:'), Label::OUTSIDE_LABEL_BEFORE))->for('bloc_action'),
(new Select('bloc_action'))->default($this->getSetting('bloc_action'))->items($this->action_bloc),
(new Select('bloc_action'))->default($this->settings->get('bloc_action'))->items($this->action_bloc),
]),
// exclude_locales
(new Para())->items([
(new Checkbox('exclude_locales', !empty($this->getSetting('exclude_locales'))))->value(1),
(new Checkbox('exclude_locales', !empty($this->settings->get('exclude_locales'))))->value(1),
(new Label(__('Do not add bloc to files from "locales" and "libs" folder'), Label::OUTSIDE_LABEL_AFTER))->for('exclude_locales')->class('classic'),
]),
// exclude_templates
(new Para())->items([
(new Checkbox('exclude_templates', !empty($this->getSetting('exclude_templates'))))->value(1),
(new Checkbox('exclude_templates', !empty($this->settings->get('exclude_templates'))))->value(1),
(new Label(__('Do not add bloc to files from "tpl" and "default-templates" folder'), Label::OUTSIDE_LABEL_AFTER))->for('exclude_templates')->class('classic'),
]),
]),
@ -157,7 +163,7 @@ class cssheader extends AbstractTask
public function openModule(): ?bool
{
if (is_null(dcCore::app()->auth)) {
$this->setWarning(__('Auth is not set'));
$this->warning->add(__('Auth is not set'));
return null;
}
@ -165,7 +171,7 @@ class cssheader extends AbstractTask
$bloc = trim($this->bloc_content);
if (empty($bloc)) {
$this->setWarning(__('bloc is empty'));
$this->warning->add(__('bloc is empty'));
return null;
}
@ -195,11 +201,11 @@ class cssheader extends AbstractTask
(string) $bloc
)
);
$this->setSuccess(__('Prepare header info'));
$this->success->add(__('Prepare header info'));
return null;
} catch (Exception $e) {
$this->setError(__('Failed to parse bloc'));
$this->error->add(__('Failed to parse bloc'));
return null;
}
@ -209,11 +215,11 @@ class cssheader extends AbstractTask
{
$skipped = $this->stop_scan;
$this->stop_scan = false;
if (!empty($this->getSetting('exclude_locales')) && preg_match('/\/(locales|libs)(\/.*?|)$/', $this->path_full)
|| !empty($this->getSetting('exclude_templates')) && preg_match('/\/(tpl|default-templates)(\/.*?|)$/', $this->path_full)
if (!empty($this->settings->get('exclude_locales')) && preg_match('/\/(locales|libs)(\/.*?|)$/', $this->path_full)
|| !empty($this->settings->get('exclude_templates')) && preg_match('/\/(tpl|default-templates)(\/.*?|)$/', $this->path_full)
) {
if (!$skipped) {
$this->setSuccess(__('Skip directory'));
$this->success->add(__('Skip directory'));
}
$this->stop_scan = true;
}
@ -223,22 +229,22 @@ class cssheader extends AbstractTask
public function readFile(&$content): ?bool
{
if ($this->stop_scan || $this->path_extension != 'css' || $this->hasError()) {
if ($this->stop_scan || $this->path_extension != 'css' || !$this->error->empty()) {
return null;
}
if (empty($this->getSetting('bloc_action'))) {
if (empty($this->settings->get('bloc_action'))) {
return null;
}
$clean = $this->deleteDocBloc($content);
if ($this->getSetting('bloc_action') == 'remove') {
if ($this->settings->get('bloc_action') == 'remove') {
$content = $clean;
return null;
}
if ($content != $clean && $this->getSetting('bloc_action') == 'create') {
if ($content != $clean && $this->settings->get('bloc_action') == 'create') {
return null;
}
if ($content == $clean && $this->getSetting('bloc_action') == 'replace') {
if ($content == $clean && $this->settings->get('bloc_action') == 'replace') {
return null;
}
@ -264,7 +270,7 @@ class cssheader extends AbstractTask
);
if ($count && $res) {
$res = str_replace("\n * \n", "\n *\n", $res);
$this->setSuccess(__('Write new doc bloc content'));
$this->success->add(__('Write new doc bloc content'));
}
return (string) $res;
@ -286,7 +292,7 @@ class cssheader extends AbstractTask
$count
);
if ($count) {
$this->setSuccess(__('Delete old doc bloc content'));
$this->succes->set(__('Delete old doc bloc content'));
}
return (string) $res;

View file

@ -18,35 +18,36 @@ use Dotclear\Helper\File\{
Files,
Path
};
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module Dotclear depreciated
*/
class dcdeprecated extends AbstractTask
class DcDeprecated extends Task
{
/** @var array Deprecated functions [filetype [pattern, deprecated, replacement, version, help link]] */
private $deprecated = ['php' => [], 'js' => []];
protected function init(): bool
protected function getProperties(): TaskDescriptor
{
$this->setProperties([
'id' => 'dcdeprecated',
'name' => __('Dotclear deprecated'),
'description' => __('Search for use of deprecated Dotclear functions'),
'priority' => 520,
'types' => ['plugin', 'theme'],
]);
$this->loadDeprecatedDefinition();
return true;
return new TaskDescriptor(
id: 'dcdeprecated',
name: __('Dotclear deprecated'),
description: __('Search for use of deprecated Dotclear functions'),
configurator: false,
types: ['plugin', 'theme'],
priority: 520
);
}
private function loadDeprecatedDefinition(): void
protected function init(): bool
{
$path = Path::real(__DIR__ . '/dcdeprecated');
if (!$path || !is_dir($path) || !is_readable($path)) {
return;
return false;
}
$files = Files::scandir($path);
@ -62,6 +63,8 @@ class dcdeprecated extends AbstractTask
$this->deprecated['js'] = array_merge($this->deprecated['js'], $tmp['js']);
}
}
return true;
}
public function isConfigured(): bool
@ -76,7 +79,7 @@ class dcdeprecated extends AbstractTask
}
foreach ($this->deprecated[$this->path_extension] as $d) {
if (preg_match('/' . $d[0] . '/i', $content)) {
$this->setWarning(sprintf(__('Possible use of deprecated "%s", you should use "%s" instead since Dotclear %s.'), $d[1], __($d[2]), $d[3]) . (empty($d[4]) ? '' : ' <a href="' . $d['4'] . '">' . __('Help') . '</a> '));
$this->warning->add(sprintf(__('Possible use of deprecated "%s", you should use "%s" instead since Dotclear %s.'), $d[1], __($d[2]), $d[3]) . (empty($d[4]) ? '' : ' <a href="' . $d['4'] . '">' . __('Help') . '</a> '));
}
}

View file

@ -27,29 +27,35 @@ use Dotclear\Helper\Html\Form\{
};
use Dotclear\Helper\Html\XmlTag;
use Dotclear\Helper\Text;
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
taskDescriptor
};
use Exception;
/**
* Improve action module dcstore.xml
*/
class dcstore extends AbstractTask
class DcStore extends Task
{
/** @var string Settings dcstore zip url pattern */
private $pattern = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'dcstore',
name: __('Store file'),
description: __('Re-create dcstore.xml file according to _define.php variables'),
configurator: true,
types: ['plugin', 'theme'],
priority: 420
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'dcstore',
'name' => __('Store file'),
'description' => __('Re-create dcstore.xml file according to _define.php variables'),
'priority' => 420,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$pattern = $this->getSetting('pattern');
$pattern = $this->settings->get('pattern');
$this->pattern = is_string($pattern) ? $pattern : '';
return true;
@ -57,13 +63,13 @@ class dcstore extends AbstractTask
public function isConfigured(): bool
{
return !empty($this->getSetting('pattern'));
return !empty($this->settings->get('pattern'));
}
public function configure($url): ?string
{
if (!empty($_POST['save']) && !empty($_POST['dcstore_pattern'])) {
$this->setSettings('pattern', (string) $_POST['dcstore_pattern']);
$this->settings->set('pattern', (string) $_POST['dcstore_pattern']);
$this->redirect($url);
}
@ -85,7 +91,7 @@ class dcstore extends AbstractTask
public function openModule(): ?bool
{
$content = $this->generateXML();
if ($this->hasError()) {
if (!$this->error->empty()) {
return false;
}
@ -93,9 +99,9 @@ class dcstore extends AbstractTask
try {
Files::putContent($this->module->get('root') . DIRECTORY_SEPARATOR . 'dcstore.xml', $content);
$this->setSuccess(__('Write dcstore.xml file.'));
$this->success->add(__('Write dcstore.xml file.'));
} catch (Exception $e) {
$this->setError(__('Failed to write dcstore.xml file'));
$this->error->add(__('Failed to write dcstore.xml file'));
return false;
}
@ -113,37 +119,37 @@ class dcstore extends AbstractTask
# name
if (empty($this->module->get('name'))) {
$this->setError(__('unknow module name'));
$this->error->add(__('unknow module name'));
}
$rsp->insertNode(new XmlTag('name', $this->module->get('name')));
# version
if (empty($this->module->get('version'))) {
$this->setError(__('unknow module version'));
$this->error->add(__('unknow module version'));
}
$rsp->insertNode(new XmlTag('version', $this->module->get('version')));
# author
if (empty($this->module->get('author'))) {
$this->setError(__('unknow module author'));
$this->error->add(__('unknow module author'));
}
$rsp->insertNode(new XmlTag('author', $this->module->get('author')));
# desc
if (empty($this->module->get('desc'))) {
$this->setError(__('unknow module description'));
$this->error->add(__('unknow module description'));
}
$rsp->insertNode(new XmlTag('desc', $this->module->get('desc')));
# repository
if (empty($this->module->get('repository'))) {
$this->setError(__('no repository set in _define.php'));
$this->error->add(__('no repository set in _define.php'));
}
# file
$file_pattern = $this->parseFilePattern();
if (empty($file_pattern)) {
$this->setError(__('no zip file pattern set in configuration'));
$this->error->add(__('no zip file pattern set in configuration'));
}
$rsp->insertNode(new XmlTag('file', $file_pattern));
@ -161,14 +167,14 @@ class dcstore extends AbstractTask
}
}
if (empty($this->module->get('dc_min'))) {
$this->setWarning(__('no minimum dotclear version'));
$this->warning->add(__('no minimum dotclear version'));
} else {
$rsp->insertNode(new XmlTag('da:dcmin', $this->module->get('dc_min')));
}
# da details
if (empty($this->module->get('details'))) {
$this->setWarning(__('no details URL'));
$this->warning->add(__('no details URL'));
} else {
$rsp->insertNode(new XmlTag('da:details', $this->module->get('details')));
}
@ -183,7 +189,7 @@ class dcstore extends AbstractTask
# da support
if (empty($this->module->get('support'))) {
$this->setWarning(__('no support URL'));
$this->warning->add(__('no support URL'));
} else {
$rsp->insertNode(new XmlTag('da:support', $this->module->get('support')));
}

View file

@ -23,24 +23,30 @@ use Dotclear\Helper\Html\Form\{
Note,
Para
};
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module end of file
*/
class endoffile extends AbstractTask
class EndOfFile extends Task
{
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'endoffile',
name: __('End of files'),
description: __('Remove php tag and empty lines from end of files'),
configurator: true,
types: ['plugin', 'theme'],
priority: 860
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'endoffile',
'name' => __('End of files'),
'description' => __('Remove php tag and empty lines from end of files'),
'priority' => 860,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
return true;
}
@ -52,7 +58,7 @@ class endoffile extends AbstractTask
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings('psr2', !empty($_POST['endoffile_psr2']));
$this->settings->set('psr2', !empty($_POST['endoffile_psr2']));
$this->redirect($url);
}
@ -60,7 +66,7 @@ class endoffile extends AbstractTask
(new Fieldset())->class('fieldset')->legend((new Legend(__('Contents'))))->fields([
// endoffile_psr2
(new Para())->items([
(new Checkbox('endoffile_psr2', !empty($this->getSetting('psr2'))))->value(1),
(new Checkbox('endoffile_psr2', !empty($this->settings->get('psr2'))))->value(1),
(new Label(__('Add a blank line to the end of file'), Label::OUTSIDE_LABEL_AFTER))->for('endoffile_psr2')->class('classic'),
]),
(new Note())->text(__('PSR2 must have a blank line, whereas PSR12 must not.'))->class('form-note'),
@ -77,9 +83,9 @@ class endoffile extends AbstractTask
['/(\s*)(\?>\s*)$/', '/\n+$/'],
'',
$content
) . ($this->getSetting('psr2') ? "\n" : '');
) . ($this->settings->get('psr2') ? "\n" : '');
if ($content != $clean) {
$this->setSuccess(__('Replace end of file'));
$this->success->add(__('Replace end of file'));
$content = $clean;
}

View file

@ -25,12 +25,15 @@ use Dotclear\Helper\Html\Form\{
Note,
Para
};
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module Github shields.io
*/
class gitshields extends AbstractTask
class GitShields extends Task
{
/** @var string Username of git repo */
private $username = '';
@ -60,33 +63,36 @@ class gitshields extends AbstractTask
'license' => '[![License](https://img.shields.io/github/license/%username%/%module%)](https://github.com/%username%/%module%/blob/master/LICENSE)',
];
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'gitshields',
name: __('Shields badges'),
description: __('Add and maintain shields.io badges to the REDAME.md file'),
configurator: true,
types: ['plugin', 'theme'],
priority: 380
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'gitshields',
'name' => __('Shields badges'),
'description' => __('Add and maintain shields.io badges to the REDAME.md file'),
'priority' => 380,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$username = $this->getSetting('username');
$username = $this->settings->get('username');
$this->username = is_string($username) ? $username : '';
$this->dotaddict = (bool) $this->getSetting('dotaddict');
$this->dotaddict = (bool) $this->settings->get('dotaddict');
return true;
}
public function isConfigured(): bool
{
return !empty($this->getSetting('username'));
return !empty($this->settings->get('username'));
}
public function configure($url): ?string
{
if (!empty($_POST['save']) && !empty($_POST['username'])) {
$this->setSettings([
$this->settings->set([
'username' => (string) $_POST['username'],
'dotaddict' => !empty($_POST['dotaddict']),
]);
@ -158,7 +164,7 @@ class gitshields extends AbstractTask
));
}
$this->blocs = $blocs;
$this->setSuccess(__('Prepare custom shield info'));
$this->success->add(__('Prepare custom shield info'));
}
private function getDotclearVersion(): string
@ -192,7 +198,7 @@ class gitshields extends AbstractTask
$count
);
if ($count && $res) {
$this->setSuccess(__('Write new shield bloc'));
$this->success->add(__('Write new shield bloc'));
}
return (string) $res;
@ -208,7 +214,7 @@ class gitshields extends AbstractTask
$count
);
if ($count && $res) {
$this->setSuccess(__('Delete old shield bloc'));
$this->success->add(__('Delete old shield bloc'));
}
return (string) $res;

View file

@ -24,13 +24,16 @@ use Dotclear\Helper\Html\Form\{
Para,
Select
};
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
use Exception;
/**
* Improve action module license file
*/
class licensefile extends AbstractTask
class LicenseFile extends Task
{
/** @var array Possible license filenames */
protected static $license_filenames = [
@ -45,16 +48,20 @@ class licensefile extends AbstractTask
/** @var array Action */
private $action_full = [];
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'license',
name: __('License file'),
description: __('Add or remove full license file to module root'),
configurator: true,
types: ['plugin', 'theme'],
priority: 330
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'license',
'name' => __('License file'),
'description' => __('Add or remove full license file to module root'),
'priority' => 330,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$this->action_version = [
__('no version selected') => '',
__('gpl2 - GNU General Public License v2') => 'gpl2',
@ -82,7 +89,7 @@ class licensefile extends AbstractTask
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'action_version' => !empty($_POST['action_version']) ? $_POST['action_version'] : '',
'action_full' => !empty($_POST['action_full']) ? $_POST['action_full'] : '',
]);
@ -94,12 +101,12 @@ class licensefile extends AbstractTask
// action_version
(new Para())->items([
(new Label(__('License version:'), Label::OUTSIDE_LABEL_BEFORE))->for('action_version'),
(new Select('action_version'))->default($this->getSetting('action_version'))->items($this->action_version),
(new Select('action_version'))->default($this->settings->get('action_version'))->items($this->action_version),
]),
// action_full
(new Para())->items([
(new Label(__('Action on file:'), Label::OUTSIDE_LABEL_BEFORE))->for('action_full'),
(new Select('action_full'))->default($this->getSetting('action_full'))->items($this->action_full),
(new Select('action_full'))->default($this->settings->get('action_full'))->items($this->action_full),
]),
]),
])->render();
@ -107,12 +114,12 @@ class licensefile extends AbstractTask
public function openModule(): ?bool
{
if (in_array($this->getSetting('action_full'), ['remove', 'full','overwrite'])) {
$this->deleteFullLicense(($this->getSetting('action_full') == 'overwrite'));
if (in_array($this->settings->get('action_full'), ['remove', 'full','overwrite'])) {
$this->deleteFullLicense(($this->settings->get('action_full') == 'overwrite'));
}
if (in_array($this->getSetting('action_full'), ['create', 'overwrite', 'full'])) {
if (empty($this->getSetting('action_version'))) {
$this->setWarning(__('No full license type selected'));
if (in_array($this->settings->get('action_full'), ['create', 'overwrite', 'full'])) {
if (empty($this->settings->get('action_version'))) {
$this->warning->add(__('No full license type selected'));
} else {
$this->writeFullLicense();
}
@ -124,16 +131,16 @@ class licensefile extends AbstractTask
private function writeFullLicense(): ?bool
{
try {
$full = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'licensefile' . DIRECTORY_SEPARATOR . $this->getSetting('action_version') . '.full.txt');
$full = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'licensefile' . DIRECTORY_SEPARATOR . $this->settings->get('action_version') . '.full.txt');
if (empty($full)) {
$this->setError(__('Failed to load license content'));
$this->error->add(__('Failed to load license content'));
return null;
}
Files::putContent($this->module->get('root') . DIRECTORY_SEPARATOR . 'LICENSE', str_replace("\r\n", "\n", $full));
$this->setSuccess(__('Write new license file "LICENSE"'));
$this->success->add(__('Write new license file "LICENSE"'));
} catch (Exception $e) {
$this->setError(__('Failed to write new license file'));
$this->error->add(__('Failed to write new license file'));
return null;
}
@ -148,11 +155,11 @@ class licensefile extends AbstractTask
continue;
}
if (!Files::isDeletable($this->module->get('root') . DIRECTORY_SEPARATOR . $file)) {
$this->setWarning(sprintf(__('Old license file is not deletable (%s)'), $file));
$this->warning->add(sprintf(__('Old license file is not deletable (%s)'), $file));
} elseif (!@unlink($this->module->get('root') . DIRECTORY_SEPARATOR . $file)) {
$this->setError(sprintf(__('Failed to delete old license file (%s)'), $file));
$this->error->add(sprintf(__('Failed to delete old license file (%s)'), $file));
} else {
$this->setSuccess(sprintf(__('Delete old license file "%s"'), $file));
$this->success->add(sprintf(__('Delete old license file "%s"'), $file));
}
}

View file

@ -24,45 +24,49 @@ use Dotclear\Helper\Html\Form\{
Para
};
use Dotclear\Plugin\improve\{
AbstractTask,
Core
Task,
Core,
TaskDescriptor
};
/**
* Improve action module new line
*/
class newline extends AbstractTask
class NewLine extends Task
{
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'newline',
name: __('Newlines'),
description: __('Replace bad and repetitive and empty newline by single newline in files'),
configurator: true,
types: ['plugin', 'theme'],
priority: 840
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'newline',
'name' => __('Newlines'),
'description' => __('Replace bad and repetitive and empty newline by single newline in files'),
'priority' => 840,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
return true;
}
public function isConfigured(): bool
{
return !empty($this->getSetting('extensions'));
return !empty($this->settings->get('extensions'));
}
public function configure($url): ?string
{
if (!empty($_POST['save']) && !empty($_POST['newline_extensions'])) {
$this->setSettings(
$this->settings->set(
'extensions',
Core::cleanExtensions($_POST['newline_extensions'])
);
$this->redirect($url);
}
$ext = $this->getSetting('extensions');
$ext = $this->settings->get('extensions');
if (!is_array($ext)) {
$ext = [];
}
@ -81,7 +85,7 @@ class newline extends AbstractTask
public function readFile(string &$content): ?bool
{
$ext = $this->getSetting('extensions');
$ext = $this->settings->get('extensions');
if (!is_array($ext) || !in_array($this->path_extension, $ext)) {
return null;
}
@ -99,7 +103,7 @@ class newline extends AbstractTask
)
);
if ($content != $clean) {
$this->setSuccess(__('Replace bad new lines'));
$this->success->add(__('Replace bad new lines'));
$content = $clean;
}

View file

@ -29,15 +29,16 @@ use Dotclear\Helper\Html\Form\{
};
use Dotclear\Helper\Html\Html;
use Dotclear\Plugin\improve\{
AbstractTask,
My
Task,
My,
TaskDescriptor
};
use Exception;
/**
* Improve action module PHP CS Fixer
*/
class phpcsfixer extends AbstractTask
class PhpCsFixer extends Task
{
/** @var array<int,string> Type of runtime errors */
protected static $errors = [
@ -59,17 +60,20 @@ class phpcsfixer extends AbstractTask
/** @var string Settings PHP executable path */
private $phpexe_path = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'phpcsfixer',
name: __('PHP CS Fixer'),
description: __('Fix PSR coding style using Php CS Fixer'),
configurator: true,
types: ['plugin', 'theme'],
priority: 920
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'phpcsfixer',
'name' => __('PHP CS Fixer'),
'description' => __('Fix PSR coding style using Php CS Fixer'),
'priority' => 920,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$this->getPhpPath();
if (null !== dcCore::app()->auth?->user_prefs) {
@ -98,7 +102,7 @@ class phpcsfixer extends AbstractTask
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'phpexe_path' => !empty($_POST['phpexe_path']) ? $_POST['phpexe_path'] : '',
]);
$this->redirect($url);
@ -142,18 +146,18 @@ class phpcsfixer extends AbstractTask
exec($command, $output, $error);
if (empty($output)) {
if (isset(self::$errors[$error])) {
$this->setError(self::$errors[$error]);
$this->error->add(self::$errors[$error]);
return false;
}
throw new Exception('oops');
}
$this->setSuccess(sprintf('<pre>%s</pre>', implode('<br />', $output)));
$this->success->add(sprintf('<pre>%s</pre>', implode('<br />', $output)));
return true;
} catch (Exception $e) {
$this->setError(__('Failed to run php-cs-fixer'));
$this->error->add(__('Failed to run php-cs-fixer'));
return false;
}
@ -164,7 +168,7 @@ class phpcsfixer extends AbstractTask
*/
private function getPhpPath(): void
{
$phpexe_path = $this->getSetting('phpexe_path');
$phpexe_path = $this->settings->get('phpexe_path');
if (!is_string($phpexe_path)) {
$phpexe_path = '';
}

View file

@ -27,13 +27,16 @@ use Dotclear\Helper\Html\Form\{
Textarea
};
use Dotclear\Helper\Html\Html;
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
use Exception;
/**
* Improve action module php header
*/
class phpheader extends AbstractTask
class PhpHeader extends Task
{
/** @var string Exemple of header */
private static $exemple = <<<EOF
@ -73,17 +76,20 @@ class phpheader extends AbstractTask
/** @var string Settings bloc content */
private $bloc_content = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'phpheader',
name: __('PHP header'),
description: __('Add or remove phpdoc header bloc from php file'),
configurator: true,
types: ['plugin', 'theme'],
priority: 340
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'phpheader',
'name' => __('PHP header'),
'description' => __('Add or remove phpdoc header bloc from php file'),
'priority' => 340,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$this->action_bloc = [
__('Do nothing') => 0,
__('Add bloc if it does not exist') => 'create',
@ -92,7 +98,7 @@ class phpheader extends AbstractTask
__('Remove existing bloc header') => 'remove',
];
$bloc_content = $this->getSetting('bloc_content');
$bloc_content = $this->settings->get('bloc_content');
$this->bloc_content = is_string($bloc_content) ? $bloc_content : '';
return true;
@ -100,13 +106,13 @@ class phpheader extends AbstractTask
public function isConfigured(): bool
{
return !empty($this->getSetting('bloc_action')) || !empty($this->getSetting('remove_old'));
return !empty($this->settings->get('bloc_action')) || !empty($this->settings->get('remove_old'));
}
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'bloc_action' => !empty($_POST['bloc_action']) ? $_POST['bloc_action'] : '',
'bloc_content' => !empty($_POST['bloc_content']) ? $_POST['bloc_content'] : '',
'remove_old' => !empty($_POST['remove_old']),
@ -120,16 +126,16 @@ class phpheader extends AbstractTask
// bloc_action
(new Para())->items([
(new Label(__('Action:'), Label::OUTSIDE_LABEL_BEFORE))->for('bloc_action'),
(new Select('bloc_action'))->default($this->getSetting('bloc_action'))->items($this->action_bloc),
(new Select('bloc_action'))->default($this->settings->get('bloc_action'))->items($this->action_bloc),
]),
// remove_old
(new Para())->items([
(new Checkbox('remove_old', !empty($this->getSetting('remove_old'))))->value(1),
(new Checkbox('remove_old', !empty($this->settings->get('remove_old'))))->value(1),
(new Label(__('Remove old style bloc header (using #)'), Label::OUTSIDE_LABEL_AFTER))->for('remove_old')->class('classic'),
]),
// exclude_locales
(new Para())->items([
(new Checkbox('exclude_locales', !empty($this->getSetting('exclude_locales'))))->value(1),
(new Checkbox('exclude_locales', !empty($this->settings->get('exclude_locales'))))->value(1),
(new Label(__('Do not add bloc to files from "locales" and "libs" folder'), Label::OUTSIDE_LABEL_AFTER))->for('exclude_locales')->class('classic'),
]),
]),
@ -156,7 +162,7 @@ class phpheader extends AbstractTask
public function openModule(): ?bool
{
if (is_null(dcCore::app()->auth)) {
$this->setWarning(__('Auth is not set'));
$this->warning->add(__('Auth is not set'));
return null;
}
@ -164,7 +170,7 @@ class phpheader extends AbstractTask
$bloc = trim($this->bloc_content);
if (empty($bloc)) {
$this->setWarning(__('bloc is empty'));
$this->waring->set(__('bloc is empty'));
return null;
}
@ -194,11 +200,11 @@ class phpheader extends AbstractTask
(string) $bloc
)
);
$this->setSuccess(__('Prepare header info'));
$this->success->add(__('Prepare header info'));
return null;
} catch (Exception $e) {
$this->setError(__('Failed to parse bloc'));
$this->error->add(__('Failed to parse bloc'));
return null;
}
@ -208,9 +214,9 @@ class phpheader extends AbstractTask
{
$skipped = $this->stop_scan;
$this->stop_scan = false;
if (!empty($this->getSetting('exclude_locales')) && preg_match('/\/(locales|libs)(\/.*?|)$/', $this->path_full)) {
if (!empty($this->settings->get('exclude_locales')) && preg_match('/\/(locales|libs)(\/.*?|)$/', $this->path_full)) {
if (!$skipped) {
$this->setSuccess(__('Skip directory'));
$this->success->add(__('Skip directory'));
}
$this->stop_scan = true;
}
@ -220,26 +226,26 @@ class phpheader extends AbstractTask
public function readFile(&$content): ?bool
{
if ($this->stop_scan || $this->path_extension != 'php' || $this->hasError()) {
if ($this->stop_scan || $this->path_extension != 'php' || !$this->error->empty()) {
return null;
}
if (!empty($this->getSetting('remove_old'))) {
if (!empty($this->settings->get('remove_old'))) {
$content = $this->deleteOldBloc($content);
}
if (empty($this->getSetting('bloc_action'))) {
if (empty($this->settings->get('bloc_action'))) {
return null;
}
$clean = $this->deleteDocBloc($content);
if ($this->getSetting('bloc_action') == 'remove') {
if ($this->settings->get('bloc_action') == 'remove') {
$content = $clean;
return null;
}
if ($content != $clean && $this->getSetting('bloc_action') == 'create') {
if ($content != $clean && $this->settings->get('bloc_action') == 'create') {
return null;
}
if ($content == $clean && $this->getSetting('bloc_action') == 'replace') {
if ($content == $clean && $this->settings->get('bloc_action') == 'replace') {
return null;
}
@ -265,7 +271,7 @@ class phpheader extends AbstractTask
);
if ($count && $res) {
$res = str_replace("\n * \n", "\n *\n", $res);
$this->setSuccess(__('Write new doc bloc content'));
$this->success->add(__('Write new doc bloc content'));
}
return (string) $res;
@ -287,7 +293,7 @@ class phpheader extends AbstractTask
$count
);
if ($count) {
$this->setSuccess(__('Delete old doc bloc content'));
$this->success->add(__('Delete old doc bloc content'));
}
return (string) $res;
@ -309,7 +315,7 @@ class phpheader extends AbstractTask
$count
);
if ($count) {
$this->setSuccess(__('Delete old style bloc content'));
$this->success->add(__('Delete old style bloc content'));
}
return (string) $res;

View file

@ -31,15 +31,16 @@ use Dotclear\Helper\Html\Form\{
};
use Dotclear\Helper\Html\Html;
use Dotclear\Plugin\improve\{
AbstractTask,
My
Task,
My,
TaskDescriptor
};
use Exception;
/**
* Improve action module PHPStan
*/
class phpstan extends AbstractTask
class PhpStan extends Task
{
/** @var boolean User pref to use colored synthax */
protected static $user_ui_colorsyntax = false;
@ -56,23 +57,26 @@ class phpstan extends AbstractTask
/** @var string Settings PHP executable path */
private $phpexe_path = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'phpstan',
name: __('PHPStan'),
description: __('Analyse php code using PHPStan'),
configurator: true,
types: ['plugin'],
priority: 910
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'phpstan',
'name' => __('PHPStan'),
'description' => __('Analyse php code using PHPStan'),
'priority' => 910,
'configurator' => true,
'types' => ['plugin'],
]);
$this->getPhpPath();
$run_level = $this->getSetting('run_level');
$run_level = $this->settings->get('run_level');
$this->run_level = is_int($run_level) ? $run_level : 5;
$ignored_vars = $this->getSetting('ignored_vars');
$ignored_vars = $this->settings->get('ignored_vars');
$this->ignored_vars = is_string($ignored_vars) ? $ignored_vars : '';
if (null !== dcCore::app()->auth?->user_prefs) {
@ -101,7 +105,7 @@ class phpstan extends AbstractTask
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'phpexe_path' => (!empty($_POST['phpexe_path']) ? $_POST['phpexe_path'] : ''),
'run_level' => (int) $_POST['run_level'],
'ignored_vars' => (!empty($_POST['ignored_vars']) ? $_POST['ignored_vars'] : ''),
@ -140,19 +144,19 @@ class phpstan extends AbstractTask
)->class('form-note'),
// ignored_default
(new Para())->items([
(new Checkbox('ignored_default', !empty($this->getSetting('ignored_default'))))->value(1),
(new Checkbox('ignored_default', !empty($this->settings->get('ignored_default'))))->value(1),
(new Label(__('Do not use rules from default ignored errors list.'), Label::OUTSIDE_LABEL_AFTER))->for('ignored_default')->class('classic'),
]),
(new Note())->text(__('See ignored errors from configuration file below.'))->class('form-note'),
// split_report
(new Para())->items([
(new Checkbox('split_report', !empty($this->getSetting('split_report'))))->value(1),
(new Checkbox('split_report', !empty($this->settings->get('split_report'))))->value(1),
(new Label(__('Split report by file rather than all in the end.'), Label::OUTSIDE_LABEL_AFTER))->for('split_report')->class('classic'),
]),
(new Note())->text(__('Enable this can cause timeout.'))->class('form-note'),
// clear_cache
(new Para())->items([
(new Checkbox('clear_cache', !empty($this->getSetting('clear_cache'))))->value(1),
(new Checkbox('clear_cache', !empty($this->settings->get('clear_cache'))))->value(1),
(new Label(__('Clear result cache before each analizes.'), Label::OUTSIDE_LABEL_AFTER))->for('clear_cache')->class('classic'),
]),
(new Note())->text(__('Enable this can cause timeout.'))->class('form-note'),
@ -174,7 +178,7 @@ class phpstan extends AbstractTask
public function openModule(): bool
{
if (!$this->writeConf()) {
$this->setError(__('Failed to write phpstan configuration'));
$this->error->add(__('Failed to write phpstan configuration'));
return false;
}
@ -184,27 +188,27 @@ class phpstan extends AbstractTask
public function closeFile(): ?bool
{
if (!$this->getSetting('split_report')
if (!$this->settings->get('split_report')
|| !in_array($this->path_extension, ['php', 'in'])
) {
return null;
}
$clear = $this->getSetting('clear_cache') ? $this->execClear($this->path_full) : true;
$clear = $this->settings->get('clear_cache') ? $this->execClear($this->path_full) : true;
return $clear && $this->execFixer($this->path_full);
}
public function closeModule(): ?bool
{
if ($this->getSetting('split_report')) {
if ($this->settings->get('split_report')) {
return null;
}
if ($this->hasError()) {
if (!$this->error->empty()) {
return false;
}
$clear = $this->getSetting('clear_cache') ? $this->execClear() : true;
$clear = $this->settings->get('clear_cache') ? $this->execClear() : true;
return $clear && $this->execFixer();
}
@ -245,14 +249,14 @@ class phpstan extends AbstractTask
throw new Exception('oops');
}
if (count($output) < 4) {
$this->setSuccess($from_clear ? __('Cache cleared') : __('No errors found'));
$this->success->add($from_clear ? __('Cache cleared') : __('No errors found'));
} else {
$this->setWarning(sprintf('<pre>%s</pre>', implode('<br />', $output)));
$this->warning->add(sprintf('<pre>%s</pre>', implode('<br />', $output)));
}
return true;
} catch (Exception $e) {
$this->setError(__('Failed to run phpstan'));
$this->error->add(__('Failed to run phpstan'));
return false;
}
@ -263,7 +267,7 @@ class phpstan extends AbstractTask
*/
private function getPhpPath(): void
{
$phpexe_path = $this->getSetting('phpexe_path');
$phpexe_path = $this->settings->get('phpexe_path');
if (!is_string($phpexe_path)) {
$phpexe_path = '';
}
@ -279,7 +283,7 @@ class phpstan extends AbstractTask
private function writeConf(): bool
{
$full = $this->getSetting('ignored_default') ? '' : 'full.';
$full = $this->settings->get('ignored_default') ? '' : 'full.';
$content = str_replace(
[
'%LEVEL%',

View file

@ -15,12 +15,15 @@ declare(strict_types=1);
namespace Dotclear\Plugin\improve\Task;
use Dotclear\Helper\L10n;
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module dcstore.xml
*/
class po2php extends AbstractTask
class Po2Php extends Task
{
/** @var string License bloc */
private $license = <<<EOF
@ -32,16 +35,20 @@ class po2php extends AbstractTask
*/
EOF;
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'po2php',
name: __('Translation files'),
description: __('Compile existing translation .po files to fresh .lang.php files'),
configurator: false,
types: ['plugin', 'theme'],
priority: 310
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'po2php',
'name' => __('Translation files'),
'description' => __('Compile existing translation .po files to fresh .lang.php files'),
'priority' => 310,
'types' => ['plugin', 'theme'],
]);
return true;
}
@ -57,9 +64,9 @@ class po2php extends AbstractTask
}
if (L10n::generatePhpFileFromPo(substr($this->path_full, 0, -3), $this->license)) {
$this->setSuccess(__('Compile .po file to .lang.php'));
$this->success->add(__('Compile .po file to .lang.php'));
} else {
$this->setError(__('Failed to compile .po file'));
$this->error->add(__('Failed to compile .po file'));
}
return true;

View file

@ -14,23 +14,30 @@ declare(strict_types=1);
namespace Dotclear\Plugin\improve\Task;
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module tab
*/
class tab extends AbstractTask
class Tab extends Task
{
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'tab',
name: __('Tabulations'),
description: __('Replace tabulation by four space in php files'),
configurator: false,
types: ['plugin', 'theme'],
priority: 820
);
}
protected function init(): bool
{
$this->setProperties([
'id' => 'tab',
'name' => __('Tabulations'),
'description' => __('Replace tabulation by four space in php files'),
'priority' => 820,
'types' => ['plugin', 'theme'],
]);
return true;
}
@ -41,7 +48,7 @@ class tab extends AbstractTask
}
$clean = preg_replace('/(\t)/', ' ', $content);// . "\n";
if ($content != $clean) {
$this->setSuccess(__('Replace tabulation by spaces'));
$this->success->add(__('Replace tabulation by spaces'));
$content = $clean;
}

View file

@ -29,12 +29,15 @@ use Dotclear\Helper\Html\Form\{
Note,
Para
};
use Dotclear\Plugin\improve\AbstractTask;
use Dotclear\Plugin\improve\{
Task,
TaskDescriptor
};
/**
* Improve action module zip
*/
class zip extends AbstractTask
class Zip extends Task
{
/** @var array List of excluded file pattern */
public static $exclude = [
@ -68,26 +71,29 @@ class zip extends AbstractTask
/** @var string Settings Second package filename */
private $secondpack_filename = '';
protected function getProperties(): TaskDescriptor
{
return new TaskDescriptor(
id: 'zip',
name: __('Zip module'),
description: __('Compress module into a ready to install package'),
configurator: true,
types: ['plugin', 'theme'],
priority: 980
);
}
protected function init(): bool
{
require_once implode(DIRECTORY_SEPARATOR, [__DIR__, 'zip', 'Zip.php']);
$this->setProperties([
'id' => 'zip',
'name' => __('Zip module'),
'description' => __('Compress module into a ready to install package'),
'priority' => 980,
'configurator' => true,
'types' => ['plugin', 'theme'],
]);
$pack_excludefiles = $this->getSetting('pack_excludefiles');
$pack_excludefiles = $this->settings->get('pack_excludefiles');
$this->pack_excludefiles = is_string($pack_excludefiles) ? $pack_excludefiles : '';
$pack_filename = $this->getSetting('pack_filename');
$pack_filename = $this->settings->get('pack_filename');
$this->pack_filename = is_string($pack_filename) ? $pack_filename : '';
$secondpack_filename = $this->getSetting('secondpack_filename');
$secondpack_filename = $this->settings->get('secondpack_filename');
$this->secondpack_filename = is_string($secondpack_filename) ? $secondpack_filename : '';
return true;
@ -95,13 +101,13 @@ class zip extends AbstractTask
public function isConfigured(): bool
{
return !empty($this->getSetting('pack_repository')) && !empty($this->getSetting('pack_filename'));
return !empty($this->settings->get('pack_repository')) && !empty($this->settings->get('pack_filename'));
}
public function configure($url): ?string
{
if (!empty($_POST['save'])) {
$this->setSettings([
$this->settings->set([
'pack_repository' => !empty($_POST['pack_repository']) ? $_POST['pack_repository'] : '',
'pack_filename' => !empty($_POST['pack_filename']) ? $_POST['pack_filename'] : '',
'secondpack_filename' => !empty($_POST['secondpack_filename']) ? $_POST['secondpack_filename'] : '',
@ -117,7 +123,7 @@ class zip extends AbstractTask
// pack_repository
(new Para())->items([
(new Label(__('Path to repository:'), Label::OUTSIDE_LABEL_BEFORE))->for('pack_repository'),
(new Input('pack_repository'))->size(65)->maxlenght(255)->value($this->getSetting('pack_repository')),
(new Input('pack_repository'))->size(65)->maxlenght(255)->value($this->settings->get('pack_repository')),
]),
(new Note())->text(sprintf(
__('Preconization: %s'),
@ -129,18 +135,18 @@ class zip extends AbstractTask
// pack_filename
(new Para())->items([
(new Label(__('Name of exported package:'), Label::OUTSIDE_LABEL_BEFORE))->for('pack_filename'),
(new Input('pack_filename'))->size(65)->maxlenght(255)->value($this->getSetting('pack_filename')),
(new Input('pack_filename'))->size(65)->maxlenght(255)->value($this->settings->get('pack_filename')),
]),
(new Note())->text(sprintf(__('Preconization: %s'), '%type%-%id%'))->class('form-note'),
// secondpack_filename
(new Para())->items([
(new Label(__('Name of second exported package:'), Label::OUTSIDE_LABEL_BEFORE))->for('secondpack_filename'),
(new Input('secondpack_filename'))->size(65)->maxlenght(255)->value($this->getSetting('secondpack_filename')),
(new Input('secondpack_filename'))->size(65)->maxlenght(255)->value($this->settings->get('secondpack_filename')),
]),
(new Note())->text(sprintf(__('Preconization: %s'), '%type%-%id%-%version%'))->class('form-note'),
// pack_overwrite
(new Para())->items([
(new Checkbox('pack_overwrite', !empty($this->getSetting('pack_overwrite'))))->value(1),
(new Checkbox('pack_overwrite', !empty($this->settings->get('pack_overwrite'))))->value(1),
(new Label(__('Overwrite existing languages'), Label::OUTSIDE_LABEL_AFTER))->for('pack_overwrite')->class('classic'),
]),
]),
@ -148,12 +154,12 @@ class zip extends AbstractTask
// pack_excludefiles
(new Para())->items([
(new Label(__('Extra files to exclude from package:'), Label::OUTSIDE_LABEL_BEFORE))->for('pack_excludefiles'),
(new Input('pack_excludefiles'))->size(65)->maxlenght(255)->value($this->getSetting('pack_excludefiles')),
(new Input('pack_excludefiles'))->size(65)->maxlenght(255)->value($this->settings->get('pack_excludefiles')),
]),
(new Note())->text(sprintf(__('By default all these files are always removed from packages : %s'), implode(', ', self::$exclude)))->class('form-note'),
// pack_nocomment
(new Para())->items([
(new Checkbox('pack_nocomment', !empty($this->getSetting('pack_nocomment'))))->value(1),
(new Checkbox('pack_nocomment', !empty($this->settings->get('pack_nocomment'))))->value(1),
(new Label(__('Remove comments from files'), Label::OUTSIDE_LABEL_AFTER))->for('pack_nocomment')->class('classic'),
]),
]),
@ -166,15 +172,15 @@ class zip extends AbstractTask
self::$exclude,
explode(',', $this->pack_excludefiles)
);
$this->setSuccess(sprintf(__('Prepare excluded files "%s"'), implode(', ', $exclude)));
if (!empty($this->getSetting('pack_nocomment'))) {
$this->success->add(sprintf(__('Prepare excluded files "%s"'), implode(', ', $exclude)));
if (!empty($this->settings->get('pack_nocomment'))) {
zip\Zip::$remove_comment = true;
$this->setSuccess(__('Prepare comment removal'));
$this->success->add(__('Prepare comment removal'));
}
if (!empty($this->getSetting('pack_filename'))) {
if (!empty($this->settings->get('pack_filename'))) {
$this->zipModule($this->pack_filename, $exclude);
}
if (!empty($this->getSetting('secondpack_filename'))) {
if (!empty($this->settings->get('secondpack_filename'))) {
$this->zipModule($this->secondpack_filename, $exclude);
}
@ -198,14 +204,14 @@ class zip extends AbstractTask
foreach ($parts as $i => $part) {
$parts[$i] = Files::tidyFileName($part);
}
$path = $this->getSetting('pack_repository') . '/' . implode('/', $parts) . '.zip';
if (file_exists($path) && empty($this->getSetting('pack_overwrite'))) {
$this->setWarning(__('Destination filename already exists'));
$path = $this->settings->get('pack_repository') . '/' . implode('/', $parts) . '.zip';
if (file_exists($path) && empty($this->settings->get('pack_overwrite'))) {
$this->warning->add(__('Destination filename already exists'));
return;
}
if (!is_dir(dirname($path)) || !is_writable(dirname($path))) {
$this->setError(__('Destination path is not writable'));
$this->error->add(__('Destination path is not writable'));
return;
}
@ -229,6 +235,6 @@ class zip extends AbstractTask
$zip->write();
unset($zip);
$this->setSuccess(sprintf(__('Zip module into "%s"'), $path));
$this->success->add(sprintf(__('Zip module into "%s"'), $path));
}
}

55
src/TaskDescriptor.php Normal file
View file

@ -0,0 +1,55 @@
<?php
/**
* @brief improve, 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\improve;
use dcCore;
/**
* Task description.
*/
class TaskDescriptor
{
/** @var string The priority overload settings prefix */
public const PREFIX = 'priority_';
/** @var int $priority The task priority */
public readonly int $priority;
/**
* Constructor sets all properties
*
* @param string $id The task ID
* @param string $name The task translated name
* @param string $description The task short descripton
* @param bool $configurator The task has configuration form
* @param array $types The task supported modules types
* @param int $priority The task default priority
*/
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly string $description,
public readonly array $types,
public readonly bool $configurator,
int $priority = 500
) {
// Overload task priority from settings
if (!is_null(dcCore::app()->blog) && 1 < ($p = (int) dcCore::app()->blog?->settings->get(My::id())->get(self::PREFIX . $this->id))) {
$this->priority = $p;
} else {
$this->priority = abs($priority);
}
}
}

79
src/TaskMessages.php Normal file
View file

@ -0,0 +1,79 @@
<?php
/**
* @brief improve, 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\improve;
/**
* Task messages group.
*/
class TaskMessages
{
/** @ var array<string,array> $stack The messages by path stack */
private array $stack;
/** @ var string $path The current path */
private string $path = 'root';
/**
* Set current working path.
*
* @param string $path The path
*/
public function path(string $path = 'root'): void
{
$this->path = $path;
}
/**
* Check if there are messages.
*
* return bool True if not empty
*/
public function empty(): bool
{
return empty($this->stack);
}
/**
* Add a message for current path.
*
* @param string $message The message
*/
public function add(string $message): void
{
$this->stack[$this->path][] = $message;
}
/**
* Get a path messages.
*
* @param string The path
*
* @return array The messages
*/
public function get(string $path): array
{
return $this->stack[$path] ?? [];
}
/**
* Get all messages
*
* @return array<string,array> The messages stack
*/
public function dump(): array
{
return $this->stack;
}
}

90
src/TaskSettings.php Normal file
View file

@ -0,0 +1,90 @@
<?php
/**
* @brief improve, 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\improve;
use dcCore;
/**
* Task settings management.
*/
class TaskSettings
{
/** @var string The setting prefix */
public const PREFIX = 'settings_';
/** @ var array<string,mixed> $stack The settings stack */
private array $stack = [];
/**
* Constructor sets settings suffix.
*
* @param string $suffit The settings suffix (ie taks id)
*/
public function __construct(
private string $suffix
) {
if (is_null(dcCore::app()->blog)) {
throw new Exception(__('Blog is not set'));
}
if (null !== ($settings = dcCore::app()->blog->settings->get(My::id())->get(self::PREFIX . $this->suffix))) {
$settings = json_decode($settings, true);
$this->stack = is_array($settings) ? $settings : [];
}
}
/**
* Get a task setting.
*
* @param string $key The setting ID
*
* @return mixed The setting value
*/
public function get(string $key)
{
return $this->stack[$key] ?? null;
}
/**
* Set one or more setting(s).
*
* @param mixed $settings one or more settings
* @param mixed $value value for a single setting
*/
public function set($settings, $value = null): void
{
foreach (is_array($settings) ? $settings : [$settings => $value] as $k => $v) {
$this->stack[$k] = $v;
}
}
/**
* Save settings.
*/
public function save()
{
if (!is_null(dcCore::app()->blog)) {
dcCore::app()->blog->settings->get(My::id())->put(
self::PREFIX . $this->suffix,
json_encode($this->stack),
'string',
null,
true,
true
);
dcCore::app()->blog->triggerBlog();
}
}
}

View file

@ -15,14 +15,13 @@ declare(strict_types=1);
namespace Dotclear\Plugin\improve;
use dcCore;
use Exception;
/**
* The Tasks stack.
*/
class Tasks
{
/** @var array<string,AbstractTask> $stack The tasks stack */
/** @var array<string,Task> $stack The tasks stack */
private array $stack = [];
/**
@ -33,21 +32,21 @@ class Tasks
# --BEHAVIOR-- improveTaskAdd: Tasks
dcCore::app()->callBehavior('improveTaskAdd', $this);
uasort($this->stack, fn ($a, $b) => $a->name() <=> $b->name());
uasort($this->stack, fn ($a, $b) => $a->priority() <=> $b->priority());
uasort($this->stack, fn ($a, $b) => $a->properties->name <=> $b->properties->name);
uasort($this->stack, fn ($a, $b) => $a->properties->priority <=> $b->properties->priority);
}
/**
* Add an task.
*
* @param AbstractTask $task The task instance
* @param Task $task The task instance
*
* @return Tasks Self instance
*/
public function add(AbstractTask $task): Tasks
public function add(Task $task): Tasks
{
if (!isset($this->stack[$task->id()])) {
$this->stack[$task->id()] = $task;
if (!isset($this->stack[$task->properties->id])) {
$this->stack[$task->properties->id] = $task;
}
return $this;
@ -56,7 +55,7 @@ class Tasks
/**
* Get all tasks.
*
* @return array<string,AbstractTask> The tasks stack
* @return array<string,Task> The tasks stack
*/
public function dump(): array
{
@ -68,9 +67,9 @@ class Tasks
*
* @param string $id The task id
*
* @return null|AbstractTask The task
* @return null|Task The task
*/
public function get(string $id): ?AbstractTask
public function get(string $id): ?Task
{
return $this->stack[$id] ?? null;
}